Rewrite NTS Framework.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / app / manager_operations.c
1 /*************************************************************************
2 *
3 * Copyright 2020 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 #define _GNU_SOURCE
19
20 #include "manager.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26
27 #include "core/framework.h"
28 #include "core/docker.h"
29 #include "core/session.h"
30 #include "utils/nc_client.h"
31 #include "utils/http_client.h"
32 #include "utils/nts_utils.h"
33
34 static uint16_t manager_start_port = 0;
35 static uint8_t manager_port[65536];
36
37 void manager_operations_init(void) {
38     manager_start_port = framework_environment.host_base_port;
39     for(int i = 0; i < 65536; i++) {
40         manager_port[i] = 0;
41     }
42 }
43
44 int manager_start_instance(manager_network_function_type *function_type) {
45     assert(function_type);
46     assert_session();
47
48     function_type->started_instances++;
49     function_type->instance = (manager_network_function_instance_t *)realloc(function_type->instance, sizeof(manager_network_function_instance_t) * function_type->started_instances);
50     if(function_type->instance == 0) {
51         log_error("realloc failed");
52         return NTS_ERR_FAILED;
53     }
54
55     manager_network_function_instance_t *instance = &function_type->instance[function_type->started_instances - 1];
56     instance->is_configured = false;
57     instance->is_mounted = false;
58     
59     asprintf(&instance->name, "%s-%d", function_type->docker_instance_name, function_type->started_instances - 1);
60
61     instance->mount_point_addressing_method = strdup(function_type->mount_point_addressing_method);
62
63     instance->docker_port = STANDARD_NETCONF_PORT;
64     instance->host_ip = strdup(framework_environment.host_ip);
65     instance->host_port = 0;
66
67     //find start host port
68     for(int i = manager_start_port; i < 65536 - (framework_environment.ssh_connections + framework_environment.tls_connections + framework_environment.ftp_connections + framework_environment.sftp_connections); i += (framework_environment.ssh_connections + framework_environment.tls_connections + framework_environment.ftp_connections + framework_environment.sftp_connections)) {
69         if(manager_port[i] == 0) {
70             manager_port[i] = 1;
71             instance->host_port = i;
72             break;
73         }
74     }
75
76     if(instance->host_port == 0) {
77         log_error("no ports available for operation");
78         return NTS_ERR_FAILED;
79     }
80
81     int rc = docker_device_start(function_type, instance);
82     if(rc != NTS_ERR_OK) {
83         log_error("docker_device_start failed");
84         return NTS_ERR_FAILED;
85     }
86
87     return NTS_ERR_OK;
88 }
89
90 int manager_config_instance(manager_network_function_type *function_type, manager_network_function_instance_t *instance) {
91     assert(function_type);
92     assert(instance);
93
94     //first wait for the nc server to be up and running
95     while(check_port_open(instance->docker_ip, instance->docker_port) == false) {
96         usleep(10000);
97     }
98
99     //populate sdn-controller and ves-endpoint
100     struct lyd_node *local_tree = 0;
101     int rc = lyd_utils_dup(session_running, "/nts-manager:simulation/sdn-controller", "/nts-network-function:simulation/sdn-controller", &local_tree);
102     if(rc != NTS_ERR_OK) {
103         log_error("lyd_utils_dup failed");
104         return NTS_ERR_FAILED;
105     }
106
107     rc = lyd_utils_dup(session_running, "/nts-manager:simulation/ves-endpoint", "/nts-network-function:simulation/ves-endpoint", &local_tree);
108     if(rc != NTS_ERR_OK) {
109         log_error("lyd_utils_dup failed");
110         return NTS_ERR_FAILED;
111     }
112
113     char *xpath_s = 0;
114     asprintf(&xpath_s, "/nts-manager:simulation/network-functions/network-function[function-type='%s']", function_type->function_type_string);
115     rc = lyd_utils_dup(session_running, xpath_s, "/nts-network-function:simulation/network-function", &local_tree);
116     if(rc != NTS_ERR_OK) {
117         log_error("lyd_utils_dup failed");
118         return NTS_ERR_FAILED;
119     }
120     free(xpath_s);
121
122     nc_client_t *nc_client = nc_client_ssh_connect(instance->docker_ip, instance->docker_port, "netconf", "netconf");
123     if(nc_client == 0) {
124         log_error("nc_client_ssh_connect");
125         return NTS_ERR_FAILED;
126     }
127
128     rc += nc_client_edit_batch(nc_client, local_tree, 1000);
129     if(rc != NTS_ERR_OK) {
130         log_error("nc_client_edit_batch failed %d\n", rc);
131         return NTS_ERR_FAILED;
132     }
133     lyd_free_withsiblings(local_tree);
134
135     
136     if(instance->is_configured == false) {
137         //run datastore-random-populate rpc
138         struct lyd_node *rpc_node = 0;
139         struct lyd_node *rpcout = 0;
140         rpc_node = lyd_new_path(0, session_context, "/nts-network-function:datastore-random-populate", 0, 0, 0);
141         if(rpc_node == 0) {
142             log_error("failed to create rpc node");
143             return NTS_ERR_FAILED;
144         }
145
146         rpcout = nc_client_send_rpc(nc_client, rpc_node, 5000);
147         if(rpcout == 0) {
148             log_error("datastore-random-populate rpc failed");
149             return NTS_ERR_FAILED;
150         }
151         else {
152             lyd_free_withsiblings(rpcout);
153         }
154         lyd_free_withsiblings(rpc_node);
155
156         //run feature-control rpc
157         rpc_node = lyd_new_path(0, session_context, "/nts-network-function:feature-control/features", "ves-file-ready ves-heartbeat ves-pnf-registration manual-notification-generation netconf-call-home", 0, 0);
158         if(rpc_node == 0) {
159             log_error("failed to create rpc node");
160             return NTS_ERR_FAILED;
161         }
162
163         rpcout = nc_client_send_rpc(nc_client, rpc_node, 1000);
164         if(rpcout == 0) {
165             log_error("feature-control rpc failed");
166             return NTS_ERR_FAILED;
167         }
168         else {
169             lyd_free_withsiblings(rpcout);
170         }
171         lyd_free_withsiblings(rpc_node);
172     }
173
174     instance->is_configured = true;
175
176     nc_client_disconnect(nc_client);
177
178     return NTS_ERR_OK;
179 }
180
181 int manager_stop_instance(manager_network_function_type *function_type) {
182     assert(function_type);
183
184     manager_network_function_instance_t *instance = &function_type->instance[function_type->started_instances - 1];
185
186     if(instance->is_mounted) {
187         if(manager_unmount_instance(function_type) != NTS_ERR_OK) {
188             return NTS_ERR_FAILED;
189         }
190     }
191
192     int rc = docker_device_stop(instance);
193     if(rc != NTS_ERR_OK) {
194         log_error("docker_device_stop failed");
195         return NTS_ERR_FAILED;
196     }
197
198     //clear unused ports
199     manager_port[instance->host_port] = 0;
200
201     free(instance->mount_point_addressing_method);
202     free(instance->docker_id);
203     free(instance->name);
204     free(instance->docker_ip);
205     free(instance->host_ip);
206
207     function_type->started_instances--;
208     if(function_type->started_instances) {
209         function_type->instance = (manager_network_function_instance_t *)realloc(function_type->instance, sizeof(manager_network_function_instance_t) * function_type->started_instances);
210         if(function_type->instance == 0) {
211             log_error("realloc failed");
212             return NTS_ERR_FAILED;
213         }
214     }
215     else {
216         free(function_type->instance);
217         function_type->instance = 0;
218     }
219     return NTS_ERR_OK;
220 }
221
222 int manager_mount_instance(manager_network_function_type *function_type) {
223     assert(function_type);
224
225     manager_network_function_instance_t *instance = &function_type->instance[function_type->mounted_instances];
226
227     if(instance->is_mounted == true) {
228         return NTS_ERR_FAILED;
229     }
230
231     controller_details_t *controller;
232     controller = controller_details_get(0);
233     if(controller == 0) {
234         log_error("could not get controller detailes");
235         return NTS_ERR_FAILED;
236     }
237
238     for(int i = 0; i < (framework_environment.ssh_connections + framework_environment.tls_connections); i++) {
239         char *protocol;
240         char *protocol_data;
241         if(i < framework_environment.ssh_connections) {
242             protocol = "SSH";
243             protocol_data = "\
244             \"network-topology:netconf-node-topology:username\": \"netconf\",\
245             \"network-topology:netconf-node-topology:password\": \"netconf\"";
246
247         }
248         else {
249             protocol = "TLS";
250             protocol_data = "\
251             \"netconf-node-topology:key-based\" : {\
252                 \"netconf-node-topology:username\" : \"netconf\",\
253                 \"netconf-node-topology:key-id\" : \""KS_KEY_NAME"\"\
254             }";
255         }
256
257         char *json_template = "\
258         {\
259             \"network-topology:node\": [{\
260                     \"network-topology:node-id\": \"%s\",\
261                     \"network-topology:netconf-node-topology:host\": \"%s\",\
262                     \"network-topology:netconf-node-topology:port\": \"%d\",\
263                     \"network-topology:netconf-node-topology:tcp-only\": \"false\",\
264                     \"network-topology:netconf-node-topology:protocol\": {\
265                         \"network-topology:netconf-node-topology:name\": \"%s\"\
266                     },\
267                     %s,\
268                     \"network-topology:netconf-node-topology:connection-timeout-millis\": \"20000\",\
269                     \"network-topology:netconf-node-topology:default-request-timeout-millis\": \"60000\",\
270                     \"network-topology:netconf-node-topology:max-connection-attempts\": \"3\"\
271             }]\
272         }";
273
274         char *json = 0;
275         uint16_t port = 0;
276         char *ip = 0;
277         if(instance->mount_point_addressing_method[0] == 'd') {
278             ip = instance->docker_ip;
279             port = instance->docker_port + i;
280         }
281         else {
282             ip = instance->host_ip;
283             port = instance->host_port + i;
284         }
285         char *node_id = 0;
286         asprintf(&node_id, "%s-%d", instance->name, port);
287         asprintf(&json, json_template, node_id, ip, port, protocol, protocol_data);
288
289         char *url = 0;
290         asprintf(&url, "%s/rests/data/network-topology:network-topology/topology=topology-netconf/node=%s", controller->base_url, node_id);
291         int rc = http_request(url, controller->username, controller->password, "PUT", json, 0, 0);
292         if(rc != NTS_ERR_OK) {
293             log_error("http_request failed");
294             return NTS_ERR_FAILED;
295         }
296
297         free(url);
298         free(node_id);
299         free(json);
300     }
301
302     controller_details_free(controller);
303
304     instance->is_mounted = true;
305     function_type->mounted_instances++;
306
307     return NTS_ERR_OK;
308 }
309
310 int manager_unmount_instance(manager_network_function_type *function_type) {
311     assert(function_type);
312
313     manager_network_function_instance_t *instance = &function_type->instance[function_type->mounted_instances - 1];
314
315     if(instance->is_mounted == false) {
316         log_error("tried to unmount an unmounted instance");
317         return NTS_ERR_FAILED;
318     }
319
320     controller_details_t *controller;
321     controller = controller_details_get(0);
322     if(controller == 0) {
323         log_error("could not get controller detailes");
324         return NTS_ERR_FAILED;
325     }
326
327     for(int i = 0; i < (framework_environment.ssh_connections + framework_environment.tls_connections); i++) {
328         uint16_t port = 0;
329         if(function_type->mount_point_addressing_method[0] == 'd') {
330             port = instance->docker_port + i;
331         }
332         else {
333             port = instance->host_port + i;
334         }
335         char *node_id = 0;
336         asprintf(&node_id, "%s-%d", instance->name, port);
337
338         char *url = 0;
339         asprintf(&url, "%s/rests/data/network-topology:network-topology/topology=topology-netconf/node=%s", controller->base_url, node_id);
340         int rc = http_request(url, controller->username, controller->password, "DELETE", "", 0, 0);
341         if(rc != NTS_ERR_OK) {
342             log_error("http_request failed");
343             return NTS_ERR_FAILED;
344         }
345
346         free(url);
347         free(node_id);
348     }
349
350     controller_details_free(controller);
351
352     function_type->mounted_instances--;
353     function_type->instance[function_type->mounted_instances].is_mounted = false;
354
355     return NTS_ERR_OK;
356 }