blob: d82e346f767ff08ab117e7aa8098f14ceccd2ff7 [file] [log] [blame]
Tommy Carpenter81b9ed72017-08-23 11:21:44 -04001# org.onap.dcae
2# ================================================================================
3# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
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# ============LICENSE_END=========================================================
17#
18# ECOMP is a trademark and service mark of AT&T Intellectual Property.
19
20import json
21import docker
22import requests
23from dockering.exceptions import DockerError, DockerConnectionError
24from dockering import config_building as cb
25from dockering import utils
26
27
Tommy Carpenter81b9ed72017-08-23 11:21:44 -040028def create_client(hostname, port, reauth=False, logins=[]):
29 """Create Docker client
30
31 Args:
32 -----
33 reauth: (boolean) Forces reauthentication e.g. Docker login
34 """
35 base_url = "tcp://{0}:{1}".format(hostname, port)
36 try:
37 client = docker.Client(base_url=base_url)
38
39 for dcl in logins:
40 dcl["reauth"] = reauth
41 client.login(**dcl)
42
43 return client
44 except requests.exceptions.ConnectionError as e:
45 raise DockerConnectionError(str(e))
46
47
48def create_container_using_config(client, service_component_name, container_config):
49 try:
50 image_name = container_config["Image"]
51
52 if not client.images(image_name):
53 def parse_pull_response(response):
54 """Pull response is a giant string of JSON messages concatentated
55 by `\r\n`. This method returns back those messages in the form of
56 list of dicts."""
57 # NOTE: There's a trailing `\r\n` so the last element is empty
58 # string. Remove that.
59 return list(map(json.loads, response.split("\r\n")[:-1]))
60
61 def get_error_message(response):
62 """Attempts to pull out and return an error message from parsed
63 response if it exists else return None"""
64 return response[-1].get("error", None)
65
66 # TODO: Implement this as verbose?
67 # for resp in client.pull(image, stream=True, decode=True):
68 response = parse_pull_response(client.pull(image_name))
69 error_message = get_error_message(response)
70
71 if error_message:
72 raise DockerError("Error pulling Docker image: {0}".format(error_message))
73 else:
74 utils.logger.info("Pulled Docker image: {0}".format(image_name))
75
76 return client.create_container_from_config(container_config,
77 service_component_name)
78 except requests.exceptions.ConnectionError as e:
79 # This separates connection failures so that caller can decide what to do.
80 # Underlying errors this inspired were socket.errors that are sourced
81 # from http://www.virtsync.com/c-error-codes-include-errno
82 raise DockerConnectionError(str(e))
83 except Exception as e:
84 raise DockerError(str(e))
85
86
87def create_container(client, image_name, service_component_name, envs,
88 host_config_params):
89 """Creates Docker container
90
91 Args:
92 -----
93 envs (dict): dict of environment variables to pass into the docker containers.
94 Gets passed into docker-py.create_container call
95 host_config_params (dict): Dict of input parameters to the docker-py
96 "create_host_config" method call
97 """
98 config = cb.create_container_config(client, image_name, envs, host_config_params)
99 return create_container_using_config(client, service_component_name, config)
100
101
102def start_container(client, container):
103 try:
104 # TODO: Have logic to inspect response and through NonRecoverableError
105 # when start fails. Docker-py docs don't quickly tell me what the
106 # response looks like.
Michael Hwange11be5d2017-09-21 12:19:32 -0400107 client.start(container=container["Id"])
Tommy Carpenter81b9ed72017-08-23 11:21:44 -0400108 utils.logger.info("Container started: {0}".format(container["Id"]))
109
110 # TODO: Maybe check stats?
111 return container["Id"]
112 except Exception as e:
113 raise DockerError(str(e))
114
115
116def stop_then_remove_container(client, service_component_name):
117 try:
118 client.stop(service_component_name)
119 client.remove_container(service_component_name)
120 except docker.errors.NotFound as e:
121 raise DockerError("Container not found: {0}".format(service_component_name))
122 except Exception as e:
123 raise DockerError(str(e))
124
125
126def remove_image(client, image_name):
127 """Remove the Docker image"""
128 try:
129 client.remove_image(image_name)
130 return True
131 except:
132 # Failure to remove image is not classified as terrible..for now
133 return False
134
Michael Hwangf5ce3032017-09-07 16:00:09 -0400135
136def build_policy_update_cmd(script_path, use_sh=True, msg_type="policy", **kwargs):
137 """Build command to execute for policy update"""
138 data = json.dumps(kwargs or {})
139
140 if use_sh:
141 return ['/bin/sh', script_path, msg_type, data]
142 else:
143 return [script_path, msg_type, data]
144
145def notify_for_policy_update(client, container_id, cmd):
146 """Notify Docker container that policy update occurred
147
148 Notify the Docker container by doing Docker exec of passed-in command
149
150 Args:
151 -----
152 container_id: (string)
153 cmd: (list) of strings each entry being part of the command
154 """
155 try:
156 result = client.exec_create(container=container_id,
157 cmd=cmd)
158 result = client.exec_start(exec_id=result['Id'])
159
160 utils.logger.info("Pass to docker exec {0} {1} {2}".format(
161 container_id, cmd, result))
162
163 return result
164 except Exception as e:
165 raise DockerError(e)