summaryrefslogtreecommitdiffstats
path: root/dovetail/container.py
diff options
context:
space:
mode:
Diffstat (limited to 'dovetail/container.py')
-rw-r--r--dovetail/container.py360
1 files changed, 135 insertions, 225 deletions
diff --git a/dovetail/container.py b/dovetail/container.py
index 89c68ccb..b2a9428f 100644
--- a/dovetail/container.py
+++ b/dovetail/container.py
@@ -9,21 +9,23 @@
# http://www.apache.org/licenses/LICENSE-2.0
#
-import os
+import docker
+import sys
-import utils.dovetail_logger as dt_logger
-import utils.dovetail_utils as dt_utils
-from utils.dovetail_config import DovetailConfig as dt_cfg
+import dovetail.utils.dovetail_logger as dt_logger
+import dovetail.utils.dovetail_utils as dt_utils
+from dovetail.utils.dovetail_config import DovetailConfig as dt_cfg
class Container(object):
- container_list = {}
-
logger = None
- def __init__(self):
- pass
+ def __init__(self, testcase):
+ self.container = None
+ self.testcase = testcase
+ self.valid_type = self.testcase.validate_type()
+ self.client = docker.from_env(timeout=None)
def __str__(self):
pass
@@ -32,260 +34,168 @@ class Container(object):
def create_log(cls):
cls.logger = dt_logger.Logger(__name__ + '.Container').getLogger()
- @classmethod
- def get(cls, type):
- return cls.container_list[type]
-
- @classmethod
- def _get_config(cls, field, project_cfg, testcase_cfg):
+ def _get_config(self, field, project_cfg, testcase_cfg):
value = dt_utils.get_value_from_dict(field, testcase_cfg)
if not value:
value = dt_utils.get_value_from_dict(field, project_cfg)
if not value:
- cls.logger.error("Couldn't find key {}.".format(field))
+ self.logger.error("Couldn't find key {}.".format(field))
return None
return value
- @classmethod
- def get_docker_image(cls, testcase):
- project_cfg = dt_cfg.dovetail_config[testcase.validate_type()]
- testcase_cfg = testcase.testcase['validate']
+ def get_docker_image(self):
+ project_cfg = dt_cfg.dovetail_config[self.valid_type]
+ testcase_cfg = self.testcase.testcase['validate']
- name = cls._get_config('image_name', project_cfg, testcase_cfg)
- tag = cls._get_config('docker_tag', project_cfg, testcase_cfg)
- return "{}:{}".format(name, tag) if name and tag else None
+ name = self._get_config('image_name', project_cfg, testcase_cfg)
+ tag = self._get_config('docker_tag', project_cfg, testcase_cfg)
+ return '{}:{}'.format(name, tag) if name and tag else None
- # get the openrc_volume for creating the container
- @classmethod
- def openrc_volume(cls, type):
+ def create(self, docker_image):
dovetail_config = dt_cfg.dovetail_config
- dovetail_config['openrc'] = os.path.join(dovetail_config['config_dir'],
- dovetail_config['env_file'])
- if os.path.isfile(dovetail_config['openrc']):
- openrc = ' -v %s:%s ' % (dovetail_config['openrc'],
- dovetail_config[type]['openrc'])
- return openrc
- else:
- cls.logger.error(
- "File {} doesn't exist.".format(dovetail_config['openrc']))
- return None
-
- @classmethod
- def set_vnftest_config(cls):
- dovetail_config = dt_cfg.dovetail_config
-
- log_vol = '-v %s:%s ' % (dovetail_config['result_dir'],
- dovetail_config["vnftest"]['result']['log'])
-
- key_file = os.path.join(dovetail_config['config_dir'],
- dovetail_config['pri_key'])
- key_container_path = dovetail_config["vnftest"]['result']['key_path']
- if not os.path.isfile(key_file):
- cls.logger.debug("Key file {} is not found".format(key_file))
- key_vol = ''
- else:
- key_vol = '-v %s:%s ' % (key_file, key_container_path)
- return "%s %s" % (log_vol, key_vol)
-
- @classmethod
- def create(cls, valid_type, testcase_name, docker_image):
- dovetail_config = dt_cfg.dovetail_config
- project_cfg = dovetail_config[valid_type]
-
- # credentials file openrc.sh is neccessary
- openrc = cls.openrc_volume(valid_type)
- if not openrc:
- return None
-
- opts = dt_utils.get_value_from_dict('opts', project_cfg)
- envs = dt_utils.get_value_from_dict('envs', project_cfg)
- volumes = dt_utils.get_value_from_dict('volumes', project_cfg)
- opts = ' ' if not opts else opts
- envs = ' ' if not envs else envs
- volumes = ' ' if not volumes else ' '.join(volumes)
-
- # CI_DEBUG is used for showing the debug logs of the upstream projects
- # BUILD_TAG is the unique id for this test
- DEBUG = os.getenv('DEBUG')
- if DEBUG is not None and DEBUG.lower() == "true":
- envs = envs + ' -e CI_DEBUG=true'
- else:
- envs = envs + ' -e CI_DEBUG=false'
- envs = envs + ' -e BUILD_TAG=%s-%s' % (dovetail_config['build_tag'],
- testcase_name)
-
- hosts_config = dt_utils.get_hosts_info(cls.logger)
-
- # This part will be totally removed after remove the 4 functions
- # set_functest_config has been removed
- # set_yardstick_config has been removed
- # set_bottlenecks_config has been removed
- # set_vnftest_config
- config = " "
- if valid_type.lower() == "vnftest":
- config = cls.set_vnftest_config()
- if not config:
- return None
-
- # for refstack, support user self_defined configuration
- config_volume = \
- ' -v %s:%s ' % (os.getenv("DOVETAIL_HOME"),
- project_cfg['config']['dir'])
-
- cacert_volume = ""
- https_enabled = dt_utils.check_https_enabled(cls.logger)
- cacert = os.getenv('OS_CACERT')
- insecure = os.getenv('OS_INSECURE')
- if cacert is not None:
- if dt_utils.check_cacert_file(cacert, cls.logger):
- cacert_volume = ' -v %s:%s ' % (cacert, cacert)
- else:
- return None
- elif https_enabled:
- if insecure and insecure.lower() == 'true':
- cls.logger.debug("Use the insecure mode...")
- else:
- cls.logger.error("https enabled, please set OS_CACERT or "
- "insecure mode...")
- return None
-
- images_volume = ''
- if project_cfg['config'].get('images', None):
- images_volume = '-v {}:{}'.format(
- dovetail_config['images_dir'],
- project_cfg['config']['images'])
-
- result_volume = ' -v %s:%s ' % (dovetail_config['result_dir'],
- project_cfg['result']['dir'])
- cmd = 'sudo docker run {opts} {envs} {volumes} {config} ' \
- '{hosts_config} {openrc} {cacert_volume} {config_volume} ' \
- '{result_volume} {images_volume} {docker_image} /bin/bash' \
- .format(**locals())
- dt_utils.exec_cmd(cmd, cls.logger)
- ret, container_id = \
- dt_utils.exec_cmd("sudo docker ps | grep " + docker_image +
- " | awk '{print $1}' | head -1", cls.logger)
- cls.container_list[valid_type] = container_id
-
- if valid_type.lower() == 'vnftest':
- cls.set_vnftest_conf_file(container_id)
-
- return container_id
-
- @classmethod
- def get_image_id(cls, image_name):
- cmd = 'sudo docker images -q %s' % (image_name)
- ret, image_id = dt_utils.exec_cmd(cmd, cls.logger)
- if ret == 0:
- return image_id
- else:
- return None
+ project_cfg = dovetail_config[self.valid_type]
+
+ kwargs = dt_utils.get_value_from_dict('opts', project_cfg)
+ shell = dt_utils.get_value_from_dict('shell', project_cfg)
+ if not shell:
+ return None, "Lacking of key word 'shell' in config file."
+ env_list = dt_utils.get_value_from_dict('envs', project_cfg)
+ if env_list:
+ kwargs['environment'] = \
+ [env for env in env_list if env is not None]
+ volume_list = dt_utils.get_value_from_dict('volumes', project_cfg)
+ kwargs['volumes'] = [vol for vol in volume_list if vol is not None]
+
+ kwargs['mounts'], msg = dt_utils.get_mount_list(project_cfg)
+ if not kwargs['mounts']:
+ return None, msg
+
+ kwargs['extra_hosts'] = dt_utils.get_hosts_info(self.logger)
+
+ try:
+ self.container = self.client.containers.run(
+ docker_image, shell, **kwargs)
+ except (docker.errors.ContainerError, docker.errors.ImageNotFound,
+ docker.errors.APIError) as e:
+ return None, e
+
+ return self.container.id, 'Successfully to create container.'
+
+ def get_image_id(self, image_name):
+ try:
+ image_id = self.client.images.get(image_name).id
+ except (docker.errors.ImageNotFound, docker.errors.APIError):
+ image_id = None
+ return image_id
# remove the image according to the image_id
# if there exists containers using this image, then skip
- @classmethod
- def remove_image(cls, image_id):
- cmd = "sudo docker ps -aq -f 'ancestor=%s'" % (image_id)
- ret, msg = dt_utils.exec_cmd(cmd, cls.logger)
- if msg and ret == 0:
- cls.logger.debug('Image {} has containers, skip.'.format(image_id))
+ def remove_image(self, image_id):
+ try:
+ containers = self.client.containers.list(
+ filters={'ancestor': image_id})
+ except docker.errors.APIError:
+ containers = []
+ if containers:
+ self.logger.debug('Image {} has containers, skip.'
+ .format(image_id))
return True
- cmd = 'sudo docker rmi %s' % (image_id)
- cls.logger.debug('Remove image {}.'.format(image_id))
- ret, msg = dt_utils.exec_cmd(cmd, cls.logger)
- if ret == 0:
- cls.logger.debug('Remove image {} successfully.'.format(image_id))
+ self.logger.debug('Remove image {}.'.format(image_id))
+ try:
+ self.client.images.remove(image_id)
+ self.logger.debug('Remove image {} successfully.'.format(image_id))
return True
- cls.logger.error('Failed to remove image {}.'.format(image_id))
- return False
+ except (docker.errors.ImageNotFound, docker.errors.APIError):
+ self.logger.error('Failed to remove image {}.'.format(image_id))
+ return False
- @classmethod
- def pull_image_only(cls, image_name):
- cmd = 'sudo docker pull %s' % (image_name)
- ret, msg = dt_utils.exec_cmd(cmd, cls.logger)
- if ret != 0:
- cls.logger.error(
+ def pull_image_only(self, image_name):
+ try:
+ self.client.images.pull(image_name)
+ self.logger.debug(
+ 'Success to pull docker image {}!'.format(image_name))
+ return True
+ except docker.errors.APIError:
+ self.logger.error(
'Failed to pull docker image {}!'.format(image_name))
return False
- cls.logger.debug('Success to pull docker image {}!'.format(image_name))
- return True
- @classmethod
- def pull_image(cls, docker_image):
+ def pull_image(self, docker_image):
if not docker_image:
return None
- old_image_id = cls.get_image_id(docker_image)
- if not cls.pull_image_only(docker_image):
+ old_image_id = self.get_image_id(docker_image)
+ if not self.pull_image_only(docker_image):
return None
- new_image_id = cls.get_image_id(docker_image)
+ new_image_id = self.get_image_id(docker_image)
if not new_image_id:
- cls.logger.error(
- "Failed to get the id of image {}.".format(docker_image))
+ self.logger.error(
+ 'Failed to get the id of image {}.'.format(docker_image))
return None
if not old_image_id:
return docker_image
if new_image_id == old_image_id:
- cls.logger.debug('Image {} has no changes, no need to remove.'
- .format(docker_image))
+ self.logger.debug('Image {} has no changes, no need to remove.'
+ .format(docker_image))
else:
- cls.remove_image(old_image_id)
+ self.remove_image(old_image_id)
return docker_image
- @classmethod
- def check_container_exist(cls, container_name):
- cmd = ('sudo docker ps -aq -f name={}'.format(container_name))
- ret, msg = dt_utils.exec_cmd(cmd, cls.logger)
- if ret == 0 and msg:
- return True
- return False
-
- @classmethod
- def clean(cls, container_id, valid_type):
- cmd = ('sudo docker rm -f {}'.format(container_id))
- dt_utils.exec_cmd(cmd, cls.logger)
- if valid_type.lower() == 'bottlenecks':
- containers = dt_cfg.dovetail_config[valid_type]['extra_container']
- for container in containers:
- if cls.check_container_exist(container):
- cmd = ('sudo docker rm -f {}'.format(container))
- dt_utils.exec_cmd(cmd, cls.logger)
-
- @classmethod
- def exec_cmd(cls, container_id, sub_cmd, exit_on_error=False):
- if sub_cmd == "":
+ def get_container(self, container_name):
+ try:
+ container = self.client.containers.get(container_name)
+ except (docker.errors.NotFound, docker.errors.APIError):
+ container = None
+ return container
+
+ def clean(self):
+ try:
+ self.container.remove(force=True)
+ self.logger.debug(
+ 'container: {} was removed'.format(self.container.name))
+ except docker.errors.APIError as e:
+ self.logger.error(e)
+ extra_containers = dt_utils.get_value_from_dict(
+ 'extra_container', dt_cfg.dovetail_config[self.valid_type])
+ if extra_containers:
+ for container_name in extra_containers:
+ container = self.get_container(container_name)
+ if container:
+ try:
+ container.remove(force=True)
+ self.logger.debug(
+ 'container: {} was removed'.format(container_name))
+ except docker.errors.APIError as e:
+ self.logger.error(e)
+
+ def exec_cmd(self, sub_cmd, exit_on_error=False):
+ if not sub_cmd:
return (1, 'sub_cmd is empty')
- cmd = 'sudo docker exec %s /bin/bash -c "%s"' % (container_id, sub_cmd)
- return dt_utils.exec_cmd(cmd, cls.logger, exit_on_error)
-
- @classmethod
- def copy_file(cls, container_id, src_path, dest_path,
- exit_on_error=False):
+ shell = dt_utils.get_value_from_dict(
+ 'shell', dt_cfg.dovetail_config[self.valid_type])
+ if not shell:
+ return (1, 'shell is empty')
+ cmd = '{} -c "{}"'.format(shell, sub_cmd)
+ try:
+ result = self.container.exec_run(cmd)
+ except docker.errors.APIError as e:
+ result = (e.response.status_code, str(e))
+ self.logger.error(e)
+ if exit_on_error:
+ sys.exit(1)
+
+ return result
+
+ def copy_file(self, src_path, dest_path, exit_on_error=False):
if not src_path or not dest_path:
return (1, 'src_path or dest_path is empty')
cmd = 'cp %s %s' % (src_path, dest_path)
- return cls.exec_cmd(container_id, cmd, exit_on_error)
+ return self.exec_cmd(cmd, exit_on_error)
- @classmethod
- def docker_copy(cls, container_id, src_path, dest_path):
- if not src_path or not dest_path:
- return (1, 'src_path or dest_path is empty')
- cmd = 'docker cp %s %s:%s' % (src_path, container_id, dest_path)
- return dt_utils.exec_cmd(cmd, cls.logger)
-
- @classmethod
- def copy_files_in_container(cls, valid_type, container_id):
- project_config = dt_cfg.dovetail_config[valid_type]
+ def copy_files_in_container(self):
+ project_config = dt_cfg.dovetail_config[self.valid_type]
if 'copy_file_in_container' not in project_config.keys():
return
if not project_config['copy_file_in_container']:
return
for item in project_config['copy_file_in_container']:
- cls.copy_file(container_id, item['src_file'], item['dest_file'])
-
- @classmethod
- def set_vnftest_conf_file(cls, container_id):
- valid_type = 'vnftest'
- for conf_file in dt_cfg.dovetail_config[valid_type]['vnftest_conf']:
- src = conf_file['src_file']
- dest = conf_file['dest_file']
- cls.docker_copy(container_id, src, dest)
+ self.copy_file(item['src_file'], item['dest_file'])