Add simulator improvements.
[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                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
500
501                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "device-ip");
502                         v[current_num_of_values - 1].type = SR_STRING_T;
503                         v[current_num_of_values - 1].data.string_val = getenv("NTS_IP");
504
505                         for (int i = 0; i < NETCONF_CONNECTIONS_PER_DEVICE; ++i)
506                         {
507                                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
508
509                                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "device-port");
510                                 v[current_num_of_values - 1].type = SR_UINT32_T;
511                                 v[current_num_of_values - 1].data.uint32_val = current_device->netconf_port + i;
512                         }
513
514                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
515
516                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "is-mounted");
517                         v[current_num_of_values - 1].type = SR_BOOL_T;
518                         v[current_num_of_values - 1].data.bool_val = current_device->is_mounted;
519
520                         char *operational_state = get_docker_container_operational_state(device_list, current_device->device_id);
521
522                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
523
524                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "operational-state");
525                         sr_val_build_str_data(&v[current_num_of_values - 1], SR_ENUM_T, "%s", operational_state);
526
527             CREATE_NEW_VALUE(rc, v, current_num_of_values);
528
529             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, current_device->device_id, "normal");
530             v[current_num_of_values - 1].type = SR_UINT32_T;
531             v[current_num_of_values - 1].data.uint32_val = vesCount.normal;
532
533             CREATE_NEW_VALUE(rc, v, current_num_of_values);
534
535             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, current_device->device_id, "warning");
536             v[current_num_of_values - 1].type = SR_UINT32_T;
537             v[current_num_of_values - 1].data.uint32_val = vesCount.warning;
538
539             CREATE_NEW_VALUE(rc, v, current_num_of_values);
540
541             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, current_device->device_id, "minor");
542             v[current_num_of_values - 1].type = SR_UINT32_T;
543             v[current_num_of_values - 1].data.uint32_val = vesCount.minor;
544
545             CREATE_NEW_VALUE(rc, v, current_num_of_values);
546
547             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, current_device->device_id, "major");
548             v[current_num_of_values - 1].type = SR_UINT32_T;
549             v[current_num_of_values - 1].data.uint32_val = vesCount.major;
550
551             CREATE_NEW_VALUE(rc, v, current_num_of_values);
552
553             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/ves-notifications/%s", xpath, current_device->device_id, "critical");
554             v[current_num_of_values - 1].type = SR_UINT32_T;
555             v[current_num_of_values - 1].data.uint32_val = vesCount.critical;
556
557             CREATE_NEW_VALUE(rc, v, current_num_of_values);
558
559             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, current_device->device_id, "normal");
560             v[current_num_of_values - 1].type = SR_UINT32_T;
561             v[current_num_of_values - 1].data.uint32_val = netconfCount.normal;
562
563             CREATE_NEW_VALUE(rc, v, current_num_of_values);
564
565             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, current_device->device_id, "warning");
566             v[current_num_of_values - 1].type = SR_UINT32_T;
567             v[current_num_of_values - 1].data.uint32_val = netconfCount.warning;
568
569             CREATE_NEW_VALUE(rc, v, current_num_of_values);
570
571             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, current_device->device_id, "minor");
572             v[current_num_of_values - 1].type = SR_UINT32_T;
573             v[current_num_of_values - 1].data.uint32_val = netconfCount.minor;
574
575             CREATE_NEW_VALUE(rc, v, current_num_of_values);
576
577             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, current_device->device_id, "major");
578             v[current_num_of_values - 1].type = SR_UINT32_T;
579             v[current_num_of_values - 1].data.uint32_val = netconfCount.major;
580
581             CREATE_NEW_VALUE(rc, v, current_num_of_values);
582
583             sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/notification-count/netconf-notifications/%s", xpath, current_device->device_id, "critical");
584             v[current_num_of_values - 1].type = SR_UINT32_T;
585             v[current_num_of_values - 1].data.uint32_val = netconfCount.critical;
586
587                         current_device = current_device->next;
588                 }
589
590                 //return the values that we have just created
591                 *values = v;
592                 *values_cnt = current_num_of_values;
593          }
594          else if (sr_xpath_node_name_eq(xpath, "simulation-usage-details"))
595          {
596                 float cpu_usage = 0.0, mem_usage = 0.0;
597
598                 char *resource_usage_from_script = get_docker_container_resource_stats();
599
600                 if (resource_usage_from_script != NULL)
601                 {
602                         printf("Received line: %s\n", resource_usage_from_script);
603                         sscanf(resource_usage_from_script, "CPU=%f%%;RAM=%fMiB", &cpu_usage, &mem_usage);
604                         printf("Read cpu=\"%f\" and mem=\"%f\"\n", cpu_usage, mem_usage);
605                         free(resource_usage_from_script);
606                 }
607
608                 sr_val_t *v;
609                 /* convenient functions such as this can be found in sysrepo/values.h */
610                 size_t current_num_of_values= 0;
611
612                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
613
614                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-simulated-devices");
615                 v[current_num_of_values - 1].type = SR_UINT32_T;
616                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_devices(device_list);
617
618                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
619
620                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-mounted-devices");
621                 v[current_num_of_values - 1].type = SR_UINT32_T;
622                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_mounted_devices(device_list);
623
624                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
625
626                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "base-netconf-port");
627                 v[current_num_of_values - 1].type = SR_UINT32_T;
628                 v[current_num_of_values - 1].data.uint32_val = get_netconf_port_base();
629
630                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
631
632                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "cpu-usage");
633                 v[current_num_of_values - 1].type = SR_DECIMAL64_T;
634                 v[current_num_of_values - 1].data.decimal64_val = cpu_usage;
635
636                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
637
638                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "mem-usage");
639                 v[current_num_of_values - 1].type = SR_UINT32_T;
640                 v[current_num_of_values - 1].data.uint32_val = (int)mem_usage;
641
642         CREATE_NEW_VALUE(rc, v, current_num_of_values);
643
644         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "ssh-connections");
645         v[current_num_of_values - 1].type = SR_UINT32_T;
646         v[current_num_of_values - 1].data.uint32_val = getSshConnectionsFromConfigJson();
647
648         CREATE_NEW_VALUE(rc, v, current_num_of_values);
649
650         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "tls-connections");
651         v[current_num_of_values - 1].type = SR_UINT32_T;
652         v[current_num_of_values - 1].data.uint32_val = getTlsConnectionsFromConfigJson();
653
654                 //return the values that we have just created
655                 *values = v;
656                 *values_cnt = current_num_of_values;
657          }
658      else if (sr_xpath_node_name_eq(xpath, "total-ves-notifications"))
659      {
660         sr_val_t *v;
661         /* convenient functions such as this can be found in sysrepo/values.h */
662         size_t current_num_of_values= 0;
663
664         CREATE_NEW_VALUE(rc, v, current_num_of_values);
665
666         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "normal");
667         v[current_num_of_values - 1].type = SR_UINT32_T;
668         v[current_num_of_values - 1].data.uint32_val = ves_counter.normal;
669
670         CREATE_NEW_VALUE(rc, v, current_num_of_values);
671
672         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "warning");
673         v[current_num_of_values - 1].type = SR_UINT32_T;
674         v[current_num_of_values - 1].data.uint32_val = ves_counter.warning;
675
676         CREATE_NEW_VALUE(rc, v, current_num_of_values);
677
678         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "minor");
679         v[current_num_of_values - 1].type = SR_UINT32_T;
680         v[current_num_of_values - 1].data.uint32_val = ves_counter.minor;
681
682         CREATE_NEW_VALUE(rc, v, current_num_of_values);
683
684         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "major");
685         v[current_num_of_values - 1].type = SR_UINT32_T;
686         v[current_num_of_values - 1].data.uint32_val = ves_counter.major;
687
688         CREATE_NEW_VALUE(rc, v, current_num_of_values);
689
690         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "critical");
691         v[current_num_of_values - 1].type = SR_UINT32_T;
692         v[current_num_of_values - 1].data.uint32_val = ves_counter.critical;
693
694         //return the values that we have just created
695         *values = v;
696         *values_cnt = current_num_of_values;
697      }
698      else if (sr_xpath_node_name_eq(xpath, "total-netconf-notifications"))
699      {
700         sr_val_t *v;
701         /* convenient functions such as this can be found in sysrepo/values.h */
702         size_t current_num_of_values= 0;
703
704         CREATE_NEW_VALUE(rc, v, current_num_of_values);
705
706         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "normal");
707         v[current_num_of_values - 1].type = SR_UINT32_T;
708         v[current_num_of_values - 1].data.uint32_val = netconf_counter.normal;
709
710         CREATE_NEW_VALUE(rc, v, current_num_of_values);
711
712         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "warning");
713         v[current_num_of_values - 1].type = SR_UINT32_T;
714         v[current_num_of_values - 1].data.uint32_val = netconf_counter.warning;
715
716         CREATE_NEW_VALUE(rc, v, current_num_of_values);
717
718         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "minor");
719         v[current_num_of_values - 1].type = SR_UINT32_T;
720         v[current_num_of_values - 1].data.uint32_val = netconf_counter.minor;
721
722         CREATE_NEW_VALUE(rc, v, current_num_of_values);
723
724         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "major");
725         v[current_num_of_values - 1].type = SR_UINT32_T;
726         v[current_num_of_values - 1].data.uint32_val = netconf_counter.major;
727
728         CREATE_NEW_VALUE(rc, v, current_num_of_values);
729
730         sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "critical");
731         v[current_num_of_values - 1].type = SR_UINT32_T;
732         v[current_num_of_values - 1].data.uint32_val = netconf_counter.critical;
733
734         //return the values that we have just created
735         *values = v;
736         *values_cnt = current_num_of_values;
737      }
738
739     return SR_ERR_OK;
740 }
741
742 static int odl_add_key_pair_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
743       sr_val_t **output, size_t *output_cnt, void *private_ctx)
744 {
745     int rc = SR_ERR_OK;
746     controller_t controller_list[CONTROLLER_LIST_MAX_LEN];
747     int controller_list_size = 0;
748
749     controller_list[0] = controller_details;
750     controller_list_size++;
751
752     for (int i = 0; i < controller_list_size; ++i)
753     {
754         printf("%d iteration: Got back url=%s and credentials=%s\n", i, controller_list[i].url, controller_list[i].credentials);
755     }
756
757     rc = add_key_pair_to_odl(controller_list, controller_list_size);
758     if (rc != SR_ERR_OK)
759     {
760         printf("Failed to add key pair to ODL.\n");
761         return SR_ERR_OPERATION_FAILED;
762     }
763
764     return rc;
765 }
766
767 static int invoke_notification_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
768       sr_val_t **output, size_t *output_cnt, void *private_ctx)
769 {
770     int rc = SR_ERR_OK;
771
772     char *device_name = NULL, *module_name = NULL, *notification_object = NULL;
773     
774     /* print input values */
775     printf("\n\n ========== RECEIVED RPC REQUEST ==========\n\n");
776     printf(">>> RPC Input:\n\n");
777     
778     device_name = strdup(input[0].data.string_val);
779     module_name = strdup(input[1].data.string_val);
780     notification_object = strdup(input[2].data.string_val);
781
782     rc = sr_new_values(1, output);
783     if (SR_ERR_OK != rc) {
784         return rc;
785     }
786
787     /* set 'output/step-count' leaf */
788     rc = sr_val_set_xpath(&(*output)[0], "/network-topology-simulator:invoke-notification/status");
789     if (SR_ERR_OK != rc) {
790         return rc;
791     }
792
793     rc = invoke_device_notification(device_name, module_name, notification_object);
794     
795     if (rc != SR_ERR_OK)
796     {
797         sr_val_build_str_data(&(*output)[0], SR_ENUM_T, "%s", "ERROR");
798     }
799     else
800     {
801         sr_val_build_str_data(&(*output)[0], SR_ENUM_T, "%s", "SUCCESS");
802     }
803     *output_cnt = 1;
804
805     return SR_ERR_OK;
806 }
807
808
809 static void
810 sigint_handler(int signum)
811 {
812     exit_application = 1;
813 }
814
815 int
816 main(int argc, char **argv)
817 {
818     sr_conn_ctx_t *connection = NULL;
819     sr_session_ctx_t *session = NULL;
820     sr_subscription_ctx_t *subscription = NULL;
821     int rc = SR_ERR_OK;
822
823     setbuf(stdout, NULL);
824
825     rc = _init_curl_k8s();
826     if (rc != SR_ERR_OK)
827     {
828         fprintf(stderr, "Could not initialize cURL for K8S connection: %s\n", sr_strerror(rc));
829     }
830
831     device_list = new_device_stack();
832     rc = _init_curl();
833     if (rc != SR_ERR_OK)
834     {
835         fprintf(stderr, "Could not initialize cURL: %s\n", sr_strerror(rc));
836     }
837
838     rc = writeSkeletonConfigFile();
839     if (rc != SR_ERR_OK)
840     {
841         fprintf(stderr, "Could not initialize configuration JSON file: %s\n", sr_strerror(rc));
842     }
843
844     /* connect to sysrepo */
845     rc = sr_connect("network-topology-simulator", SR_CONN_DEFAULT, &connection);
846     if (SR_ERR_OK != rc) {
847         fprintf(stderr, "Error by sr_connect: %s\n", sr_strerror(rc));
848         goto cleanup;
849     }
850
851     /* start session */
852     rc = sr_session_start(connection, SR_DS_STARTUP, SR_SESS_DEFAULT, &session);
853     if (SR_ERR_OK != rc) {
854         fprintf(stderr, "Error by sr_session_start: %s\n", sr_strerror(rc));
855         goto cleanup;
856     }
857
858     // setting the values that come in an ENV variable as defaults - ves-heartbeat-period
859     int vesHeartbeatPeriod = getIntFromString(getenv("VesHeartbeatPeriod"), 0);
860
861     sr_val_t value = { 0 };
862     value.type = SR_UINT32_T;
863     value.data.uint32_val = vesHeartbeatPeriod;
864     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/ves-heartbeat-period", 
865             &value, SR_EDIT_DEFAULT);
866     if (SR_ERR_OK != rc) {
867         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
868         goto cleanup;
869     }
870
871     rc = ves_heartbeat_period_changed(vesHeartbeatPeriod);
872     if (SR_ERR_OK != rc) {
873         printf("Error by ves_heartbeat_period_changed: %s\n", sr_strerror(rc));
874         goto cleanup;
875     }
876
877     // setting the values that come in an ENV variable as defaults - is-netconf-available
878
879     int isNetconfAvailable = 1;
880
881     char *isNetconfAvailablString = getenv("IsNetconfAvailable");
882     if (isNetconfAvailablString != NULL)
883     {
884         if (strcmp(isNetconfAvailablString, "false") == 0)
885         {
886             isNetconfAvailable = 0;
887         }
888     }
889
890     value = (const sr_val_t) { 0 };
891     value.type = SR_BOOL_T;
892     value.data.bool_val = isNetconfAvailable;
893     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-netconf-available", 
894             &value, SR_EDIT_DEFAULT);
895     if (SR_ERR_OK != rc) {
896         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
897         goto cleanup;
898     }
899
900     rc = is_netconf_available_changed(isNetconfAvailable);
901     if (SR_ERR_OK != rc) {
902         printf("Error by is_netconf_available_changed: %s\n", sr_strerror(rc));
903         goto cleanup;
904     }
905
906     // setting the values that come in an ENV variable as defaults - is-ves-available
907
908     int isVesAvailable = 1;
909
910     char *isVesAvailablString = getenv("IsVesAvailable");
911     if (isVesAvailablString != NULL)
912     {
913         if (strcmp(isVesAvailablString, "false") == 0)
914         {
915             isVesAvailable = 0;
916         }
917     }
918
919     value = (const sr_val_t) { 0 };
920     value.type = SR_BOOL_T;
921     value.data.bool_val = isVesAvailable;
922     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-ves-available", 
923             &value, SR_EDIT_DEFAULT);
924     if (SR_ERR_OK != rc) {
925         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
926         goto cleanup;
927     }
928
929     rc = is_ves_available_changed(isVesAvailable);
930     if (SR_ERR_OK != rc) {
931         printf("Error by is_ves_available_changed: %s\n", sr_strerror(rc));
932         goto cleanup;
933     }
934
935     // setting the values that come in an ENV variable as defaults - ves-endpoint-port
936
937     int vesEndpointPort = getIntFromString(getenv("VesEndpointPort"), 8080);
938
939     value = (const sr_val_t) { 0 };
940     value.type = SR_UINT16_T;
941     value.data.uint16_val = vesEndpointPort;
942     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-port", 
943             &value, SR_EDIT_DEFAULT);
944     if (SR_ERR_OK != rc) {
945         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
946         goto cleanup;
947     }
948
949     rc = ves_port_changed(vesEndpointPort);
950     if (SR_ERR_OK != rc) {
951         printf("Error by ves_port_changed: %s\n", sr_strerror(rc));
952         goto cleanup;
953     }
954
955     // setting the values that come in an ENV variable as defaults - ves-endpoint-ip
956
957     value = (const sr_val_t) { 0 };
958     value.type = SR_STRING_T;
959     value.data.string_val = getenv("VesEndpointIp");
960     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-ip", 
961             &value, SR_EDIT_DEFAULT);
962     if (SR_ERR_OK != rc) {
963         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
964         goto cleanup;
965     }
966
967     rc = ves_ip_changed(getenv("VesEndpointIp"));
968     if (SR_ERR_OK != rc) {
969         printf("Error by ves_ip_changed: %s\n", sr_strerror(rc));
970         goto cleanup;
971     }
972
973     // setting the values that come in an ENV variable as defaults - ssh-connections
974
975     int sshConnections = getIntFromString(getenv("SshConnections"), 1);
976
977     rc = ssh_connections_changed(sshConnections);
978     if (SR_ERR_OK != rc) {
979         printf("Error by ssh_connections_changed: %s\n", sr_strerror(rc));
980         goto cleanup;
981     }
982
983     // setting the values that come in an ENV variable as defaults - tls-connections
984
985     int tlsConnections = getIntFromString(getenv("TlsConnections"), 0);
986
987     rc = tls_connections_changed(tlsConnections);
988     if (SR_ERR_OK != rc) {
989         printf("Error by tls_connections_changed: %s\n", sr_strerror(rc));
990         goto cleanup;
991     }
992
993     if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0)
994     {
995         rc = send_k8s_extend_port();
996         if (rc != SR_ERR_OK)
997         {
998             printf("Could not send the number of ports to k8s cluster\n");
999         }
1000     }
1001
1002     // setting the values that come in an ENV variable as defaults - controller-ip
1003
1004     value = (const sr_val_t) { 0 };
1005     value.type = SR_STRING_T;
1006     value.data.string_val = getenv("ControllerIp");
1007     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-ip", 
1008             &value, SR_EDIT_DEFAULT);
1009     if (SR_ERR_OK != rc) {
1010         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1011         goto cleanup;
1012     }
1013
1014     rc = controller_ip_changed(getenv("ControllerIp"));
1015     if (SR_ERR_OK != rc) {
1016         printf("Error by controller_ip_changed: %s\n", sr_strerror(rc));
1017         goto cleanup;
1018     }
1019
1020     // setting the values that come in an ENV variable as defaults - controller-port
1021
1022     int controllerPort = getIntFromString(getenv("ControllerPort"), 8181);
1023
1024     value = (const sr_val_t) { 0 };
1025     value.type = SR_UINT16_T;
1026     value.data.uint16_val = controllerPort;
1027     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-port", 
1028             &value, SR_EDIT_DEFAULT);
1029     if (SR_ERR_OK != rc) {
1030         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1031         goto cleanup;
1032     }
1033
1034     rc = controller_port_changed(controllerPort);
1035     if (SR_ERR_OK != rc) {
1036         printf("Error by controller_port_changed: %s\n", sr_strerror(rc));
1037         goto cleanup;
1038     }
1039
1040     // setting the values that come in an ENV variable as defaults - netconf-call-home-port
1041
1042     int netconfCallHomePort = getIntFromString(getenv("NetconfCallHomePort"), 6666);
1043
1044     value = (const sr_val_t) { 0 };
1045     value.type = SR_UINT16_T;
1046     value.data.uint16_val = netconfCallHomePort;
1047     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/netconf-call-home-port", 
1048             &value, SR_EDIT_DEFAULT);
1049     if (SR_ERR_OK != rc) {
1050         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1051         goto cleanup;
1052     }
1053
1054     rc = controller_netconf_call_home_port_changed(netconfCallHomePort);
1055     if (SR_ERR_OK != rc) {
1056         printf("Error by controller_netconf_call_home_port_changed: %s\n", sr_strerror(rc));
1057         goto cleanup;
1058     }
1059
1060     // setting the values that come in an ENV variable as defaults - controller-username
1061
1062     value = (const sr_val_t) { 0 };
1063     value.type = SR_STRING_T;
1064     value.data.string_val = getenv("ControllerUsername");
1065     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-username", 
1066             &value, SR_EDIT_DEFAULT);
1067     if (SR_ERR_OK != rc) {
1068         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1069         goto cleanup;
1070     }
1071
1072     rc = controller_username_changed(getenv("ControllerUsername"));
1073     if (SR_ERR_OK != rc) {
1074         printf("Error by controller_username_changed: %s\n", sr_strerror(rc));
1075         goto cleanup;
1076     }
1077
1078     // setting the values that come in an ENV variable as defaults - controller-password
1079
1080     value = (const sr_val_t) { 0 };
1081     value.type = SR_STRING_T;
1082     value.data.string_val = getenv("ControllerPassword");
1083     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/controller-details/controller-password", 
1084             &value, SR_EDIT_DEFAULT);
1085     if (SR_ERR_OK != rc) {
1086         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1087         goto cleanup;
1088     }
1089
1090     rc = controller_password_changed(getenv("ControllerPassword"));
1091     if (SR_ERR_OK != rc) {
1092         printf("Error by controller_password_changed: %s\n", sr_strerror(rc));
1093         goto cleanup;
1094     }
1095
1096     // setting the values that come in an ENV variable as defaults - netconf-call-home
1097
1098     int netconfCallHome = 1;
1099
1100     char *netconfCallHomeString = getenv("NetconfCallHome");
1101     if (netconfCallHomeString != NULL)
1102     {
1103         if (strcmp(netconfCallHomeString, "false") == 0)
1104         {
1105             netconfCallHome = 0;
1106         }
1107     }
1108
1109     value = (const sr_val_t) { 0 };
1110     value.type = SR_BOOL_T;
1111     value.data.bool_val = netconfCallHome;
1112     rc = sr_set_item(session, "/network-topology-simulator:simulator-config/netconf-call-home", 
1113             &value, SR_EDIT_DEFAULT);
1114     if (SR_ERR_OK != rc) {
1115         printf("Error by sr_set_item: %s\n", sr_strerror(rc));
1116         goto cleanup;
1117     }
1118
1119     rc = netconf_call_home_changed(netconfCallHome);
1120     if (SR_ERR_OK != rc) {
1121         printf("Error by netconf_call_home_changed: %s\n", sr_strerror(rc));
1122         goto cleanup;
1123     }
1124
1125     //commit the changes that we have done until now
1126     rc = sr_commit(session);
1127     if (SR_ERR_OK != rc) {
1128         printf("Error by sr_commit: %s\n", sr_strerror(rc));
1129         goto cleanup;
1130     }
1131
1132         /* read startup config */
1133         printf("\n\n ========== READING STARTUP CONFIG network-topology-simulator: ==========\n\n");
1134         print_current_config(session, "network-topology-simulator");
1135
1136         /* subscribe for changes in running config */
1137         rc = sr_module_change_subscribe(session, "network-topology-simulator", simulator_config_change_cb, NULL,
1138                         0, SR_SUBSCR_DEFAULT | SR_SUBSCR_APPLY_ONLY, &subscription);
1139         if (SR_ERR_OK != rc) {
1140                 fprintf(stderr, "Error by sr_module_change_subscribe: %s\n", sr_strerror(rc));
1141                 goto cleanup;
1142         }
1143
1144     /* subscribe as state data provider for the ntsimulator state data */
1145     rc = sr_dp_get_items_subscribe(session, "/network-topology-simulator:simulator-status", simulator_status_cb, NULL,
1146                 SR_SUBSCR_CTX_REUSE, &subscription);
1147     if (rc != SR_ERR_OK) {
1148         goto cleanup;
1149     }
1150
1151     rc = notification_delay_period_changed(NULL, 0);
1152     if (rc != SR_ERR_OK) {
1153         printf("Could not write the delay period to file!\n");
1154         goto cleanup;
1155     }
1156
1157     rc = _init_curl_odl();
1158     if (rc != SR_ERR_OK)
1159     {
1160         fprintf(stderr, "Could not initialize cURL for ODL connection: %s\n", sr_strerror(rc));
1161     }
1162
1163     rc = sr_rpc_subscribe(session, "/network-topology-simulator:add-key-pair-to-odl", odl_add_key_pair_cb, (void *)session,
1164                 SR_SUBSCR_CTX_REUSE, &subscription);
1165
1166         printf("\n\n ========== STARTUP CONFIG network-topology-simulator APPLIED AS RUNNING ==========\n\n");
1167
1168     rc = writeSkeletonStatusFile();
1169     if (rc != SR_ERR_OK)
1170     {
1171         fprintf(stderr, "Could not initialize status JSON file: %s\n", sr_strerror(rc));
1172     }
1173
1174     rc = sr_rpc_subscribe(session, "/network-topology-simulator:invoke-notification", invoke_notification_cb,
1175             (void *)session, SR_SUBSCR_DEFAULT, &subscription);
1176     if (SR_ERR_OK != rc) {
1177         fprintf(stderr, "Error by sr_rpc_subscribe: %s\n", sr_strerror(rc));
1178         goto cleanup;
1179     }
1180
1181     rc = pull_docker_image_of_simulated_device();
1182
1183     /* loop until ctrl-c is pressed / SIGINT is received */
1184     signal(SIGINT, sigint_handler);
1185     signal(SIGTERM, sigint_handler);
1186     signal(SIGPIPE, SIG_IGN);
1187
1188     while (!exit_application) {
1189
1190                 sleep(1);  /* or do some more useful work... */
1191     }
1192
1193     printf("Application exit requested, exiting.\n");
1194
1195 cleanup:
1196     if (NULL != subscription) {
1197         sr_unsubscribe(session, subscription);
1198     }
1199     if (NULL != session) {
1200         sr_session_stop(session);
1201     }
1202     if (NULL != connection) {
1203         sr_disconnect(connection);
1204     }
1205
1206     clean_current_docker_configuration();
1207     rc = cleanup_curl();
1208     rc = cleanup_curl_odl();
1209     rc = cleanup_curl_k8s();
1210
1211     return rc;
1212 }
1213
1214 static void clean_current_docker_configuration(void)
1215 {
1216     if (strcmp(getenv("K8S_DEPLOYMENT"), "true"))
1217     {
1218         return;
1219     }
1220
1221         printf("Cleaning docker containers...\n");
1222
1223         if (device_list == NULL)
1224         {
1225                 return;
1226         }
1227
1228         for (int i = 0; i < simulated_devices_config; ++i)
1229         {
1230                 stop_device(device_list);
1231         }
1232
1233         printf("Cleaning completed!\n");
1234 }