Add supoprt for D release use-case.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / app / manager_operations.c
index e382b02..c847a6c 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <pthread.h>
 
 #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];
+static manager_operation_t *manager_operations;
+static pthread_mutex_t manager_operations_mutex;
+static sem_t manager_operations_sem;
 
-void manager_operations_init(void) {
-    manager_start_port = framework_environment.host_base_port;
-    for(int i = 0; i < 65536; i++) {
-        manager_port[i] = 0;
-    }
-}
+manager_protocol_type_t manager_port[65536];
 
-int manager_start_instance(manager_network_function_type *function_type) {
-    assert(function_type);
-    assert_session();
+static int manager_operations_execute(manager_operation_t *oper);
 
-    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;
+int manager_operations_init(void) {
+    manager_operations = 0;
+    if(pthread_mutex_init(&manager_operations_mutex, NULL) != 0) { 
+        log_error("mutex init has failed\n"); 
+        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(sem_init(&manager_operations_sem, 0, 0) != 0) {
+        log_error("sem init has failed\n"); 
+        return NTS_ERR_FAILED; 
     }
 
-    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;
+    //checkAL ar fi misto sa stim ce porturi sunt si ce porturi nu sunt available...
+    for(int i = 0; i < 1000; i++) {
+        manager_port[i] = MANAGER_PROTOCOL_UNAVAILABLE;
     }
 
-    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;
+    for(int i = 1000; i < 65536; i++) {
+        manager_port[i] = MANAGER_PROTOCOL_UNUSED;
     }
 
     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);
+void manager_operations_loop(void) {
+    int rc;
+
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_sec += 1;
+
+    if(sem_timedwait(&manager_operations_sem, &ts) == 0) {
+        int retries = 10;
+        while(retries) {
+            rc = sr_lock(session_running, NTS_MANAGER_MODULE);
+            if(rc == SR_ERR_OK) {
+                break;
+            }
+            else {
+                sleep(1);
+            }
+            retries--;
+        }
 
-    //first wait for the nc server to be up and running
-    while(check_port_open(instance->docker_ip, instance->docker_port) == false) {
-        usleep(10000);
-    }
+        if(retries == 0) {
+            log_error("sr_lock failed\n");
+            //checkAL ce facem acum ?
+        }
 
-    //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;
+        pthread_mutex_lock(&manager_operations_mutex);
+
+        const char *status = "SUCCESS";
+        char errmsg[256];
+        errmsg[0] = 0;
+
+        while(manager_operations) {
+            //pop operation from list
+            manager_operation_t *oper = manager_operations;
+            manager_operations = manager_operations->next;       
+            
+            //if operation is RPC first update any *other* fields
+
+            rc = manager_operations_execute(oper);
+            if(rc != NTS_ERR_OK) {
+                log_error("manager_operations_execute failed\n");
+                status = "FAILED";
+                strcpy(errmsg, oper->errmsg);
+                manager_operations_free_oper(oper);
+                break;
+            }
+
+            manager_operations_free_oper(oper);
+        }
+
+        for(int i = 0; i < docker_context_count; i++) {
+            //do any reconfig necesarry
+            for(int j = 0; j < manager_context[i].started_instances; j++) {
+                if(manager_context[i].instance[j].is_configured == false) {
+                    rc = manager_actions_config_instance(&manager_context[i], &manager_context[i].instance[j]);
+                    if(rc != NTS_ERR_OK) {
+                        status = "reconfig FAILED";
+                        sprintf(errmsg, "reconfig FAILED - instance %s", manager_context[i].instance[j].container.name);
+                        log_error("%s\n", errmsg);
+                    }
+                }
+            }
+        }
+
+        rc = manager_sr_on_last_operation_status(status, errmsg);
+        if(rc != NTS_ERR_OK) {
+            log_error("manager_sr_on_last_operation_status failed\n");
+        }
+
+        pthread_mutex_unlock(&manager_operations_mutex);
+        rc = sr_unlock(session_running, NTS_MANAGER_MODULE);    //release datastore
+        if(rc != SR_ERR_OK) {
+            log_error("sr_unlock failed\n");
+        }
     }
+}
 
-    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;
+void manager_operations_free(void) {
+    //terminate all containers
+    for(int i = 0; i < docker_context_count; i++) {
+        while(manager_context[i].started_instances) {
+            manager_actions_stop(&manager_context[i]);
+        }
     }
 
-    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;
-    }    
+    sem_destroy(&manager_operations_sem);
+    pthread_mutex_destroy(&manager_operations_mutex);
+}
 
-    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;
+manager_operation_t *manager_operations_new_oper(manager_operation_type_t type) {
+    manager_operation_t *new_oper = malloc(sizeof(manager_operation_t));
+    if(new_oper == 0) {
+        log_error("malloc failed\n");
+        return 0;
     }
 
-    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;
-    }
+    new_oper->type = type;
+
+    new_oper->ft_index = -1;
+    new_oper->function_type = 0;
+
+    new_oper->started_instances = -1;
+    new_oper->mounted_instances = -1;
     
-    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;
-        }
+    new_oper->docker_instance_name = 0;
+    new_oper->docker_version_tag = 0;
+    new_oper->docker_repository = 0;
 
-        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;
-        }
+    new_oper->mount_point_addressing_method = 0;
 
-        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);
-    }
+    new_oper->fault_generation.delay_period = 0;
+    new_oper->fault_generation.delay_period_count = -1;
 
-    instance->is_configured = true;
+    new_oper->netconf.faults_enabled = -1;
+    new_oper->netconf.call_home = -1;
 
-    nc_client_disconnect(nc_client);
+    new_oper->ves.faults_enabled = -1;
+    new_oper->ves.pnf_registration = -1;
+    new_oper->ves.heartbeat_period = -1;
 
-    return NTS_ERR_OK;
+    new_oper->errmsg = 0;
+    new_oper->next = 0;
+    
+    return new_oper;
 }
 
-int manager_stop_instance(manager_network_function_type *function_type) {
-    assert(function_type);
+int manager_operations_free_oper(manager_operation_t *oper) {
+    assert(oper);
 
-    manager_network_function_instance_t *instance = &function_type->instance[function_type->started_instances - 1];
+    free(oper->function_type);
+    free(oper->docker_instance_name);
+    free(oper->docker_repository);
+    free(oper->docker_version_tag);
+    free(oper->mount_point_addressing_method);
+    free(oper->errmsg);
 
-    if(instance->is_mounted) {
-        if(manager_unmount_instance(function_type) != NTS_ERR_OK) {
-            log_error("failed to unmount instance");
-        }
-    }
+    free(oper);
+    return NTS_ERR_OK;
+}
 
-    int rc = docker_device_stop(instance);
-    if(rc != NTS_ERR_OK) {
-        log_error("docker_device_stop failed");
-        return NTS_ERR_FAILED;
-    }
+int manager_operations_begin(void) {
+    return pthread_mutex_lock(&manager_operations_mutex);
+}
 
-    //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;
-        }
+int manager_operations_add(manager_operation_t *oper) {
+    assert(oper);
+
+    if(manager_operations == 0) {
+        manager_operations = oper;
     }
     else {
-        free(function_type->instance);
-        function_type->instance = 0;
+        manager_operation_t *h = manager_operations;
+        while(h->next) {
+            h = h->next;
+        }
+        h->next = oper;
     }
+
     return NTS_ERR_OK;
 }
 
-int manager_mount_instance(manager_network_function_type *function_type) {
-    assert(function_type);
+void manager_operations_finish_and_execute(void) {
+    pthread_mutex_unlock(&manager_operations_mutex);
+    sem_post(&manager_operations_sem);
+}
 
-    manager_network_function_instance_t *instance = &function_type->instance[function_type->mounted_instances];
+void manager_operations_finish_with_error(void) {
+    while(manager_operations) {
+        manager_operation_t *h = manager_operations->next;
+        manager_operations_free_oper(manager_operations);
+        manager_operations = h;
+    }
+    pthread_mutex_unlock(&manager_operations_mutex);
+    sem_post(&manager_operations_sem);
+}
 
-    if(instance->is_mounted == true) {
-        return NTS_ERR_FAILED;
+
+
+int manager_operations_validate(manager_operation_t *oper) {
+    assert(oper);
+    
+    //prepopulate unset values
+    if(oper->docker_instance_name == 0) {
+        oper->docker_instance_name = strdup(manager_context[oper->ft_index].docker_instance_name);
     }
 
-    controller_details_t *controller;
-    controller = controller_details_get(0);
-    if(controller == 0) {
-        log_error("could not get controller detailes");
-        return NTS_ERR_FAILED;
+    if(oper->docker_repository == 0) {
+        oper->docker_repository = strdup(manager_context[oper->ft_index].docker_repository);
     }
 
-    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\"";
+    if(oper->docker_version_tag == 0) {
+        oper->docker_version_tag = strdup(manager_context[oper->ft_index].docker_version_tag);
+    }
 
-        }
-        else {
-            protocol = "TLS";
-            protocol_data = "\
-            \"netconf-node-topology:key-based\" : {\
-                \"netconf-node-topology:username\" : \"netconf\",\
-                \"netconf-node-topology:key-id\" : \""KS_KEY_NAME"\"\
-            }";
-        }
+    if(oper->started_instances == -1) {
+        oper->started_instances = manager_context[oper->ft_index].started_instances;
+    }
 
-        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);
+    if(oper->mounted_instances == -1) {
+        oper->mounted_instances = manager_context[oper->ft_index].mounted_instances;
+    }
 
-        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;
+    //check docker image if exists
+    bool found = false;
+    for(int i = 0; i < docker_context[oper->ft_index].available_images_count; i++) {
+        if(strcmp(docker_context[oper->ft_index].available_images[i].repo, oper->docker_repository) == 0) {
+            if(strcmp(docker_context[oper->ft_index].available_images[i].tag, oper->docker_version_tag) == 0) {
+                found = true;
+                break;
+            }
         }
-
-        free(url);
-        free(node_id);
-        free(json);
     }
 
-    controller_details_free(controller);
-
-    instance->is_mounted = true;
-    function_type->mounted_instances++;
+    if(found == false) {
+        log_error("could not find image: %s/%s:%s\n", oper->docker_repository, docker_context[oper->ft_index].image, oper->docker_version_tag);
+        return NTS_ERR_FAILED;
+    }
 
     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];
+static int manager_operations_execute(manager_operation_t *oper) {
+    assert(oper);
+
+    int k = oper->ft_index;
+    int rc;
+
+    //operation --> actions
+    if(manager_context[k].started_instances > oper->started_instances) {
+        //stop instances
+        while(manager_context[k].started_instances > oper->started_instances) {
+
+            rc = manager_actions_stop(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                asprintf(&oper->errmsg, "stop FAILED - function-type %s", manager_context[k].function_type);
+                log_error("%s\n", oper->errmsg);
+                
+                return NTS_ERR_FAILED;
+            }
+
+            rc = manager_sr_update_context(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                log_error("manager_sr_update_context failed\n");
+            }
+        }
 
-    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;
+    else if(manager_context[k].started_instances < oper->started_instances) {
+        //start instances     
+        while(manager_context[k].started_instances < oper->started_instances) {
+
+            rc = manager_actions_start(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                asprintf(&oper->errmsg, "start FAILED - function-type %s", manager_context[k].function_type);
+                log_error("%s\n", oper->errmsg);
+                return NTS_ERR_FAILED;
+            }
+          
+            rc = manager_sr_update_context(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                log_error("manager_sr_update_context failed\n");
+            }
+
+            rc = manager_actions_config_instance(&manager_context[k], &manager_context[k].instance[manager_context[k].started_instances - 1]);
+            if(rc != NTS_ERR_OK) {
+                asprintf(&oper->errmsg, "config FAILED - instance %s", manager_context[k].instance[manager_context[k].started_instances - 1].container.name);
+                log_error("%s\n", oper->errmsg);
+            }
+        }
     }
 
-    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;
+    if(manager_context[k].mounted_instances > oper->mounted_instances) {
+        //unmount instances
+        while(manager_context[k].mounted_instances > oper->mounted_instances) {
+            rc = manager_actions_unmount(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                asprintf(&oper->errmsg, "unmount FAILED - instance %s", manager_context[k].instance[manager_context[k].mounted_instances - 1].container.name);
+                log_error("%s\n", oper->errmsg);
+                return NTS_ERR_FAILED;
+            }
+
+            rc = manager_sr_update_context(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                log_error("manager_sr_update_context failed\n");
+            }
         }
-        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;
+    }
+    else if(manager_context[k].mounted_instances < oper->mounted_instances) {
+        //mount instances     
+        while(manager_context[k].mounted_instances < oper->mounted_instances) {
+            rc = manager_actions_mount(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                asprintf(&oper->errmsg, "mount FAILED - instance %s", manager_context[k].instance[manager_context[k].mounted_instances].container.name);
+                log_error("%s\n", oper->errmsg);
+                return NTS_ERR_FAILED;
+            }
+            
+            rc = manager_sr_update_context(&manager_context[k]);
+            if(rc != NTS_ERR_OK) {
+                log_error("manager_sr_update_context failed\n");
+            }
         }
-
-        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;
+    return NTS_ERR_OK;
 }