X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=ntsimulator%2Fsrc%2Fntsimulator-manager%2Fsimulator-operations.c;h=6d02c01b33a7be16993aaf04c2da64efd75f1b57;hb=19cb6d014bb190c158f28a7ececd203d50ca1996;hp=872513af9647e0c4234947fdd26fabcd59b2a01f;hpb=29ce368a8b49cb41f3a1640581ff9958ea50ad8c;p=sim%2Fo1-interface.git diff --git a/ntsimulator/src/ntsimulator-manager/simulator-operations.c b/ntsimulator/src/ntsimulator-manager/simulator-operations.c index 872513a..6d02c01 100644 --- a/ntsimulator/src/ntsimulator-manager/simulator-operations.c +++ b/ntsimulator/src/ntsimulator-manager/simulator-operations.c @@ -1,9 +1,19 @@ -/* - * simulator-operations.c - * - * Created on: Mar 9, 2019 - * Author: parallels - */ +/************************************************************************* +* +* Copyright 2019 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. +***************************************************************************/ #include "simulator-operations.h" #include "sysrepo.h" @@ -11,6 +21,7 @@ #include #include #include +#include #include "utils.h" @@ -18,6 +29,11 @@ static CURL *curl; //share the same curl connection for communicating with the Docker Engine API static CURL *curl_odl; //share the same curl connection for mounting servers in ODL +static CURL *curl_k8s; //share the same curl connection for communicating with the K8S cluster + +/* +curl -X POST -H 'Content-Type: application/json' -i http://localhost:5000/scale --data '{"simulatedDevices":2}' +*/ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { @@ -41,35 +57,49 @@ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, voi static void set_curl_common_info() { - struct curl_slist *chunk = NULL; - chunk = curl_slist_append(chunk, "Content-Type: application/json"); - chunk = curl_slist_append(chunk, "Accept: application/json"); + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + chunk = curl_slist_append(chunk, "Accept: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); - curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); + curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl_odl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection - curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 5L); //seconds timeout for an operation + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); } static void set_curl_common_info_odl() { - struct curl_slist *chunk = NULL; - chunk = curl_slist_append(chunk, "Content-Type: application/xml"); - chunk = curl_slist_append(chunk, "Accept: application/xml"); + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/xml"); + chunk = curl_slist_append(chunk, "Accept: application/xml"); curl_easy_setopt(curl_odl, CURLOPT_HTTPHEADER, chunk); curl_easy_setopt(curl_odl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection - curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 5L); //seconds timeout for an operation + curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation curl_easy_setopt(curl_odl, CURLOPT_VERBOSE, 1L); } +static void set_curl_common_info_k8s() +{ + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + chunk = curl_slist_append(chunk, "Accept: application/json"); + + curl_easy_setopt(curl_k8s, CURLOPT_HTTPHEADER, chunk); + + curl_easy_setopt(curl_k8s, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection + curl_easy_setopt(curl_k8s, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation + + curl_easy_setopt(curl_k8s, CURLOPT_VERBOSE, 1L); +} + static cJSON* get_docker_container_bindings(void) { struct MemoryStruct curl_response_mem; @@ -82,8 +112,8 @@ static cJSON* get_docker_container_bindings(void) curl_easy_reset(curl); set_curl_common_info(); - char url[100]; - sprintf(url, "http:/v%s/containers/NTS_Manager/json", getenv("DOCKER_ENGINE_VERSION")); + char url[200]; + sprintf(url, "http:/v%s/containers/%s/json", getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -136,14 +166,87 @@ static cJSON* get_docker_container_bindings(void) return NULL; } -static char* create_docker_container_curl(int base_netconf_port, cJSON* managerBinds) +static cJSON* get_docker_container_network_node(void) +{ + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[200]; + sprintf(url, "http:/v%s/containers/%s/json", getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + return NULL; + } + else + { + cJSON *json_response = cJSON_Parse(curl_response_mem.memory); + + printf("%lu bytes retrieved\n", (unsigned long)curl_response_mem.size); + + if (json_response == NULL) + { + printf("Could not parse JSON response for url=\"%s\"\n", url); + return NULL; + } + + cJSON *hostConfig = cJSON_GetObjectItemCaseSensitive(json_response, "HostConfig"); + + if (hostConfig == NULL) + { + printf("Could not get HostConfig object\n"); + return NULL; + } + + cJSON *networkMode = cJSON_GetObjectItemCaseSensitive(hostConfig, "NetworkMode"); + + if (networkMode == NULL) + { + printf("Could not get NetworkMode object\n"); + return NULL; + } + + cJSON *networkCopy = cJSON_Duplicate(networkMode, 1); + + cJSON_Delete(json_response); + + return networkCopy; + } + + return NULL; +} + +static char* create_docker_container_curl(int base_netconf_port, cJSON* managerBinds, cJSON* networkMode, int device_number) { - if (managerBinds == NULL) + if (managerBinds == NULL) + { + printf("Could not retrieve JSON object: Binds\n"); + return NULL; + } + cJSON *binds = cJSON_Duplicate(managerBinds, 1); + + if (networkMode == NULL) { - printf("Could not retrieve JSON object: Binds\n"); + printf("Could not retrieve JSON object: NetworkMode\n"); return NULL; } - cJSON *binds = cJSON_Duplicate(managerBinds, 1); + cJSON *netMode = cJSON_Duplicate(networkMode, 1); struct MemoryStruct curl_response_mem; @@ -168,26 +271,35 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB cJSON *postDataJson = cJSON_CreateObject(); if (cJSON_AddStringToObject(postDataJson, "Image", models_var) == NULL) - { - printf("Could not create JSON object: Image\n"); - return NULL; - } + { + printf("Could not create JSON object: Image\n"); + return NULL; + } + + char device_name[100]; + sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), device_number); + + if (cJSON_AddStringToObject(postDataJson, "Hostname", device_name) == NULL) + { + printf("Could not create JSON object: Hostname\n"); + return NULL; + } cJSON *hostConfig = cJSON_CreateObject(); if (hostConfig == NULL) - { - printf("Could not create JSON object: HostConfig\n"); - return NULL; - } + { + printf("Could not create JSON object: HostConfig\n"); + return NULL; + } cJSON_AddItemToObject(postDataJson, "HostConfig", hostConfig); cJSON *portBindings = cJSON_CreateObject(); if (portBindings == NULL) - { - printf("Could not create JSON object: PortBindings\n"); - return NULL; - } + { + printf("Could not create JSON object: PortBindings\n"); + return NULL; + } cJSON_AddItemToObject(hostConfig, "PortBindings", portBindings); @@ -243,6 +355,12 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB return NULL; } + if (cJSON_AddStringToObject(labels, "NTS_Manager", getenv("HOSTNAME")) == NULL) + { + printf("Could not create JSON object: NTS Manager\n"); + return NULL; + } + cJSON *env_variables_array = cJSON_CreateArray(); if (env_variables_array == NULL) { @@ -272,18 +390,40 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB } cJSON_AddItemToArray(env_variables_array, env_var_obj_2); - char scripts_dir[200]; - sprintf(scripts_dir, "SCRIPTS_DIR=%s", getenv("SCRIPTS_DIR")); - cJSON *env_var_obj_3 = cJSON_CreateString(scripts_dir); - if (env_var_obj_3 == NULL) - { - printf("Could not create JSON object: Env array object SCRIPTS_DIR\n"); - return NULL; - } - cJSON_AddItemToArray(env_variables_array, env_var_obj_3); + char scripts_dir[200]; + sprintf(scripts_dir, "SCRIPTS_DIR=%s", getenv("SCRIPTS_DIR")); + cJSON *env_var_obj_3 = cJSON_CreateString(scripts_dir); + if (env_var_obj_3 == NULL) + { + printf("Could not create JSON object: Env array object SCRIPTS_DIR\n"); + return NULL; + } + cJSON_AddItemToArray(env_variables_array, env_var_obj_3); + + char k8s_deployment[50]; + sprintf(k8s_deployment, "K8S_DEPLOYMENT=%s", getenv("K8S_DEPLOYMENT")); + cJSON *env_var_obj_4 = cJSON_CreateString(k8s_deployment); + if (env_var_obj_4 == NULL) + { + printf("Could not create JSON object: Env array object K8S_DEPLOYMENT\n"); + return NULL; + } + cJSON_AddItemToArray(env_variables_array, env_var_obj_4); + + char ipv6_enabled[50]; + sprintf(ipv6_enabled, "IPv6Enabled=%s", getenv("IPv6Enabled")); + cJSON *env_var_obj_5 = cJSON_CreateString(ipv6_enabled); + if (env_var_obj_5 == NULL) + { + printf("Could not create JSON object: Env array object IPv6Enabled\n"); + return NULL; + } + cJSON_AddItemToArray(env_variables_array, env_var_obj_5); cJSON_AddItemToObject(hostConfig, "Binds", binds); + cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode); + char *post_data_string = NULL; post_data_string = cJSON_PrintUnformatted(postDataJson); @@ -301,6 +441,11 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB res = curl_easy_perform(curl); + if (post_data_string != NULL) + { + free(post_data_string); + } + if (res != CURLE_OK) { return NULL; @@ -337,37 +482,76 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB static int start_docker_container_curl(char *container_id) { - struct MemoryStruct curl_response_mem; + struct MemoryStruct curl_response_mem; - curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ - curl_response_mem.size = 0; /* no data at this point */ + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ - CURLcode res; + CURLcode res; - curl_easy_reset(curl); - set_curl_common_info(); + curl_easy_reset(curl); + set_curl_common_info(); - char url[100]; - sprintf(url, "http:/v%s/containers/%s/start", getenv("DOCKER_ENGINE_VERSION"), container_id); + char url[100]; + sprintf(url, "http:/v%s/containers/%s/start", getenv("DOCKER_ENGINE_VERSION"), container_id); - curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); - res = curl_easy_perform(curl); + res = curl_easy_perform(curl); - if (res != CURLE_OK) - { - return SR_ERR_OPERATION_FAILED; - } - else - { - printf("Container %s started successfully!\n", container_id); - } + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + else + { + printf("Container %s started successfully!\n", container_id); + } - return SR_ERR_OK; + return SR_ERR_OK; +} + +static int rename_docker_container_curl(char *container_id, int device_number) +{ + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char device_name[100]; + sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), device_number); + + char url[100]; + sprintf(url, "http:/v%s/containers/%s/rename?name=%s", getenv("DOCKER_ENGINE_VERSION"), container_id, + device_name); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + else + { + printf("Container %s renamed successfully to %s!\n", container_id, device_name); + } + + return SR_ERR_OK; } static int kill_and_remove_docker_container_curl(char *container_id) @@ -418,18 +602,23 @@ static int send_mount_device_instance_ssh(char *url, char *credentials, char *de curl_easy_setopt(curl_odl, CURLOPT_URL, url_for_curl); - char post_data_xml[1000]; + char post_data_xml[1500]; sprintf(post_data_xml, - "" - "%s_%d" - "%s" - "%d" - "%s" - "%s" - "false" - "120" - "", + "" + "%s_%d" + "%s" + "%d" + "%s" + "%s" + "false" + "120" + "false" + "1.5" + "20000" + "100" + "2000" + "", device_name, device_port, getenv("NTS_IP"), device_port, "netconf", "netconf"); printf("Post data:\n%s\n", post_data_xml); @@ -471,7 +660,7 @@ static int send_mount_device_instance_tls(char *url, char *credentials, char *de curl_easy_setopt(curl_odl, CURLOPT_URL, url_for_curl); - char post_data_xml[1000]; + char post_data_xml[1500]; sprintf(post_data_xml, "" @@ -487,6 +676,11 @@ static int send_mount_device_instance_tls(char *url, char *credentials, char *de "%d" "false" "120" + "false" + "1.5" + "20000" + "100" + "2000" "", device_name, device_port, getenv("NTS_IP"), "netconf", device_port); @@ -551,7 +745,6 @@ static int send_unmount_device_instance(char *url, char *credentials, char *devi return SR_ERR_OPERATION_FAILED; } - return SR_ERR_OK; } @@ -560,21 +753,27 @@ static int send_mount_device(device_t *current_device, controller_t controller_d { int rc = SR_ERR_OK; bool is_mounted = true; + int port = 0; + + char device_name[100]; + sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), current_device->device_number); //This is where we hardcoded: 7 devices will have SSH connections and 3 devices will have TLS connections - for (int port = 0; port < NETCONF_CONNECTIONS_PER_DEVICE - 3; ++port) + for (int i = 0; i < SSH_CONNECTIONS_PER_DEVICE; ++port, ++i) { + + rc = send_mount_device_instance_ssh(controller_details.url, controller_details.credentials, - current_device->device_id, current_device->netconf_port + port); + device_name, current_device->netconf_port + port); if (rc != SR_ERR_OK) { is_mounted = false; } } - for (int port = NETCONF_CONNECTIONS_PER_DEVICE - 3; port < NETCONF_CONNECTIONS_PER_DEVICE; ++port) + for (int i = 0; i < TLS_CONNECTIONS_PER_DEVICE; ++port, ++i) { rc = send_mount_device_instance_tls(controller_details.url, controller_details.credentials, - current_device->device_id, current_device->netconf_port + port); + device_name, current_device->netconf_port + port); if (rc != SR_ERR_OK) { is_mounted = false; @@ -589,15 +788,17 @@ static int send_mount_device(device_t *current_device, controller_t controller_d static int send_unmount_device(device_t *current_device, controller_t controller_details) { int rc = SR_ERR_OK; + char device_name[100]; + sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), current_device->device_number); for (int port = 0; port < NETCONF_CONNECTIONS_PER_DEVICE; ++port) { rc = send_unmount_device_instance(controller_details.url, controller_details.credentials, - current_device->device_id, current_device->netconf_port + port); + device_name, current_device->netconf_port + port); if (rc != SR_ERR_OK) { printf("Could not send unmount for ODL with url=\"%s\", for device=\"%s\" and port=%d\n", - controller_details.url, current_device->device_id, current_device->netconf_port); + controller_details.url, device_name, current_device->netconf_port); } } current_device->is_mounted = false; @@ -616,13 +817,14 @@ device_stack_t *new_device_stack(void) return stack; } -void push_device(device_stack_t *theStack, char *dev_id, int port) +void push_device(device_stack_t *theStack, char *dev_id, int port, int dev_num) { device_t *new_dev = malloc(sizeof(*new_dev)); if (new_dev) { new_dev->device_id = strdup(dev_id); new_dev->netconf_port = port; + new_dev->device_number = dev_num; new_dev->is_mounted = false; new_dev->operational_state = strdup("not-specified"); @@ -648,39 +850,38 @@ void pop_device(device_stack_t *theStack) int get_netconf_port_next(device_stack_t *theStack) { - if (theStack && theStack->stack_size > 0) { - return theStack->head->netconf_port + NETCONF_CONNECTIONS_PER_DEVICE; - } + if (theStack && theStack->stack_size > 0) { + return theStack->head->netconf_port + NETCONF_CONNECTIONS_PER_DEVICE; + } - return get_netconf_port_base(); + return get_netconf_port_base(); } int get_netconf_port_base() { - int netconf_port_base = 0, rc; + int netconf_port_base; - char *netconf_base_string = getenv("NETCONF_BASE"); - - if (netconf_base_string != NULL) - { - rc = sscanf(netconf_base_string, "%d", &netconf_port_base); - if (rc != 1) - { - printf("Could not get the NETCONF_BASE port! Using the default 30.000...\n"); - netconf_port_base = 30000; - } - } + netconf_port_base = getIntFromString(getenv("NETCONF_BASE"), 50000); - return netconf_port_base; + return netconf_port_base; } +// we start numbering the containers from 0 +int get_device_number_next(device_stack_t *theStack) +{ + if (theStack && theStack->stack_size > 0) { + return theStack->head->device_number + 1; + } + + return 0; +} char *get_id_last_device(device_stack_t *theStack) { - if (theStack && theStack->head) { - return theStack->head->device_id; - } - return NULL; + if (theStack && theStack->head) { + return theStack->head->device_id; + } + return NULL; } int get_current_number_of_mounted_devices(device_stack_t *theStack) @@ -706,6 +907,12 @@ int get_current_number_of_mounted_devices(device_stack_t *theStack) int get_current_number_of_devices(device_stack_t *theStack) { + //TODO implement function for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + return 0; + } + struct MemoryStruct curl_response_mem; curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ @@ -717,8 +924,8 @@ int get_current_number_of_devices(device_stack_t *theStack) set_curl_common_info(); char url[100]; - sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS\"],\"status\":[\"running\"]}", - getenv("DOCKER_ENGINE_VERSION")); + sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS_Manager=%s\"],\"status\":[\"running\"]}", + getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -801,18 +1008,29 @@ char* get_docker_container_operational_state(device_stack_t *theStack, char *con int start_device(device_stack_t *theStack) { int rc = SR_ERR_OK; - static cJSON* managerBindings = NULL; + static cJSON *managerBindings = NULL, *networkMode = NULL; + + if (managerBindings == NULL) + { + managerBindings = get_docker_container_bindings(); + } - if (managerBindings == NULL) + if (networkMode == NULL) { - managerBindings = get_docker_container_bindings(); + networkMode = get_docker_container_network_node(); } int netconf_base = get_netconf_port_next(theStack); + int device_number = get_device_number_next(theStack); - char *dev_id = create_docker_container_curl(netconf_base, managerBindings); + char *dev_id = create_docker_container_curl(netconf_base, managerBindings, networkMode, device_number); + if (dev_id == NULL) + { + printf("ERROR: Could not create docker container!\n"); + return SR_ERR_OPERATION_FAILED; + } - push_device(theStack, dev_id, netconf_base); + push_device(theStack, dev_id, netconf_base, device_number); rc = start_docker_container_curl(dev_id); if (rc != SR_ERR_OK) @@ -820,6 +1038,12 @@ int start_device(device_stack_t *theStack) printf("Could not start device with device_id=\"%s\"\n", dev_id); } + rc = rename_docker_container_curl(dev_id, device_number); + if (rc != SR_ERR_OK) + { + printf("Could not rename device with device_id=\"%s\"\n", dev_id); + } + if (dev_id) { free(dev_id); } @@ -871,6 +1095,28 @@ int cleanup_curl_odl() return SR_ERR_OK; } +int _init_curl_k8s() +{ + curl_k8s = curl_easy_init(); + + if (curl_k8s == NULL) { + printf("cURL initialization error! Aborting call!\n"); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} + +int cleanup_curl_k8s() +{ + if (curl_k8s != NULL) + { + curl_easy_cleanup(curl_k8s); + } + + return SR_ERR_OK; +} + int stop_device(device_stack_t *theStack) { int rc = SR_ERR_OK; @@ -882,6 +1128,12 @@ int stop_device(device_stack_t *theStack) printf("Could not kill and remove docker container with uuid=\"%s\"\n", last_id); } + rc = removeDeviceEntryFromStatusFile(last_id); + if (rc != SR_ERR_OK) + { + printf("Could not remove entry from status file for uuid=\"%s\"\n", last_id); + } + pop_device(theStack); return SR_ERR_OK; @@ -943,6 +1195,13 @@ int unmount_device(device_stack_t *theStack, controller_t controller_list) int get_docker_containers_operational_state_curl(device_stack_t *theStack) { + + //TODO implement function for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + return SR_ERR_OK; + } + int rc = SR_ERR_OK; struct MemoryStruct curl_response_mem; @@ -955,7 +1214,8 @@ int get_docker_containers_operational_state_curl(device_stack_t *theStack) set_curl_common_info(); char url[100]; - sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS\"]}", getenv("DOCKER_ENGINE_VERSION")); + sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS_Manager=%s\"]}", + getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -1001,6 +1261,7 @@ int get_docker_containers_operational_state_curl(device_stack_t *theStack) if (rc != SR_ERR_OK) { printf("Could not set the operational state for the device with uuid=\"%s\"\n", container_id_short); + return SR_ERR_OPERATION_FAILED; } } } @@ -1014,13 +1275,19 @@ int get_docker_containers_operational_state_curl(device_stack_t *theStack) char* get_docker_container_resource_stats(device_stack_t *theStack) { + //TOD need to implement this for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true")) + { + return strdup("CPU=0%;RAM=0MiB"); + } + char line[LINE_BUFSIZE]; int linenr; FILE *pipe; /* Get a pipe where the output from the scripts comes in */ char script[200]; - sprintf(script, "%s/docker_stats.sh", getenv("SCRIPTS_DIR")); + sprintf(script, "/opt/dev/docker_stats.sh %s", getenv("HOSTNAME")); pipe = popen(script, "r"); if (pipe == NULL) { /* check for errors */ @@ -1043,7 +1310,7 @@ char* get_docker_container_resource_stats(device_stack_t *theStack) return NULL; /* return with exit code indicating success. */ } -int notification_delay_period_changed(int period) +int notification_delay_period_changed(sr_val_t *val, size_t count) { char *stringConfiguration = readConfigFileInString(); @@ -1072,26 +1339,66 @@ int notification_delay_period_changed(int period) if (!cJSON_IsObject(notifConfig)) { printf("Configuration JSON is not as expected: notification-config is not an object"); - free(jsonConfig); + cJSON_Delete(jsonConfig); return SR_ERR_OPERATION_FAILED; } cJSON *faultNotifDelay = cJSON_GetObjectItemCaseSensitive(notifConfig, "fault-notification-delay-period"); - if (!cJSON_IsNumber(faultNotifDelay)) + if (!cJSON_IsArray(faultNotifDelay)) { - printf("Configuration JSON is not as expected: fault-notification-delay-period is not an object"); - free(jsonConfig); + printf("Configuration JSON is not as expected: fault-notification-delay-period is not an array."); + cJSON_Delete(jsonConfig); return SR_ERR_OPERATION_FAILED; } - //we set the value of the fault-notification-delay-period object - cJSON_SetNumberValue(faultNotifDelay, period); + cJSON_DeleteItemFromObject(notifConfig, "fault-notification-delay-period"); + + faultNotifDelay = NULL; + + faultNotifDelay = cJSON_CreateArray(); + if (faultNotifDelay == NULL) + { + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + cJSON_AddItemToObject(notifConfig, "fault-notification-delay-period", faultNotifDelay); + + if (val != NULL && count > 0) + { + cJSON *arrayEntry = NULL; + for (size_t i=0; i= 200 && http_response_code <= 226 && http_response_code != CURLE_ABORTED_BY_CALLBACK) + { + printf("cURL succeeded to url=%s\n", url_for_curl); + } + else + { + printf("cURL to url=%s failed with code=%ld\n", url_for_curl, http_response_code); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} + +/* +curl -X POST -H 'Content-Type: application/json' -i http://localhost:5000/scale --data '{"simulatedDevices":2}' +*/ +int send_k8s_scale(int number_of_devices) +{ + CURLcode res; + + curl_easy_reset(curl_k8s); + set_curl_common_info_k8s(); + + char url_for_curl[100]; + sprintf(url_for_curl, "http://localhost:5000/scale"); + + curl_easy_setopt(curl_k8s, CURLOPT_URL, url_for_curl); + + char post_data_json[1500]; + + sprintf(post_data_json, + "{\"simulatedDevices\":%d}", + number_of_devices); + + printf("Post data:\n%s\n", post_data_json); + + curl_easy_setopt(curl_k8s, CURLOPT_POSTFIELDS, post_data_json); + curl_easy_setopt(curl_k8s, CURLOPT_CUSTOMREQUEST, "POST"); + + res = curl_easy_perform(curl_k8s); + if (res != CURLE_OK) + { + printf("cURL failed to url=%s\n", url_for_curl); + } + + long http_response_code = 0; + curl_easy_getinfo (curl_k8s, CURLINFO_RESPONSE_CODE, &http_response_code); + if (http_response_code >= 200 && http_response_code <= 226 && http_response_code != CURLE_ABORTED_BY_CALLBACK) + { + printf("cURL succeeded to url=%s\n", url_for_curl); + } + else + { + printf("cURL to url=%s failed with code=%ld\n", url_for_curl, http_response_code); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} + +int controller_ip_changed(char *new_ip) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *controllerDetails = cJSON_GetObjectItemCaseSensitive(jsonConfig, "controller-details"); + if (!cJSON_IsObject(controllerDetails)) + { + printf("Configuration JSON is not as expected: controller-details is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *controllerIp = cJSON_GetObjectItemCaseSensitive(controllerDetails, "controller-ip"); + if (!cJSON_IsString(controllerIp)) + { + printf("Configuration JSON is not as expected: controller-ip is not a string"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the fault-notification-delay-period object + cJSON_ReplaceItemInObject(controllerDetails, "controller-ip", cJSON_CreateString(new_ip)); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int controller_port_changed(int new_port) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *controllerDetails = cJSON_GetObjectItemCaseSensitive(jsonConfig, "controller-details"); + if (!cJSON_IsObject(controllerDetails)) + { + printf("Configuration JSON is not as expected: controller-details is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *controllerPort = cJSON_GetObjectItemCaseSensitive(controllerDetails, "controller-port"); + if (!cJSON_IsNumber(controllerPort)) + { + printf("Configuration JSON is not as expected: controller-port is not a number."); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the fault-notification-delay-period object + cJSON_SetNumberValue(controllerPort, new_port); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int controller_netconf_call_home_port_changed(int new_port) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *controllerDetails = cJSON_GetObjectItemCaseSensitive(jsonConfig, "controller-details"); + if (!cJSON_IsObject(controllerDetails)) + { + printf("Configuration JSON is not as expected: controller-details is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *netconfCallHomePort = cJSON_GetObjectItemCaseSensitive(controllerDetails, "netconf-call-home-port"); + if (!cJSON_IsNumber(netconfCallHomePort)) + { + printf("Configuration JSON is not as expected: netconf-call-home-port is not a number."); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the fault-notification-delay-period object + cJSON_SetNumberValue(netconfCallHomePort, new_port); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int controller_username_changed(char *new_username) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *controllerDetails = cJSON_GetObjectItemCaseSensitive(jsonConfig, "controller-details"); + if (!cJSON_IsObject(controllerDetails)) + { + printf("Configuration JSON is not as expected: controller-details is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *controllerUsername = cJSON_GetObjectItemCaseSensitive(controllerDetails, "controller-username"); + if (!cJSON_IsString(controllerUsername)) + { + printf("Configuration JSON is not as expected: controller-username is not a string"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the fault-notification-delay-period object + cJSON_ReplaceItemInObject(controllerDetails, "controller-username", cJSON_CreateString(new_username)); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int controller_password_changed(char *new_password) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *controllerDetails = cJSON_GetObjectItemCaseSensitive(jsonConfig, "controller-details"); + if (!cJSON_IsObject(controllerDetails)) + { + printf("Configuration JSON is not as expected: controller-details is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *controllerPassword = cJSON_GetObjectItemCaseSensitive(controllerDetails, "controller-password"); + if (!cJSON_IsString(controllerPassword)) + { + printf("Configuration JSON is not as expected: controller-password is not a string"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the fault-notification-delay-period object + cJSON_ReplaceItemInObject(controllerDetails, "controller-password", cJSON_CreateString(new_password)); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int netconf_call_home_changed(cJSON_bool new_bool) +{ + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *netconfCallHome = cJSON_GetObjectItemCaseSensitive(jsonConfig, "netconf-call-home"); + if (!cJSON_IsBool(netconfCallHome)) + { + printf("Configuration JSON is not as expected: netconf-call-home is not a bool."); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the ves-registration object + cJSON_ReplaceItemInObject(jsonConfig, "netconf-call-home", cJSON_CreateBool(new_bool)); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +static int start_device_notification(char *exec_id) +{ + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[100]; + sprintf(url, "http:/v%s/exec/%s/start", getenv("DOCKER_ENGINE_VERSION"), exec_id); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + cJSON *postDataJson = cJSON_CreateObject(); + + if (cJSON_AddFalseToObject(postDataJson, "Detach") == NULL) + { + printf("Could not create JSON object: Detach\n"); + return SR_ERR_OPERATION_FAILED; + } + + if (cJSON_AddFalseToObject(postDataJson, "Tty") == NULL) + { + printf("Could not create JSON object: Tty\n"); + return SR_ERR_OPERATION_FAILED; + } + + char *post_data_string = NULL; + + post_data_string = cJSON_PrintUnformatted(postDataJson); + + printf("Post data JSON:\n%s\n", post_data_string); + + if (postDataJson != NULL) + { + cJSON_Delete(postDataJson); + } + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data_string); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (post_data_string != NULL) + { + free(post_data_string); + } + + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + else + { + cJSON *json_response = cJSON_Parse(curl_response_mem.memory); + const cJSON *message = NULL; + + printf("%lu bytes retrieved\n", (unsigned long)curl_response_mem.size); + + message = cJSON_GetObjectItemCaseSensitive(json_response, "message"); + + if (cJSON_IsString(message) && (message->valuestring != NULL)) + { + printf("Message: \"%s\"\n", message->valuestring); + } + + cJSON_Delete(json_response); + } + + return SR_ERR_OK; +} + +static int inspect_device_notification_execution(char *exec_id) +{ + int rc = SR_ERR_OK; + + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[100]; + sprintf(url, "http:/v%s/exec/%s/json", getenv("DOCKER_ENGINE_VERSION"), exec_id); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + rc = SR_ERR_OPERATION_FAILED; + } + else + { + cJSON *json_response = cJSON_Parse(curl_response_mem.memory); + const cJSON *exit_code = NULL; + + exit_code = cJSON_GetObjectItemCaseSensitive(json_response, "ExitCode"); + + if (cJSON_IsNumber(exit_code)) + { + rc = exit_code->valueint; + } + else + { + printf("Exit code is not a number!\n"); + rc = SR_ERR_OPERATION_FAILED; + } + + cJSON_Delete(json_response); + } + + return rc; +} + +int invoke_device_notification(char *device_id, char *module_name, char *notification_string) +{ + int rc = SR_ERR_OK; + + printf("Device-name = %s\nModule-name = %s\nNotification-object = %s\n", device_id, module_name, notification_string); + + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[100]; + sprintf(url, "http:/v%s/containers/%s/exec", getenv("DOCKER_ENGINE_VERSION"), device_id); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + cJSON *postDataJson = cJSON_CreateObject(); + + if (cJSON_AddFalseToObject(postDataJson, "AtttachStdin") == NULL) + { + printf("Could not create JSON object: AtttachStdin\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + if (cJSON_AddTrueToObject(postDataJson, "AtttachStdout") == NULL) + { + printf("Could not create JSON object: AtttachStdout\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + if (cJSON_AddTrueToObject(postDataJson, "AtttachStderr") == NULL) + { + printf("Could not create JSON object: AtttachStderr\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + if (cJSON_AddTrueToObject(postDataJson, "Privileged") == NULL) + { + printf("Could not create JSON object: Privileged\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + if (cJSON_AddStringToObject(postDataJson, "User", "root") == NULL) + { + printf("Could not create JSON object: User\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + cJSON *cmd_array = cJSON_CreateArray(); + if (cmd_array == NULL) + { + printf("Could not create JSON object: Cmd array\n"); + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + + cJSON_AddItemToObject(postDataJson, "Cmd", cmd_array); + + cJSON *cmd_string_1 = cJSON_CreateString("sh"); + cJSON_AddItemToArray(cmd_array, cmd_string_1); + + cJSON *cmd_string_2 = cJSON_CreateString("-c"); + cJSON_AddItemToArray(cmd_array, cmd_string_2); + + char string_command[500]; + sprintf(string_command, "/usr/local/bin/generic-notifications %s '%s'", module_name, notification_string); + + cJSON *cmd_string_3 = cJSON_CreateString(string_command); + cJSON_AddItemToArray(cmd_array, cmd_string_3); + + char *post_data_string = NULL; + + post_data_string = cJSON_PrintUnformatted(postDataJson); + + printf("Post data JSON:\n%s\n", post_data_string); + + if (postDataJson != NULL) + { + cJSON_Delete(postDataJson); + } + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data_string); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (post_data_string != NULL) + { + free(post_data_string); + } + + if (res != CURLE_OK) + { + rc = SR_ERR_OPERATION_FAILED; + goto cleanup; + } + else + { + cJSON *json_response = cJSON_Parse(curl_response_mem.memory); + const cJSON *exec_id = NULL; + + exec_id = cJSON_GetObjectItemCaseSensitive(json_response, "Id"); + + if (cJSON_IsString(exec_id) && (exec_id->valuestring != NULL)) + { + printf("Exec id: \"%s\"\n", exec_id->valuestring); + + rc = start_device_notification(exec_id->valuestring); + if (rc != SR_ERR_OK) + { + printf("Could not start the execution of the notification...\n"); + } + + sleep(1); + + rc = inspect_device_notification_execution(exec_id->valuestring); + } + + cJSON_Delete(json_response); + } + +cleanup: + if (device_id != NULL) + { + free(device_id); + } + if (module_name != NULL) + { + free(module_name); + } + if (notification_string != NULL) + { + free(notification_string); + } + + return rc; +} + +int pull_docker_image_of_simulated_device() +{ + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[300]; + sprintf(url, "http:/v%s/images/create?fromImage=%s", getenv("DOCKER_ENGINE_VERSION"), getenv("MODELS_IMAGE")); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} \ No newline at end of file