/************************************************************************* * * Copyright 2020 highstreet technologies GmbH and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ #define _GNU_SOURCE #include "manager.h" #include "utils/log_utils.h" #include "utils/sys_utils.h" #include #include #include #include "core/framework.h" #include "core/docker.h" #include "core/session.h" #include "utils/nc_client.h" #include "utils/http_client.h" #include "utils/nts_utils.h" static uint16_t manager_start_port = 0; static uint8_t manager_port[65536]; void manager_operations_init(void) { manager_start_port = framework_environment.host_base_port; for(int i = 0; i < 65536; i++) { manager_port[i] = 0; } } int manager_start_instance(manager_network_function_type *function_type) { assert(function_type); assert_session(); function_type->started_instances++; function_type->instance = (manager_network_function_instance_t *)realloc(function_type->instance, sizeof(manager_network_function_instance_t) * function_type->started_instances); if(function_type->instance == 0) { log_error("realloc failed"); function_type->started_instances--; return NTS_ERR_FAILED; } manager_network_function_instance_t *instance = &function_type->instance[function_type->started_instances - 1]; instance->is_configured = false; instance->is_mounted = false; asprintf(&instance->name, "%s-%d", function_type->docker_instance_name, function_type->started_instances - 1); instance->mount_point_addressing_method = strdup(function_type->mount_point_addressing_method); instance->docker_port = STANDARD_NETCONF_PORT; instance->host_ip = strdup(framework_environment.host_ip); instance->host_port = 0; //find start host port 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)) { if(manager_port[i] == 0) { manager_port[i] = 1; instance->host_port = i; break; } } if(instance->host_port == 0) { log_error("no ports available for operation"); free(instance->name); free(instance->mount_point_addressing_method); free(instance->host_ip); function_type->started_instances--; return NTS_ERR_FAILED; } int rc = docker_device_start(function_type, instance); if(rc != NTS_ERR_OK) { log_error("docker_device_start failed"); free(instance->name); free(instance->mount_point_addressing_method); free(instance->host_ip); manager_port[instance->host_port] = 0; function_type->started_instances--; return NTS_ERR_FAILED; } return NTS_ERR_OK; } int manager_config_instance(manager_network_function_type *function_type, manager_network_function_instance_t *instance) { assert(function_type); assert(instance); //first wait for the nc server to be up and running while(check_port_open(instance->docker_ip, instance->docker_port) == false) { usleep(10000); } //populate sdn-controller and ves-endpoint struct lyd_node *local_tree = 0; int rc = lyd_utils_dup(session_running, "/nts-manager:simulation/sdn-controller", "/nts-network-function:simulation/sdn-controller", &local_tree); if(rc != NTS_ERR_OK) { log_error("lyd_utils_dup failed"); return NTS_ERR_FAILED; } rc = lyd_utils_dup(session_running, "/nts-manager:simulation/ves-endpoint", "/nts-network-function:simulation/ves-endpoint", &local_tree); if(rc != NTS_ERR_OK) { log_error("lyd_utils_dup failed"); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } char *xpath_s = 0; asprintf(&xpath_s, "/nts-manager:simulation/network-functions/network-function[function-type='%s']", function_type->function_type_string); rc = lyd_utils_dup(session_running, xpath_s, "/nts-network-function:simulation/network-function", &local_tree); free(xpath_s); if(rc != NTS_ERR_OK) { log_error("lyd_utils_dup failed"); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } nc_client_t *nc_client = nc_client_ssh_connect(instance->docker_ip, instance->docker_port, "netconf", "netconf"); if(nc_client == 0) { log_error("nc_client_ssh_connect"); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } rc += nc_client_edit_batch(nc_client, local_tree, 1000); lyd_free_withsiblings(local_tree); if(rc != NTS_ERR_OK) { log_error("nc_client_edit_batch failed %d\n", rc); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } if(instance->is_configured == false) { //run datastore-random-populate rpc struct lyd_node *rpc_node = 0; struct lyd_node *rpcout = 0; rpc_node = lyd_new_path(0, session_context, "/nts-network-function:datastore-random-populate", 0, 0, 0); if(rpc_node == 0) { log_error("failed to create rpc node"); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } rpcout = nc_client_send_rpc(nc_client, rpc_node, 5000); if(rpcout == 0) { log_error("datastore-random-populate rpc failed"); nc_client_disconnect(nc_client); lyd_free_withsiblings(rpc_node); return NTS_ERR_FAILED; } else { lyd_free_withsiblings(rpcout); } lyd_free_withsiblings(rpc_node); //run feature-control rpc 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 web-cut-through", 0, 0); if(rpc_node == 0) { log_error("failed to create rpc node"); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } rpcout = nc_client_send_rpc(nc_client, rpc_node, 1000); if(rpcout == 0) { log_error("feature-control rpc failed"); nc_client_disconnect(nc_client); lyd_free_withsiblings(rpc_node); return NTS_ERR_FAILED; } else { lyd_free_withsiblings(rpcout); } lyd_free_withsiblings(rpc_node); } instance->is_configured = true; nc_client_disconnect(nc_client); return NTS_ERR_OK; } int manager_stop_instance(manager_network_function_type *function_type) { assert(function_type); manager_network_function_instance_t *instance = &function_type->instance[function_type->started_instances - 1]; if(instance->is_mounted) { if(manager_unmount_instance(function_type) != NTS_ERR_OK) { log_error("failed to unmount instance"); } } int rc = docker_device_stop(instance); if(rc != NTS_ERR_OK) { log_error("docker_device_stop failed"); return NTS_ERR_FAILED; } //clear unused ports manager_port[instance->host_port] = 0; free(instance->mount_point_addressing_method); free(instance->docker_id); free(instance->name); free(instance->docker_ip); free(instance->host_ip); function_type->started_instances--; if(function_type->started_instances) { function_type->instance = (manager_network_function_instance_t *)realloc(function_type->instance, sizeof(manager_network_function_instance_t) * function_type->started_instances); if(function_type->instance == 0) { log_error("realloc failed"); return NTS_ERR_FAILED; } } else { free(function_type->instance); function_type->instance = 0; } return NTS_ERR_OK; } int manager_mount_instance(manager_network_function_type *function_type) { assert(function_type); manager_network_function_instance_t *instance = &function_type->instance[function_type->mounted_instances]; if(instance->is_mounted == true) { return NTS_ERR_FAILED; } controller_details_t *controller; controller = controller_details_get(0); if(controller == 0) { log_error("could not get controller detailes"); return NTS_ERR_FAILED; } for(int i = 0; i < (framework_environment.ssh_connections + framework_environment.tls_connections); i++) { char *protocol; char *protocol_data; if(i < framework_environment.ssh_connections) { protocol = "SSH"; protocol_data = "\ \"network-topology:netconf-node-topology:username\": \"netconf\",\ \"network-topology:netconf-node-topology:password\": \"netconf\""; } else { protocol = "TLS"; protocol_data = "\ \"netconf-node-topology:key-based\" : {\ \"netconf-node-topology:username\" : \"netconf\",\ \"netconf-node-topology:key-id\" : \""KS_KEY_NAME"\"\ }"; } char *json_template = "\ {\ \"network-topology:node\": [{\ \"network-topology:node-id\": \"%s\",\ \"network-topology:netconf-node-topology:host\": \"%s\",\ \"network-topology:netconf-node-topology:port\": \"%d\",\ \"network-topology:netconf-node-topology:tcp-only\": \"false\",\ \"network-topology:netconf-node-topology:protocol\": {\ \"network-topology:netconf-node-topology:name\": \"%s\"\ },\ %s,\ \"network-topology:netconf-node-topology:connection-timeout-millis\": \"20000\",\ \"network-topology:netconf-node-topology:default-request-timeout-millis\": \"60000\",\ \"network-topology:netconf-node-topology:max-connection-attempts\": \"3\"\ }]\ }"; char *json = 0; uint16_t port = 0; char *ip = 0; if(instance->mount_point_addressing_method[0] == 'd') { ip = instance->docker_ip; port = instance->docker_port + i; } else { ip = instance->host_ip; port = instance->host_port + i; } char *node_id = 0; asprintf(&node_id, "%s-%d", instance->name, port); asprintf(&json, json_template, node_id, ip, port, protocol, protocol_data); char *url = 0; asprintf(&url, "%s/rests/data/network-topology:network-topology/topology=topology-netconf/node=%s", controller->base_url, node_id); int rc = http_request(url, controller->username, controller->password, "PUT", json, 0, 0); if(rc != NTS_ERR_OK) { log_error("http_request failed"); free(url); free(node_id); free(json); controller_details_free(controller); return NTS_ERR_FAILED; } free(url); free(node_id); free(json); } controller_details_free(controller); instance->is_mounted = true; function_type->mounted_instances++; return NTS_ERR_OK; } int manager_unmount_instance(manager_network_function_type *function_type) { assert(function_type); int ret = NTS_ERR_OK; manager_network_function_instance_t *instance = &function_type->instance[function_type->mounted_instances - 1]; if(instance->is_mounted == false) { log_error("tried to unmount an unmounted instance"); return NTS_ERR_FAILED; } controller_details_t *controller; controller = controller_details_get(0); if(controller == 0) { log_error("could not get controller detailes"); return NTS_ERR_FAILED; } for(int i = 0; i < (framework_environment.ssh_connections + framework_environment.tls_connections); i++) { uint16_t port = 0; if(function_type->mount_point_addressing_method[0] == 'd') { port = instance->docker_port + i; } else { port = instance->host_port + i; } char *node_id = 0; asprintf(&node_id, "%s-%d", instance->name, port); char *url = 0; asprintf(&url, "%s/rests/data/network-topology:network-topology/topology=topology-netconf/node=%s", controller->base_url, node_id); int rc = http_request(url, controller->username, controller->password, "DELETE", "", 0, 0); if(rc != NTS_ERR_OK) { log_error("http_request failed"); ret = NTS_ERR_FAILED; } free(url); free(node_id); } controller_details_free(controller); function_type->mounted_instances--; function_type->instance[function_type->mounted_instances].is_mounted = false; return ret; }