/************************************************************************* * * 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 #include "core/framework.h" #include "core/docker.h" #include "core/session.h" #include "core/nc_config.h" #include "utils/nc_client.h" #include "utils/http_client.h" #include "utils/nts_utils.h" int manager_actions_start(manager_context_t *ctx) { assert(ctx); int started_instances = ctx->started_instances; char instance_name[512]; sprintf(instance_name, "%s-%d", ctx->docker_instance_name, started_instances); uint16_t netconf_ssh_port = framework_environment.host.ssh_base_port; uint16_t netconf_tls_port = framework_environment.host.tls_base_port; uint16_t ftp_port = framework_environment.host.ftp_base_port; uint16_t sftp_port = framework_environment.host.sftp_base_port; if(framework_environment.settings.ssh_connections) { while((netconf_ssh_port) && (manager_port[netconf_ssh_port] != MANAGER_PROTOCOL_UNUSED)) { netconf_ssh_port++; } for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { if(manager_port[i + netconf_ssh_port] != MANAGER_PROTOCOL_UNUSED) { log_error("no ports available for operation for ssh\n"); manager_sr_notif_send_instance_changed("start FAILED - no ports available for ssh", ctx->function_type, instance_name, 0); return NTS_ERR_FAILED; } } for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_NETCONF_SSH; } } if(framework_environment.settings.tls_connections) { while((netconf_tls_port) && (manager_port[netconf_tls_port] != MANAGER_PROTOCOL_UNUSED)) { netconf_tls_port++; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { if(manager_port[i + netconf_tls_port] != MANAGER_PROTOCOL_UNUSED) { log_error("no ports available for operation for tls\n"); manager_sr_notif_send_instance_changed("start FAILED - no ports available for tls", ctx->function_type, instance_name, 0); for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } return NTS_ERR_FAILED; } } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + netconf_tls_port] = MANAGER_PROTOCOL_NETCONF_TLS; } } if(framework_environment.settings.ftp_connections) { while((ftp_port) && (manager_port[ftp_port] != MANAGER_PROTOCOL_UNUSED)) { ftp_port++; } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { if(manager_port[i + ftp_port] != MANAGER_PROTOCOL_UNUSED) { log_error("no ports available for operation for ftp\n"); manager_sr_notif_send_instance_changed("start FAILED - no ports available for ftp", ctx->function_type, instance_name, 0); for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + netconf_tls_port] = MANAGER_PROTOCOL_UNUSED; } return NTS_ERR_FAILED; } } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { manager_port[i + ftp_port] = MANAGER_PROTOCOL_FTP; } } if(framework_environment.settings.sftp_connections) { while((sftp_port) && (manager_port[sftp_port] != MANAGER_PROTOCOL_UNUSED)) { sftp_port++; } for(int i = 0; i < framework_environment.settings.sftp_connections; i++) { if(manager_port[i + sftp_port] != MANAGER_PROTOCOL_UNUSED) { log_error("no ports available for operation for sftp\n"); manager_sr_notif_send_instance_changed("start FAILED - no ports available for sftp", ctx->function_type, instance_name, 0); for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + netconf_tls_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { manager_port[i + ftp_port] = MANAGER_PROTOCOL_UNUSED; } return NTS_ERR_FAILED; } } for(int i = 0; i < framework_environment.settings.sftp_connections; i++) { manager_port[i + sftp_port] = MANAGER_PROTOCOL_SFTP; } } ctx->instance = (manager_network_function_instance_t *)realloc(ctx->instance, sizeof(manager_network_function_instance_t) * (started_instances + 1)); if(ctx->instance == 0) { log_error("realloc failed\n"); manager_sr_notif_send_instance_changed("start FAILED - realloc error", ctx->function_type, instance_name, 0); for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + netconf_tls_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { manager_port[i + ftp_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.sftp_connections; i++) { manager_port[i + sftp_port] = MANAGER_PROTOCOL_UNUSED; } return NTS_ERR_FAILED; } manager_network_function_instance_t *instance = &ctx->instance[started_instances]; instance->is_init = false; instance->is_configured = false; instance->is_mounted = false; instance->mount_point_addressing_method = strdup(ctx->mount_point_addressing_method); int rc = docker_start(instance_name, ctx->docker_version_tag, ctx->docker->image, ctx->docker_repository, netconf_ssh_port, netconf_tls_port, ftp_port, sftp_port, &instance->container); if(rc != NTS_ERR_OK) { log_error("docker_start failed\n"); manager_sr_notif_send_instance_changed("start FAILED - Docker start error", ctx->function_type, instance_name, 0); for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + netconf_tls_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { manager_port[i + ftp_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.sftp_connections; i++) { manager_port[i + sftp_port] = MANAGER_PROTOCOL_UNUSED; } return NTS_ERR_FAILED; } ctx->started_instances++; manager_sr_notif_send_instance_changed("start SUCCESS", ctx->function_type, instance_name, instance); log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" started (%s); ports host(docker): ", instance_name, instance->mount_point_addressing_method); if(framework_environment.settings.ssh_connections > 1) { log_add(1, "NETCONF SSH: %d-%d(%d-%d)", instance->container.host_netconf_ssh_port, (instance->container.host_netconf_ssh_port + framework_environment.settings.ssh_connections - 1), instance->container.docker_netconf_ssh_port, (instance->container.docker_netconf_ssh_port + framework_environment.settings.ssh_connections - 1)); } else if(framework_environment.settings.ssh_connections == 1) { log_add(1, "NETCONF SSH: %d(%d)", instance->container.host_netconf_ssh_port, instance->container.docker_netconf_ssh_port); } else { log_add(1, "NETCONF SSH: disabled"); } if(framework_environment.settings.tls_connections > 1) { log_add(1, " | NETCONF TLS: %d-%d(%d-%d)", instance->container.host_netconf_tls_port, (instance->container.host_netconf_tls_port + framework_environment.settings.tls_connections - 1), instance->container.docker_netconf_tls_port, (instance->container.docker_netconf_tls_port + framework_environment.settings.tls_connections - 1)); } else if(framework_environment.settings.tls_connections == 1) { log_add(1, " | NETCONF TLS: %d(%d)", instance->container.host_netconf_tls_port, instance->container.docker_netconf_tls_port); } else { log_add(1, " | NETCONF TLS: disabled"); } if(framework_environment.settings.ftp_connections > 1) { log_add(1, " | FTP: %d-%d(%d-%d)", instance->container.host_ftp_port, (instance->container.host_ftp_port + framework_environment.settings.ftp_connections - 1), instance->container.docker_ftp_port, (instance->container.docker_ftp_port + framework_environment.settings.ftp_connections - 1)); } else if(framework_environment.settings.ftp_connections == 1) { log_add(1, " | FTP: %d(%d)", instance->container.host_ftp_port, instance->container.docker_ftp_port); } else { log_add(1, " | FTP: disabled"); } if(framework_environment.settings.sftp_connections > 1) { log_add(1, " | SFTP: %d-%d(%d-%d)", instance->container.host_sftp_port, (instance->container.host_sftp_port + framework_environment.settings.sftp_connections - 1), instance->container.docker_sftp_port, (instance->container.docker_sftp_port + framework_environment.settings.sftp_connections - 1)); } else if(framework_environment.settings.sftp_connections == 1) { log_add(1, " | SFTP: %d(%d)", instance->container.host_sftp_port, instance->container.docker_sftp_port); } else { log_add(1, " | SFTP: disabled"); } log_add(1, "\n"); return NTS_ERR_OK; } int manager_actions_config_instance(manager_context_t *ctx, manager_network_function_instance_t *instance) { assert(ctx); assert(instance); //first wait for the nc server to be up and running int retries = 0; while(check_port_open(instance->container.docker_ip, CLIENT_CONFIG_TLS_PORT) == false) { usleep(50000); retries++; if(retries >= 200) { log_error("manager_actions_config_instance() could not connect to %s, as port is not open\n", instance->container.name); return NTS_ERR_FAILED; } } //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\n"); manager_sr_notif_send_instance_changed("config FAILED - libyang", ctx->function_type, instance->container.name, instance); 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\n"); manager_sr_notif_send_instance_changed("config FAILED - libyang", ctx->function_type, instance->container.name, instance); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } char xpath_s[512]; sprintf(xpath_s, "/nts-manager:simulation/network-functions/network-function[function-type='%s']", ctx->function_type); rc = lyd_utils_dup(session_running, xpath_s, "/nts-network-function:simulation/network-function", &local_tree); if(rc != NTS_ERR_OK) { log_error("lyd_utils_dup failed\n"); manager_sr_notif_send_instance_changed("config FAILED - libyang", ctx->function_type, instance->container.name, instance); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } nc_client_t *nc_client = nc_client_tls_connect(instance->container.docker_ip, CLIENT_CONFIG_TLS_PORT); // nc_client_t *nc_client = nc_client_ssh_connect(instance->container.docker_ip, instance->container.docker_port, "netconf", "netconf!"); if(nc_client == 0) { log_error("nc_client_tls_connect\n"); manager_sr_notif_send_instance_changed("config FAILED - netconf client connect", ctx->function_type, instance->container.name, instance); lyd_free_withsiblings(local_tree); return NTS_ERR_FAILED; } rc += nc_client_edit_batch(nc_client, local_tree, 5000); lyd_free_withsiblings(local_tree); if(rc != NTS_ERR_OK) { log_error("nc_client_edit_batch failed %d\n", rc); manager_sr_notif_send_instance_changed("config FAILED - netconf edit batch", ctx->function_type, instance->container.name, instance); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } if(instance->is_init == false) { //run datastore-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-populate", 0, 0, 0); if(rpc_node == 0) { log_error("failed to create rpc node\n"); manager_sr_notif_send_instance_changed("config FAILED - populate RPC", ctx->function_type, instance->container.name, instance); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } rpcout = nc_client_send_rpc(nc_client, rpc_node, 10000); if(rpcout == 0) { log_error("datastore-populate rpc failed\n"); manager_sr_notif_send_instance_changed("config FAILED - datastore populate RPC", ctx->function_type, instance->container.name, instance); 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/start-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\n"); manager_sr_notif_send_instance_changed("config FAILED - feature-control RPC", ctx->function_type, instance->container.name, instance); nc_client_disconnect(nc_client); return NTS_ERR_FAILED; } rpcout = nc_client_send_rpc(nc_client, rpc_node, 10000); if(rpcout == 0) { log_error("feature-control rpc failed\n"); manager_sr_notif_send_instance_changed("config FAILED - feature-control RPC", ctx->function_type, instance->container.name, instance); 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_init = true; instance->is_configured = true; nc_client_disconnect(nc_client); manager_sr_notif_send_instance_changed("config SUCCESS", ctx->function_type, instance->container.name, instance); log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" configured via netconf\n", instance->container.name); return NTS_ERR_OK; } int manager_actions_stop(manager_context_t *ctx) { assert(ctx); manager_network_function_instance_t *instance = &ctx->instance[ctx->started_instances - 1]; char instance_name[512]; strcpy(instance_name, instance->container.name); if(instance->is_mounted) { if(manager_actions_unmount(ctx) != NTS_ERR_OK) { log_error("failed to unmount instance\n"); } } int rc = docker_stop(&instance->container); if(rc != NTS_ERR_OK) { log_error("docker_device_stop failed\n"); manager_sr_notif_send_instance_changed("stop FAILED - Docker error", ctx->function_type, instance_name, 0); return NTS_ERR_FAILED; } //free ports for(int i = 0; i < framework_environment.settings.ssh_connections; i++) { manager_port[i + ctx->instance[ctx->started_instances - 1].container.host_netconf_ssh_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.tls_connections; i++) { manager_port[i + ctx->instance[ctx->started_instances - 1].container.host_netconf_tls_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.ftp_connections; i++) { manager_port[i + ctx->instance[ctx->started_instances - 1].container.host_ftp_port] = MANAGER_PROTOCOL_UNUSED; } for(int i = 0; i < framework_environment.settings.sftp_connections; i++) { manager_port[i + ctx->instance[ctx->started_instances - 1].container.host_sftp_port] = MANAGER_PROTOCOL_UNUSED; } free(instance->mount_point_addressing_method); if(ctx->started_instances > 1) { ctx->instance = (manager_network_function_instance_t *)realloc(ctx->instance, sizeof(manager_network_function_instance_t) * ctx->started_instances); if(ctx->instance == 0) { log_error("realloc failed\n"); manager_sr_notif_send_instance_changed("stop FAILED - realloc", ctx->function_type, instance_name, 0); return NTS_ERR_FAILED; } } else { free(ctx->instance); ctx->instance = 0; } ctx->started_instances--; manager_sr_notif_send_instance_changed("stop SUCCESS", ctx->function_type, instance_name, 0); log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" stopped\n", instance_name); return NTS_ERR_OK; } int manager_actions_mount(manager_context_t *ctx) { assert(ctx); manager_network_function_instance_t *instance = &ctx->instance[ctx->mounted_instances]; if(instance->is_mounted == true) { manager_sr_notif_send_instance_changed("mount SUCCESS - already mounted", ctx->function_type, instance->container.name, instance); return NTS_ERR_OK; } controller_details_t *controller; controller = controller_details_get(0); if(controller == 0) { log_error("could not get controller details\n"); manager_sr_notif_send_instance_changed("mount FAILED - no controller details", ctx->function_type, instance->container.name, instance); return NTS_ERR_FAILED; } for(int i = 0; i < (framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections); i++) { char *protocol; char *protocol_data; char *ip = 0; uint16_t port = 0; if(instance->mount_point_addressing_method[0] == 'd') { ip = instance->container.docker_ip; } else { ip = instance->container.host_ip; } if(i < framework_environment.settings.ssh_connections) { protocol = "SSH"; protocol_data = "\ \"network-topology:netconf-node-topology:username\": \"netconf\",\ \"network-topology:netconf-node-topology:password\": \"netconf!\""; if(instance->mount_point_addressing_method[0] == 'd') { port = instance->container.docker_netconf_ssh_port + i; } else { port = instance->container.host_netconf_ssh_port + i; } } else { protocol = "TLS"; protocol_data = "\ \"netconf-node-topology:key-based\" : {\ \"netconf-node-topology:username\" : \"netconf\",\ \"netconf-node-topology:key-id\" : \""KS_KEY_NAME"\"\ }"; if(instance->mount_point_addressing_method[0] == 'd') { port = instance->container.docker_netconf_tls_port + i - framework_environment.settings.ssh_connections; } else { port = instance->container.host_netconf_tls_port + i - framework_environment.settings.ssh_connections; } } 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 node_id[128]; char json[4096]; if(framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections > 1) { sprintf(node_id, "%s-%d", instance->container.name, port); } else { sprintf(node_id, "%s", instance->container.name); } sprintf(json, json_template, node_id, ip, port, protocol, protocol_data); char url[512]; sprintf(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\n"); controller_details_free(controller); manager_sr_notif_send_instance_changed("mount FAILED - REST request", ctx->function_type, instance->container.name, instance); return NTS_ERR_FAILED; } } controller_details_free(controller); instance->is_mounted = true; ctx->mounted_instances++; manager_sr_notif_send_instance_changed("mount SUCCESS", ctx->function_type, instance->container.name, instance); log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" mounted\n", instance->container.name); return NTS_ERR_OK; } int manager_actions_unmount(manager_context_t *ctx) { assert(ctx); int ret = NTS_ERR_OK; manager_network_function_instance_t *instance = &ctx->instance[ctx->mounted_instances - 1]; if(instance->is_mounted == false) { log_error("tried to unmount an unmounted instance\n"); manager_sr_notif_send_instance_changed("unmount FAILED - already unmounted", ctx->function_type, instance->container.name, instance); return NTS_ERR_OK; } controller_details_t *controller; controller = controller_details_get(0); if(controller == 0) { log_error("could not get controller details\n"); manager_sr_notif_send_instance_changed("unmount FAILED - no controller details", ctx->function_type, instance->container.name, instance); return NTS_ERR_FAILED; } for(int i = 0; i < (framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections); i++) { uint16_t port = 0; if(i < framework_environment.settings.ssh_connections) { if(instance->mount_point_addressing_method[0] == 'd') { port = instance->container.docker_netconf_ssh_port + i; } else { port = instance->container.host_netconf_ssh_port + i; } } else { if(instance->mount_point_addressing_method[0] == 'd') { port = instance->container.docker_netconf_tls_port + i - framework_environment.settings.ssh_connections; } else { port = instance->container.host_netconf_tls_port + i - framework_environment.settings.ssh_connections; } } char node_id[128]; if(framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections > 1) { sprintf(node_id, "%s-%d", instance->container.name, port); } else { sprintf(node_id, "%s", instance->container.name); } char url[512]; sprintf(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\n"); manager_sr_notif_send_instance_changed("unmount FAILED - REST request", ctx->function_type, instance->container.name, instance); ret = NTS_ERR_FAILED; } } controller_details_free(controller); ctx->mounted_instances--; ctx->instance[ctx->mounted_instances].is_mounted = false; manager_sr_notif_send_instance_changed("unmount SUCCESS", ctx->function_type, instance->container.name, instance); log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" unmounted\n", instance->container.name); return ret; }