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