From ef2d5282306a277e591aa3c355277c6f158d8e76 Mon Sep 17 00:00:00 2001 From: Stamatis Katsaounis Date: Mon, 4 Mar 2019 16:12:10 +0200 Subject: Replace subprocess commands with Docker SDK JIRA: DOVETAIL-752 This patch replaces all python subprocess commands with the Docker SDK relevant commands. Change-Id: Iac7caffd80a863a8a022247d735b2a7f2792e49d Signed-off-by: Stamatis Katsaounis --- dovetail/container.py | 142 +++++++++--------- dovetail/tests/unit/test_container.py | 175 +++++++++++------------ dovetail/tests/unit/utils/test_dovetail_utils.py | 75 ++++------ dovetail/utils/dovetail_utils.py | 26 ++-- etc/conf/bottlenecks_config.yml | 26 ++-- etc/conf/functest-k8s_config.yml | 20 ++- etc/conf/functest_config.yml | 32 +++-- etc/conf/onap-vtp_config.yml | 14 +- etc/conf/onap-vvp_config.yml | 9 +- etc/conf/yardstick_config.yml | 24 ++-- requirements.txt | 1 + 11 files changed, 282 insertions(+), 262 deletions(-) diff --git a/dovetail/container.py b/dovetail/container.py index 57145034..250bb54a 100644 --- a/dovetail/container.py +++ b/dovetail/container.py @@ -9,6 +9,9 @@ # http://www.apache.org/licenses/LICENSE-2.0 # +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 @@ -19,9 +22,10 @@ class Container(object): logger = None def __init__(self, testcase): - self.container_id = None + self.container = None self.testcase = testcase self.valid_type = self.testcase.validate_type() + self.client = docker.from_env(timeout=None) def __str__(self): pass @@ -45,70 +49,69 @@ class Container(object): 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 + return '{}:{}'.format(name, tag) if name and tag else None def create(self, docker_image): dovetail_config = dt_cfg.dovetail_config project_cfg = dovetail_config[self.valid_type] - opts = dt_utils.get_value_from_dict('opts', project_cfg) + 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 - envs = dt_utils.get_value_from_dict('envs', project_cfg) - volumes_list = dt_utils.get_value_from_dict('volumes', project_cfg) - opts = ' ' if not opts else opts - envs = ' ' if not envs else envs - volumes = ' '.join(volume for volume in volumes_list if volume) \ - if volumes_list else ' ' - - hosts_config = dt_utils.get_hosts_info(self.logger) - - cmd = 'sudo docker run {opts} {envs} {volumes} ' \ - '{hosts_config} {docker_image} {shell}'.format(**locals()) - ret, container_id = dt_utils.exec_cmd(cmd, self.logger) - if ret != 0: + env_list = dt_utils.get_value_from_dict('envs', project_cfg) + 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['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): return None - self.container_id = container_id - return container_id + return self.container.id def get_image_id(self, image_name): - cmd = 'sudo docker images -q %s' % (image_name) - ret, image_id = dt_utils.exec_cmd(cmd, self.logger) - if ret == 0: - return image_id - else: - return None + 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 def remove_image(self, image_id): - cmd = "sudo docker ps -aq -f 'ancestor=%s'" % (image_id) - ret, msg = dt_utils.exec_cmd(cmd, self.logger) - if msg and ret == 0: + 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) self.logger.debug('Remove image {}.'.format(image_id)) - ret, msg = dt_utils.exec_cmd(cmd, self.logger) - if ret == 0: + try: + self.client.images.remove(image_id) self.logger.debug('Remove image {} successfully.'.format(image_id)) return True - self.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 def pull_image_only(self, image_name): - cmd = 'sudo docker pull %s' % (image_name) - ret, _ = dt_utils.exec_cmd(cmd, self.logger) - if ret != 0: + 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 - self.logger.debug('Success to pull docker image {}!' - .format(image_name)) - return True def pull_image(self, docker_image): if not docker_image: @@ -119,7 +122,7 @@ class Container(object): new_image_id = self.get_image_id(docker_image) if not new_image_id: self.logger.error( - "Failed to get the id of image {}.".format(docker_image)) + 'Failed to get the id of image {}.'.format(docker_image)) return None if not old_image_id: return docker_image @@ -130,35 +133,50 @@ class Container(object): self.remove_image(old_image_id) return docker_image - def check_container_exist(self, container_name): - cmd = ('sudo docker ps -aq -f name={}'.format(container_name)) - ret, msg = dt_utils.exec_cmd(cmd, self.logger) - if ret == 0 and msg: - return True - return False + 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): - cmd = ('sudo docker rm -f {}'.format(self.container_id)) - dt_utils.exec_cmd(cmd, self.logger) + 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 in extra_containers: - if self.check_container_exist(container): - cmd = ('sudo docker rm -f {}'.format(container)) - dt_utils.exec_cmd(cmd, self.logger) + 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 sub_cmd == "": + if not sub_cmd: return (1, 'sub_cmd is empty') - dovetail_config = dt_cfg.dovetail_config - project_cfg = dovetail_config[self.valid_type] - shell = dt_utils.get_value_from_dict('shell', project_cfg) + shell = dt_utils.get_value_from_dict( + 'shell', dt_cfg.dovetail_config[self.valid_type]) if not shell: return (1, 'shell is empty') - cmd = 'sudo docker exec {} {} -c "{}"'.format(self.container_id, shell, - sub_cmd) - return dt_utils.exec_cmd(cmd, self.logger, exit_on_error) + 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: @@ -166,14 +184,6 @@ class Container(object): cmd = 'cp %s %s' % (src_path, dest_path) return self.exec_cmd(cmd, exit_on_error) - def docker_copy(self, src_path, dest_path): - if not src_path or not dest_path: - return (1, 'src_path or dest_path is empty') - cmd = 'docker cp {} {}:{}'.format(src_path, - self.container_id, - dest_path) - return dt_utils.exec_cmd(cmd, self.logger) - 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(): diff --git a/dovetail/tests/unit/test_container.py b/dovetail/tests/unit/test_container.py index 6a8b99fc..7c758a7c 100644 --- a/dovetail/tests/unit/test_container.py +++ b/dovetail/tests/unit/test_container.py @@ -10,6 +10,7 @@ import unittest from mock import patch, call, Mock +import docker from dovetail.container import Container @@ -19,6 +20,7 @@ __author__ = 'Stamatis Katsaounis ' class ContainerTesting(unittest.TestCase): def setUp(self): + self.patcher1 = patch.object(docker, 'from_env') testcase = patch.object(Container, 'testcase') testcase.testcase = {'validate': { 'type': 'bottlenecks'}} @@ -28,12 +30,13 @@ class ContainerTesting(unittest.TestCase): val_type_obj = Mock() val_type_obj.return_value = 'bottlenecks' testcase.validate_type = val_type_obj + self.client = self.patcher1.start().return_value self.container = Container(testcase) self.logger = Mock() self.container.logger = self.logger def tearDown(self): - pass + self.patcher1.stop() @patch('dovetail.container.dt_cfg') @patch.object(Container, 'copy_file') @@ -72,22 +75,6 @@ class ContainerTesting(unittest.TestCase): mock_copy.assert_not_called() - def test_docker_copy_error(self): - expected = (1, 'src_path or dest_path is empty') - result = self.container.docker_copy(None, None) - - self.assertEqual(expected, result) - - @patch('dovetail.container.dt_utils') - def test_docker_copy(self, mock_utils): - expected = (0, 'success') - mock_utils.exec_cmd.return_value = expected - result = self.container.docker_copy('source', 'dest') - - mock_utils.exec_cmd.assert_called_once_with( - 'docker cp source None:dest', self.logger) - self.assertEqual(expected, result) - def test_copy_file_error(self): expected = (1, 'src_path or dest_path is empty') result = self.container.copy_file(None, None) @@ -114,15 +101,35 @@ class ContainerTesting(unittest.TestCase): @patch('dovetail.container.dt_utils') def test_exec_cmd(self, mock_utils, mock_config): expected = (0, 'success') - mock_utils.exec_cmd.return_value = expected mock_utils.get_value_from_dict.return_value = 'shell' mock_config.dovetail_config = {'bottlenecks': 'value'} + container_obj = Mock() + container_obj.exec_run.return_value = expected + self.container.container = container_obj + result = self.container.exec_cmd('command') - mock_utils.exec_cmd.assert_called_once_with( - 'sudo docker exec None shell -c "command"', self.logger, False) self.assertEqual(expected, result) + @patch('dovetail.container.dt_cfg') + @patch('dovetail.container.dt_utils') + @patch('sys.exit') + def test_exec_cmd_exception(self, mock_exit, mock_utils, mock_config): + mock_utils.get_value_from_dict.return_value = 'shell' + mock_config.dovetail_config = {'bottlenecks': 'value'} + container_obj = Mock() + response_obj = Mock() + response_obj.status_code = 1 + container_obj.exec_run.side_effect = \ + docker.errors.APIError('error', response=response_obj) + self.container.container = container_obj + + expected = (1, 'error') + result = self.container.exec_cmd('command', exit_on_error=True) + + self.assertEqual(expected, result) + mock_exit.assert_called_once_with(1) + @patch('dovetail.container.dt_cfg') @patch('dovetail.container.dt_utils') def test_exec_cmd_no_shell(self, mock_utils, mock_config): @@ -136,50 +143,65 @@ class ContainerTesting(unittest.TestCase): @patch('dovetail.container.dt_cfg') @patch('dovetail.container.dt_utils') - @patch.object(Container, 'check_container_exist') + @patch.object(Container, 'get_container') def test_clean(self, mock_check, mock_utils, mock_config): container_name = 'container' mock_config.dovetail_config = {'bottlenecks': 'value'} mock_utils.get_value_from_dict.return_value = [container_name] - mock_check.return_value = True + self.container.container = Mock() + mock_check.return_value = Mock() + + self.container.clean() + + mock_utils.get_value_from_dict.assert_called_once_with( + 'extra_container', 'value') + mock_check.assert_called_once_with(container_name) + + @patch('dovetail.container.dt_cfg') + @patch('dovetail.container.dt_utils') + @patch.object(Container, 'get_container') + def test_clean_extra_error(self, mock_check, mock_utils, mock_config): + container_name = 'container' + mock_config.dovetail_config = {'bottlenecks': 'value'} + mock_utils.get_value_from_dict.return_value = [container_name] + container_obj = Mock() + container_obj.remove.side_effect = docker.errors.APIError('error') + self.container.container = Mock() + mock_check.return_value = container_obj self.container.clean() mock_utils.get_value_from_dict.assert_called_once_with( 'extra_container', 'value') mock_check.assert_called_once_with(container_name) - mock_utils.exec_cmd.assert_has_calls([ - call('sudo docker rm -f None', self.logger), - call('sudo docker rm -f container', self.logger)]) @patch('dovetail.container.dt_cfg') @patch('dovetail.container.dt_utils') def test_clean_no_extra_container(self, mock_utils, mock_config): mock_utils.get_value_from_dict.return_value = None + container_obj = Mock() + container_obj.remove.side_effect = docker.errors.APIError('error') + self.container.container = container_obj self.container.clean() mock_utils.get_value_from_dict.assert_called_once() - @patch('dovetail.container.dt_utils') - def test_check_container_exist_true(self, mock_utils): + def test_get_container_exist_true(self): container_name = 'container' - cmd = ('sudo docker ps -aq -f name={}'.format(container_name)) - mock_utils.exec_cmd.return_value = (0, 'msg') + expected = Mock() + self.client.containers.get.return_value = expected - result = self.container.check_container_exist(container_name) + result = self.container.get_container(container_name) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) - self.assertEquals(True, result) + self.assertEquals(expected, result) - @patch('dovetail.container.dt_utils') - def test_check_container_exist_false(self, mock_utils): + def test_get_container_none(self): container_name = 'container' - cmd = ('sudo docker ps -aq -f name={}'.format(container_name)) - mock_utils.exec_cmd.return_value = (1, 'msg') + self.client.containers.get.side_effect = \ + docker.errors.APIError('error') - result = self.container.check_container_exist(container_name) + result = self.container.get_container(container_name) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) - self.assertEquals(False, result) + self.assertEquals(None, result) def test_pull_image_none(self): result = self.container.pull_image(None) @@ -260,98 +282,78 @@ class ContainerTesting(unittest.TestCase): mock_remove.assert_called_once_with(old_obj) self.assertEquals(docker_image, result) - @patch('dovetail.container.dt_utils') - def test_pull_image_only(self, mock_utils): + def test_pull_image_only(self): docker_image = 'image' - mock_utils.exec_cmd.return_value = (0, 'msg') result = self.container.pull_image_only(docker_image) - cmd = 'sudo docker pull %s' % (docker_image) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) self.logger.debug.assert_called_once_with( 'Success to pull docker image {}!'.format(docker_image)) self.assertEquals(True, result) - @patch('dovetail.container.dt_utils') - def test_pull_image_only_error(self, mock_utils): + def test_pull_image_only_error(self): docker_image = 'image' - mock_utils.exec_cmd.return_value = (1, 'error') + self.client.images.pull.side_effect = docker.errors.APIError('error') result = self.container.pull_image_only(docker_image) - cmd = 'sudo docker pull %s' % (docker_image) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) self.logger.error.assert_called_once_with( 'Failed to pull docker image {}!'.format(docker_image)) self.assertEquals(False, result) - @patch('dovetail.container.dt_utils') - def test_remove_image(self, mock_utils): + def test_remove_image(self): image_id = 'image_id' - mock_utils.exec_cmd.side_effect = [(1, 'error'), (0, 'msg')] + self.client.containers.list.side_effect = \ + docker.errors.APIError('error') result = self.container.remove_image(image_id) - mock_utils.exec_cmd.assert_has_calls([ - call("sudo docker ps -aq -f 'ancestor=%s'" % (image_id), - self.logger), - call('sudo docker rmi %s' % (image_id), self.logger)]) self.logger.debug.assert_has_calls([ call('Remove image {}.'.format(image_id)), call('Remove image {} successfully.'.format(image_id))]) self.assertEquals(True, result) - @patch('dovetail.container.dt_utils') - def test_remove_image_ancestors(self, mock_utils): + def test_remove_image_ancestors(self): image_id = 'image_id' - mock_utils.exec_cmd.return_value = (0, 'msg') + self.client.containers.list.return_value = ['cont_a'] result = self.container.remove_image(image_id) - cmd = "sudo docker ps -aq -f 'ancestor=%s'" % (image_id) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) self.logger.debug.assert_called_once_with( 'Image {} has containers, skip.'.format(image_id)) self.assertEquals(True, result) - @patch('dovetail.container.dt_utils') - def test_remove_image_error(self, mock_utils): + def test_remove_image_error(self): image_id = 'image_id' - mock_utils.exec_cmd.return_value = (1, 'error') + self.client.containers.list.return_value = [] + self.client.images.remove.side_effect = \ + docker.errors.ImageNotFound('error') result = self.container.remove_image(image_id) - mock_utils.exec_cmd.assert_has_calls([ - call("sudo docker ps -aq -f 'ancestor=%s'" % (image_id), - self.logger), - call('sudo docker rmi %s' % (image_id), self.logger)]) self.logger.debug.assert_called_once_with( 'Remove image {}.'.format(image_id)) self.logger.error.assert_called_once_with( 'Failed to remove image {}.'.format(image_id)) self.assertEquals(False, result) - @patch('dovetail.container.dt_utils') - def test_get_image_id(self, mock_utils): + def test_get_image_id(self): image_name = 'image_id' - mock_utils.exec_cmd.return_value = (0, image_name) + mock_img = Mock() + mock_img.id = image_name + self.client.images.get.return_value = mock_img result = self.container.get_image_id(image_name) - cmd = 'sudo docker images -q %s' % (image_name) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) self.assertEquals(image_name, result) - @patch('dovetail.container.dt_utils') - def test_get_image_id_error(self, mock_utils): + def test_get_image_id_error(self): image_name = 'image_id' - mock_utils.exec_cmd.return_value = (1, 'error') + self.client.images.get.side_effect = \ + docker.errors.ImageNotFound('error') result = self.container.get_image_id(image_name) - cmd = 'sudo docker images -q %s' % (image_name) - mock_utils.exec_cmd.assert_called_once_with(cmd, self.logger) self.assertEquals(None, result) @patch('dovetail.container.dt_utils') @@ -406,9 +408,11 @@ class ContainerTesting(unittest.TestCase): docker_image = 'docker_image' container_id = 'container_id' mock_utils.get_value_from_dict.side_effect = [ - 'opts', 'shell', 'envs', ['volume_one', 'volume_two']] + {'key': 'value'}, 'shell', 'envs', ['volume_one', 'volume_two']] mock_utils.get_hosts_info.return_value = 'host_info' - mock_utils.exec_cmd.return_value = (0, container_id) + container_obj = Mock() + container_obj.id = container_id + self.client.containers.run.return_value = container_obj project_config = {} mock_config.dovetail_config = {'bottlenecks': project_config} @@ -421,9 +425,6 @@ class ContainerTesting(unittest.TestCase): call('envs', project_config), call('volumes', project_config)]) mock_utils.get_hosts_info.assert_called_once_with(self.logger) - mock_utils.exec_cmd.assert_called_once_with( - 'sudo docker run opts envs volume_one volume_two host_info ' - 'docker_image shell', self.logger) self.assertEquals(expected, result) @patch('dovetail.container.dt_utils') @@ -446,10 +447,11 @@ class ContainerTesting(unittest.TestCase): def test_create_error(self, mock_config, mock_utils): docker_image = 'docker_image' mock_utils.get_value_from_dict.side_effect = [ - 'opts', 'shell', 'envs', ['volume_one']] + {'key': 'value'}, 'shell', ['envs'], ['volume_one']] mock_utils.get_hosts_info.return_value = 'host_info' mock_utils.check_https_enabled.return_value = True - mock_utils.exec_cmd.return_value = (1, 'error') + self.client.containers.run.side_effect = \ + docker.errors.ImageNotFound('error') project_config = {} mock_config.dovetail_config = {'bottlenecks': project_config} result = self.container.create(docker_image) @@ -460,7 +462,4 @@ class ContainerTesting(unittest.TestCase): call('envs', project_config), call('volumes', project_config)]) mock_utils.get_hosts_info.assert_called_once_with(self.logger) - mock_utils.exec_cmd.assert_called_once_with( - 'sudo docker run opts envs volume_one host_info ' - 'docker_image shell', self.logger) self.assertEquals(None, result) diff --git a/dovetail/tests/unit/utils/test_dovetail_utils.py b/dovetail/tests/unit/utils/test_dovetail_utils.py index 0f0e14f3..33fc1eae 100644 --- a/dovetail/tests/unit/utils/test_dovetail_utils.py +++ b/dovetail/tests/unit/utils/test_dovetail_utils.py @@ -227,7 +227,7 @@ class DovetailUtilsTesting(unittest.TestCase): mock_path.isfile.return_value = False logger = Mock() - expected = '' + expected = {} result = dovetail_utils.get_hosts_info(logger) mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -248,7 +248,7 @@ class DovetailUtilsTesting(unittest.TestCase): mock_load.return_value = None logger = Mock() - expected = '' + expected = {} result = dovetail_utils.get_hosts_info(logger) mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -274,7 +274,7 @@ class DovetailUtilsTesting(unittest.TestCase): mock_load.return_value = {'a': 'b'} logger = Mock() - expected = '' + expected = {} result = dovetail_utils.get_hosts_info(logger) mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -299,7 +299,7 @@ class DovetailUtilsTesting(unittest.TestCase): mock_open.return_value.__enter__.return_value = file_obj mock_load.return_value = {'hosts_info': {'127.0.0.1': []}} - expected = '' + expected = {} result = dovetail_utils.get_hosts_info() mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -324,7 +324,7 @@ class DovetailUtilsTesting(unittest.TestCase): hosts_info = {'127.0.0.1': [None]} mock_load.return_value = {'hosts_info': hosts_info} - expected = '' + expected = {} result = dovetail_utils.get_hosts_info() mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -353,7 +353,7 @@ class DovetailUtilsTesting(unittest.TestCase): logger = Mock() names_str = ' '.join(hostnames) - expected = ' --add-host=\'{}\':{} '.format(names_str, hosts_ip) + expected = {names_str: hosts_ip} result = dovetail_utils.get_hosts_info(logger) mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') @@ -547,66 +547,45 @@ class DovetailUtilsTesting(unittest.TestCase): mock_open.assert_called_once_with(file_path, 'r') mock_env.update.assert_called_once_with({env_name: env_value}) - @patch('dovetail.utils.dovetail_utils.exec_cmd') - def test_check_docker_version(self, mock_exec): - server_version = client_version = '1.12.3' - server_ret = client_ret = 0 - mock_exec.side_effect = [(server_ret, server_version), - (client_ret, client_version)] + @patch('dovetail.utils.dovetail_utils.docker') + def test_check_docker_version(self, mock_docker): + server_version = '1.12.3' + client_obj = Mock() + mock_docker.from_env.return_value = client_obj + client_obj.version.return_value = {'Version': server_version} logger = Mock() dovetail_utils.check_docker_version(logger) - mock_exec.assert_has_calls( - [call("sudo docker version -f'{{.Server.Version}}'", - logger=logger), - call("sudo docker version -f'{{.Client.Version}}'", - logger=logger)]) logger.debug.assert_has_calls( - [call('docker server version: {}'.format(server_version)), - call('docker client version: {}'.format(client_version))]) + [call('Docker server version: {}'.format(server_version))]) - @patch('dovetail.utils.dovetail_utils.exec_cmd') - def test_check_docker_version_error(self, mock_exec): - server_version = client_version = '1.12.3' - server_ret = client_ret = 1 - mock_exec.side_effect = [(server_ret, server_version), - (client_ret, client_version)] + @patch('dovetail.utils.dovetail_utils.docker') + def test_check_docker_version_error(self, mock_docker): + client_obj = Mock() + mock_docker.from_env.return_value = client_obj + client_obj.version.return_value = {} logger = Mock() dovetail_utils.check_docker_version(logger) - mock_exec.assert_has_calls( - [call("sudo docker version -f'{{.Server.Version}}'", - logger=logger), - call("sudo docker version -f'{{.Client.Version}}'", - logger=logger)]) logger.error.assert_has_calls( [call("Don't support this Docker server version. " - "Docker server should be updated to at least 1.12.3."), - call("Don't support this Docker client version. " - "Docker client should be updated to at least 1.12.3.")]) - - @patch('dovetail.utils.dovetail_utils.exec_cmd') - def test_check_docker_version_less_than(self, mock_exec): - server_version = client_version = '1.12.1' - server_ret = client_ret = 0 - mock_exec.side_effect = [(server_ret, server_version), - (client_ret, client_version)] + "Docker server should be updated to at least 1.12.3.")]) + + @patch('dovetail.utils.dovetail_utils.docker') + def test_check_docker_version_less_than(self, mock_docker): + server_version = '1.12.1' + client_obj = Mock() + mock_docker.from_env.return_value = client_obj + client_obj.version.return_value = {'Version': server_version} logger = Mock() dovetail_utils.check_docker_version(logger) - mock_exec.assert_has_calls( - [call("sudo docker version -f'{{.Server.Version}}'", - logger=logger), - call("sudo docker version -f'{{.Client.Version}}'", - logger=logger)]) logger.error.assert_has_calls( [call("Don't support this Docker server version. " - "Docker server should be updated to at least 1.12.3."), - call("Don't support this Docker client version. " - "Docker client should be updated to at least 1.12.3.")]) + "Docker server should be updated to at least 1.12.3.")]) @patch('__builtin__.open') @patch('os.path') diff --git a/dovetail/utils/dovetail_utils.py b/dovetail/utils/dovetail_utils.py index 0f26eb26..a3d07824 100644 --- a/dovetail/utils/dovetail_utils.py +++ b/dovetail/utils/dovetail_utils.py @@ -19,6 +19,7 @@ from datetime import datetime from distutils.version import LooseVersion import yaml import python_hosts +import docker from dovetail import constants from dovetail_config import DovetailConfig as dt_cfg @@ -157,20 +158,17 @@ def show_progress_bar(length): def check_docker_version(logger=None): - server_ret, server_ver = \ - exec_cmd("sudo docker version -f'{{.Server.Version}}'", logger=logger) - client_ret, client_ver = \ - exec_cmd("sudo docker version -f'{{.Client.Version}}'", logger=logger) - if server_ret == 0: - logger.debug('docker server version: {}'.format(server_ver)) - if server_ret != 0 or (LooseVersion(server_ver) < LooseVersion('1.12.3')): + client = docker.from_env() + server_ver = None + try: + server_ver = client.version()['Version'] + except Exception: + logger.error('Failed to get Docker server version') + if server_ver and (LooseVersion(server_ver) >= LooseVersion('1.12.3')): + logger.debug('Docker server version: {}'.format(server_ver)) + else: logger.error("Don't support this Docker server version. " "Docker server should be updated to at least 1.12.3.") - if client_ret == 0: - logger.debug('docker client version: {}'.format(client_ver)) - if client_ret != 0 or (LooseVersion(client_ver) < LooseVersion('1.12.3')): - logger.error("Don't support this Docker client version. " - "Docker client should be updated to at least 1.12.3.") def add_hosts_info(ip, hostnames): @@ -316,7 +314,7 @@ def check_cacert_file(cacert, logger=None): def get_hosts_info(logger=None): - hosts_config = '' + hosts_config = {} hosts_config_file = os.path.join(dt_cfg.dovetail_config['config_dir'], 'hosts.yaml') if not os.path.isfile(hosts_config_file): @@ -341,7 +339,7 @@ def get_hosts_info(logger=None): if hostname) if not names_str: continue - hosts_config += ' --add-host=\'{}\':{} '.format(names_str, ip) + hosts_config[names_str] = ip logger.debug('Get hosts info {}:{}.'.format(ip, names_str)) return hosts_config diff --git a/etc/conf/bottlenecks_config.yml b/etc/conf/bottlenecks_config.yml index 8a0a39bc..eb067c4a 100644 --- a/etc/conf/bottlenecks_config.yml +++ b/etc/conf/bottlenecks_config.yml @@ -8,7 +8,7 @@ {% set build_tag = build_tag or '' %} {% set cacert_volume = '' %} {% if cacert %} - {% set cacert_volume = ' -v ' + cacert + ':' + cacert %} + {% set cacert_volume = cacert + ':' + cacert %} {% endif %} {% set openrc_file = '/tmp/admin_rc.sh' %} {% set result_dir = '/home/opnfv/bottlenecks/results' %} @@ -19,18 +19,24 @@ bottlenecks: image_name: opnfv/bottlenecks docker_tag: latest - opts: '-id --privileged=true' + opts: + detach: true + stdin_open: true + privileged: true shell: '/bin/bash' - envs: '-e DEPLOY_SCENARIO={{deploy_scenario}} -e Yardstick_TAG=stable - -e OUTPUT_FILE={{testcase}}.out -e CI_DEBUG={{debug}} - -e BUILD_TAG={{build_tag}}-{{testcase}}' + envs: + - 'DEPLOY_SCENARIO={{deploy_scenario}}' + - 'Yardstick_TAG=stable' + - 'OUTPUT_FILE={{testcase}}.out' + - 'CI_DEBUG={{debug}}' + - 'BUILD_TAG={{build_tag}}-{{testcase}}' volumes: - - '-v /var/run/docker.sock:/var/run/docker.sock' - - '-v {{dovetail_home}}/results/bottlenecks:/tmp' - - '-v {{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' + - '/var/run/docker.sock:/var/run/docker.sock' + - '{{dovetail_home}}/results/bottlenecks:/tmp' + - '{{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' - {{cacert_volume}} - - '-v {{dovetail_home}}/images:{{images_dir}}' - - '-v {{dovetail_home}}/results:{{result_dir}}' + - '{{dovetail_home}}/images:{{images_dir}}' + - '{{dovetail_home}}/results:{{result_dir}}' pre_condition: - 'cp {{images_dir}}/ubuntu-16.04-server-cloudimg-amd64-disk1.img {{image_file}}' cmds: diff --git a/etc/conf/functest-k8s_config.yml b/etc/conf/functest-k8s_config.yml index b5ad12b6..e4781715 100644 --- a/etc/conf/functest-k8s_config.yml +++ b/etc/conf/functest-k8s_config.yml @@ -12,15 +12,21 @@ functest-k8s: image_name: opnfv/functest-kubernetes-healthcheck docker_tag: gambia - opts: '-id' + opts: + detach: true + stdin_open: true shell: '/bin/bash' - envs: '-e INSTALLER_TYPE=unknown -e DEPLOY_SCENARIO=k8-deploy -e NODE_NAME=unknown - -e TEST_DB_URL=file:///home/opnfv/functest/results/functest_results.txt - -e CI_DEBUG={{debug}} -e BUILD_TAG={{build_tag}}-{{testcase}}' + envs: + - 'INSTALLER_TYPE=unknown' + - 'DEPLOY_SCENARIO=k8-deploy' + - 'NODE_NAME=unknown' + - 'TEST_DB_URL=file:///home/opnfv/functest/results/functest_results.txt' + - 'CI_DEBUG={{debug}}' + - 'BUILD_TAG={{build_tag}}-{{testcase}}' volumes: - - '-v {{dovetail_home}}/pre_config/k8.creds:{{openrc_file}}' - - '-v {{dovetail_home}}/pre_config/admin.conf:{{kube_file}}' - - '-v {{dovetail_home}}/results/:{{result_dir}}' + - '{{dovetail_home}}/pre_config/k8.creds:{{openrc_file}}' + - '{{dovetail_home}}/pre_config/admin.conf:{{kube_file}}' + - '{{dovetail_home}}/results/:{{result_dir}}' pre_condition: - 'echo test for precondition in functest' cmds: diff --git a/etc/conf/functest_config.yml b/etc/conf/functest_config.yml index bf637541..3bf90218 100644 --- a/etc/conf/functest_config.yml +++ b/etc/conf/functest_config.yml @@ -6,14 +6,14 @@ {% set os_insecure = os_insecure or 'False' %} {% set os_verify = '' %} {% if os_insecure == 'True' %} - {% set os_verify = ' -e OS_VERIFY= ' %} + {% set os_verify = 'OS_VERIFY=' %} {% endif %} {% set dovetail_home = dovetail_home or '' %} {% set debug = debug or 'false' %} {% set build_tag = build_tag or '' %} {% set cacert_volume = '' %} {% if cacert %} - {% set cacert_volume = ' -v ' + cacert + ':' + cacert %} + {% set cacert_volume = cacert + ':' + cacert %} {% endif %} {% set openrc_file = '/home/opnfv/functest/conf/env_file' %} {% set result_dir = '/home/opnfv/functest/results' %} @@ -24,19 +24,27 @@ functest: image_name: opnfv/functest-smoke docker_tag: gambia - opts: '-id --privileged=true' + opts: + detach: true + stdin_open: true + privileged: true shell: '/bin/bash' - envs: '{{os_verify}} -e INSTALLER_TYPE=unknown -e DEPLOY_SCENARIO={{deploy_scenario}} -e NODE_NAME=unknown - -e TEST_DB_URL=file://{{result_dir}}/functest_results.txt - -e CI_DEBUG={{debug}} -e BUILD_TAG={{build_tag}}-{{testcase}}' + envs: + - {{os_verify}} + - 'INSTALLER_TYPE=unknown' + - 'DEPLOY_SCENARIO={{deploy_scenario}}' + - 'NODE_NAME=unknown' + - 'TEST_DB_URL=file://{{result_dir}}/functest_results.txt' + - 'CI_DEBUG={{debug}}' + - 'BUILD_TAG={{build_tag}}-{{testcase}}' volumes: - - '-v {{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' + - '{{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' - {{cacert_volume}} - - '-v {{dovetail_home}}/pre_config:/home/opnfv/pre_config' - - '-v {{dovetail_home}}/userconfig:{{userconfig_dir}}' - - '-v {{dovetail_home}}/patches:{{patches_dir}}' - - '-v {{dovetail_home}}/results:{{result_dir}}' - - '-v {{dovetail_home}}/images:{{images_dir}}' + - '{{dovetail_home}}/pre_config:/home/opnfv/pre_config' + - '{{dovetail_home}}/userconfig:{{userconfig_dir}}' + - '{{dovetail_home}}/patches:{{patches_dir}}' + - '{{dovetail_home}}/results:{{result_dir}}' + - '{{dovetail_home}}/images:{{images_dir}}' patches_dir: {{patches_dir}} pre_condition: - 'echo test for precondition in functest' diff --git a/etc/conf/onap-vtp_config.yml b/etc/conf/onap-vtp_config.yml index 72ebeab2..eb9b024d 100644 --- a/etc/conf/onap-vtp_config.yml +++ b/etc/conf/onap-vtp_config.yml @@ -10,13 +10,17 @@ onap-vtp: image_name: nexus3.onap.org:10001/onap/cli docker_tag: 2.0.5 - opts: '-td ' + opts: + detach: true + tty: true shell: '/bin/bash' - envs: '-e OPEN_CLI_MODE=daemon -e BUILD_TAG={{build_tag}}-{{testcase}} - -e OPEN_CLI_PRODUCT_IN_USE=onap-vtp' + envs: + - 'OPEN_CLI_MODE=daemon' + - 'BUILD_TAG={{build_tag}}-{{testcase}}' + - 'OPEN_CLI_PRODUCT_IN_USE=onap-vtp' volumes: - - '-v {{dovetail_home}}/pre_config/{{csar_file}}:/{{csar_file}}' - - '-v {{dovetail_home}}/results:{{result_dir}}' + - '{{dovetail_home}}/pre_config/{{csar_file}}:/{{csar_file}}' + - '{{dovetail_home}}/results:{{result_dir}}' pre_condition: - 'echo this is pre_condition' cmds: diff --git a/etc/conf/onap-vvp_config.yml b/etc/conf/onap-vvp_config.yml index 67d21fae..d812e1f4 100644 --- a/etc/conf/onap-vvp_config.yml +++ b/etc/conf/onap-vvp_config.yml @@ -7,11 +7,14 @@ onap-vvp: image_name: nexus3.onap.org:10001/onap/vvp/validation-scripts docker_tag: latest - opts: '-td --entrypoint=""' + opts: + detach: true + tty: true + entrypoint: '' shell: '/bin/ash' volumes: - - '-v {{dovetail_home}}/pre_config/{{heat_templates_archive}}.tar.gz:/tmp/{{heat_templates_archive}}.tar.gz' - - '-v {{dovetail_home}}/results:{{result_dir}}' + - '{{dovetail_home}}/pre_config/{{heat_templates_archive}}.tar.gz:/tmp/{{heat_templates_archive}}.tar.gz' + - '{{dovetail_home}}/results:{{result_dir}}' pre_condition: - 'tar xf /tmp/{{heat_templates_archive}}.tar.gz -C /vvp' cmds: diff --git a/etc/conf/yardstick_config.yml b/etc/conf/yardstick_config.yml index e43989e2..610cbfd8 100644 --- a/etc/conf/yardstick_config.yml +++ b/etc/conf/yardstick_config.yml @@ -7,14 +7,14 @@ {% set os_insecure = os_insecure or 'False' %} {% set os_verify = '' %} {% if os_insecure == 'True' %} - {% set os_verify = ' -e OS_VERIFY= ' %} + {% set os_verify = 'OS_VERIFY=' %} {% endif %} {% set dovetail_home = dovetail_home or '' %} {% set debug = debug or 'false' %} {% set build_tag = build_tag or '' %} {% set cacert_volume = '' %} {% if cacert %} - {% set cacert_volume = ' -v ' + cacert + ':' + cacert %} + {% set cacert_volume = cacert + ':' + cacert %} {% endif %} {% set openrc_file = '/etc/yardstick/openstack.creds' %} {% set pod_file = '/etc/yardstick/pod.yaml' %} @@ -23,16 +23,22 @@ yardstick: image_name: opnfv/yardstick docker_tag: latest - opts: '-id --privileged=true' + opts: + detach: true + stdin_open: true + privileged: true shell: '/bin/bash' - envs: "{{os_verify}} -e YARDSTICK_BRANCH=fraser -e CI_DEBUG={{debug}} - -e BUILD_TAG={{build_tag}}-{{testcase}}" + envs: + - {{os_verify}} + - 'YARDSTICK_BRANCH=fraser' + - 'CI_DEBUG={{debug}}' + - 'BUILD_TAG={{build_tag}}-{{testcase}}"' volumes: - - '-v {{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' + - '{{dovetail_home}}/pre_config/env_config.sh:{{openrc_file}}' - {{cacert_volume}} - - '-v {{dovetail_home}}/pre_config/pod.yaml:{{pod_file}}' - - '-v {{dovetail_home}}/images:/home/opnfv/images' - - '-v {{dovetail_home}}/results:{{result_dir}}' + - '{{dovetail_home}}/pre_config/pod.yaml:{{pod_file}}' + - '{{dovetail_home}}/images:/home/opnfv/images' + - '{{dovetail_home}}/results:{{result_dir}}' pre_condition: - 'echo this is pre_condition' cmds: diff --git a/requirements.txt b/requirements.txt index b79c1b64..7f95c60e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ pbr==3.1.1 python-hosts==0.4.1 PyYAML==3.12 shade==1.27.2 +docker==3.4.1 -- cgit 1.2.3-korg