Rewrite NTS Framework.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / docker.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
20 #include "docker.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include "utils/http_client.h"
24 #include "core/framework.h"
25 #include "core/session.h"
26 #include "core/context.h"
27 #include <sysrepo.h>
28 #include <dirent.h>
29 #include <assert.h>
30 #include <sys/sysinfo.h>
31
32 #include <cjson/cJSON.h>
33
34 #define DOCKER_SOCK_FNAME       "/var/run/docker.sock"
35
36 static cJSON *docker_network_info = 0;
37
38 struct installable_module {
39     char *name;
40     char *fullpath;
41     bool installed;
42     bool submodule;
43 };
44
45 typedef struct {
46     char *name;
47     char *value;
48 } environment_var_t;
49
50 static environment_var_t *docker_environment_var;
51 static int docker_environment_var_count = 0;
52
53 static int get_installable_modules(struct installable_module **modules);    //list available modules for install
54 static void list_yangs(const char *path, struct installable_module **modules, int *total);
55
56 static char *docker_parse_json_message(const char *json_string);
57
58 static int docker_container_create(const char *image, manager_network_function_instance_t *instance);
59 static int docker_container_start(manager_network_function_instance_t *instance);
60 static int docker_container_inspect(manager_network_function_instance_t *instance);
61
62
63 bool docker_container_init(void) {
64     int rc;
65
66     sr_log_stderr(SR_LL_NONE);
67     log_message(1, "Entering container-init mode...\n");
68
69     // connect to sysrepo
70     rc = sr_connect(0, &session_connection);
71     if(SR_ERR_OK != rc) {
72         log_error("sr_connect failed");
73         return false;
74     }
75
76     /* get context */
77     session_context = (struct ly_ctx *)sr_get_context(session_connection);
78     if(session_context == 0) {
79         log_error("sr_get_context failed");
80         return false;
81     }
82
83     /* install yang files */
84     log_message(1, "Installing yang files...\n");
85     struct installable_module *modules;
86     int total_modules = get_installable_modules(&modules);
87     log_message(1, "Found total modules: %d\n", total_modules);
88
89     int old_failed_installations = 1;
90     int failed_installations = 0;
91     int install_round = 0;
92     while(failed_installations != old_failed_installations) {
93         old_failed_installations = failed_installations;
94         failed_installations = 0;
95         install_round++;
96         for(int i = 0; i < total_modules; i++) {
97             if(!modules[i].installed) {
98                 modules[i].submodule = context_yang_is_module(modules[i].fullpath);
99                 if(!modules[i].submodule) {
100                     if(!framework_is_docker_excluded_module(modules[i].name)) {
101                         log_message(1, "[round %d] trying to install module %s from %s... ", install_round, modules[i].name, modules[i].fullpath);
102                         if(!context_module_install(modules[i].name, modules[i].fullpath)) {
103                             failed_installations++;
104                             log_message(1, LOG_COLOR_BOLD_YELLOW"failed"LOG_COLOR_RESET"\n");
105                         }
106                         else {
107                             log_message(1, LOG_COLOR_BOLD_GREEN"done"LOG_COLOR_RESET"\n");
108                             modules[i].installed = true;
109                         }
110                     }
111                     else {
112                         log_message(1, "[round %d] not installing module %s as it's excluded in config.\n", install_round, modules[i].name);
113                         modules[i].installed = true;
114                     }
115                 }
116                 else {
117                     log_message(1, "[round %d] %s is a submodule... "LOG_COLOR_BOLD_YELLOW"skipping"LOG_COLOR_RESET"\n", install_round, modules[i].name);
118                     modules[i].installed = true;
119                 }
120             }
121         }
122     }
123
124     if(failed_installations != 0) {
125         log_error("Failed to install all modules in %d rounds...", install_round);
126         return false;
127     }
128     else {
129         log_message(1, LOG_COLOR_BOLD_GREEN"successfully"LOG_COLOR_RESET" installed "LOG_COLOR_BOLD_GREEN"ALL"LOG_COLOR_RESET" modules in "LOG_COLOR_BOLD_YELLOW"%d"LOG_COLOR_RESET" rounds\n", (install_round - 1));
130     }
131
132     //set access for all installed modules
133     log_message(1, "Setting access configuration for installed modules... ");
134     for(int i = 0; i < total_modules; i++) {
135         if((!framework_is_docker_excluded_module(modules[i].name)) && (!modules[i].submodule)) {
136             if(!context_module_set_access(modules[i].name)) {
137                 log_error("Failed to set access to module %s...", modules[i].name);
138                 return false;
139             }
140         }
141     }
142     log_message(1, LOG_COLOR_BOLD_GREEN"done"LOG_COLOR_RESET"\n");
143
144     //cleanup module-install used memory
145     for(int i = 0; i < total_modules; i++) {
146         free(modules[i].name);
147         free(modules[i].fullpath);
148     }
149     free(modules);
150
151     //get context
152     session_context = (struct ly_ctx *)sr_get_context(session_connection);
153     if(session_context == 0) {
154         log_error("sr_get_context failed");
155         return false;
156     }
157
158     //init context so we can see all the available modules, features, etc
159     rc = context_init(session_context);
160     if(rc != 0) {
161         log_error("context_init() failed");
162         return false;
163     }
164
165     /* enable features */
166     log_message(1, "Enabling yang features...\n");
167     char **available_features;
168     int total_available_features;
169     total_available_features = context_get_features(&available_features);
170     log_message(1, "Found total features: %d\n", total_available_features);
171     for(int i = 0; i < total_available_features; i++) {
172         log_message(1, "feature %s: ", available_features[i]);
173
174         if(!context_get_feature_enabled(available_features[i])) {
175             if(!framework_is_docker_excluded_feature(available_features[i])) {
176                 if(context_feature_enable(available_features[i])) {
177                     log_message(1, "enabling... "LOG_COLOR_BOLD_GREEN"done"LOG_COLOR_RESET"\n");
178                 }
179                 else {
180                     log_error("enabling... failed\n");
181                 }
182             }
183             else {
184                 log_message(1, "excluded in config, skipping\n");
185             }
186         }
187         else {
188             log_message(1, "already "LOG_COLOR_BOLD_GREEN"enabled"LOG_COLOR_RESET", skipping.\n");
189         }
190     }
191     for(int i = 0; i < total_available_features; i++) {
192         free(available_features[i]);
193     }
194     free(available_features);
195
196     sr_disconnect(session_connection);
197     context_free();
198
199     log_message(1, LOG_COLOR_BOLD_GREEN"ntsim successfully initialized Docker container"LOG_COLOR_RESET"\n");
200     return true;
201 }
202
203 static int get_installable_modules(struct installable_module **modules) {
204     int total = 0;
205     *modules = 0;
206     list_yangs("/opt/dev/deploy/yang", modules, &total);
207     return total;
208 }
209
210 static void list_yangs(const char *path, struct installable_module **modules, int *total) {
211     DIR *d;
212     struct dirent *dir;
213     d = opendir(path);
214     if(d) {
215         while((dir = readdir(d)) != NULL) {
216             if(dir->d_type == DT_DIR) {
217                 if(strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0)
218                 {
219                     char new_path[1024];
220                     snprintf(new_path, sizeof(new_path), "%s/%s", path, dir->d_name);
221                     list_yangs(new_path, modules, total);
222                 }
223             } else {
224                 if(strstr(dir->d_name, ".yang") != 0) {
225                     *modules = (struct installable_module *)realloc(*modules, sizeof(struct installable_module) * (*total + 1));
226                     if(!*modules) {
227                         log_error("could not realloc");
228                         return;
229                     }
230
231                     (*modules)[*total].name = (char*)malloc(sizeof(char) * (strlen(dir->d_name) + 1));
232                     if(!(*modules)[*total].name) {
233                         log_error("could not alloc");
234                         return;
235                     }
236                     strcpy((*modules)[*total].name, dir->d_name);
237                     (*modules)[*total].name[strlen(dir->d_name) - 5] = 0;   //extract ".yang"
238                     char *rev = strstr((*modules)[*total].name, "@");
239                     if(rev) { //extract revision, if exists
240                         *rev = 0;
241                     }
242
243                     (*modules)[*total].fullpath = (char*)malloc(sizeof(char) * (strlen(path) + 1 + strlen(dir->d_name) + 1));
244                     if(!(*modules)[*total].fullpath) {
245                         log_error("could not alloc");
246                         return;
247                     }
248                     sprintf((*modules)[*total].fullpath, "%s/%s", path, dir->d_name);
249
250                     (*modules)[*total].installed = false;
251                     (*modules)[*total].submodule = false;
252
253                     (*total)++;
254                 }
255             }
256         }
257         closedir(d);
258     }
259 }
260
261 int docker_device_init(void) {
262     char *response = 0;
263     char *url = 0;
264     asprintf(&url, "http://v%s/containers/%s/json", framework_environment.docker_engine_version, framework_environment.hostname);
265
266     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", 0, 0, &response);
267     if(rc != NTS_ERR_OK) {
268         log_error("http_socket_request failed");
269         return NTS_ERR_FAILED;
270     }
271
272     cJSON *json_response = cJSON_Parse(response);
273     free(response);
274
275     if(json_response == 0) {
276         log_error("could not parse JSON response for url=\"%s\"", url);
277         return NTS_ERR_FAILED;
278     }
279
280     cJSON *hostConfig = cJSON_GetObjectItemCaseSensitive(json_response, "HostConfig");
281     if(hostConfig == 0) {
282         log_error("could not get HostConfig object");
283         return NTS_ERR_FAILED;
284     }
285
286     cJSON *networkMode = cJSON_GetObjectItemCaseSensitive(hostConfig, "NetworkMode");
287     if(networkMode == 0) {
288         log_error("could not get NetworkMode object");
289         return NTS_ERR_FAILED;
290     }
291
292     docker_network_info = cJSON_Duplicate(networkMode, 1);
293     cJSON_Delete(json_response);
294
295     log_message(2, "finished parsing docker inspect...\n");
296
297
298     docker_environment_var_count = 5;
299     docker_environment_var = (environment_var_t *)malloc(sizeof(environment_var_t) * docker_environment_var_count);
300     if(docker_environment_var == 0) {
301         log_error("malloc failed");
302         return NTS_ERR_FAILED;
303     }
304     
305     //set env variables for network functions
306     docker_environment_var[0].name = ENV_VAR_SSH_CONNECTIONS;
307     asprintf(&docker_environment_var[0].value, "%d", framework_environment.ssh_connections);
308     docker_environment_var[1].name = ENV_VAR_TLS_CONNECTIONS;
309     asprintf(&docker_environment_var[1].value, "%d", framework_environment.tls_connections);
310     docker_environment_var[2].name = ENV_VAR_IPV6ENABLED;
311     docker_environment_var[2].value = framework_environment.ip_v6_enabled ? "true" : "false";
312     docker_environment_var[3].name = ENV_VAR_HOST_IP;
313     docker_environment_var[3].value = framework_environment.host_ip;
314     docker_environment_var[4].name = ENV_VAR_HOST_BASE_PORT;
315     // docker_environment_var[4].value = will be updated by docker_create...
316
317     return NTS_ERR_OK;
318 }
319
320 int docker_device_start(const manager_network_function_type *function_type, manager_network_function_instance_t *instance) {
321     assert(function_type);
322     assert(instance);
323     assert(docker_network_info);
324
325     char *image = 0;
326     if(function_type->docker_version_tag && (function_type->docker_version_tag[0] != 0)) {
327         if(function_type->docker_repository && (function_type->docker_repository[0] != 0)) {
328             asprintf(&image, "%s/%s:%s", function_type->docker_repository, function_type->docker_image_name, function_type->docker_version_tag);    
329         }
330         else {
331             asprintf(&image, "%s:%s", function_type->docker_image_name, function_type->docker_version_tag);
332         }
333     }
334     else {
335         if(function_type->docker_repository && (function_type->docker_repository[0] != 0)) {
336             asprintf(&image, "%s/%s:latest", function_type->docker_repository, function_type->docker_image_name);    
337         }
338         else {
339             asprintf(&image, "%s:latest", function_type->docker_image_name);
340         }
341     }
342
343     int rc = docker_container_create(image, instance);
344     if(rc != NTS_ERR_OK) {
345         log_error("docker_container_create failed");
346         return NTS_ERR_FAILED;
347     }
348     free(image);
349
350     rc = docker_container_start(instance);
351     if(rc != NTS_ERR_OK) {
352         log_error("docker_container_start failed");
353         return NTS_ERR_FAILED;
354     }
355
356     rc = docker_container_inspect(instance);
357     if(rc != NTS_ERR_OK) {
358         log_error("docker_container_inspect failed");
359         return NTS_ERR_FAILED;
360     }
361
362     log_message(2, "docker_device_start: docker_id: %s | name: %s | docker_ip: %s | host_port: %d\n", instance->docker_id, instance->name, instance->docker_ip, instance->host_port);
363
364     return NTS_ERR_OK;
365 }
366
367 int docker_device_stop(manager_network_function_instance_t *instance) {
368     assert(instance);
369
370     char *url = 0;
371     asprintf(&url, "http://v%s/containers/%s?force=true", framework_environment.docker_engine_version, instance->docker_id);
372
373     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "DELETE", "", 0, 0);
374     free(url);
375     if(rc != NTS_ERR_OK) {
376         log_error("http_socket_request failed");
377         return NTS_ERR_FAILED;
378     }
379
380     return NTS_ERR_OK;
381 }
382
383 docker_usage_t docker_usage_get(const manager_network_function_type *function_type, int function_type_count) {
384     docker_usage_t ret;
385     ret.cpu = 0;
386     ret.mem = 0;
387
388     char buffer[1024];
389     char full_text[1024 * 1024];
390     FILE* pipe = popen("docker stats --no-stream --format \"table {{.ID}}|{{.CPUPerc}}|{{.MemUsage}}|\"", "r");
391     if (!pipe) {
392         log_error("popen() failed");
393         return ret;
394     }
395
396     int n = 1;
397     int k = 0;
398     while(n != 0) {
399         n = fread(buffer, 1, sizeof(buffer), pipe);
400         for(int i = 0; i < n; i++) {
401             full_text[k++] = buffer[i];
402         }
403     }
404     pclose(pipe);
405     full_text[k] = 0;
406
407     char *c = full_text;
408     
409     c = strstr(c, "\n");
410     while(c) {
411         char line[1024];
412         line[0] = 0;
413
414         char *d = strstr(c + 1, "\n");
415         if(d) {
416             for(char *i = c + 1; i < d; i++) {
417                 line[i - c - 1] = *i;
418                 line[i - c] = 0;
419             }
420
421             char container_name[1024];
422             char buff[1024];
423             float cpu = 0.0;
424             float mem = 0.0;
425
426             char *x = strstr(line, "|");
427             for(char *i = line; i < x; i++) {
428                 container_name[i - line] = *i;
429                 container_name[i - line + 1] = 0;
430             }
431
432             char *start = x + 1;
433             x = strstr(start, "|");
434             for(char *i = start; i < x; i++) {
435                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
436                     buff[i - start] = *i;
437                 }
438                 else {
439                     buff[i - start] = 0;
440                     break;
441                 }
442             }
443
444             cpu = strtof(buff, 0);
445
446             int mul = 1;
447             start = x + 1;
448             x = strstr(start, "|");
449             for(char *i = start; i < x; i++) {
450                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
451                     buff[i - start] = *i;
452                 }
453                 else {
454                     if(*i == 'G') {
455                         mul = 1024;
456                     }
457                     buff[i - start] = 0;
458                     break;
459                 }
460             }
461
462             mem = strtof(buff, 0) * mul;
463             
464             
465             if(strcmp(container_name, framework_environment.hostname) == 0) {
466                 ret.cpu += cpu;
467                 ret.mem += mem;
468             }
469             else {
470                 for(int i = 0; i < function_type_count; i++) {
471                     for(int j = 0; j < function_type[i].started_instances; j++) {
472                         
473                         if(strcmp(container_name, function_type[i].instance[j].docker_id) == 0) {
474                             ret.cpu += cpu;
475                             ret.mem += mem;
476                             break;
477                         }
478                     }
479                 }
480             }
481         }
482         
483         c = d;
484     }
485
486
487     ret.cpu /= get_nprocs();
488
489     return ret;
490 }
491
492 static char *docker_parse_json_message(const char *json_string) {
493     assert(json_string);
494
495     cJSON *json_response = cJSON_Parse(json_string);
496     if(json_response == 0) {
497         log_error("cJSON_Parse failed");
498         return 0;
499     }
500
501     cJSON *message;
502     message = cJSON_GetObjectItem(json_response, "message");
503     if(message == 0) {
504         log_error("json parsing failed");
505         return 0;
506     }
507
508     char *ret = strdup(message->valuestring);
509     cJSON_Delete(json_response);
510     return ret;
511 }
512
513 static int docker_container_create(const char *image, manager_network_function_instance_t *instance) {
514     assert(image);
515     assert(instance);
516
517     cJSON *postDataJson = cJSON_CreateObject();
518     if(cJSON_AddStringToObject(postDataJson, "Image", image) == 0) {
519         log_error("could not create JSON object: Image");
520         return NTS_ERR_FAILED;
521     }
522
523     if(cJSON_AddStringToObject(postDataJson, "Hostname", instance->name) == 0) {
524         log_error("could not create JSON object: Hostname");
525         return NTS_ERR_FAILED;
526     }    
527
528     cJSON *hostConfig = cJSON_CreateObject();
529     if(hostConfig == 0) {
530         log_error("could not create JSON object: HostConfig");
531         return NTS_ERR_FAILED;
532     }
533     cJSON_AddItemToObject(postDataJson, "HostConfig", hostConfig);
534
535     cJSON *portBindings = cJSON_CreateObject();
536     if(portBindings == 0) {
537         printf("could not create JSON object: PortBindings");
538         return NTS_ERR_FAILED;
539     }
540     cJSON_AddItemToObject(hostConfig, "PortBindings", portBindings);
541     
542     for(int i = 0; i < (framework_environment.ssh_connections + framework_environment.tls_connections + framework_environment.ftp_connections + framework_environment.sftp_connections); ++i) {
543         cJSON *port = cJSON_CreateArray();
544         if(port == 0) {
545             log_error("could not create JSON object: port");
546             return NTS_ERR_FAILED;
547         }
548
549         char dockerContainerPort[20];
550         if(i < framework_environment.ssh_connections + framework_environment.tls_connections) {
551             sprintf(dockerContainerPort, "%d/tcp", STANDARD_NETCONF_PORT + i);
552         }
553         else if(i < (framework_environment.ssh_connections + framework_environment.tls_connections + framework_environment.ftp_connections)) {
554             sprintf(dockerContainerPort, "%d/tcp", STANDARD_FTP_PORT);
555         }
556         else if(i < (framework_environment.ssh_connections + framework_environment.tls_connections + framework_environment.ftp_connections + framework_environment.sftp_connections)) {
557             sprintf(dockerContainerPort, "%d/tcp", STANDARD_SFTP_PORT);
558         }
559         cJSON_AddItemToObject(portBindings, dockerContainerPort, port);
560
561         cJSON *hostPort = cJSON_CreateObject();
562         if(hostPort == 0) {
563             log_error("could not create JSON object: HostPort");
564             return NTS_ERR_FAILED;
565         }
566
567         char dockerHostPort[20];
568         sprintf(dockerHostPort, "%d", instance->host_port + i);
569         if(cJSON_AddStringToObject(hostPort, "HostPort", dockerHostPort) == 0) {
570             log_error("could not create JSON object: HostPortString");
571             return NTS_ERR_FAILED;
572         }
573
574         if(cJSON_AddStringToObject(hostPort, "HostIp", "0.0.0.0") == 0) {   //instance->host_ip
575             log_error("could not create JSON object: HostIpString");
576             return NTS_ERR_FAILED;
577         }
578
579         cJSON_AddItemToArray(port, hostPort);
580     }
581
582     
583     //environment vars start
584     asprintf(&docker_environment_var[4].value, "%d", instance->host_port);
585
586     cJSON *env_variables_array = cJSON_CreateArray();
587     if (env_variables_array == 0) {
588         log_error("Could not create JSON object: Env array");
589         return NTS_ERR_FAILED;
590     }
591     cJSON_AddItemToObject(postDataJson, "Env", env_variables_array);
592
593     for(int i = 0; i < docker_environment_var_count; i++) {
594         char *environment_var = 0;
595         asprintf(&environment_var, "%s=%s", docker_environment_var[i].name, docker_environment_var[i].value);
596
597         cJSON *env_var_obj = cJSON_CreateString(environment_var);
598         if(env_var_obj == 0) {
599             log_error("could not create JSON object");
600             return NTS_ERR_FAILED;
601         }
602         cJSON_AddItemToArray(env_variables_array, env_var_obj);
603
604         free(environment_var);
605     }
606
607     free(docker_environment_var[4].value);
608     //environment vars finished
609
610
611     cJSON *netMode = cJSON_Duplicate(docker_network_info, 1);
612     cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode);
613
614     char *post_data_string = 0;
615     post_data_string = cJSON_PrintUnformatted(postDataJson);
616     cJSON_Delete(postDataJson);
617
618     char *url = 0;
619     asprintf(&url, "http:/v%s/containers/create?name=%s", framework_environment.docker_engine_version, instance->name);
620
621     char *response = 0;
622     int response_code = 0;
623     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", post_data_string, &response_code, &response);
624     if(rc != NTS_ERR_OK) {
625         log_error("http_socket_request failed");
626         return NTS_ERR_FAILED;
627     }
628     free(url);
629
630     if(response_code != 201) {
631         char *message = docker_parse_json_message(response);
632         log_error("docker_container_create failed (%d): %s", response_code, message);
633         free(message);
634         free(response);
635         return NTS_ERR_FAILED;
636     }
637     else {
638         cJSON *json_response = cJSON_Parse(response);
639         free(response);
640         const cJSON *container_id = 0;
641
642         container_id = cJSON_GetObjectItemCaseSensitive(json_response, "Id");
643
644         if(cJSON_IsString(container_id) && (container_id->valuestring != 0)) {
645             char container_id_short[13];
646             memset(container_id_short, '\0', sizeof(container_id_short));
647             strncpy(container_id_short, container_id->valuestring, 12);
648
649             instance->docker_id = strdup(container_id_short);
650
651             cJSON_Delete(json_response);
652             return NTS_ERR_OK;
653         }
654         else {
655             cJSON_Delete(json_response);
656             return NTS_ERR_FAILED;
657         }
658     }
659 }
660
661 static int docker_container_start(manager_network_function_instance_t *instance) {
662     assert(instance);
663
664     char *url = 0;
665     asprintf(&url, "http://v%s/containers/%s/start", framework_environment.docker_engine_version, instance->docker_id);
666
667     char *response = 0;
668     int response_code = 0;
669     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", "", &response_code, &response);
670     free(url);
671     if(rc != NTS_ERR_OK) {
672         log_error("http_socket_request failed");
673         return NTS_ERR_FAILED;
674     }
675     else {
676         if(response_code == 304) {
677             log_error("docker_container_start failed (%d): container already started\n", response_code);
678             free(response);
679             return NTS_ERR_FAILED;
680         }
681         else if(response_code != 204) {
682             char *message = docker_parse_json_message(response);
683             log_error("docker_container_start failed (%d): %s", response_code, message);
684             free(message);
685             free(response);
686             return NTS_ERR_FAILED;
687         }
688         
689     }
690
691     return NTS_ERR_OK;
692 }
693
694 static int docker_container_inspect(manager_network_function_instance_t *instance) {
695     assert(instance);
696
697     char *url = 0;
698     asprintf(&url, "http://v%s/containers/%s/json", framework_environment.docker_engine_version, instance->docker_id);
699
700     char *response = 0;    
701     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
702     free(url);
703     if(rc != NTS_ERR_OK) {
704         log_error("http_socket_request failed");
705         return NTS_ERR_FAILED;
706     }
707
708     cJSON *json_response = cJSON_Parse(response);
709     free(response);
710     if(json_response == 0) {
711         log_error("cJSON_Parse failed");
712         return NTS_ERR_FAILED;
713     }
714
715
716     cJSON *main_node = cJSON_GetObjectItem(json_response, "NetworkSettings");
717     if(main_node == 0) {
718         log_error("json parsing failed");
719         return NTS_ERR_FAILED;
720     }
721
722     cJSON *node = cJSON_GetObjectItem(main_node, "Networks");
723     if(node == 0) {
724         log_error("json parsing failed");
725         return NTS_ERR_FAILED;
726     }
727         
728     node = node->child;   //get info from the first in array
729     if(node == 0) {
730         log_error("json parsing failed");
731         return NTS_ERR_FAILED;
732     }
733
734     char *ipv6_env_var = getenv("IPv6_ENABLED");
735     if(ipv6_env_var == 0) {
736         log_error("could not get the IPv6 Enabled env variable");
737         return NTS_ERR_FAILED;
738     }
739
740     cJSON *element = cJSON_GetObjectItem(node, "IPAddress");
741     if(strcmp(ipv6_env_var, "true") == 0) {
742         element = cJSON_GetObjectItem(node, "GlobalIPv6Address");
743     }
744     else {
745         element = cJSON_GetObjectItem(node, "IPAddress");
746     } 
747
748     if(element == 0) {
749         log_error("json parsing failed");
750         return NTS_ERR_FAILED;
751     }
752     instance->docker_ip = strdup(element->valuestring);
753
754     cJSON_Delete(json_response);
755     return NTS_ERR_OK;
756 }