7674824ebeb3463b5e85c93e4209e78b71f3b70e
[sim/o1-interface.git] / ntsimulator / src / ntsimulator-manager / ntsimulator-manager.c
1 /*************************************************************************
2 *
3 * Copyright 2019 highstreet technologies GmbH and others
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
17
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <inttypes.h>
24 #include <limits.h>
25 #include <string.h>
26
27 #include "sysrepo.h"
28 #include "sysrepo/values.h"
29
30 #include "utils.h"
31 #include "simulator-operations.h"
32
33 volatile int exit_application = 0;
34
35 volatile unsigned int simulated_devices_config = 0;
36 volatile unsigned int mounted_devices_config = 0;
37
38
39 static device_stack_t *device_list = NULL;
40
41 controller_t controller_details;
42
43 #define XPATH_MAX_LEN 500
44 #define CONTROLLER_LIST_MAX_LEN 1
45
46 static void
47 print_current_config(sr_session_ctx_t *session, const char *module_name)
48 {
49     sr_val_t *values = NULL;
50     size_t count = 0;
51     int rc = SR_ERR_OK;
52     char xpath[XPATH_MAX_LEN] = {0};
53     snprintf(xpath, XPATH_MAX_LEN, "/%s:*//.", module_name);
54
55     sr_val_t *odl_ip = NULL;
56     sr_val_t *odl_port = NULL;
57     sr_val_t *odl_username = NULL;
58     sr_val_t *odl_password = NULL;
59
60     rc = sr_get_items(session, xpath, &values, &count);
61     if (SR_ERR_OK != rc) {
62         printf("Error by sr_get_items: %s\n", sr_strerror(rc));
63         return;
64     }
65     for (size_t i = 0; i < count; i++){
66
67         sr_print_val(&values[i]);
68
69         if (sr_xpath_node_name_eq(values[i].xpath, "controller-ip"))
70         {
71                 rc = sr_dup_val(&values[i], &odl_ip);
72         }
73         else if (sr_xpath_node_name_eq(values[i].xpath, "controller-port"))
74         {
75                 rc = sr_dup_val(&values[i], &odl_port);
76         }
77         else if (sr_xpath_node_name_eq(values[i].xpath, "controller-username"))
78         {
79                 rc = sr_dup_val(&values[i], &odl_username);
80         }
81         else if (sr_xpath_node_name_eq(values[i].xpath, "controller-password"))
82         {
83                 rc = sr_dup_val(&values[i], &odl_password);
84         }
85     }
86
87     char *ipv6 = strchr(odl_ip->data.string_val, ':');
88     char odl_ip_string[URL_AND_CREDENTIALS_MAX_LEN];
89     if (ipv6 != NULL)
90     {
91         sprintf(odl_ip_string, "[%s]", odl_ip->data.string_val);
92     }
93     else
94     {
95         sprintf(odl_ip_string, "%s", odl_ip->data.string_val);
96     }
97
98
99     //URL used for mounting/unmounting a device; the device name needs to be appended
100    char url[URL_AND_CREDENTIALS_MAX_LEN];
101    sprintf(url, "http://%s:%d/restconf/config/network-topology:network-topology/topology/"
102                  "topology-netconf/node/",
103                  odl_ip_string, odl_port->data.uint32_val);
104
105    char credentials[URL_AND_CREDENTIALS_MAX_LEN];
106    sprintf(credentials, "%s:%s", odl_username->data.string_val, odl_password->data.string_val);
107
108    //URLs used for adding key pair to ODL, for TLS connections
109    char url_for_keystore_add[URL_AND_CREDENTIALS_MAX_LEN];
110    sprintf(url_for_keystore_add, "http://%s:%d/restconf/operations/netconf-keystore:add-keystore-entry",
111                          odl_ip_string, odl_port->data.uint32_val);
112
113    char url_for_private_key_add[URL_AND_CREDENTIALS_MAX_LEN];
114    sprintf(url_for_private_key_add, "http://%s:%d/restconf/operations/netconf-keystore:add-private-key",
115                          odl_ip_string, odl_port->data.uint32_val);
116
117    char url_for_trusted_ca_add[URL_AND_CREDENTIALS_MAX_LEN];
118    sprintf(url_for_trusted_ca_add, "http://%s:%d/restconf/operations/netconf-keystore:add-trusted-certificate",
119                          odl_ip_string, odl_port->data.uint32_val);
120
121    strcpy(controller_details.url, url);
122    strcpy(controller_details.credentials, credentials);
123    strcpy(controller_details.url_for_keystore_add, url_for_keystore_add);
124    strcpy(controller_details.url_for_private_key_add, url_for_private_key_add);
125    strcpy(controller_details.url_for_trusted_ca_add, url_for_trusted_ca_add);
126
127    sr_free_val(odl_ip);
128    sr_free_val(odl_port);
129    sr_free_val(odl_username);
130    sr_free_val(odl_password);
131
132    sr_free_values(values, count);
133 }
134
135 static void clean_current_docker_configuration(void);
136
137 static int simulated_devices_changed(int new_value)
138 {
139         int rc = SR_ERR_OK;
140
141     if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0)
142     {
143         if (new_value != simulated_devices_config)
144         {
145             simulated_devices_config = new_value;
146             rc = send_k8s_scale(new_value);
147             if (rc != SR_ERR_OK)
148             {
149                 printf("Could not send new_scale=%d to k8s cluster.\n", new_value);
150             }
151         }
152         return SR_ERR_OK;
153     }
154
155     if (simulated_devices_config > new_value)
156     {
157         //we are configuring less elements that currently
158         for (int i = 0; i < simulated_devices_config - new_value; ++i)
159         {
160                 rc = stop_device(device_list);
161         }
162     }
163     else if (simulated_devices_config < new_value)
164     {
165         //we are configuring more elements that currently
166         for (int i = 0; i < new_value - simulated_devices_config; ++i)
167         {
168                 rc = start_device(device_list);
169             if (rc != SR_ERR_OK)
170             {
171                 printf("ERROR: Could not start simulated device. Ignoring, trying with the next simulated device, if any...\n");
172             }
173         }
174     }
175
176     simulated_devices_config = new_value;
177
178     return rc;
179 }
180
181 int mounted_devices_changed(sr_session_ctx_t *session, int new_value)
182 {
183         int rc = SR_ERR_OK;
184
185         if (mounted_devices_config > new_value)
186         {
187           //we need have less mounted elements
188           for (int i = 0; i < mounted_devices_config - new_value; ++i)
189           {
190                   printf("Sending unmount device...\n");
191                   rc = unmount_device(device_list, controller_details);
192           }
193         }
194         else if (mounted_devices_config < new_value)
195         {
196           //we are configuring more elements that currently
197           for (int i = 0; i < new_value - mounted_devices_config; ++i)
198           {
199                   printf("Sending mount device...\n");
200                   rc = mount_device(device_list, controller_details);
201           }
202         }
203
204         mounted_devices_config = new_value;
205
206     return rc;
207 }
208
209 static int
210 simulator_config_change_cb(sr_session_ctx_t *session, const char *module_name, sr_notif_event_t event, void *private_ctx)
211 {
212         int rc;
213
214     printf("\n\n ========== CONFIG HAS CHANGED, CURRENT RUNNING CONFIG %s: ==========\n\n", module_name);
215     print_current_config(session, module_name);
216
217     sr_val_t *val = NULL;
218
219     /* get the value from sysrepo, we do not care if the value did not change in our case */
220     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/simulated-devices", &val);
221     if (rc != SR_ERR_OK) {
222         goto sr_error;
223     }
224
225     rc = simulated_devices_changed(val->data.uint32_val);
226     if (rc != SR_ERR_OK) {
227         goto sr_error;
228     }
229
230     sr_free_val(val);
231         val = NULL;
232
233     /* get the value from sysrepo, we do not care if the value did not change in our case */
234     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/mounted-devices", &val);
235     if (rc != SR_ERR_OK) {
236         goto sr_error;
237     }
238
239     if (mounted_devices_config != val->data.uint32_val)
240     {
241         if (val->data.uint32_val > simulated_devices_config)
242         {
243                 printf("Cannot set mount value greater than number of simulated devices.\n");
244                 sr_free_val(val);
245                         val = NULL;
246                 return SR_ERR_OK;
247         }
248
249                 rc = mounted_devices_changed(session, val->data.uint32_val);
250                 if (rc != SR_ERR_OK) {
251                         goto sr_error;
252                 }
253     }
254
255     sr_free_val(val);
256         val = NULL;
257
258     size_t count = 0;
259
260     /* get the value from sysrepo, we do not care if the value did not change in our case */
261     rc = sr_get_items(session, "/network-topology-simulator:simulator-config/notification-config/fault-notification-delay-period", &val, &count);
262     if (rc != SR_ERR_OK) {
263         goto sr_error;
264     }
265
266     rc = notification_delay_period_changed(val, count);
267     if (rc != SR_ERR_OK) {
268         goto sr_error;
269     }
270     sr_free_values(val, count);
271         val = NULL;
272
273     /* get the value from sysrepo, we do not care if the value did not change in our case */
274         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/ves-heartbeat-period", &val);
275         if (rc != SR_ERR_OK) {
276                 goto sr_error;
277         }
278
279         rc = ves_heartbeat_period_changed(val->data.uint32_val);
280         if (rc != SR_ERR_OK) {
281                 goto sr_error;
282         }
283
284         sr_free_val(val);
285         val = NULL;
286
287         /* get the value from sysrepo, we do not care if the value did not change in our case */
288         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-ip", &val);
289         if (rc != SR_ERR_OK) {
290                 goto sr_error;
291         }
292
293         rc = ves_ip_changed(val->data.string_val);
294         if (rc != SR_ERR_OK) {
295                 goto sr_error;
296         }
297
298         sr_free_val(val);
299         val = NULL;
300
301         /* get the value from sysrepo, we do not care if the value did not change in our case */
302         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-port", &val);
303         if (rc != SR_ERR_OK) {
304                 goto sr_error;
305         }
306
307         rc = ves_port_changed(val->data.uint16_val);
308         if (rc != SR_ERR_OK) {
309                 goto sr_error;
310         }
311
312         sr_free_val(val);
313         val = NULL;
314
315         /* get the value from sysrepo, we do not care if the value did not change in our case */
316         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-registration", &val);
317         if (rc != SR_ERR_OK) {
318                 goto sr_error;
319         }
320
321         rc = ves_registration_changed(val->data.bool_val);
322         if (rc != SR_ERR_OK) {
323                 goto sr_error;
324         }
325
326         sr_free_val(val);
327         val = NULL;
328
329         /* get the value from sysrepo, we do not care if the value did not change in our case */
330         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/is-netconf-available", &val);
331         if (rc != SR_ERR_OK) {
332                 goto sr_error;
333         }
334
335         rc = is_netconf_available_changed(val->data.bool_val);
336         if (rc != SR_ERR_OK) {
337                 goto sr_error;
338         }
339
340         sr_free_val(val);
341         val = NULL;
342
343     /* get the value from sysrepo, we do not care if the value did not change in our case */
344     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/is-ves-available", &val);
345     if (rc != SR_ERR_OK) {
346         goto sr_error;
347     }
348
349     rc = is_ves_available_changed(val->data.bool_val);
350     if (rc != SR_ERR_OK) {
351         goto sr_error;
352     }
353
354     sr_free_val(val);
355     val = NULL;
356
357     /* get the value from sysrepo, we do not care if the value did not change in our case */
358     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/netconf-call-home", &val);
359     if (rc != SR_ERR_OK) {
360         goto sr_error;
361     }
362
363     rc = netconf_call_home_changed(val->data.bool_val);
364     if (rc != SR_ERR_OK) {
365         goto sr_error;
366     }
367
368     sr_free_val(val);
369     val = NULL;
370
371     /* get the value from sysrepo, we do not care if the value did not change in our case */
372     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-ip", &val);
373     if (rc != SR_ERR_OK) {
374         goto sr_error;
375     }
376
377     rc = controller_ip_changed(val->data.string_val);
378     if (rc != SR_ERR_OK) {
379         goto sr_error;
380     }
381
382     sr_free_val(val);
383     val = NULL;
384
385     /* get the value from sysrepo, we do not care if the value did not change in our case */
386     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-port", &val);
387     if (rc != SR_ERR_OK) {
388         goto sr_error;
389     }
390
391     rc = controller_port_changed(val->data.uint16_val);
392     if (rc != SR_ERR_OK) {
393         goto sr_error;
394     }
395
396     sr_free_val(val);
397     val = NULL;
398
399     /* get the value from sysrepo, we do not care if the value did not change in our case */
400     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/controller-details/netconf-call-home-port", &val);
401     if (rc != SR_ERR_OK) {
402         goto sr_error;
403     }
404
405     rc = controller_netconf_call_home_port_changed(val->data.uint16_val);
406     if (rc != SR_ERR_OK) {
407         goto sr_error;
408     }
409
410     sr_free_val(val);
411     val = NULL;
412
413     /* get the value from sysrepo, we do not care if the value did not change in our case */
414     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-username", &val);
415     if (rc != SR_ERR_OK) {
416         goto sr_error;
417     }
418
419     rc = controller_username_changed(val->data.string_val);
420     if (rc != SR_ERR_OK) {
421         goto sr_error;
422     }
423
424     sr_free_val(val);
425     val = NULL;
426
427     /* get the value from sysrepo, we do not care if the value did not change in our case */
428     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-password", &val);
429     if (rc != SR_ERR_OK) {
430         goto sr_error;
431     }
432
433     rc = controller_password_changed(val->data.string_val);
434     if (rc != SR_ERR_OK) {
435         goto sr_error;
436     }
437
438     sr_free_val(val);
439     val = NULL;
440
441     return SR_ERR_OK;
442
443 sr_error:
444         printf("NTSimulator config change callback failed: %s.", sr_strerror(rc));
445         if (val != NULL)
446         {
447                 sr_free_val(val);
448                 val = NULL;
449         }
450         return rc;
451 }
452
453 static int
454 simulator_status_cb(const char *xpath, sr_val_t **values, size_t *values_cnt,
455         uint64_t request_id, const char *original_xpath, void *private_ctx)
456 {
457         int rc;
458
459         // printf("\n\n ========== Called simulator_status_cb for xpath: %s ==========\n\n", xpath);
460
461     counterAlarms ves_counter, netconf_counter;
462     rc = compute_notifications_count(&ves_counter, &netconf_counter);
463     if (rc != SR_ERR_OK)
464     {
465         printf("Could not compute the total number of notification count.\n");
466     }
467
468         if (sr_xpath_node_name_eq(xpath, "simulated-devices-list")) 
469     {
470                 sr_val_t *v;
471                 size_t current_num_of_values= 0;
472
473                 if (simulated_devices_config == 0) //nothing to return if no devices are running
474                 {
475                         *values = NULL;
476                         *values_cnt = 0;
477
478                         return SR_ERR_OK;
479                 }
480
481         rc = get_docker_containers_operational_state_curl(device_list);
482         if (rc != SR_ERR_OK)
483         {
484             printf("Could not get the operational state for the devices simulated.\n");
485             return SR_ERR_OPERATION_FAILED;
486         }
487
488                 device_t *current_device = device_list->head;
489
490                 while (current_device != NULL)
491                 {
492             counterAlarms vesCount, netconfCount;
493             rc = getDeviceCounters(current_device->device_id, &vesCount, &netconfCount);
494             if (rc != SR_ERR_OK)
495             {
496                 printf("Could not get Notification Counters for device with uuid=\"%s\"", current_device->device_id);
497             }
498
499             char device_name[200];
500             sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), current_device->device_number);            
501
502                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
503
504                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, device_name, "device-ip");
505                         v[current_num_of_values - 1].type = SR_STRING_T;
506                         v[current_num_of_values - 1].data.string_val = getenv("NTS_IP");
507
508                         for (int i = 0; i < NETCONF_CONNECTIONS_PER_DEVICE; ++i)
509                         {
510                                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
511
512                                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, device_name, "device-port");
513                                 v[current_num_of_values - 1].type = SR_UINT32_T;
514                                 v[current_num_of_values - 1].data.uint32_val = current_device->netconf_port + i;
515                         }
516
517                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
518
519                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, device_name, "is-mounted");
520                         v[current_num_of_values - 1].type = SR_BOOL_T;
521                         v[current_num_of_values - 1].data.bool_val = current_device->is_mounted;
522
523                         char *operational_state = get_docker_container_operational_state(device_list, current_device->device_id);
524
525                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
526
527                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, device_name, "operational-state");
528                         sr_val_build_str_data(&v[current_num_of_values - 1], SR_ENUM_T, "%s", operational_state);
529
530             CREATE_NEW_VALUE(rc, v, current_num_of_values);
531
532             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, device_name, "normal");
533             v[current_num_of_values - 1].type = SR_UINT32_T;
534             v[current_num_of_values - 1].data.uint32_val = vesCount.normal;
535
536             CREATE_NEW_VALUE(rc, v, current_num_of_values);
537
538             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, device_name, "warning");
539             v[current_num_of_values - 1].type = SR_UINT32_T;
540             v[current_num_of_values - 1].data.uint32_val = vesCount.warning;
541
542             CREATE_NEW_VALUE(rc, v, current_num_of_values);
543
544             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, device_name, "minor");
545             v[current_num_of_values - 1].type = SR_UINT32_T;
546             v[current_num_of_values - 1].data.uint32_val = vesCount.minor;
547
548             CREATE_NEW_VALUE(rc, v, current_num_of_values);
549
550             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, device_name, "major");
551             v[current_num_of_values - 1].type = SR_UINT32_T;
552             v[current_num_of_values - 1].data.uint32_val = vesCount.major;
553
554             CREATE_NEW_VALUE(rc, v, current_num_of_values);
555
556             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, device_name, "critical");
557             v[current_num_of_values - 1].type = SR_UINT32_T;
558             v[current_num_of_values - 1].data.uint32_val = vesCount.critical;
559
560             CREATE_NEW_VALUE(rc, v, current_num_of_values);
561
562             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, device_name, "normal");
563             v[current_num_of_values - 1].type = SR_UINT32_T;
564             v[current_num_of_values - 1].data.uint32_val = netconfCount.normal;
565
566             CREATE_NEW_VALUE(rc, v, current_num_of_values);
567
568             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, device_name, "warning");
569             v[current_num_of_values - 1].type = SR_UINT32_T;
570             v[current_num_of_values - 1].data.uint32_val = netconfCount.warning;
571
572             CREATE_NEW_VALUE(rc, v, current_num_of_values);
573
574             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, device_name, "minor");
575             v[current_num_of_values - 1].type = SR_UINT32_T;
576             v[current_num_of_values - 1].data.uint32_val = netconfCount.minor;
577
578             CREATE_NEW_VALUE(rc, v, current_num_of_values);
579
580             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, device_name, "major");
581             v[current_num_of_values - 1].type = SR_UINT32_T;
582             v[current_num_of_values - 1].data.uint32_val = netconfCount.major;
583
584             CREATE_NEW_VALUE(rc, v, current_num_of_values);
585
586             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, device_name, "critical");
587             v[current_num_of_values - 1].type = SR_UINT32_T;
588             v[current_num_of_values - 1].data.uint32_val = netconfCount.critical;
589
590                         current_device = current_device->next;
591                 }
592
593                 //return the values that we have just created
594                 *values = v;
595                 *values_cnt = current_num_of_values;
596          }
597          else if (sr_xpath_node_name_eq(xpath, "simulation-usage-details"))
598          {
599                 float cpu_usage = 0.0, mem_usage = 0.0;
600
601                 char *resource_usage_from_script = get_docker_container_resource_stats();
602
603                 if (resource_usage_from_script != NULL)
604                 {
605                         printf("Received line: %s\n", resource_usage_from_script);
606                         sscanf(resource_usage_from_script, "CPU=%f%%;RAM=%fMiB", &cpu_usage, &mem_usage);
607                         printf("Read cpu=\"%f\" and mem=\"%f\"\n", cpu_usage, mem_usage);
608                         free(resource_usage_from_script);
609                 }
610
611                 sr_val_t *v;
612                 /* convenient functions such as this can be found in sysrepo/values.h */
613                 size_t current_num_of_values= 0;
614
615                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
616
617                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-simulated-devices");
618                 v[current_num_of_values - 1].type = SR_UINT32_T;
619                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_devices(device_list);
620
621                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
622
623                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-mounted-devices");
624                 v[current_num_of_values - 1].type = SR_UINT32_T;
625                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_mounted_devices(device_list);
626
627                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
628
629                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "base-netconf-port");
630                 v[current_num_of_values - 1].type = SR_UINT32_T;
631                 v[current_num_of_values - 1].data.uint32_val = get_netconf_port_base();
632
633                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
634
635                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "cpu-usage");
636                 v[current_num_of_values - 1].type = SR_DECIMAL64_T;
637                 v[current_num_of_values - 1].data.decimal64_val = cpu_usage;
638
639                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
640
641                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "mem-usage");
642                 v[current_num_of_values - 1].type = SR_UINT32_T;
643                 v[current_num_of_values - 1].data.uint32_val = (int)mem_usage;
644
645         CREATE_NEW_VALUE(rc, v, current_num_of_values);
646
647         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "ssh-connections");
648         v[current_num_of_values - 1].type = SR_UINT32_T;
649         v[current_num_of_values - 1].data.uint32_val = getSshConnectionsFromConfigJson();
650
651         CREATE_NEW_VALUE(rc, v, current_num_of_values);
652
653         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "tls-connections");
654         v[current_num_of_values - 1].type = SR_UINT32_T;
655         v[current_num_of_values - 1].data.uint32_val = getTlsConnectionsFromConfigJson();
656
657                 //return the values that we have just created
658                 *values = v;
659                 *values_cnt = current_num_of_values;
660          }
661      else if (sr_xpath_node_name_eq(xpath, "total-ves-notifications"))
662      {
663         sr_val_t *v;
664         /* convenient functions such as this can be found in sysrepo/values.h */
665         size_t current_num_of_values= 0;
666
667         CREATE_NEW_VALUE(rc, v, current_num_of_values);
668
669         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "normal");
670         v[current_num_of_values - 1].type = SR_UINT32_T;
671         v[current_num_of_values - 1].data.uint32_val = ves_counter.normal;
672
673         CREATE_NEW_VALUE(rc, v, current_num_of_values);
674
675         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "warning");
676         v[current_num_of_values - 1].type = SR_UINT32_T;
677         v[current_num_of_values - 1].data.uint32_val = ves_counter.warning;
678
679         CREATE_NEW_VALUE(rc, v, current_num_of_values);
680
681         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "minor");
682         v[current_num_of_values - 1].type = SR_UINT32_T;
683         v[current_num_of_values - 1].data.uint32_val = ves_counter.minor;
684
685         CREATE_NEW_VALUE(rc, v, current_num_of_values);
686
687         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "major");
688         v[current_num_of_values - 1].type = SR_UINT32_T;
689         v[current_num_of_values - 1].data.uint32_val = ves_counter.major;
690
691         CREATE_NEW_VALUE(rc, v, current_num_of_values);
692
693         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "critical");
694         v[current_num_of_values - 1].type = SR_UINT32_T;
695         v[current_num_of_values - 1].data.uint32_val = ves_counter.critical;
696
697         //return the values that we have just created
698         *values = v;
699         *values_cnt = current_num_of_values;
700      }
701      else if (sr_xpath_node_name_eq(xpath, "total-netconf-notifications"))
702      {
703         sr_val_t *v;
704         /* convenient functions such as this can be found in sysrepo/values.h */
705         size_t current_num_of_values= 0;
706
707         CREATE_NEW_VALUE(rc, v, current_num_of_values);
708
709         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "normal");
710         v[current_num_of_values - 1].type = SR_UINT32_T;
711         v[current_num_of_values - 1].data.uint32_val = netconf_counter.normal;
712
713         CREATE_NEW_VALUE(rc, v, current_num_of_values);
714
715         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "warning");
716         v[current_num_of_values - 1].type = SR_UINT32_T;
717         v[current_num_of_values - 1].data.uint32_val = netconf_counter.warning;
718
719         CREATE_NEW_VALUE(rc, v, current_num_of_values);
720
721         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "minor");
722         v[current_num_of_values - 1].type = SR_UINT32_T;
723         v[current_num_of_values - 1].data.uint32_val = netconf_counter.minor;
724
725         CREATE_NEW_VALUE(rc, v, current_num_of_values);
726
727         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "major");
728         v[current_num_of_values - 1].type = SR_UINT32_T;
729         v[current_num_of_values - 1].data.uint32_val = netconf_counter.major;
730
731         CREATE_NEW_VALUE(rc, v, current_num_of_values);
732
733         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "critical");
734         v[current_num_of_values - 1].type = SR_UINT32_T;
735         v[current_num_of_values - 1].data.uint32_val = netconf_counter.critical;
736
737         //return the values that we have just created
738         *values = v;
739         *values_cnt = current_num_of_values;
740      }
741
742     return SR_ERR_OK;
743 }
744
745 static int odl_add_key_pair_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
746       sr_val_t **output, size_t *output_cnt, void *private_ctx)
747 {
748     int rc = SR_ERR_OK;
749     controller_t controller_list[CONTROLLER_LIST_MAX_LEN];
750     int controller_list_size = 0;
751
752     controller_list[0] = controller_details;
753     controller_list_size++;
754
755     for (int i = 0; i < controller_list_size; ++i)
756     {
757         printf("%d iteration: Got back url=%s and credentials=%s\n", i, controller_list[i].url, controller_list[i].credentials);
758     }
759
760     rc = add_key_pair_to_odl(controller_list, controller_list_size);
761     if (rc != SR_ERR_OK)
762     {
763         printf("Failed to add key pair to ODL.\n");
764         return SR_ERR_OPERATION_FAILED;
765     }
766
767     return rc;
768 }
769
770 static int invoke_notification_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
771       sr_val_t **output, size_t *output_cnt, void *private_ctx)
772 {
773     int rc = SR_ERR_OK;
774
775     char *device_name = NULL, *module_name = NULL, *notification_object = NULL;
776     
777     /* print input values */
778     printf("\n\n ========== RECEIVED RPC REQUEST ==========\n\n");
779     printf(">>> RPC Input:\n\n");
780     
781     device_name = strdup(input[0].data.string_val);
782     module_name = strdup(input[1].data.string_val);
783     notification_object = strdup(input[2].data.string_val);
784
785     rc = sr_new_values(1, output);
786     if (SR_ERR_OK != rc) {
787         return rc;
788     }
789
790     /* set 'output/step-count' leaf */
791     rc = sr_val_set_xpath(&(*output)[0], "/network-topology-simulator:invoke-notification/status");
792     if (SR_ERR_OK != rc) {
793         return rc;
794     }
795
796     rc = invoke_device_notification(device_name, module_name, notification_object);
797     
798     if (rc != SR_ERR_OK)
799     {
800         sr_val_build_str_data(&(*output)[0], SR_ENUM_T, "%s", "ERROR");
801     }
802     else
803     {
804         sr_val_build_str_data(&(*output)[0], SR_ENUM_T, "%s", "SUCCESS");
805     }
806     *output_cnt = 1;
807
808     return SR_ERR_OK;
809 }
810
811
812 static void
813 sigint_handler(int signum)
814 {
815     exit_application = 1;
816 }
817
818 int
819 main(int argc, char **argv)
820 {
821     sr_conn_ctx_t *connection = NULL;
822     sr_session_ctx_t *session = NULL;
823     sr_subscription_ctx_t *subscription = NULL;
824     int rc = SR_ERR_OK;
825
826     setbuf(stdout, NULL);
827
828     rc = _init_curl_k8s();
829     if (rc != SR_ERR_OK)
830     {
831         fprintf(stderr, "Could not initialize cURL for K8S connection: %s\n", sr_strerror(rc));
832     }
833
834     device_list = new_device_stack();
835     rc = _init_curl();
836     if (rc != SR_ERR_OK)
837     {
838         fprintf(stderr, "Could not initialize cURL: %s\n", sr_strerror(rc));
839     }
840
841     rc = writeSkeletonConfigFile();
842     if (rc != SR_ERR_OK)
843     {
844         fprintf(stderr, "Could not initialize configuration JSON file: %s\n", sr_strerror(rc));
845     }
846
847     /* connect to sysrepo */
848     rc = sr_connect("network-topology-simulator", SR_CONN_DEFAULT, &connection);
849     if (SR_ERR_OK != rc) {
850         fprintf(stderr, "Error by sr_connect: %s\n", sr_strerror(rc));
851         goto cleanup;
852     }
853
854     /* start session */
855     rc = sr_session_start(connection, SR_DS_STARTUP, SR_SESS_DEFAULT, &session);
856     if (SR_ERR_OK != rc) {
857         fprintf(stderr, "Error by sr_session_start: %s\n", sr_strerror(rc));
858         goto cleanup;
859     }
860
861     // setting the values that come in an ENV variable as defaults - ves-heartbeat-period
862     int vesHeartbeatPeriod = getIntFromString(getenv("VesHeartbeatPeriod"), 0);
863
864     sr_val_t value = { 0 };
865     value.type = SR_UINT32_T;
866     value.data.uint32_val = vesHeartbeatPeriod;
867     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/ves-heartbeat-period", 
868             &value, SR_EDIT_DEFAULT);
869     if (SR_ERR_OK != rc) {
870         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
871         goto cleanup;
872     }
873
874     rc = ves_heartbeat_period_changed(vesHeartbeatPeriod);
875     if (SR_ERR_OK != rc) {
876         printf("Error by ves_heartbeat_period_changed: %s\n", sr_strerror(rc));
877         goto cleanup;
878     }
879
880     // setting the values that come in an ENV variable as defaults - is-netconf-available
881
882     int isNetconfAvailable = 1;
883
884     char *isNetconfAvailablString = getenv("IsNetconfAvailable");
885     if (isNetconfAvailablString != NULL)
886     {
887         if (strcmp(isNetconfAvailablString, "false") == 0)
888         {
889             isNetconfAvailable = 0;
890         }
891     }
892
893     value = (const sr_val_t) { 0 };
894     value.type = SR_BOOL_T;
895     value.data.bool_val = isNetconfAvailable;
896     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-netconf-available", 
897             &value, SR_EDIT_DEFAULT);
898     if (SR_ERR_OK != rc) {
899         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
900         goto cleanup;
901     }
902
903     rc = is_netconf_available_changed(isNetconfAvailable);
904     if (SR_ERR_OK != rc) {
905         printf("Error by is_netconf_available_changed: %s\n", sr_strerror(rc));
906         goto cleanup;
907     }
908
909     // setting the values that come in an ENV variable as defaults - is-ves-available
910
911     int isVesAvailable = 1;
912
913     char *isVesAvailablString = getenv("IsVesAvailable");
914     if (isVesAvailablString != NULL)
915     {
916         if (strcmp(isVesAvailablString, "false") == 0)
917         {
918             isVesAvailable = 0;
919         }
920     }
921
922     value = (const sr_val_t) { 0 };
923     value.type = SR_BOOL_T;
924     value.data.bool_val = isVesAvailable;
925     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-ves-available", 
926             &value, SR_EDIT_DEFAULT);
927     if (SR_ERR_OK != rc) {
928         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
929         goto cleanup;
930     }
931
932     rc = is_ves_available_changed(isVesAvailable);
933     if (SR_ERR_OK != rc) {
934         printf("Error by is_ves_available_changed: %s\n", sr_strerror(rc));
935         goto cleanup;
936     }
937
938     // setting the values that come in an ENV variable as defaults - ves-endpoint-port
939
940     int vesEndpointPort = getIntFromString(getenv("VesEndpointPort"), 8080);
941
942     value = (const sr_val_t) { 0 };
943     value.type = SR_UINT16_T;
944     value.data.uint16_val = vesEndpointPort;
945     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-port", 
946             &value, SR_EDIT_DEFAULT);
947     if (SR_ERR_OK != rc) {
948         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
949         goto cleanup;
950     }
951
952     rc = ves_port_changed(vesEndpointPort);
953     if (SR_ERR_OK != rc) {
954         printf("Error by ves_port_changed: %s\n", sr_strerror(rc));
955         goto cleanup;
956     }
957
958     // setting the values that come in an ENV variable as defaults - ves-endpoint-ip
959
960     value = (const sr_val_t) { 0 };
961     value.type = SR_STRING_T;
962     value.data.string_val = getenv("VesEndpointIp");
963     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-ip", 
964             &value, SR_EDIT_DEFAULT);
965     if (SR_ERR_OK != rc) {
966         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
967         goto cleanup;
968     }
969
970     rc = ves_ip_changed(getenv("VesEndpointIp"));
971     if (SR_ERR_OK != rc) {
972         printf("Error by ves_ip_changed: %s\n", sr_strerror(rc));
973         goto cleanup;
974     }
975
976     // setting the values that come in an ENV variable as defaults - ssh-connections
977
978     int sshConnections = getIntFromString(getenv("SshConnections"), 1);
979
980     rc = ssh_connections_changed(sshConnections);
981     if (SR_ERR_OK != rc) {
982         printf("Error by ssh_connections_changed: %s\n", sr_strerror(rc));
983         goto cleanup;
984     }
985
986     // setting the values that come in an ENV variable as defaults - tls-connections
987
988     int tlsConnections = getIntFromString(getenv("TlsConnections"), 0);
989
990     rc = tls_connections_changed(tlsConnections);
991     if (SR_ERR_OK != rc) {
992         printf("Error by tls_connections_changed: %s\n", sr_strerror(rc));
993         goto cleanup;
994     }
995
996     if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0)
997     {
998         rc = send_k8s_extend_port();
999         if (rc != SR_ERR_OK)
1000         {
1001             printf("Could not send the number of ports to k8s cluster\n");
1002         }
1003     }
1004
1005     // setting the values that come in an ENV variable as defaults - controller-ip
1006
1007     value = (const sr_val_t) { 0 };
1008     value.type = SR_STRING_T;
1009     value.data.string_val = getenv("ControllerIp");
1010     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-ip", 
1011             &value, SR_EDIT_DEFAULT);
1012     if (SR_ERR_OK != rc) {
1013         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1014         goto cleanup;
1015     }
1016
1017     rc = controller_ip_changed(getenv("ControllerIp"));
1018     if (SR_ERR_OK != rc) {
1019         printf("Error by controller_ip_changed: %s\n", sr_strerror(rc));
1020         goto cleanup;
1021     }
1022
1023     // setting the values that come in an ENV variable as defaults - controller-port
1024
1025     int controllerPort = getIntFromString(getenv("ControllerPort"), 8181);
1026
1027     value = (const sr_val_t) { 0 };
1028     value.type = SR_UINT16_T;
1029     value.data.uint16_val = controllerPort;
1030     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-port", 
1031             &value, SR_EDIT_DEFAULT);
1032     if (SR_ERR_OK != rc) {
1033         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1034         goto cleanup;
1035     }
1036
1037     rc = controller_port_changed(controllerPort);
1038     if (SR_ERR_OK != rc) {
1039         printf("Error by controller_port_changed: %s\n", sr_strerror(rc));
1040         goto cleanup;
1041     }
1042
1043     // setting the values that come in an ENV variable as defaults - netconf-call-home-port
1044
1045     int netconfCallHomePort = getIntFromString(getenv("NetconfCallHomePort"), 6666);
1046
1047     value = (const sr_val_t) { 0 };
1048     value.type = SR_UINT16_T;
1049     value.data.uint16_val = netconfCallHomePort;
1050     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/netconf-call-home-port", 
1051             &value, SR_EDIT_DEFAULT);
1052     if (SR_ERR_OK != rc) {
1053         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1054         goto cleanup;
1055     }
1056
1057     rc = controller_netconf_call_home_port_changed(netconfCallHomePort);
1058     if (SR_ERR_OK != rc) {
1059         printf("Error by controller_netconf_call_home_port_changed: %s\n", sr_strerror(rc));
1060         goto cleanup;
1061     }
1062
1063     // setting the values that come in an ENV variable as defaults - controller-username
1064
1065     value = (const sr_val_t) { 0 };
1066     value.type = SR_STRING_T;
1067     value.data.string_val = getenv("ControllerUsername");
1068     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-username", 
1069             &value, SR_EDIT_DEFAULT);
1070     if (SR_ERR_OK != rc) {
1071         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1072         goto cleanup;
1073     }
1074
1075     rc = controller_username_changed(getenv("ControllerUsername"));
1076     if (SR_ERR_OK != rc) {
1077         printf("Error by controller_username_changed: %s\n", sr_strerror(rc));
1078         goto cleanup;
1079     }
1080
1081     // setting the values that come in an ENV variable as defaults - controller-password
1082
1083     value = (const sr_val_t) { 0 };
1084     value.type = SR_STRING_T;
1085     value.data.string_val = getenv("ControllerPassword");
1086     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-password", 
1087             &value, SR_EDIT_DEFAULT);
1088     if (SR_ERR_OK != rc) {
1089         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1090         goto cleanup;
1091     }
1092
1093     rc = controller_password_changed(getenv("ControllerPassword"));
1094     if (SR_ERR_OK != rc) {
1095         printf("Error by controller_password_changed: %s\n", sr_strerror(rc));
1096         goto cleanup;
1097     }
1098
1099     // setting the values that come in an ENV variable as defaults - netconf-call-home
1100
1101     int netconfCallHome = 1;
1102
1103     char *netconfCallHomeString = getenv("NetconfCallHome");
1104     if (netconfCallHomeString != NULL)
1105     {
1106         if (strcmp(netconfCallHomeString, "false") == 0)
1107         {
1108             netconfCallHome = 0;
1109         }
1110     }
1111
1112     value = (const sr_val_t) { 0 };
1113     value.type = SR_BOOL_T;
1114     value.data.bool_val = netconfCallHome;
1115     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/netconf-call-home", 
1116             &value, SR_EDIT_DEFAULT);
1117     if (SR_ERR_OK != rc) {
1118         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1119         goto cleanup;
1120     }
1121
1122     rc = netconf_call_home_changed(netconfCallHome);
1123     if (SR_ERR_OK != rc) {
1124         printf("Error by netconf_call_home_changed: %s\n", sr_strerror(rc));
1125         goto cleanup;
1126     }
1127
1128     //commit the changes that we have done until now
1129     rc = sr_commit(session);
1130     if (SR_ERR_OK != rc) {
1131         printf("Error by sr_commit: %s\n", sr_strerror(rc));
1132         goto cleanup;
1133     }
1134
1135         /* read startup config */
1136         printf("\n\n ========== READING STARTUP CONFIG network-topology-simulator: ==========\n\n");
1137         print_current_config(session, "network-topology-simulator");
1138
1139         /* subscribe for changes in running config */
1140         rc = sr_module_change_subscribe(session, "network-topology-simulator", simulator_config_change_cb, NULL,
1141                         0, SR_SUBSCR_DEFAULT | SR_SUBSCR_APPLY_ONLY, &subscription);
1142         if (SR_ERR_OK != rc) {
1143                 fprintf(stderr, "Error by sr_module_change_subscribe: %s\n", sr_strerror(rc));
1144                 goto cleanup;
1145         }
1146
1147     /* subscribe as state data provider for the ntsimulator state data */
1148     rc = sr_dp_get_items_subscribe(session, "/network-topology-simulator:simulator-status", simulator_status_cb, NULL,
1149                 SR_SUBSCR_CTX_REUSE, &subscription);
1150     if (rc != SR_ERR_OK) {
1151         goto cleanup;
1152     }
1153
1154     rc = notification_delay_period_changed(NULL, 0);
1155     if (rc != SR_ERR_OK) {
1156         printf("Could not write the delay period to file!\n");
1157         goto cleanup;
1158     }
1159
1160     rc = _init_curl_odl();
1161     if (rc != SR_ERR_OK)
1162     {
1163         fprintf(stderr, "Could not initialize cURL for ODL connection: %s\n", sr_strerror(rc));
1164     }
1165
1166     rc = sr_rpc_subscribe(session, "/network-topology-simulator:add-key-pair-to-odl", odl_add_key_pair_cb, (void *)session,
1167                 SR_SUBSCR_CTX_REUSE, &subscription);
1168
1169         printf("\n\n ========== STARTUP CONFIG network-topology-simulator APPLIED AS RUNNING ==========\n\n");
1170
1171     rc = writeSkeletonStatusFile();
1172     if (rc != SR_ERR_OK)
1173     {
1174         fprintf(stderr, "Could not initialize status JSON file: %s\n", sr_strerror(rc));
1175     }
1176
1177     rc = sr_rpc_subscribe(session, "/network-topology-simulator:invoke-notification", invoke_notification_cb,
1178             (void *)session, SR_SUBSCR_DEFAULT, &subscription);
1179     if (SR_ERR_OK != rc) {
1180         fprintf(stderr, "Error by sr_rpc_subscribe: %s\n", sr_strerror(rc));
1181         goto cleanup;
1182     }
1183
1184     rc = pull_docker_image_of_simulated_device();
1185
1186     /* loop until ctrl-c is pressed / SIGINT is received */
1187     signal(SIGINT, sigint_handler);
1188     signal(SIGTERM, sigint_handler);
1189     signal(SIGPIPE, SIG_IGN);
1190
1191     while (!exit_application) {
1192
1193                 sleep(1);  /* or do some more useful work... */
1194     }
1195
1196     printf("Application exit requested, exiting.\n");
1197
1198 cleanup:
1199     if (NULL != subscription) {
1200         sr_unsubscribe(session, subscription);
1201     }
1202     if (NULL != session) {
1203         sr_session_stop(session);
1204     }
1205     if (NULL != connection) {
1206         sr_disconnect(connection);
1207     }
1208
1209     clean_current_docker_configuration();
1210     rc = cleanup_curl();
1211     rc = cleanup_curl_odl();
1212     rc = cleanup_curl_k8s();
1213
1214     return rc;
1215 }
1216
1217 static void clean_current_docker_configuration(void)
1218 {
1219     if (strcmp(getenv("K8S_DEPLOYMENT"), "true"))
1220     {
1221         return;
1222     }
1223
1224         printf("Cleaning docker containers...\n");
1225
1226         if (device_list == NULL)
1227         {
1228                 return;
1229         }
1230
1231         for (int i = 0; i < simulated_devices_config; ++i)
1232         {
1233                 stop_device(device_list);
1234         }
1235
1236         printf("Cleaning completed!\n");
1237 }