blob: d82e346f767ff08ab117e7aa8098f14ceccd2ff7 [file] [log] [blame]
# org.onap.dcae
# ================================================================================
# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
#
# ECOMP is a trademark and service mark of AT&T Intellectual Property.
import json
import docker
import requests
from dockering.exceptions import DockerError, DockerConnectionError
from dockering import config_building as cb
from dockering import utils
def create_client(hostname, port, reauth=False, logins=[]):
"""Create Docker client
Args:
-----
reauth: (boolean) Forces reauthentication e.g. Docker login
"""
base_url = "tcp://{0}:{1}".format(hostname, port)
try:
client = docker.Client(base_url=base_url)
for dcl in logins:
dcl["reauth"] = reauth
client.login(**dcl)
return client
except requests.exceptions.ConnectionError as e:
raise DockerConnectionError(str(e))
def create_container_using_config(client, service_component_name, container_config):
try:
image_name = container_config["Image"]
if not client.images(image_name):
def parse_pull_response(response):
"""Pull response is a giant string of JSON messages concatentated
by `\r\n`. This method returns back those messages in the form of
list of dicts."""
# NOTE: There's a trailing `\r\n` so the last element is empty
# string. Remove that.
return list(map(json.loads, response.split("\r\n")[:-1]))
def get_error_message(response):
"""Attempts to pull out and return an error message from parsed
response if it exists else return None"""
return response[-1].get("error", None)
# TODO: Implement this as verbose?
# for resp in client.pull(image, stream=True, decode=True):
response = parse_pull_response(client.pull(image_name))
error_message = get_error_message(response)
if error_message:
raise DockerError("Error pulling Docker image: {0}".format(error_message))
else:
utils.logger.info("Pulled Docker image: {0}".format(image_name))
return client.create_container_from_config(container_config,
service_component_name)
except requests.exceptions.ConnectionError as e:
# This separates connection failures so that caller can decide what to do.
# Underlying errors this inspired were socket.errors that are sourced
# from http://www.virtsync.com/c-error-codes-include-errno
raise DockerConnectionError(str(e))
except Exception as e:
raise DockerError(str(e))
def create_container(client, image_name, service_component_name, envs,
host_config_params):
"""Creates Docker container
Args:
-----
envs (dict): dict of environment variables to pass into the docker containers.
Gets passed into docker-py.create_container call
host_config_params (dict): Dict of input parameters to the docker-py
"create_host_config" method call
"""
config = cb.create_container_config(client, image_name, envs, host_config_params)
return create_container_using_config(client, service_component_name, config)
def start_container(client, container):
try:
# TODO: Have logic to inspect response and through NonRecoverableError
# when start fails. Docker-py docs don't quickly tell me what the
# response looks like.
client.start(container=container["Id"])
utils.logger.info("Container started: {0}".format(container["Id"]))
# TODO: Maybe check stats?
return container["Id"]
except Exception as e:
raise DockerError(str(e))
def stop_then_remove_container(client, service_component_name):
try:
client.stop(service_component_name)
client.remove_container(service_component_name)
except docker.errors.NotFound as e:
raise DockerError("Container not found: {0}".format(service_component_name))
except Exception as e:
raise DockerError(str(e))
def remove_image(client, image_name):
"""Remove the Docker image"""
try:
client.remove_image(image_name)
return True
except:
# Failure to remove image is not classified as terrible..for now
return False
def build_policy_update_cmd(script_path, use_sh=True, msg_type="policy", **kwargs):
"""Build command to execute for policy update"""
data = json.dumps(kwargs or {})
if use_sh:
return ['/bin/sh', script_path, msg_type, data]
else:
return [script_path, msg_type, data]
def notify_for_policy_update(client, container_id, cmd):
"""Notify Docker container that policy update occurred
Notify the Docker container by doing Docker exec of passed-in command
Args:
-----
container_id: (string)
cmd: (list) of strings each entry being part of the command
"""
try:
result = client.exec_create(container=container_id,
cmd=cmd)
result = client.exec_start(exec_id=result['Id'])
utils.logger.info("Pass to docker exec {0} {1} {2}".format(
container_id, cmd, result))
return result
except Exception as e:
raise DockerError(e)