Refactor folder structure.
[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     //URL used for mounting/unmounting a device; the device name needs to be appended
88    char url[URL_AND_CREDENTIALS_MAX_LEN];
89    sprintf(url, "http://%s:%d/restconf/config/network-topology:network-topology/topology/"
90                  "topology-netconf/node/",
91                  odl_ip->data.string_val, odl_port->data.uint32_val);
92
93    char credentials[URL_AND_CREDENTIALS_MAX_LEN];
94    sprintf(credentials, "%s:%s", odl_username->data.string_val, odl_password->data.string_val);
95
96    //URLs used for adding key pair to ODL, for TLS connections
97    char url_for_keystore_add[URL_AND_CREDENTIALS_MAX_LEN];
98    sprintf(url_for_keystore_add, "http://%s:%d/restconf/operations/netconf-keystore:add-keystore-entry",
99                          odl_ip->data.string_val, odl_port->data.uint32_val);
100
101    char url_for_private_key_add[URL_AND_CREDENTIALS_MAX_LEN];
102    sprintf(url_for_private_key_add, "http://%s:%d/restconf/operations/netconf-keystore:add-private-key",
103                          odl_ip->data.string_val, odl_port->data.uint32_val);
104
105    char url_for_trusted_ca_add[URL_AND_CREDENTIALS_MAX_LEN];
106    sprintf(url_for_trusted_ca_add, "http://%s:%d/restconf/operations/netconf-keystore:add-trusted-certificate",
107                          odl_ip->data.string_val, odl_port->data.uint32_val);
108
109    strcpy(controller_details.url, url);
110    strcpy(controller_details.credentials, credentials);
111    strcpy(controller_details.url_for_keystore_add, url_for_keystore_add);
112    strcpy(controller_details.url_for_private_key_add, url_for_private_key_add);
113    strcpy(controller_details.url_for_trusted_ca_add, url_for_trusted_ca_add);
114
115    sr_free_val(odl_ip);
116    sr_free_val(odl_port);
117    sr_free_val(odl_username);
118    sr_free_val(odl_password);
119
120    sr_free_values(values, count);
121 }
122
123 static void clean_current_docker_configuration(void);
124
125 static int simulated_devices_changed(int new_value)
126 {
127         int rc = SR_ERR_OK;
128
129     if (simulated_devices_config > new_value)
130     {
131         //we are configuring less elements that currently
132         for (int i = 0; i < simulated_devices_config - new_value; ++i)
133         {
134                 rc = stop_device(device_list);
135         }
136     }
137     else if (simulated_devices_config < new_value)
138     {
139         //we are configuring more elements that currently
140         for (int i = 0; i < new_value - simulated_devices_config; ++i)
141         {
142                 rc = start_device(device_list);
143         }
144     }
145
146     simulated_devices_config = new_value;
147
148     return rc;
149 }
150
151 int mounted_devices_changed(sr_session_ctx_t *session, int new_value)
152 {
153         int rc = SR_ERR_OK;
154
155         if (mounted_devices_config > new_value)
156         {
157           //we need have less mounted elements
158           for (int i = 0; i < mounted_devices_config - new_value; ++i)
159           {
160                   printf("Sending unmount device...\n");
161                   rc = unmount_device(device_list, controller_details);
162           }
163         }
164         else if (mounted_devices_config < new_value)
165         {
166           //we are configuring more elements that currently
167           for (int i = 0; i < new_value - mounted_devices_config; ++i)
168           {
169                   printf("Sending mount device...\n");
170                   rc = mount_device(device_list, controller_details);
171           }
172         }
173
174         mounted_devices_config = new_value;
175
176     return rc;
177 }
178
179 static int
180 simulator_config_change_cb(sr_session_ctx_t *session, const char *module_name, sr_notif_event_t event, void *private_ctx)
181 {
182         int rc;
183
184     printf("\n\n ========== CONFIG HAS CHANGED, CURRENT RUNNING CONFIG %s: ==========\n\n", module_name);
185     print_current_config(session, module_name);
186
187     sr_val_t *val = NULL;
188
189     /* get the value from sysrepo, we do not care if the value did not change in our case */
190     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/simulated-devices", &val);
191     if (rc != SR_ERR_OK) {
192         goto sr_error;
193     }
194
195     rc = simulated_devices_changed(val->data.uint32_val);
196     if (rc != SR_ERR_OK) {
197         goto sr_error;
198     }
199
200     sr_free_val(val);
201         val = NULL;
202
203     /* get the value from sysrepo, we do not care if the value did not change in our case */
204     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/mounted-devices", &val);
205     if (rc != SR_ERR_OK) {
206         goto sr_error;
207     }
208
209     if (mounted_devices_config != val->data.uint32_val)
210     {
211         if (val->data.uint32_val > simulated_devices_config)
212         {
213                 printf("Cannot set mount value greater than number of simulated devices.\n");
214                 sr_free_val(val);
215                         val = NULL;
216                 return SR_ERR_OK;
217         }
218
219                 rc = mounted_devices_changed(session, val->data.uint32_val);
220                 if (rc != SR_ERR_OK) {
221                         goto sr_error;
222                 }
223     }
224
225     sr_free_val(val);
226         val = NULL;
227
228     /* get the value from sysrepo, we do not care if the value did not change in our case */
229     rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/fault-notification-delay-period", &val);
230     if (rc != SR_ERR_OK) {
231         goto sr_error;
232     }
233
234     rc = notification_delay_period_changed(val->data.uint32_val);
235     if (rc != SR_ERR_OK) {
236         goto sr_error;
237     }
238
239     sr_free_val(val);
240         val = NULL;
241
242     /* get the value from sysrepo, we do not care if the value did not change in our case */
243         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/ves-heartbeat-period", &val);
244         if (rc != SR_ERR_OK) {
245                 goto sr_error;
246         }
247
248         rc = ves_heartbeat_period_changed(val->data.uint32_val);
249         if (rc != SR_ERR_OK) {
250                 goto sr_error;
251         }
252
253         sr_free_val(val);
254         val = NULL;
255
256         /* get the value from sysrepo, we do not care if the value did not change in our case */
257         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-ip", &val);
258         if (rc != SR_ERR_OK) {
259                 goto sr_error;
260         }
261
262         rc = ves_ip_changed(val->data.string_val);
263         if (rc != SR_ERR_OK) {
264                 goto sr_error;
265         }
266
267         sr_free_val(val);
268         val = NULL;
269
270         /* get the value from sysrepo, we do not care if the value did not change in our case */
271         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-port", &val);
272         if (rc != SR_ERR_OK) {
273                 goto sr_error;
274         }
275
276         rc = ves_port_changed(val->data.uint16_val);
277         if (rc != SR_ERR_OK) {
278                 goto sr_error;
279         }
280
281         sr_free_val(val);
282         val = NULL;
283
284         /* get the value from sysrepo, we do not care if the value did not change in our case */
285         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-registration", &val);
286         if (rc != SR_ERR_OK) {
287                 goto sr_error;
288         }
289
290         rc = ves_registration_changed(val->data.bool_val);
291         if (rc != SR_ERR_OK) {
292                 goto sr_error;
293         }
294
295         sr_free_val(val);
296         val = NULL;
297
298         /* get the value from sysrepo, we do not care if the value did not change in our case */
299         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/is-netconf-available", &val);
300         if (rc != SR_ERR_OK) {
301                 goto sr_error;
302         }
303
304         rc = is_netconf_available_changed(val->data.bool_val);
305         if (rc != SR_ERR_OK) {
306                 goto sr_error;
307         }
308
309         sr_free_val(val);
310         val = NULL;
311
312         /* get the value from sysrepo, we do not care if the value did not change in our case */
313         rc = sr_get_item(session, "/network-topology-simulator:simulator-config/notification-config/is-ves-available", &val);
314         if (rc != SR_ERR_OK) {
315                 goto sr_error;
316         }
317
318         rc = is_ves_available_changed(val->data.bool_val);
319         if (rc != SR_ERR_OK) {
320                 goto sr_error;
321         }
322
323         sr_free_val(val);
324         val = NULL;
325
326     return SR_ERR_OK;
327
328 sr_error:
329         printf("NTSimulator config change callback failed: %s.", sr_strerror(rc));
330         if (val != NULL)
331         {
332                 sr_free_val(val);
333                 val = NULL;
334         }
335         return rc;
336 }
337
338 static int
339 simulator_status_cb(const char *xpath, sr_val_t **values, size_t *values_cnt,
340         uint64_t request_id, const char *original_xpath, void *private_ctx)
341 {
342         int rc;
343
344         printf("\n\n ========== Called simulator_status_cb for xpath: %s ==========\n\n", xpath);
345
346         if (sr_xpath_node_name_eq(xpath, "simulated-devices-list")) {
347                 sr_val_t *v;
348                 size_t current_num_of_values= 0;
349
350                 if (simulated_devices_config == 0) //nothing to return if no devices are running
351                 {
352                         *values = NULL;
353                         *values_cnt = 0;
354
355                         return SR_ERR_OK;
356                 }
357
358                 rc = get_docker_containers_operational_state_curl(device_list);
359                 if (rc != SR_ERR_OK)
360                 {
361                         printf("Could not get the operational state for the devices simulated.\n");
362                         return SR_ERR_OPERATION_FAILED;
363                 }
364
365                 device_t *current_device = device_list->head;
366
367                 while (current_device != NULL)
368                 {
369                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
370
371                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "device-ip");
372                         v[current_num_of_values - 1].type = SR_STRING_T;
373                         v[current_num_of_values - 1].data.string_val = getenv("NTS_IP");
374
375                         for (int i = 0; i < NETCONF_CONNECTIONS_PER_DEVICE; ++i)
376                         {
377                                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
378
379                                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "device-port");
380                                 v[current_num_of_values - 1].type = SR_UINT32_T;
381                                 v[current_num_of_values - 1].data.uint32_val = current_device->netconf_port + i;
382                         }
383
384                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
385
386                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "is-mounted");
387                         v[current_num_of_values - 1].type = SR_BOOL_T;
388                         v[current_num_of_values - 1].data.bool_val = current_device->is_mounted;
389
390                         char *operational_state = get_docker_container_operational_state(device_list, current_device->device_id);
391
392                         CREATE_NEW_VALUE(rc, v, current_num_of_values);
393
394                         sr_val_build_xpath(&v[current_num_of_values - 1], "%s[uuid='%s']/%s", xpath, current_device->device_id, "operational-state");
395                         sr_val_build_str_data(&v[current_num_of_values - 1], SR_ENUM_T, "%s", operational_state);
396
397                         current_device = current_device->next;
398                 }
399
400                 //return the values that we have just created
401                 *values = v;
402                 *values_cnt = current_num_of_values;
403          }
404          else if (sr_xpath_node_name_eq(xpath, "simulation-usage-details"))
405          {
406                 float cpu_usage = 0.0, mem_usage = 0.0;
407
408                 char *resource_usage_from_script = get_docker_container_resource_stats();
409
410                 if (resource_usage_from_script != NULL)
411                 {
412                         printf("Received line: %s\n", resource_usage_from_script);
413                         sscanf(resource_usage_from_script, "CPU=%f%%;RAM=%fMiB", &cpu_usage, &mem_usage);
414                         printf("Read cpu=\"%f\" and mem=\"%f\"\n", cpu_usage, mem_usage);
415                         free(resource_usage_from_script);
416                 }
417
418                 sr_val_t *v;
419                 /* convenient functions such as this can be found in sysrepo/values.h */
420                 size_t current_num_of_values= 0;
421
422                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
423
424                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-simulated-devices");
425                 v[current_num_of_values - 1].type = SR_UINT32_T;
426                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_devices(device_list);
427
428                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
429
430                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "running-mounted-devices");
431                 v[current_num_of_values - 1].type = SR_UINT32_T;
432                 v[current_num_of_values - 1].data.uint32_val = get_current_number_of_mounted_devices(device_list);
433
434                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
435
436                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "base-netconf-port");
437                 v[current_num_of_values - 1].type = SR_UINT32_T;
438                 v[current_num_of_values - 1].data.uint32_val = get_netconf_port_base();
439
440                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
441
442                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "cpu-usage");
443                 v[current_num_of_values - 1].type = SR_DECIMAL64_T;
444                 v[current_num_of_values - 1].data.decimal64_val = cpu_usage;
445
446                 CREATE_NEW_VALUE(rc, v, current_num_of_values);
447
448                 sr_val_build_xpath(&v[current_num_of_values - 1], "%s/%s", xpath, "mem-usage");
449                 v[current_num_of_values - 1].type = SR_UINT32_T;
450                 v[current_num_of_values - 1].data.uint32_val = (int)mem_usage;
451
452                 //return the values that we have just created
453                 *values = v;
454                 *values_cnt = current_num_of_values;
455          }
456
457     return SR_ERR_OK;
458 }
459
460 int odl_add_key_pair_cb(const char *xpath, const sr_val_t *input, const size_t input_cnt,
461                 sr_val_t **output, size_t *output_cnt, void *private_ctx)
462 {
463         int rc = SR_ERR_OK;
464         controller_t controller_list[CONTROLLER_LIST_MAX_LEN];
465         int controller_list_size = 0;
466
467         controller_list[0] = controller_details;
468         controller_list_size++;
469
470         for (int i = 0; i < controller_list_size; ++i)
471         {
472                 printf("%d iteration: Got back url=%s and credentials=%s\n", i, controller_list[i].url, controller_list[i].credentials);
473         }
474
475         rc = add_key_pair_to_odl(controller_list, controller_list_size);
476         if (rc != SR_ERR_OK)
477         {
478                 printf("Failed to add key pair to ODL.\n");
479                 return SR_ERR_OPERATION_FAILED;
480         }
481
482         return rc;
483 }
484
485
486 static void
487 sigint_handler(int signum)
488 {
489     exit_application = 1;
490 }
491
492 int
493 main(int argc, char **argv)
494 {
495     sr_conn_ctx_t *connection = NULL;
496     sr_session_ctx_t *session = NULL;
497     sr_subscription_ctx_t *subscription = NULL;
498     int rc = SR_ERR_OK;
499
500     setbuf(stdout, NULL);
501
502     device_list = new_device_stack();
503     rc = _init_curl();
504     if (rc != SR_ERR_OK)
505     {
506         fprintf(stderr, "Could not initialize cURL: %s\n", sr_strerror(rc));
507     }
508
509     /* connect to sysrepo */
510     rc = sr_connect("network-topology-simulator", SR_CONN_DEFAULT, &connection);
511     if (SR_ERR_OK != rc) {
512         fprintf(stderr, "Error by sr_connect: %s\n", sr_strerror(rc));
513         goto cleanup;
514     }
515
516     /* start session */
517     rc = sr_session_start(connection, SR_DS_STARTUP, SR_SESS_DEFAULT, &session);
518     if (SR_ERR_OK != rc) {
519         fprintf(stderr, "Error by sr_session_start: %s\n", sr_strerror(rc));
520         goto cleanup;
521     }
522
523         /* read startup config */
524         printf("\n\n ========== READING STARTUP CONFIG network-topology-simulator: ==========\n\n");
525         print_current_config(session, "network-topology-simulator");
526
527         /* subscribe for changes in running config */
528         rc = sr_module_change_subscribe(session, "network-topology-simulator", simulator_config_change_cb, NULL,
529                         0, SR_SUBSCR_DEFAULT | SR_SUBSCR_APPLY_ONLY, &subscription);
530         if (SR_ERR_OK != rc) {
531                 fprintf(stderr, "Error by sr_module_change_subscribe: %s\n", sr_strerror(rc));
532                 goto cleanup;
533         }
534
535     /* subscribe as state data provider for the ntsimulator state data */
536     rc = sr_dp_get_items_subscribe(session, "/network-topology-simulator:simulator-status", simulator_status_cb, NULL,
537                 SR_SUBSCR_CTX_REUSE, &subscription);
538     if (rc != SR_ERR_OK) {
539         goto cleanup;
540     }
541
542     rc = notification_delay_period_changed(0);
543     if (rc != SR_ERR_OK) {
544         printf("Could not write the delay period to file!\n");
545         goto cleanup;
546     }
547
548     rc = _init_curl_odl();
549     if (rc != SR_ERR_OK)
550     {
551         fprintf(stderr, "Could not initialize cURL for ODL connection: %s\n", sr_strerror(rc));
552     }
553
554     rc = sr_rpc_subscribe(session, "/network-topology-simulator:add-key-pair-to-odl", odl_add_key_pair_cb, (void *)session,
555                 SR_SUBSCR_CTX_REUSE, &subscription);
556
557         printf("\n\n ========== STARTUP CONFIG network-topology-simulator APPLIED AS RUNNING ==========\n\n");
558
559     /* loop until ctrl-c is pressed / SIGINT is received */
560     signal(SIGINT, sigint_handler);
561     signal(SIGTERM, sigint_handler);
562     signal(SIGPIPE, SIG_IGN);
563
564     while (!exit_application) {
565
566                 sleep(1);  /* or do some more useful work... */
567     }
568
569     printf("Application exit requested, exiting.\n");
570
571 cleanup:
572     if (NULL != subscription) {
573         sr_unsubscribe(session, subscription);
574     }
575     if (NULL != session) {
576         sr_session_stop(session);
577     }
578     if (NULL != connection) {
579         sr_disconnect(connection);
580     }
581
582     clean_current_docker_configuration();
583     rc = cleanup_curl();
584     rc = cleanup_curl_odl();
585
586     return rc;
587 }
588
589 static void clean_current_docker_configuration(void)
590 {
591         printf("Cleaning docker containers...\n");
592
593         if (device_list == NULL)
594         {
595                 return;
596         }
597
598         for (int i = 0; i < simulated_devices_config; ++i)
599         {
600                 stop_device(device_list);
601         }
602
603         printf("Cleaning completed!\n");
604 }