From 711967ae9639095ce41500bb0e6f80c8b80fab95 Mon Sep 17 00:00:00 2001 From: Stuart Mackie Date: Tue, 25 Jul 2017 10:37:57 -0700 Subject: Contrail charms Change-Id: I2d259d03f63fce38348b8384e26ac23e3fce44a8 Signed-off-by: Stuart Mackie --- contrail-analyticsdb/hooks/docker_utils.py | 185 +++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 contrail-analyticsdb/hooks/docker_utils.py (limited to 'contrail-analyticsdb/hooks/docker_utils.py') diff --git a/contrail-analyticsdb/hooks/docker_utils.py b/contrail-analyticsdb/hooks/docker_utils.py new file mode 100644 index 0000000..92605f0 --- /dev/null +++ b/contrail-analyticsdb/hooks/docker_utils.py @@ -0,0 +1,185 @@ +import functools +from time import sleep, time + +from subprocess import ( + CalledProcessError, + check_call, + check_output +) +from charmhelpers.core.hookenv import ( + resource_get, + config, + log, + ERROR, +) + + +config = config() + + +DOCKER_PACKAGES = ["docker.engine"] +DOCKER_CLI = "/usr/bin/docker" + + +def retry(f=None, timeout=10, delay=2): + """Retry decorator. + + Provides a decorator that can be used to retry a function if it raises + an exception. + + :param timeout: timeout in seconds (default 10) + :param delay: retry delay in seconds (default 2) + + Examples:: + + # retry fetch_url function + @retry + def fetch_url(): + # fetch url + + # retry fetch_url function for 60 secs + @retry(timeout=60) + def fetch_url(): + # fetch url + """ + if not f: + return functools.partial(retry, timeout=timeout, delay=delay) + + @functools.wraps(f) + def func(*args, **kwargs): + start = time() + error = None + while True: + try: + return f(*args, **kwargs) + except Exception as e: + error = e + elapsed = time() - start + if elapsed >= timeout: + raise error + remaining = timeout - elapsed + sleep(delay if delay <= remaining else remaining) + return func + + +# NOTE: this code assumes that name of container is the part of the +# name of docker image + +def add_docker_repo(): + import platform + cmd = 'curl -fsSL https://apt.dockerproject.org/gpg | sudo apt-key add -' + check_output(cmd, shell=True) + dist = platform.linux_distribution()[2].strip() + cmd = "add-apt-repository " + \ + "\"deb https://apt.dockerproject.org/repo/ " + \ + "ubuntu-%s " % (dist) + \ + "main\"" + check_output(cmd, shell=True) + + +def is_container_launched(name): + # NOTE: 'paused' state is not getting into account if someone paused it + # NOTE: assume that this cmd is the same as inspect of state: + # [DOCKER_CLI, "inspect", "-f", "{{.State.Running}}", name] + cmd = DOCKER_CLI + ' ps | grep -w ' + name + try: + check_output(cmd, shell=True) + return True + except CalledProcessError: + return False + + +def is_container_present(name): + cmd = DOCKER_CLI + ' ps --all | grep -w ' + name + try: + check_output(cmd, shell=True) + return True + except CalledProcessError: + return False + + +def dpkg_version(name, pkg): + try: + return check_output([DOCKER_CLI, + "exec", + name, + "dpkg-query", + "-f", "${Version}\\n", "-W", pkg]).rstrip() + except CalledProcessError: + return None + + +def load_docker_image(name): + img_path = resource_get(name) + if not img_path: + return None + image_id = get_docker_image_id(name) + if image_id: + # remove previous image + check_call([DOCKER_CLI, "rmi", image_id]) + check_call([DOCKER_CLI, "load", "-i", img_path]) + return get_docker_image_id(name) + + +def get_docker_image_id(name): + try: + output = check_output(DOCKER_CLI + ' images | grep -w ' + name, + shell=True) + except CalledProcessError: + return None + output = output.decode('UTF-8').split('\n') + for line in output: + parts = line.split() + if name in parts[0]: + return parts[2].strip() + return None + + +def launch_docker_image(name, additional_args=[]): + image_id = get_docker_image_id(name) + if not image_id: + log(name + " docker image is not available", level=ERROR) + return + + orchestrator = config.get("cloud_orchestrator") + args = [DOCKER_CLI, + "run", + "--net=host", + "--cap-add=AUDIT_WRITE", + "--privileged", + "--restart=unless-stopped", + "--env='CLOUD_ORCHESTRATOR=%s'" % (orchestrator), + "--volume=/etc/contrailctl:/etc/contrailctl", + "--name=%s" % name] + args.extend(additional_args) + args.extend(["-itd", image_id]) + log("Run container with cmd: " + ' '.join(args)) + check_call(args) + + +def docker_cp(name, src, dst): + check_call([DOCKER_CLI, "cp", name + ":" + src, dst]) + + +def docker_exec(name, cmd): + cli = [DOCKER_CLI, "exec", name] + if isinstance(cmd, list): + cli.extend(cmd) + else: + cli.append(cmd) + output = check_output(cli) + return output.decode('UTF-8') + + +@retry(timeout=32, delay=10) +def apply_config_in_container(name, cfg_name): + try: + cmd = (DOCKER_CLI + ' exec ' + name + ' contrailctl config sync -v' + + ' -c ' + cfg_name) + check_call(cmd, shell=True) + return True + except CalledProcessError as e: + if e.returncode == 137: + log("Container was restarted. " + str(e.output), level=ERROR) + return False + raise -- cgit 1.2.3-korg