3d5faa26b305aaa84e39660298ccba6d5243aa63
[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 int docker_pull(const char *repo, const char *image, const char *tag) {
362     assert(repo);
363     assert(image);
364     assert(tag);
365
366     char image_full[256];
367     if(tag && (tag[0] != 0)) {
368         sprintf(image_full, "%s/%s:%s", repo, image, tag);
369     }
370     else {
371         sprintf(image_full, "%s/%s:latest", repo, image);    
372     }
373
374     char url[512];
375     sprintf(url, "http:/v%s/images/create?fromImage=%s", framework_environment.settings.docker_engine_version, image_full);
376
377     char *response = 0;
378     int response_code = 0;
379     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", 0, &response_code, &response);
380
381     if(rc != NTS_ERR_OK) {
382         log_error("http_socket_request failed\n");
383         return NTS_ERR_FAILED;
384     }
385
386     if(response_code != 200) {
387         char *message = docker_parse_json_message(response);
388         log_error("docker_pull failed (%d): %s\n", response_code, message);
389         free(message);
390         free(response);
391         return NTS_ERR_FAILED;
392     }
393     
394     return NTS_ERR_OK;
395 }
396
397 static char *docker_parse_json_message(const char *json_string) {
398     assert(json_string);
399
400     cJSON *json_response = cJSON_Parse(json_string);
401     if(json_response == 0) {
402         log_error("cJSON_Parse failed\n");
403         return 0;
404     }
405
406     cJSON *message = cJSON_GetObjectItem(json_response, "message");
407     if(message == 0) {
408         log_error("json parsing failed\n");
409         cJSON_Delete(json_response);
410         return 0;
411     }
412
413     char *ret = strdup(message->valuestring);
414     cJSON_Delete(json_response);
415     return ret;
416 }
417
418 static int docker_add_port(cJSON *portBindings, uint16_t docker_port, uint16_t host_port) {
419     assert(portBindings);
420
421     cJSON *port = cJSON_CreateArray();
422     if(port == 0) {
423         log_error("could not create JSON object: port\n");
424         return NTS_ERR_FAILED;
425     }
426
427     char dockerContainerPort[20];
428     sprintf(dockerContainerPort, "%d/tcp", docker_port);
429
430     if(cJSON_AddItemToObject(portBindings, dockerContainerPort, port) == 0) {
431         log_error("cJSON_AddItemToObject failed\n");
432         return NTS_ERR_FAILED;
433     }
434
435     cJSON *hostPort = cJSON_CreateObject();
436     if(hostPort == 0) {
437         log_error("could not create JSON object: HostPort\n");
438         return NTS_ERR_FAILED;
439     }
440
441     char dockerHostPort[20];
442     sprintf(dockerHostPort, "%d", host_port);
443     if(cJSON_AddStringToObject(hostPort, "HostPort", dockerHostPort) == 0) {
444         log_error("could not create JSON object: HostPortString\n");
445         cJSON_Delete(hostPort);
446         return NTS_ERR_FAILED;
447     }
448
449     if(cJSON_AddItemToArray(port, hostPort) == 0) {
450         log_error("cJSON_AddItemToArray failed\n");
451         cJSON_Delete(hostPort);
452         return NTS_ERR_FAILED;
453     }
454
455     return NTS_ERR_OK;
456 }
457
458 static int docker_populate_images(docker_context_t *context, int count, const char *min_version) {
459     assert(context);
460     assert(count);
461     assert(min_version);
462
463     char url[512];
464     sprintf(url, "http://v%s/images/json", framework_environment.settings.docker_engine_version);
465
466     char *response = 0;    
467     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
468     if(rc != NTS_ERR_OK) {
469         log_error("http_socket_request failed\n");
470         return NTS_ERR_FAILED;
471     }
472
473     cJSON *json_response = cJSON_Parse(response);
474     free(response);
475     if(json_response == 0) {
476         log_error("cJSON_Parse failed\n");
477         return NTS_ERR_FAILED;
478     }
479     
480     cJSON *element;
481     cJSON_ArrayForEach(element, json_response) {
482         cJSON *tag = cJSON_GetObjectItem(element, "RepoTags");
483         if(tag) {
484             cJSON *ctag;
485             cJSON_ArrayForEach(ctag, tag) {
486                 char *tag_name = ctag->valuestring; //contains repo/image:tag
487                 for(int i = 0; i < count; i++) {
488                     char *s = strstr(tag_name, context[i].image);
489                     if(s != 0) {
490                         char *tag = s + strlen(context[i].image);
491                         if(*tag == ':') {
492                             tag = strdup(s + strlen(context[i].image) + 1);
493                         }
494                         else if(*tag == 0) {
495                             tag = strdup("");
496                         }
497                         else {
498                             continue;
499                         }
500
501                         if(nts_vercmp(tag, min_version) >= 0) {
502                             char *repo = 0;
503                             if(s != tag_name) {
504                                 repo = strdup(tag_name);
505                                 *(strstr(repo, context[i].image) - 1) = 0;
506                             }
507                             else {
508                                 repo = strdup("");
509                             }
510
511                             context[i].available_images = (docker_available_images_t *)realloc(context[i].available_images, (sizeof(docker_available_images_t) * (context[i].available_images_count + 1)));
512                             context[i].available_images[context[i].available_images_count].repo = repo;
513                             context[i].available_images[context[i].available_images_count].tag = tag;
514                             context[i].available_images_count++;
515                         }
516                         else {
517                             free(tag);
518                         }
519                     }
520                 }
521             }
522         }
523     }
524
525     cJSON_Delete(json_response);
526
527     return NTS_ERR_OK;
528 }
529
530 static int docker_container_create(const char *image, docker_container_t *container) {
531     assert(image);
532     assert(container);
533
534     cJSON *postDataJson = cJSON_CreateObject();
535     if(cJSON_AddStringToObject(postDataJson, "Image", image) == 0) {
536         log_error("could not create JSON object: Image\n");
537         return NTS_ERR_FAILED;
538     }
539
540     if(cJSON_AddStringToObject(postDataJson, "Hostname", container->name) == 0) {
541         log_error("could not create JSON object: Hostname\n");
542         cJSON_Delete(postDataJson);
543         return NTS_ERR_FAILED;
544     }    
545
546     cJSON *hostConfig = cJSON_CreateObject();
547     if(hostConfig == 0) {
548         log_error("could not create JSON object: HostConfig\n");
549         cJSON_Delete(postDataJson);
550         return NTS_ERR_FAILED;
551     }
552     if(cJSON_AddItemToObject(postDataJson, "HostConfig", hostConfig) == 0) {
553         log_error("cJSON_AddItemToObject failed\n");
554         cJSON_Delete(postDataJson);
555         return NTS_ERR_FAILED;
556     }
557
558     cJSON *portBindings = cJSON_CreateObject();
559     if(portBindings == 0) {
560         printf("could not create JSON object: PortBindings");
561         cJSON_Delete(postDataJson);
562         return NTS_ERR_FAILED;
563     }
564     if(cJSON_AddItemToObject(hostConfig, "PortBindings", portBindings) == 0) {
565         log_error("cJSON_AddItemToObject failed\n");
566         cJSON_Delete(postDataJson);
567         return NTS_ERR_FAILED;
568     }
569     
570     for(int i = 0; i < framework_environment.settings.ssh_connections; i++) {
571         if(docker_add_port(portBindings, container->docker_netconf_ssh_port + i, container->host_netconf_ssh_port + i) != NTS_ERR_OK) {
572             log_error("docker_add_port() failed\n");
573             cJSON_Delete(postDataJson);
574             return NTS_ERR_FAILED;
575         }
576     }
577
578     for(int i = 0; i < framework_environment.settings.tls_connections; i++) {
579         if(docker_add_port(portBindings, container->docker_netconf_tls_port + i, container->host_netconf_tls_port + i) != NTS_ERR_OK) {
580             log_error("docker_add_port() failed\n");
581             cJSON_Delete(postDataJson);
582             return NTS_ERR_FAILED;
583         }
584     }
585
586     for(int i = 0; i < framework_environment.settings.ftp_connections; i++) {
587         if(docker_add_port(portBindings, container->docker_ftp_port + i, container->host_ftp_port + i) != NTS_ERR_OK) {
588             log_error("docker_add_port() failed\n");
589             cJSON_Delete(postDataJson);
590             return NTS_ERR_FAILED;
591         }
592     }
593
594     for(int i = 0; i < framework_environment.settings.sftp_connections; i++) {
595         if(docker_add_port(portBindings, container->docker_sftp_port + i, container->host_sftp_port + i) != NTS_ERR_OK) {
596             log_error("docker_add_port() failed\n");
597             cJSON_Delete(postDataJson);
598             return NTS_ERR_FAILED;
599         }
600     }
601     
602     //environment vars start
603     asprintf(&docker_environment_var[4].value, "%d", container->host_netconf_ssh_port);
604     asprintf(&docker_environment_var[5].value, "%d", container->host_netconf_tls_port);
605     asprintf(&docker_environment_var[6].value, "%d", container->host_ftp_port);
606     asprintf(&docker_environment_var[7].value, "%d", container->host_sftp_port);
607
608     cJSON *env_variables_array = cJSON_CreateArray();
609     if(env_variables_array == 0) {
610         log_error("Could not create JSON object: Env array\n");
611         cJSON_Delete(postDataJson);
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         return NTS_ERR_FAILED;
617     }
618     cJSON_AddItemToObject(postDataJson, "Env", env_variables_array);
619
620     for(int i = 0; i < docker_environment_var_count; i++) {
621         if(docker_environment_var[i].value) {
622             char *environment_var = 0;
623             asprintf(&environment_var, "%s=%s", docker_environment_var[i].name, docker_environment_var[i].value);
624
625             cJSON *env_var_obj = cJSON_CreateString(environment_var);
626             if(env_var_obj == 0) {
627                 log_error("could not create JSON object\n");
628                 cJSON_Delete(postDataJson);
629                 free(docker_environment_var[4].value);
630                 free(docker_environment_var[5].value);
631                 free(docker_environment_var[6].value);
632                 free(docker_environment_var[7].value);
633                 free(environment_var);
634                 return NTS_ERR_FAILED;
635             }
636             cJSON_AddItemToArray(env_variables_array, env_var_obj);
637
638             free(environment_var);
639         }
640     }
641
642     free(docker_environment_var[4].value);
643     free(docker_environment_var[5].value);
644     free(docker_environment_var[6].value);
645     free(docker_environment_var[7].value);
646     //environment vars finished
647
648     cJSON *netMode = cJSON_Duplicate(docker_network_info, 1);
649     cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode);
650
651     char *post_data_string = 0;
652     post_data_string = cJSON_PrintUnformatted(postDataJson);
653     cJSON_Delete(postDataJson);
654
655     char url[512];
656     sprintf(url, "http:/v%s/containers/create?name=%s", framework_environment.settings.docker_engine_version, container->name);
657
658     char *response = 0;
659     int response_code = 0;
660     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", post_data_string, &response_code, &response);
661     free(post_data_string);
662     if(rc != NTS_ERR_OK) {
663         log_error("http_socket_request failed\n");
664         return NTS_ERR_FAILED;
665     }
666
667     if(response_code != 201) {
668         char *message = docker_parse_json_message(response);
669         log_error("docker_container_create failed (%d): %s\n", response_code, message);
670         free(message);
671         free(response);
672         return NTS_ERR_FAILED;
673     }
674     else {
675         cJSON *json_response = cJSON_Parse(response);
676         free(response);
677         const cJSON *container_id = 0;
678
679         container_id = cJSON_GetObjectItemCaseSensitive(json_response, "Id");
680
681         if(cJSON_IsString(container_id) && (container_id->valuestring != 0)) {
682             char container_id_short[13];
683             memset(container_id_short, '\0', sizeof(container_id_short));
684             strncpy(container_id_short, container_id->valuestring, 12);
685
686             container->id = strdup(container_id_short);
687
688             cJSON_Delete(json_response);
689             return NTS_ERR_OK;
690         }
691         else {
692             cJSON_Delete(json_response);
693             return NTS_ERR_FAILED;
694         }
695     }
696 }
697
698 static int docker_container_start(docker_container_t *container) {
699     assert(container);
700
701     char url[512];
702     sprintf(url, "http://v%s/containers/%s/start", framework_environment.settings.docker_engine_version, container->id);
703
704     char *response = 0;
705     int response_code = 0;
706     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "POST", "", &response_code, &response);
707     if(rc != NTS_ERR_OK) {
708         log_error("http_socket_request failed\n");
709         return NTS_ERR_FAILED;
710     }
711     else {
712         if(response_code == 304) {
713             log_error("docker_container_start failed (%d): container already started\n", response_code);
714             free(response);
715             return NTS_ERR_FAILED;
716         }
717         else if(response_code != 204) {
718             char *message = docker_parse_json_message(response);
719             log_error("docker_container_start failed (%d): %s\n", response_code, message);
720             free(message);
721             free(response);
722             return NTS_ERR_FAILED;
723         }
724         
725     }
726
727     return NTS_ERR_OK;
728 }
729
730 static int docker_container_inspect(docker_container_t *container) {
731     assert(container);
732
733     char url[512];
734     sprintf(url, "http://v%s/containers/%s/json", framework_environment.settings.docker_engine_version, container->id);
735
736     char *response = 0;    
737     int rc = http_socket_request(url, DOCKER_SOCK_FNAME, "GET", "", 0, &response);
738     if(rc != NTS_ERR_OK) {
739         log_error("http_socket_request failed\n");
740         free(response);
741         return NTS_ERR_FAILED;
742     }
743
744     cJSON *json_response = cJSON_Parse(response);
745     free(response);
746     if(json_response == 0) {
747         log_error("cJSON_Parse failed\n");
748         return NTS_ERR_FAILED;
749     }
750
751
752     cJSON *main_node = cJSON_GetObjectItem(json_response, "NetworkSettings");
753     if(main_node == 0) {
754         log_error("json parsing failed\n");
755         cJSON_Delete(json_response);
756         return NTS_ERR_FAILED;
757     }
758
759     cJSON *node = cJSON_GetObjectItem(main_node, "Networks");
760     if(node == 0) {
761         log_error("json parsing failed\n");
762         cJSON_Delete(json_response);
763         return NTS_ERR_FAILED;
764     }
765         
766     node = node->child;   //get info from the first in array
767     if(node == 0) {
768         log_error("json parsing failed\n");
769         cJSON_Delete(json_response);
770         return NTS_ERR_FAILED;
771     }
772
773     cJSON *element;
774     if(framework_environment.settings.ip_v6_enabled) {
775         element = cJSON_GetObjectItem(node, "GlobalIPv6Address");
776     }
777     else {
778         element = cJSON_GetObjectItem(node, "IPAddress");
779     } 
780
781     if(element == 0) {
782         log_error("json parsing failed\n");
783         cJSON_Delete(json_response);
784         return NTS_ERR_FAILED;
785     }
786
787     container->docker_ip = strdup(element->valuestring);
788
789     cJSON_Delete(json_response);
790     return NTS_ERR_OK;
791 }