Add supoprt for D release use-case.
[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/nts_utils.h"
24 #include "utils/http_client.h"
25 #include "core/framework.h"
26 #include "core/session.h"
27 #include "core/context.h"
28 #include <sysrepo.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 typedef struct {
39     char *name;
40     char *value;
41 } environment_var_t;
42
43 static environment_var_t *docker_environment_var;
44 static int docker_environment_var_count = 0;
45
46 static char *docker_parse_json_message(const char *json_string);
47 static int docker_add_port(cJSON *portBindings, uint16_t docker_port, uint16_t host_port);
48
49 static int docker_populate_images(docker_context_t *context, int count, const char *min_version);
50 static int docker_container_create(const char *image, docker_container_t *container);
51 static int docker_container_start(docker_container_t *container);
52 static int docker_container_inspect(docker_container_t *container);
53
54 int docker_init(const char **filter, int filter_count, const char *min_version, docker_context_t **context) {
55     assert(filter);
56     assert(filter_count);
57     assert(context);
58
59     char *response = 0;
60     char url[512];
61     sprintf(url, "http://v%s/containers/%s/json", framework_environment.settings.docker_engine_version, framework_environment.settings.hostname);
62
63     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", 0, 0, &response);
64     if(rc != NTS_ERR_OK) {
65         log_error("http_socket_request failed\n");
66         return NTS_ERR_FAILED;
67     }
68
69     cJSON *json_response = cJSON_Parse(response);
70     free(response);
71
72     if(json_response == 0) {
73         log_error("could not parse JSON response for url=\"%s\"\n", url);
74         return NTS_ERR_FAILED;
75     }
76
77     cJSON *hostConfig = cJSON_GetObjectItemCaseSensitive(json_response, "HostConfig");
78     if(hostConfig == 0) {
79         log_error("could not get HostConfig object\n");
80         cJSON_Delete(json_response);
81         return NTS_ERR_FAILED;
82     }
83
84     cJSON *networkMode = cJSON_GetObjectItemCaseSensitive(hostConfig, "NetworkMode");
85     if(networkMode == 0) {
86         log_error("could not get NetworkMode object\n");
87         cJSON_Delete(json_response);
88         return NTS_ERR_FAILED;
89     }
90
91     docker_network_info = cJSON_Duplicate(networkMode, 1);
92     cJSON_Delete(json_response);
93
94     log_add_verbose(2, "finished parsing docker inspect...\n");
95
96     docker_environment_var_count = 9;
97     docker_environment_var = (environment_var_t *)malloc(sizeof(environment_var_t) * docker_environment_var_count);
98     if(docker_environment_var == 0) {
99         log_error("malloc failed\n");
100         cJSON_Delete(networkMode);
101         return NTS_ERR_FAILED;
102     }
103     
104     //set env variables for network functions
105     docker_environment_var[0].name = ENV_VAR_SSH_CONNECTIONS;
106     asprintf(&docker_environment_var[0].value, "%d", framework_environment.settings.ssh_connections);
107     docker_environment_var[1].name = ENV_VAR_TLS_CONNECTIONS;
108     asprintf(&docker_environment_var[1].value, "%d", framework_environment.settings.tls_connections);
109     docker_environment_var[2].name = ENV_VAR_IPV6ENABLED;
110     docker_environment_var[2].value = framework_environment.settings.ip_v6_enabled ? "true" : "false";
111     docker_environment_var[3].name = ENV_VAR_HOST_IP;
112     docker_environment_var[3].value = framework_environment.host.ip;
113
114     docker_environment_var[4].name = ENV_VAR_HOST_NETCONF_SSH_BASE_PORT;
115     // docker_environment_var[4].value = will be updated by docker_create...
116     docker_environment_var[5].name = ENV_VAR_HOST_NETCONF_TLS_BASE_PORT;
117     // docker_environment_var[5].value = will be updated by docker_create...
118     docker_environment_var[6].name = ENV_VAR_HOST_TRANSFER_FTP_BASE_PORT;
119     // docker_environment_var[6].value = will be updated by docker_create...
120     docker_environment_var[7].name = ENV_VAR_HOST_TRANSFER_SFTP_BASE_PORT;
121     // docker_environment_var[7].value = will be updated by docker_create...
122
123     docker_environment_var[8].name = ENV_VAR_VES_COMMON_HEADER_VERSION;
124     docker_environment_var[8].value = framework_environment.ves_endpoint.common_header_version;
125
126
127
128     //docker context build
129     *context = (docker_context_t *)malloc(sizeof(docker_context_t) * filter_count);
130     if(*context == 0) {
131         log_error("bad malloc\n");
132         free(docker_environment_var[0].value);
133         free(docker_environment_var[1].value);
134         free(docker_environment_var);
135         return NTS_ERR_FAILED;
136     }
137
138     docker_context_t *ctx = *context;
139     for(int i = 0; i < filter_count; i++) {
140         ctx[i].image = strdup(filter[i]);
141         ctx[i].available_images = 0;
142         ctx[i].available_images_count = 0;
143     }
144
145     docker_populate_images(ctx, filter_count, min_version);
146
147     return NTS_ERR_OK;
148 }
149
150 void docker_free(docker_context_t *context, int count) {
151     free(docker_environment_var[0].value);
152     free(docker_environment_var[1].value);
153     free(docker_environment_var);
154
155     for(int i = 0; i < count; i++) {
156         free(context[i].image);
157         for(int j = 0; j < context[i].available_images_count; j++) {
158             free(context[i].available_images[j].repo);
159             free(context[i].available_images[j].tag);
160         }
161         free(context[i].available_images);
162     }
163 }
164
165 int docker_start(const char *container_name, const char *tag, const char *image, const char *repo, uint16_t host_netconf_ssh_port, uint16_t host_netconf_tls_port, uint16_t host_ftp_port, uint16_t host_sftp_port, docker_container_t *container) {
166     assert(container_name);
167     assert(image);
168     assert(container);
169     assert(docker_network_info);
170
171     char image_full[512];
172     if(tag && (tag[0] != 0)) {
173         if(repo && (repo[0] != 0) && (strcmp(repo, "local") != 0)) {
174             sprintf(image_full, "%s/%s:%s", repo, image, tag);    
175         }
176         else {
177             sprintf(image_full, "%s:%s", image, tag);
178         }
179     }
180     else {
181         if(repo && (repo[0] != 0) && (strcmp(repo, "local") != 0)) {
182             sprintf(image_full, "%s/%s:latest", repo, image);    
183         }
184         else {
185             sprintf(image_full, "%s:latest", image);
186         }
187     }
188
189     container->name = strdup(container_name);
190     container->id = 0;
191     container->docker_ip = 0;
192     container->docker_netconf_ssh_port = STANDARD_NETCONF_PORT;
193     container->docker_netconf_tls_port = container->docker_netconf_ssh_port + framework_environment.settings.ssh_connections;
194     container->docker_ftp_port= STANDARD_FTP_PORT;
195     container->docker_sftp_port= STANDARD_SFTP_PORT;
196
197     container->host_ip = strdup(framework_environment.host.ip);
198     container->host_netconf_ssh_port = host_netconf_ssh_port;
199     container->host_netconf_tls_port = host_netconf_tls_port;
200     container->host_ftp_port = host_ftp_port;
201     container->host_sftp_port = host_sftp_port;
202
203     int rc = docker_container_create(image_full, container);
204     if(rc != NTS_ERR_OK) {
205         log_error("docker_container_create failed\n");
206         return NTS_ERR_FAILED;
207     }
208
209     rc = docker_container_start(container);
210     if(rc != NTS_ERR_OK) {
211         log_error("docker_container_start failed\n");
212         docker_stop(container);
213         return NTS_ERR_FAILED;
214     }
215
216     rc = docker_container_inspect(container);
217     if(rc != NTS_ERR_OK) {
218         log_error("docker_container_inspect failed\n");
219         docker_stop(container);
220         return NTS_ERR_FAILED;
221     }
222
223     log_add_verbose(2, "docker_start: name: %s | id: %s | docker_ip: %s | netconf_ssh_port: (%d:%d) | netconf_tls_port: (%d:%d) | ftp_port: (%d:%d) | sftp_port: (%d:%d)\n", container->name, container->id, container->docker_ip, container->docker_netconf_ssh_port, container->host_netconf_ssh_port, container->docker_netconf_tls_port, container->host_netconf_tls_port, container->docker_ftp_port, container->host_ftp_port, container->docker_sftp_port, container->host_sftp_port);
224
225     return NTS_ERR_OK;
226 }
227
228 int docker_stop(docker_container_t *container) {
229     assert(container);
230
231     char url[512];
232     sprintf(url, "http://v%s/containers/%s?force=true", framework_environment.settings.docker_engine_version, container->id);
233     
234     free(container->name);
235     free(container->id);
236     free(container->docker_ip);
237     free(container->host_ip);
238
239     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "DELETE", "", 0, 0);
240     if(rc != NTS_ERR_OK) {
241         log_error("http_socket_request failed\n");
242         return NTS_ERR_FAILED;
243     }
244
245     return NTS_ERR_OK;
246 }
247
248 int docker_usage_get(const char **instances_id, int count, docker_usage_t *usage) {
249     assert(instances_id);
250     assert(usage);
251
252     usage->cpu = 0;
253     usage->mem = 0;
254
255     char buffer[1024];
256     char full_text[1024 * 1024];
257     FILE* pipe = popen("docker stats --no-stream --format \"table {{.ID}}|{{.CPUPerc}}|{{.MemUsage}}|\"", "r");
258     if (!pipe) {
259         log_error("popen() failed\n");
260         return NTS_ERR_FAILED;
261     }
262
263     int n = 1;
264     int k = 0;
265     while(n != 0) {
266         n = fread(buffer, 1, sizeof(buffer), pipe);
267         for(int i = 0; i < n; i++) {
268             full_text[k++] = buffer[i];
269         }
270     }
271     pclose(pipe);
272     full_text[k] = 0;
273
274     char *c = full_text;
275     
276     c = strstr(c, "\n");
277     while(c) {
278         char line[1024];
279         line[0] = 0;
280
281         char *d = strstr(c + 1, "\n");
282         if(d) {
283             for(char *i = c + 1; i < d; i++) {
284                 line[i - c - 1] = *i;
285                 line[i - c] = 0;
286             }
287
288             char container_name[1024];
289             char buff[1024];
290             float cpu = 0.0;
291             float mem = 0.0;
292
293             char *x = strstr(line, "|");
294             for(char *i = line; i < x; i++) {
295                 container_name[i - line] = *i;
296                 container_name[i - line + 1] = 0;
297             }
298
299             char *start = x + 1;
300             x = strstr(start, "|");
301             for(char *i = start; i < x; i++) {
302                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
303                     buff[i - start] = *i;
304                 }
305                 else {
306                     buff[i - start] = 0;
307                     break;
308                 }
309             }
310
311             cpu = strtof(buff, 0);
312
313             int mul = 1;
314             start = x + 1;
315             x = strstr(start, "|");
316             for(char *i = start; i < x; i++) {
317                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
318                     buff[i - start] = *i;
319                 }
320                 else {
321                     if(*i == 'G') {
322                         mul = 1024;
323                     }
324                     buff[i - start] = 0;
325                     break;
326                 }
327             }
328
329             mem = strtof(buff, 0) * mul;
330             
331             
332             if(strcmp(container_name, framework_environment.settings.hostname) == 0) {
333                 usage->cpu += cpu;
334                 usage->mem += mem;
335             }
336             else {
337                 for(int i = 0; i < count; i++) {   
338                     if(strcmp(container_name, instances_id[i]) == 0) {
339                         usage->cpu += cpu;
340                         usage->mem += mem;
341                         break;
342                     }
343                 }
344             }
345         }
346         
347         c = d;
348     }
349
350     usage->cpu /= get_nprocs();
351
352     return NTS_ERR_OK;
353 }
354
355 static char *docker_parse_json_message(const char *json_string) {
356     assert(json_string);
357
358     cJSON *json_response = cJSON_Parse(json_string);
359     if(json_response == 0) {
360         log_error("cJSON_Parse failed\n");
361         return 0;
362     }
363
364     cJSON *message = cJSON_GetObjectItem(json_response, "message");
365     if(message == 0) {
366         log_error("json parsing failed\n");
367         cJSON_Delete(json_response);
368         return 0;
369     }
370
371     char *ret = strdup(message->valuestring);
372     cJSON_Delete(json_response);
373     return ret;
374 }
375
376 static int docker_add_port(cJSON *portBindings, uint16_t docker_port, uint16_t host_port) {
377     assert(portBindings);
378
379     cJSON *port = cJSON_CreateArray();
380     if(port == 0) {
381         log_error("could not create JSON object: port\n");
382         return NTS_ERR_FAILED;
383     }
384
385     char dockerContainerPort[20];
386     sprintf(dockerContainerPort, "%d/tcp", docker_port);
387
388     if(cJSON_AddItemToObject(portBindings, dockerContainerPort, port) == 0) {
389         log_error("cJSON_AddItemToObject failed\n");
390         return NTS_ERR_FAILED;
391     }
392
393     cJSON *hostPort = cJSON_CreateObject();
394     if(hostPort == 0) {
395         log_error("could not create JSON object: HostPort\n");
396         return NTS_ERR_FAILED;
397     }
398
399     char dockerHostPort[20];
400     sprintf(dockerHostPort, "%d", host_port);
401     if(cJSON_AddStringToObject(hostPort, "HostPort", dockerHostPort) == 0) {
402         log_error("could not create JSON object: HostPortString\n");
403         cJSON_Delete(hostPort);
404         return NTS_ERR_FAILED;
405     }
406
407     if(cJSON_AddStringToObject(hostPort, "HostIp", "0.0.0.0") == 0) {   //or, future, bind to container->host_ip
408         log_error("could not create JSON object: HostIpString\n");
409         cJSON_Delete(hostPort);
410         return NTS_ERR_FAILED;
411     }
412
413     if(cJSON_AddItemToArray(port, hostPort) == 0) {
414         log_error("cJSON_AddItemToArray failed\n");
415         cJSON_Delete(hostPort);
416         return NTS_ERR_FAILED;
417     }
418
419     return NTS_ERR_OK;
420 }
421
422 static int docker_populate_images(docker_context_t *context, int count, const char *min_version) {
423     assert(context);
424     assert(count);
425     assert(min_version);
426
427     char url[512];
428     sprintf(url, "http://v%s/images/json", framework_environment.settings.docker_engine_version);
429
430     char *response = 0;    
431     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
432     if(rc != NTS_ERR_OK) {
433         log_error("http_socket_request failed\n");
434         return NTS_ERR_FAILED;
435     }
436
437     cJSON *json_response = cJSON_Parse(response);
438     free(response);
439     if(json_response == 0) {
440         log_error("cJSON_Parse failed\n");
441         return NTS_ERR_FAILED;
442     }
443     
444     cJSON *element;
445     cJSON_ArrayForEach(element, json_response) {
446         cJSON *tag = cJSON_GetObjectItem(element, "RepoTags");
447         if(tag) {
448             cJSON *ctag;
449             cJSON_ArrayForEach(ctag, tag) {
450                 char *tag_name = ctag->valuestring; //contains repo/image:tag
451                 for(int i = 0; i < count; i++) {
452                     char *s = strstr(tag_name, context[i].image);
453                     if(s != 0) {
454                         char *tag = s + strlen(context[i].image);
455                         if(*tag == ':') {
456                             tag = strdup(s + strlen(context[i].image) + 1);
457                         }
458                         else if(*tag == 0) {
459                             tag = strdup("");
460                         }
461                         else {
462                             continue;
463                         }
464
465                         if(nts_vercmp(tag, min_version) >= 0) {
466                             char *repo = 0;
467                             if(s != tag_name) {
468                                 repo = strdup(tag_name);
469                                 *(strstr(repo, context[i].image) - 1) = 0;
470                             }
471                             else {
472                                 repo = strdup("");
473                             }
474
475                             context[i].available_images = (docker_available_images_t *)realloc(context[i].available_images, (sizeof(docker_available_images_t) * (context[i].available_images_count + 1)));
476                             context[i].available_images[context[i].available_images_count].repo = repo;
477                             context[i].available_images[context[i].available_images_count].tag = tag;
478                             context[i].available_images_count++;
479                         }
480                         else {
481                             free(tag);
482                         }
483                     }
484                 }
485             }
486         }
487     }
488
489     cJSON_Delete(json_response);
490
491     return NTS_ERR_OK;
492 }
493
494 static int docker_container_create(const char *image, docker_container_t *container) {
495     assert(image);
496     assert(container);
497
498     cJSON *postDataJson = cJSON_CreateObject();
499     if(cJSON_AddStringToObject(postDataJson, "Image", image) == 0) {
500         log_error("could not create JSON object: Image\n");
501         return NTS_ERR_FAILED;
502     }
503
504     if(cJSON_AddStringToObject(postDataJson, "Hostname", container->name) == 0) {
505         log_error("could not create JSON object: Hostname\n");
506         cJSON_Delete(postDataJson);
507         return NTS_ERR_FAILED;
508     }    
509
510     cJSON *hostConfig = cJSON_CreateObject();
511     if(hostConfig == 0) {
512         log_error("could not create JSON object: HostConfig\n");
513         cJSON_Delete(postDataJson);
514         return NTS_ERR_FAILED;
515     }
516     if(cJSON_AddItemToObject(postDataJson, "HostConfig", hostConfig) == 0) {
517         log_error("cJSON_AddItemToObject failed\n");
518         cJSON_Delete(postDataJson);
519         return NTS_ERR_FAILED;
520     }
521
522     cJSON *portBindings = cJSON_CreateObject();
523     if(portBindings == 0) {
524         printf("could not create JSON object: PortBindings");
525         cJSON_Delete(postDataJson);
526         return NTS_ERR_FAILED;
527     }
528     if(cJSON_AddItemToObject(hostConfig, "PortBindings", portBindings) == 0) {
529         log_error("cJSON_AddItemToObject failed\n");
530         cJSON_Delete(postDataJson);
531         return NTS_ERR_FAILED;
532     }
533     
534     for(int i = 0; i < framework_environment.settings.ssh_connections; i++) {
535         if(docker_add_port(portBindings, container->docker_netconf_ssh_port + i, container->host_netconf_ssh_port + i) != NTS_ERR_OK) {
536             log_error("docker_add_port() failed\n");
537             cJSON_Delete(postDataJson);
538             return NTS_ERR_FAILED;
539         }
540     }
541
542     for(int i = 0; i < framework_environment.settings.tls_connections; i++) {
543         if(docker_add_port(portBindings, container->docker_netconf_tls_port + i, container->host_netconf_tls_port + i) != NTS_ERR_OK) {
544             log_error("docker_add_port() failed\n");
545             cJSON_Delete(postDataJson);
546             return NTS_ERR_FAILED;
547         }
548     }
549
550     for(int i = 0; i < framework_environment.settings.ftp_connections; i++) {
551         if(docker_add_port(portBindings, container->docker_ftp_port + i, container->host_ftp_port + i) != NTS_ERR_OK) {
552             log_error("docker_add_port() failed\n");
553             cJSON_Delete(postDataJson);
554             return NTS_ERR_FAILED;
555         }
556     }
557
558     for(int i = 0; i < framework_environment.settings.sftp_connections; i++) {
559         if(docker_add_port(portBindings, container->docker_sftp_port + i, container->host_sftp_port + i) != NTS_ERR_OK) {
560             log_error("docker_add_port() failed\n");
561             cJSON_Delete(postDataJson);
562             return NTS_ERR_FAILED;
563         }
564     }
565     
566     //environment vars start
567     asprintf(&docker_environment_var[4].value, "%d", container->host_netconf_ssh_port);
568     asprintf(&docker_environment_var[5].value, "%d", container->host_netconf_tls_port);
569     asprintf(&docker_environment_var[6].value, "%d", container->host_ftp_port);
570     asprintf(&docker_environment_var[7].value, "%d", container->host_sftp_port);
571
572     cJSON *env_variables_array = cJSON_CreateArray();
573     if(env_variables_array == 0) {
574         log_error("Could not create JSON object: Env array\n");
575         cJSON_Delete(postDataJson);
576         free(docker_environment_var[4].value);
577         free(docker_environment_var[5].value);
578         free(docker_environment_var[6].value);
579         free(docker_environment_var[7].value);
580         return NTS_ERR_FAILED;
581     }
582     cJSON_AddItemToObject(postDataJson, "Env", env_variables_array);
583
584     for(int i = 0; i < docker_environment_var_count; i++) {
585         if(docker_environment_var[i].value) {
586             char *environment_var = 0;
587             asprintf(&environment_var, "%s=%s", docker_environment_var[i].name, docker_environment_var[i].value);
588
589             cJSON *env_var_obj = cJSON_CreateString(environment_var);
590             if(env_var_obj == 0) {
591                 log_error("could not create JSON object\n");
592                 cJSON_Delete(postDataJson);
593                 free(docker_environment_var[4].value);
594                 free(docker_environment_var[5].value);
595                 free(docker_environment_var[6].value);
596                 free(docker_environment_var[7].value);
597                 free(environment_var);
598                 return NTS_ERR_FAILED;
599             }
600             cJSON_AddItemToArray(env_variables_array, env_var_obj);
601
602             free(environment_var);
603         }
604     }
605
606     free(docker_environment_var[4].value);
607     free(docker_environment_var[5].value);
608     free(docker_environment_var[6].value);
609     free(docker_environment_var[7].value);
610     //environment vars finished
611
612     cJSON *netMode = cJSON_Duplicate(docker_network_info, 1);
613     cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode);
614
615     char *post_data_string = 0;
616     post_data_string = cJSON_PrintUnformatted(postDataJson);
617     cJSON_Delete(postDataJson);
618
619     char url[512];
620     sprintf(url, "http:/v%s/containers/create?name=%s", framework_environment.settings.docker_engine_version, container->name);
621
622     char *response = 0;
623     int response_code = 0;
624     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", post_data_string, &response_code, &response);
625     free(post_data_string);
626     if(rc != NTS_ERR_OK) {
627         log_error("http_socket_request failed\n");
628         return NTS_ERR_FAILED;
629     }
630
631     if(response_code != 201) {
632         char *message = docker_parse_json_message(response);
633         log_error("docker_container_create failed (%d): %s\n", response_code, message);
634         free(message);
635         free(response);
636         return NTS_ERR_FAILED;
637     }
638     else {
639         cJSON *json_response = cJSON_Parse(response);
640         free(response);
641         const cJSON *container_id = 0;
642
643         container_id = cJSON_GetObjectItemCaseSensitive(json_response, "Id");
644
645         if(cJSON_IsString(container_id) && (container_id->valuestring != 0)) {
646             char container_id_short[13];
647             memset(container_id_short, '\0', sizeof(container_id_short));
648             strncpy(container_id_short, container_id->valuestring, 12);
649
650             container->id = strdup(container_id_short);
651
652             cJSON_Delete(json_response);
653             return NTS_ERR_OK;
654         }
655         else {
656             cJSON_Delete(json_response);
657             return NTS_ERR_FAILED;
658         }
659     }
660 }
661
662 static int docker_container_start(docker_container_t *container) {
663     assert(container);
664
665     char url[512];
666     sprintf(url, "http://v%s/containers/%s/start", framework_environment.settings.docker_engine_version, container->id);
667
668     char *response = 0;
669     int response_code = 0;
670     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", "", &response_code, &response);
671     if(rc != NTS_ERR_OK) {
672         log_error("http_socket_request failed\n");
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\n", 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(docker_container_t *container) {
695     assert(container);
696
697     char url[512];
698     sprintf(url, "http://v%s/containers/%s/json", framework_environment.settings.docker_engine_version, container->id);
699
700     char *response = 0;    
701     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
702     if(rc != NTS_ERR_OK) {
703         log_error("http_socket_request failed\n");
704         free(response);
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\n");
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\n");
719         cJSON_Delete(json_response);
720         return NTS_ERR_FAILED;
721     }
722
723     cJSON *node = cJSON_GetObjectItem(main_node, "Networks");
724     if(node == 0) {
725         log_error("json parsing failed\n");
726         cJSON_Delete(json_response);
727         return NTS_ERR_FAILED;
728     }
729         
730     node = node->child;   //get info from the first in array
731     if(node == 0) {
732         log_error("json parsing failed\n");
733         cJSON_Delete(json_response);
734         return NTS_ERR_FAILED;
735     }
736
737     cJSON *element;
738     if(framework_environment.settings.ip_v6_enabled) {
739         element = cJSON_GetObjectItem(node, "GlobalIPv6Address");
740     }
741     else {
742         element = cJSON_GetObjectItem(node, "IPAddress");
743     } 
744
745     if(element == 0) {
746         log_error("json parsing failed\n");
747         cJSON_Delete(json_response);
748         return NTS_ERR_FAILED;
749     }
750
751     container->docker_ip = strdup(element->valuestring);
752
753     cJSON_Delete(json_response);
754     return NTS_ERR_OK;
755 }