Bugfix for SIM-63
[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     if(framework_environment.settings.ssh_connections == 0) {
195         container->docker_netconf_ssh_port = 0;
196     }
197     if(framework_environment.settings.tls_connections == 0) {
198         container->docker_netconf_tls_port = 0;
199     }
200     container->docker_ftp_port= STANDARD_FTP_PORT;
201     container->docker_sftp_port= STANDARD_SFTP_PORT;
202
203     container->host_ip = strdup(framework_environment.host.ip);
204     container->host_netconf_ssh_port = host_netconf_ssh_port;
205     container->host_netconf_tls_port = host_netconf_tls_port;
206     container->host_ftp_port = host_ftp_port;
207     container->host_sftp_port = host_sftp_port;
208
209     int rc = docker_container_create(image_full, container);
210     if(rc != NTS_ERR_OK) {
211         log_error("docker_container_create failed\n");
212         return NTS_ERR_FAILED;
213     }
214
215     rc = docker_container_start(container);
216     if(rc != NTS_ERR_OK) {
217         log_error("docker_container_start failed\n");
218         docker_stop(container);
219         return NTS_ERR_FAILED;
220     }
221
222     rc = docker_container_inspect(container);
223     if(rc != NTS_ERR_OK) {
224         log_error("docker_container_inspect failed\n");
225         docker_stop(container);
226         return NTS_ERR_FAILED;
227     }
228
229     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);
230
231     return NTS_ERR_OK;
232 }
233
234 int docker_stop(docker_container_t *container) {
235     assert(container);
236
237     char url[512];
238     sprintf(url, "http://v%s/containers/%s?force=true", framework_environment.settings.docker_engine_version, container->id);
239     
240     free(container->name);
241     free(container->id);
242     free(container->docker_ip);
243     free(container->host_ip);
244
245     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "DELETE", "", 0, 0);
246     if(rc != NTS_ERR_OK) {
247         log_error("http_socket_request failed\n");
248         return NTS_ERR_FAILED;
249     }
250
251     return NTS_ERR_OK;
252 }
253
254 int docker_usage_get(const char **instances_id, int count, docker_usage_t *usage) {
255     assert(instances_id);
256     assert(usage);
257
258     usage->cpu = 0;
259     usage->mem = 0;
260
261     char buffer[1024];
262     char full_text[1024 * 1024];
263     FILE* pipe = popen("docker stats --no-stream --format \"table {{.ID}}|{{.CPUPerc}}|{{.MemUsage}}|\"", "r");
264     if (!pipe) {
265         log_error("popen() failed\n");
266         return NTS_ERR_FAILED;
267     }
268
269     int n = 1;
270     int k = 0;
271     while(n != 0) {
272         n = fread(buffer, 1, sizeof(buffer), pipe);
273         for(int i = 0; i < n; i++) {
274             full_text[k++] = buffer[i];
275         }
276     }
277     pclose(pipe);
278     full_text[k] = 0;
279
280     char *c = full_text;
281     
282     c = strstr(c, "\n");
283     while(c) {
284         char line[1024];
285         line[0] = 0;
286
287         char *d = strstr(c + 1, "\n");
288         if(d) {
289             for(char *i = c + 1; i < d; i++) {
290                 line[i - c - 1] = *i;
291                 line[i - c] = 0;
292             }
293
294             char container_name[1024];
295             char buff[1024];
296             float cpu = 0.0;
297             float mem = 0.0;
298
299             char *x = strstr(line, "|");
300             for(char *i = line; i < x; i++) {
301                 container_name[i - line] = *i;
302                 container_name[i - line + 1] = 0;
303             }
304
305             char *start = x + 1;
306             x = strstr(start, "|");
307             for(char *i = start; i < x; i++) {
308                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
309                     buff[i - start] = *i;
310                 }
311                 else {
312                     buff[i - start] = 0;
313                     break;
314                 }
315             }
316
317             cpu = strtof(buff, 0);
318
319             int mul = 1;
320             start = x + 1;
321             x = strstr(start, "|");
322             for(char *i = start; i < x; i++) {
323                 if(((*i >= '0') && (*i <= '9')) || (*i == '.')) {
324                     buff[i - start] = *i;
325                 }
326                 else {
327                     if(*i == 'G') {
328                         mul = 1024;
329                     }
330                     buff[i - start] = 0;
331                     break;
332                 }
333             }
334
335             mem = strtof(buff, 0) * mul;
336             
337             
338             if(strcmp(container_name, framework_environment.settings.hostname) == 0) {
339                 usage->cpu += cpu;
340                 usage->mem += mem;
341             }
342             else {
343                 for(int i = 0; i < count; i++) {   
344                     if(strcmp(container_name, instances_id[i]) == 0) {
345                         usage->cpu += cpu;
346                         usage->mem += mem;
347                         break;
348                     }
349                 }
350             }
351         }
352         
353         c = d;
354     }
355
356     usage->cpu /= get_nprocs();
357
358     return NTS_ERR_OK;
359 }
360
361 static char *docker_parse_json_message(const char *json_string) {
362     assert(json_string);
363
364     cJSON *json_response = cJSON_Parse(json_string);
365     if(json_response == 0) {
366         log_error("cJSON_Parse failed\n");
367         return 0;
368     }
369
370     cJSON *message = cJSON_GetObjectItem(json_response, "message");
371     if(message == 0) {
372         log_error("json parsing failed\n");
373         cJSON_Delete(json_response);
374         return 0;
375     }
376
377     char *ret = strdup(message->valuestring);
378     cJSON_Delete(json_response);
379     return ret;
380 }
381
382 static int docker_add_port(cJSON *portBindings, uint16_t docker_port, uint16_t host_port) {
383     assert(portBindings);
384
385     cJSON *port = cJSON_CreateArray();
386     if(port == 0) {
387         log_error("could not create JSON object: port\n");
388         return NTS_ERR_FAILED;
389     }
390
391     char dockerContainerPort[20];
392     sprintf(dockerContainerPort, "%d/tcp", docker_port);
393
394     if(cJSON_AddItemToObject(portBindings, dockerContainerPort, port) == 0) {
395         log_error("cJSON_AddItemToObject failed\n");
396         return NTS_ERR_FAILED;
397     }
398
399     cJSON *hostPort = cJSON_CreateObject();
400     if(hostPort == 0) {
401         log_error("could not create JSON object: HostPort\n");
402         return NTS_ERR_FAILED;
403     }
404
405     char dockerHostPort[20];
406     sprintf(dockerHostPort, "%d", host_port);
407     if(cJSON_AddStringToObject(hostPort, "HostPort", dockerHostPort) == 0) {
408         log_error("could not create JSON object: HostPortString\n");
409         cJSON_Delete(hostPort);
410         return NTS_ERR_FAILED;
411     }
412
413     if(cJSON_AddStringToObject(hostPort, "HostIp", "0.0.0.0") == 0) {   //or, future, bind to container->host_ip
414         log_error("could not create JSON object: HostIpString\n");
415         cJSON_Delete(hostPort);
416         return NTS_ERR_FAILED;
417     }
418
419     if(cJSON_AddItemToArray(port, hostPort) == 0) {
420         log_error("cJSON_AddItemToArray failed\n");
421         cJSON_Delete(hostPort);
422         return NTS_ERR_FAILED;
423     }
424
425     return NTS_ERR_OK;
426 }
427
428 static int docker_populate_images(docker_context_t *context, int count, const char *min_version) {
429     assert(context);
430     assert(count);
431     assert(min_version);
432
433     char url[512];
434     sprintf(url, "http://v%s/images/json", framework_environment.settings.docker_engine_version);
435
436     char *response = 0;    
437     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
438     if(rc != NTS_ERR_OK) {
439         log_error("http_socket_request failed\n");
440         return NTS_ERR_FAILED;
441     }
442
443     cJSON *json_response = cJSON_Parse(response);
444     free(response);
445     if(json_response == 0) {
446         log_error("cJSON_Parse failed\n");
447         return NTS_ERR_FAILED;
448     }
449     
450     cJSON *element;
451     cJSON_ArrayForEach(element, json_response) {
452         cJSON *tag = cJSON_GetObjectItem(element, "RepoTags");
453         if(tag) {
454             cJSON *ctag;
455             cJSON_ArrayForEach(ctag, tag) {
456                 char *tag_name = ctag->valuestring; //contains repo/image:tag
457                 for(int i = 0; i < count; i++) {
458                     char *s = strstr(tag_name, context[i].image);
459                     if(s != 0) {
460                         char *tag = s + strlen(context[i].image);
461                         if(*tag == ':') {
462                             tag = strdup(s + strlen(context[i].image) + 1);
463                         }
464                         else if(*tag == 0) {
465                             tag = strdup("");
466                         }
467                         else {
468                             continue;
469                         }
470
471                         if(nts_vercmp(tag, min_version) >= 0) {
472                             char *repo = 0;
473                             if(s != tag_name) {
474                                 repo = strdup(tag_name);
475                                 *(strstr(repo, context[i].image) - 1) = 0;
476                             }
477                             else {
478                                 repo = strdup("");
479                             }
480
481                             context[i].available_images = (docker_available_images_t *)realloc(context[i].available_images, (sizeof(docker_available_images_t) * (context[i].available_images_count + 1)));
482                             context[i].available_images[context[i].available_images_count].repo = repo;
483                             context[i].available_images[context[i].available_images_count].tag = tag;
484                             context[i].available_images_count++;
485                         }
486                         else {
487                             free(tag);
488                         }
489                     }
490                 }
491             }
492         }
493     }
494
495     cJSON_Delete(json_response);
496
497     return NTS_ERR_OK;
498 }
499
500 static int docker_container_create(const char *image, docker_container_t *container) {
501     assert(image);
502     assert(container);
503
504     cJSON *postDataJson = cJSON_CreateObject();
505     if(cJSON_AddStringToObject(postDataJson, "Image", image) == 0) {
506         log_error("could not create JSON object: Image\n");
507         return NTS_ERR_FAILED;
508     }
509
510     if(cJSON_AddStringToObject(postDataJson, "Hostname", container->name) == 0) {
511         log_error("could not create JSON object: Hostname\n");
512         cJSON_Delete(postDataJson);
513         return NTS_ERR_FAILED;
514     }    
515
516     cJSON *hostConfig = cJSON_CreateObject();
517     if(hostConfig == 0) {
518         log_error("could not create JSON object: HostConfig\n");
519         cJSON_Delete(postDataJson);
520         return NTS_ERR_FAILED;
521     }
522     if(cJSON_AddItemToObject(postDataJson, "HostConfig", hostConfig) == 0) {
523         log_error("cJSON_AddItemToObject failed\n");
524         cJSON_Delete(postDataJson);
525         return NTS_ERR_FAILED;
526     }
527
528     cJSON *portBindings = cJSON_CreateObject();
529     if(portBindings == 0) {
530         printf("could not create JSON object: PortBindings");
531         cJSON_Delete(postDataJson);
532         return NTS_ERR_FAILED;
533     }
534     if(cJSON_AddItemToObject(hostConfig, "PortBindings", portBindings) == 0) {
535         log_error("cJSON_AddItemToObject failed\n");
536         cJSON_Delete(postDataJson);
537         return NTS_ERR_FAILED;
538     }
539     
540     for(int i = 0; i < framework_environment.settings.ssh_connections; i++) {
541         if(docker_add_port(portBindings, container->docker_netconf_ssh_port + i, container->host_netconf_ssh_port + i) != NTS_ERR_OK) {
542             log_error("docker_add_port() failed\n");
543             cJSON_Delete(postDataJson);
544             return NTS_ERR_FAILED;
545         }
546     }
547
548     for(int i = 0; i < framework_environment.settings.tls_connections; i++) {
549         if(docker_add_port(portBindings, container->docker_netconf_tls_port + i, container->host_netconf_tls_port + i) != NTS_ERR_OK) {
550             log_error("docker_add_port() failed\n");
551             cJSON_Delete(postDataJson);
552             return NTS_ERR_FAILED;
553         }
554     }
555
556     for(int i = 0; i < framework_environment.settings.ftp_connections; i++) {
557         if(docker_add_port(portBindings, container->docker_ftp_port + i, container->host_ftp_port + i) != NTS_ERR_OK) {
558             log_error("docker_add_port() failed\n");
559             cJSON_Delete(postDataJson);
560             return NTS_ERR_FAILED;
561         }
562     }
563
564     for(int i = 0; i < framework_environment.settings.sftp_connections; i++) {
565         if(docker_add_port(portBindings, container->docker_sftp_port + i, container->host_sftp_port + i) != NTS_ERR_OK) {
566             log_error("docker_add_port() failed\n");
567             cJSON_Delete(postDataJson);
568             return NTS_ERR_FAILED;
569         }
570     }
571     
572     //environment vars start
573     asprintf(&docker_environment_var[4].value, "%d", container->host_netconf_ssh_port);
574     asprintf(&docker_environment_var[5].value, "%d", container->host_netconf_tls_port);
575     asprintf(&docker_environment_var[6].value, "%d", container->host_ftp_port);
576     asprintf(&docker_environment_var[7].value, "%d", container->host_sftp_port);
577
578     cJSON *env_variables_array = cJSON_CreateArray();
579     if(env_variables_array == 0) {
580         log_error("Could not create JSON object: Env array\n");
581         cJSON_Delete(postDataJson);
582         free(docker_environment_var[4].value);
583         free(docker_environment_var[5].value);
584         free(docker_environment_var[6].value);
585         free(docker_environment_var[7].value);
586         return NTS_ERR_FAILED;
587     }
588     cJSON_AddItemToObject(postDataJson, "Env", env_variables_array);
589
590     for(int i = 0; i < docker_environment_var_count; i++) {
591         if(docker_environment_var[i].value) {
592             char *environment_var = 0;
593             asprintf(&environment_var, "%s=%s", docker_environment_var[i].name, docker_environment_var[i].value);
594
595             cJSON *env_var_obj = cJSON_CreateString(environment_var);
596             if(env_var_obj == 0) {
597                 log_error("could not create JSON object\n");
598                 cJSON_Delete(postDataJson);
599                 free(docker_environment_var[4].value);
600                 free(docker_environment_var[5].value);
601                 free(docker_environment_var[6].value);
602                 free(docker_environment_var[7].value);
603                 free(environment_var);
604                 return NTS_ERR_FAILED;
605             }
606             cJSON_AddItemToArray(env_variables_array, env_var_obj);
607
608             free(environment_var);
609         }
610     }
611
612     free(docker_environment_var[4].value);
613     free(docker_environment_var[5].value);
614     free(docker_environment_var[6].value);
615     free(docker_environment_var[7].value);
616     //environment vars finished
617
618     cJSON *netMode = cJSON_Duplicate(docker_network_info, 1);
619     cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode);
620
621     char *post_data_string = 0;
622     post_data_string = cJSON_PrintUnformatted(postDataJson);
623     cJSON_Delete(postDataJson);
624
625     char url[512];
626     sprintf(url, "http:/v%s/containers/create?name=%s", framework_environment.settings.docker_engine_version, container->name);
627
628     char *response = 0;
629     int response_code = 0;
630     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", post_data_string, &response_code, &response);
631     free(post_data_string);
632     if(rc != NTS_ERR_OK) {
633         log_error("http_socket_request failed\n");
634         return NTS_ERR_FAILED;
635     }
636
637     if(response_code != 201) {
638         char *message = docker_parse_json_message(response);
639         log_error("docker_container_create failed (%d): %s\n", response_code, message);
640         free(message);
641         free(response);
642         return NTS_ERR_FAILED;
643     }
644     else {
645         cJSON *json_response = cJSON_Parse(response);
646         free(response);
647         const cJSON *container_id = 0;
648
649         container_id = cJSON_GetObjectItemCaseSensitive(json_response, "Id");
650
651         if(cJSON_IsString(container_id) && (container_id->valuestring != 0)) {
652             char container_id_short[13];
653             memset(container_id_short, '\0', sizeof(container_id_short));
654             strncpy(container_id_short, container_id->valuestring, 12);
655
656             container->id = strdup(container_id_short);
657
658             cJSON_Delete(json_response);
659             return NTS_ERR_OK;
660         }
661         else {
662             cJSON_Delete(json_response);
663             return NTS_ERR_FAILED;
664         }
665     }
666 }
667
668 static int docker_container_start(docker_container_t *container) {
669     assert(container);
670
671     char url[512];
672     sprintf(url, "http://v%s/containers/%s/start", framework_environment.settings.docker_engine_version, container->id);
673
674     char *response = 0;
675     int response_code = 0;
676     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", "", &response_code, &response);
677     if(rc != NTS_ERR_OK) {
678         log_error("http_socket_request failed\n");
679         return NTS_ERR_FAILED;
680     }
681     else {
682         if(response_code == 304) {
683             log_error("docker_container_start failed (%d): container already started\n", response_code);
684             free(response);
685             return NTS_ERR_FAILED;
686         }
687         else if(response_code != 204) {
688             char *message = docker_parse_json_message(response);
689             log_error("docker_container_start failed (%d): %s\n", response_code, message);
690             free(message);
691             free(response);
692             return NTS_ERR_FAILED;
693         }
694         
695     }
696
697     return NTS_ERR_OK;
698 }
699
700 static int docker_container_inspect(docker_container_t *container) {
701     assert(container);
702
703     char url[512];
704     sprintf(url, "http://v%s/containers/%s/json", framework_environment.settings.docker_engine_version, container->id);
705
706     char *response = 0;    
707     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
708     if(rc != NTS_ERR_OK) {
709         log_error("http_socket_request failed\n");
710         free(response);
711         return NTS_ERR_FAILED;
712     }
713
714     cJSON *json_response = cJSON_Parse(response);
715     free(response);
716     if(json_response == 0) {
717         log_error("cJSON_Parse failed\n");
718         return NTS_ERR_FAILED;
719     }
720
721
722     cJSON *main_node = cJSON_GetObjectItem(json_response, "NetworkSettings");
723     if(main_node == 0) {
724         log_error("json parsing failed\n");
725         cJSON_Delete(json_response);
726         return NTS_ERR_FAILED;
727     }
728
729     cJSON *node = cJSON_GetObjectItem(main_node, "Networks");
730     if(node == 0) {
731         log_error("json parsing failed\n");
732         cJSON_Delete(json_response);
733         return NTS_ERR_FAILED;
734     }
735         
736     node = node->child;   //get info from the first in array
737     if(node == 0) {
738         log_error("json parsing failed\n");
739         cJSON_Delete(json_response);
740         return NTS_ERR_FAILED;
741     }
742
743     cJSON *element;
744     if(framework_environment.settings.ip_v6_enabled) {
745         element = cJSON_GetObjectItem(node, "GlobalIPv6Address");
746     }
747     else {
748         element = cJSON_GetObjectItem(node, "IPAddress");
749     } 
750
751     if(element == 0) {
752         log_error("json parsing failed\n");
753         cJSON_Delete(json_response);
754         return NTS_ERR_FAILED;
755     }
756
757     container->docker_ip = strdup(element->valuestring);
758
759     cJSON_Delete(json_response);
760     return NTS_ERR_OK;
761 }