From c92c49a9031602030f0f4d4fe2f99a9054eabaee Mon Sep 17 00:00:00 2001 From: Stamatis Katsaounis Date: Thu, 8 Nov 2018 16:58:09 +0200 Subject: Add missing unit tests for utils files JIRA: DOVETAIL-724 This patch adds unit tests which were missing from dovetail code base. Furthermore it updates the test_parser existing unit test. Finally, it fixes some minor issues in dovetail_utils itself. Change-Id: I8fd7cd4f6b1ede11664914746d2279f062511639 Signed-off-by: Stamatis Katsaounis --- dovetail/tests/unit/test_parser.py | 35 +- dovetail/tests/unit/utils/__init__.py | 0 dovetail/tests/unit/utils/test_dovetail_config.py | 53 + dovetail/tests/unit/utils/test_dovetail_logger.py | 98 ++ dovetail/tests/unit/utils/test_dovetail_utils.py | 1318 +++++++++++++++++++++ dovetail/tests/unit/utils/test_openstack_utils.py | 87 ++ dovetail/utils/dovetail_utils.py | 53 +- 7 files changed, 1615 insertions(+), 29 deletions(-) create mode 100644 dovetail/tests/unit/utils/__init__.py create mode 100644 dovetail/tests/unit/utils/test_dovetail_config.py create mode 100644 dovetail/tests/unit/utils/test_dovetail_logger.py create mode 100644 dovetail/tests/unit/utils/test_dovetail_utils.py create mode 100644 dovetail/tests/unit/utils/test_openstack_utils.py diff --git a/dovetail/tests/unit/test_parser.py b/dovetail/tests/unit/test_parser.py index 53484400..acfd25cf 100644 --- a/dovetail/tests/unit/test_parser.py +++ b/dovetail/tests/unit/test_parser.py @@ -64,6 +64,37 @@ class TestParser(unittest.TestCase): "None -r") self.assertEqual(expected_output, output) + @mock.patch('dovetail.parser.jinja2') + def test_parse_cmd_exception(self, mock_jinja, mock_logger): + errorMSG = 'Exception was raised' + exception = Exception(errorMSG) + command = 'cmd' + undefined_obj = mock.Mock() + mock_jinja.StrictUndefined = undefined_obj + mock_jinja.Template.side_effect = exception -if __name__ == '__main__': - unittest.main() + expected = None + dovetail_parser = parser.Parser() + exception_obj = mock.Mock() + dovetail_parser.logger.exception = exception_obj + result = dovetail_parser.parse_cmd(command, 'testcase') + + mock_jinja.Template.assert_called_once_with(command, + undefined=undefined_obj) + exception_obj.assert_called_once_with( + 'Failed to parse cmd {}, exception: {}'.format(command, errorMSG)) + self.assertEqual(expected, result) + + @mock.patch('dovetail.parser.dt_logger.Logger') + def test_create_log(self, mock_dt_logger, mock_logger): + mock_dt_logger_obj = mock.Mock() + logger_obj = mock.Mock() + mock_dt_logger_obj.getLogger.return_value = logger_obj + mock_dt_logger.return_value = mock_dt_logger_obj + + dovetail_parser = parser.Parser() + dovetail_parser.create_log() + + mock_dt_logger.assert_called_once_with('dovetail.parser.Parser') + mock_dt_logger_obj.getLogger.assert_called_once_with() + self.assertEqual(dovetail_parser.logger, logger_obj) diff --git a/dovetail/tests/unit/utils/__init__.py b/dovetail/tests/unit/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dovetail/tests/unit/utils/test_dovetail_config.py b/dovetail/tests/unit/utils/test_dovetail_config.py new file mode 100644 index 00000000..c7ac5b96 --- /dev/null +++ b/dovetail/tests/unit/utils/test_dovetail_config.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018 mokats@intracom-telecom.com and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +## + +import unittest +from mock import patch + +from dovetail.utils.dovetail_config import DovetailConfig + +__author__ = 'Stamatis Katsaounis ' + + +class DovetailConfigTesting(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @patch.object(DovetailConfig, 'update_non_envs') + def test_update_config(self, mock_non_envs): + config_dict = {'key': {'path': ['aa / bb/ cc'], 'value': 'val'}} + dovetail_cfg = DovetailConfig() + + dovetail_cfg.update_config(config_dict) + + mock_non_envs.assert_called_once_with(['aa', 'bb', 'cc'], 'val') + + def test_set_leaf_dict(self): + dict_to_test = {} + dovetail_cfg = DovetailConfig() + + dovetail_cfg.set_leaf_dict(dict_to_test, ['aa', 'bb', 'cc'], 'val') + + self.assertEquals({'aa': {'bb': {'cc': 'val'}}}, dict_to_test) + + @patch.object(DovetailConfig, 'set_leaf_dict') + @patch.object(DovetailConfig, 'dovetail_config') + def test_update_non_envs(self, mock_cfg, mock_set_leaf): + path = ['aa', 'bb', 'cc'] + value = 'val' + dovetail_cfg = DovetailConfig() + + dovetail_cfg.update_non_envs(path, value) + + mock_set_leaf.assert_called_once_with(mock_cfg, path, value) diff --git a/dovetail/tests/unit/utils/test_dovetail_logger.py b/dovetail/tests/unit/utils/test_dovetail_logger.py new file mode 100644 index 00000000..a8867890 --- /dev/null +++ b/dovetail/tests/unit/utils/test_dovetail_logger.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018 mokats@intracom-telecom.com and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +## + +import unittest +from mock import patch, call, Mock + +from dovetail.utils import dovetail_utils +from dovetail.utils.dovetail_logger import Logger + +__author__ = 'Stamatis Katsaounis ' + + +class DovetailLoggerTesting(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @patch('sys.stdout') + @patch('dovetail.utils.dovetail_logger.os') + @patch('dovetail.utils.dovetail_logger.logging') + def test_logger_info(self, mock_logging, mock_os, mock_stdout): + file_path = 'file_path' + level_const_info = 'INFO' + level_const_debug = 'DEBUG' + logger = Mock() + mock_os.getenv.return_value = 'False' + mock_logging.INFO = level_const_info + mock_logging.DEBUG = level_const_debug + mock_logging.getLogger.return_value = logger + dovetail_utils.dt_cfg.dovetail_config = {'result_dir': file_path} + mock_os.path.exists.return_value = False + stream_handler_obj = Mock() + formatter_obj = Mock() + file_handler_obj = Mock() + mock_logging.StreamHandler.return_value = stream_handler_obj + mock_logging.Formatter.return_value = formatter_obj + mock_logging.FileHandler.return_value = file_handler_obj + + logger_name = 'name' + dovetail_logger = Logger(logger_name) + mock_logging.getLogger.assert_called_once_with(logger_name) + self.assertEquals(dovetail_logger.logger.propagate, 0) + logger.setLevel.assert_called_once_with(level_const_debug) + mock_os.path.exists.assert_called_once_with(file_path) + # mock_os.makedirs.assert_called_once_with(file_path) + mock_logging.Formatter.assert_called_once_with( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + stream_handler_obj.setFormatter.assert_called_once_with(formatter_obj) + stream_handler_obj.setLevel.assert_called_once_with(level_const_info) + file_handler_obj.setLevel.assert_called_once_with(level_const_info) + logger.addHandler.assert_has_calls([ + call(stream_handler_obj), call(file_handler_obj)]) + self.assertEquals(dovetail_logger.getLogger(), logger) + + @patch('sys.stdout') + @patch('dovetail.utils.dovetail_logger.os') + @patch('dovetail.utils.dovetail_logger.logging') + def test_logger_debug(self, mock_logging, mock_os, mock_stdout): + file_path = 'file_path' + level_const_debug = 'DEBUG' + logger = Mock() + mock_os.getenv.return_value = 'True' + mock_logging.DEBUG = level_const_debug + mock_logging.getLogger.return_value = logger + dovetail_utils.dt_cfg.dovetail_config = {'result_dir': file_path} + mock_os.path.exists.return_value = False + stream_handler_obj = Mock() + formatter_obj = Mock() + file_handler_obj = Mock() + mock_logging.StreamHandler.return_value = stream_handler_obj + mock_logging.Formatter.return_value = formatter_obj + mock_logging.FileHandler.return_value = file_handler_obj + + logger_name = 'name' + dovetail_logger = Logger(logger_name) + mock_logging.getLogger.assert_called_once_with(logger_name) + self.assertEquals(dovetail_logger.logger.propagate, 0) + logger.setLevel.assert_called_once_with(level_const_debug) + mock_os.path.exists.assert_called_once_with(file_path) + # mock_os.makedirs.assert_called_once_with(file_path) + mock_logging.Formatter.assert_called_once_with( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + stream_handler_obj.setFormatter.assert_called_once_with(formatter_obj) + stream_handler_obj.setLevel.assert_called_once_with(level_const_debug) + file_handler_obj.setLevel.assert_called_once_with(level_const_debug) + logger.addHandler.assert_has_calls([ + call(stream_handler_obj), call(file_handler_obj)]) + self.assertEquals(dovetail_logger.getLogger(), logger) diff --git a/dovetail/tests/unit/utils/test_dovetail_utils.py b/dovetail/tests/unit/utils/test_dovetail_utils.py new file mode 100644 index 00000000..60278732 --- /dev/null +++ b/dovetail/tests/unit/utils/test_dovetail_utils.py @@ -0,0 +1,1318 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018 mokats@intracom-telecom.com and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +## + +import io +import unittest +from mock import patch, call, Mock + +from dovetail import constants +from dovetail.utils import dovetail_utils + +__author__ = 'Stamatis Katsaounis ' + + +class DovetailUtilsTesting(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @patch('sys.stdout') + @patch('__builtin__.print') + def test_exec_log_no_verbose(self, mock_print, mock_stdout): + dovetail_utils.exec_log(verbose=False, logger=None, msg='', + level='info', flush=True) + + mock_print.assert_not_called() + mock_stdout.flush.assert_not_called() + + @patch('sys.stdout') + @patch('__builtin__.print') + def test_exec_log_no_logger_flush(self, mock_print, mock_stdout): + message = 'message' + + dovetail_utils.exec_log(verbose=True, logger=False, msg=message, + level='info', flush=True) + + mock_print.assert_has_calls([call(message)]) + mock_stdout.flush.assert_called_once() + + @patch('sys.stdout') + @patch('__builtin__.print') + def test_exec_log_no_logger_no_flush(self, mock_print, mock_stdout): + message = 'message' + + dovetail_utils.exec_log(verbose=True, logger=False, msg=message, + level='info', flush=False) + + mock_print.assert_has_calls([call(message)]) + mock_stdout.flush.assert_not_called() + + def test_exec_log_logger_info(self): + message = 'message' + logger = Mock() + + dovetail_utils.exec_log(verbose=True, logger=logger, msg=message, + level='info', flush=True) + + logger.info.assert_called_once_with(message) + + def test_exec_log_logger_error(self): + message = 'message' + logger = Mock() + + dovetail_utils.exec_log(verbose=True, logger=logger, msg=message, + level='error', flush=True) + + logger.error.assert_called_once_with(message) + + def test_exec_log_logger_debug(self): + message = 'message' + logger = Mock() + + dovetail_utils.exec_log(verbose=True, logger=logger, msg=message, + level='debug', flush=True) + + logger.debug.assert_called_once_with(message) + + def test_get_value_from_dict(self): + key_path = 'a.b' + input_dict = {'a': {'b': 'c'}} + + expected = 'c' + result = dovetail_utils.get_value_from_dict(key_path, input_dict) + + self.assertEqual(expected, result) + + def test_get_value_from_dict_key_path_not_str(self): + key_path = 1 + input_dict = {'a': {'b': 'c'}} + + expected = None + result = dovetail_utils.get_value_from_dict(key_path, input_dict) + + self.assertEqual(expected, result) + + def test_get_value_from_dict_input_dict_not_dict(self): + key_path = 'a.b' + input_dict = 'dictionary' + + expected = None + result = dovetail_utils.get_value_from_dict(key_path, input_dict) + + self.assertEqual(expected, result) + + def test_get_value_from_dict_no_value_for_key_in_key_path(self): + key_path = 'a.b' + input_dict = {'a': {'c': 'b'}} + + expected = None + result = dovetail_utils.get_value_from_dict(key_path, input_dict) + + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_read_plain_file_not_exist(self, mock_path): + file_path = 'unknown_file' + logger = Mock() + mock_path.isfile.return_value = False + + expected = None + result = dovetail_utils.read_plain_file(file_path, logger) + + mock_path.isfile.assert_called_once_with(file_path) + logger.error.assert_called_once_with("File {} doesn't exist." + .format(file_path)) + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_read_plain_file(self, mock_path, mock_open): + file_path = 'known_file' + file_data = u'file data' + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.return_value = io.StringIO(file_data) + + expected = file_data + result = dovetail_utils.read_plain_file(file_path) + + mock_path.isfile.assert_called_once_with(file_path) + mock_open.assert_called_once_with(file_path, 'r') + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_read_plain_file_raised_exception(self, mock_path, mock_open): + logger = Mock() + file_path = 'known_file' + errorMSG = 'Exception was raised' + exception = Exception(errorMSG) + mock_open.side_effect = exception + + expected = None + result = dovetail_utils.read_plain_file(file_path, logger) + + mock_open.assert_called_once_with(file_path, 'r') + logger.exception.assert_called_once_with( + 'Failed to read file {}, exception: {}' + .format(file_path, exception)) + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_read_yaml_file_not_exist(self, mock_path): + file_path = 'unknown_file' + logger = Mock() + mock_path.isfile.return_value = False + + expected = None + result = dovetail_utils.read_yaml_file(file_path, logger) + + mock_path.isfile.assert_called_once_with(file_path) + logger.error.assert_called_once_with("File {} doesn't exist." + .format(file_path)) + self.assertEqual(expected, result) + + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_read_yaml_file(self, mock_path, mock_open, mock_load): + file_obj = Mock() + file_path = 'known_file' + file_data = 'file data' + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = file_data + + expected = file_data + result = dovetail_utils.read_yaml_file(file_path) + + mock_path.isfile.assert_called_once_with(file_path) + mock_open.assert_called_once_with(file_path, 'r') + mock_load.assert_called_once_with(file_obj) + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_read_yaml_file_raised_exception(self, mock_path, mock_open): + logger = Mock() + file_path = 'known_file' + errorMSG = 'Exception was raised' + exception = Exception(errorMSG) + mock_open.side_effect = exception + + expected = None + result = dovetail_utils.read_yaml_file(file_path, logger) + + mock_open.assert_called_once_with(file_path, 'r') + logger.exception.assert_called_once_with( + 'Failed to read file {}, exception: {}' + .format(file_path, exception)) + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_get_hosts_info_not_exist(self, mock_path): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = False + + expected = '' + result = dovetail_utils.get_hosts_info(file_path) + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + self.assertEqual(expected, result) + + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_get_hosts_info_not_yaml(self, mock_path, mock_open, mock_load): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = True + file_obj = Mock() + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = None + logger = Mock() + + expected = '' + result = dovetail_utils.get_hosts_info(logger) + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + mock_open.assert_called_once_with(file_complete_name) + mock_load.assert_called_once_with(file_obj) + logger.debug.assert_called_once_with( + 'File {} is empty.'.format(file_complete_name)) + self.assertEqual(expected, result) + + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_get_hosts_info_no_hosts_info(self, mock_path, mock_open, + mock_load): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = True + file_obj = Mock() + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = {'a': 'b'} + logger = Mock() + + expected = '' + result = dovetail_utils.get_hosts_info(logger) + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + mock_open.assert_called_once_with(file_complete_name) + mock_load.assert_called_once_with(file_obj) + logger.error.assert_called_once_with( + 'There is no key hosts_info in file {}' + .format(file_complete_name)) + self.assertEqual(expected, result) + + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_get_hosts_info_no_hostname(self, mock_path, mock_open, mock_load): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = True + file_obj = Mock() + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = {'hosts_info': {'127.0.0.1': []}} + + expected = '' + result = dovetail_utils.get_hosts_info() + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + mock_open.assert_called_once_with(file_complete_name) + mock_load.assert_called_once_with(file_obj) + self.assertEqual(expected, result) + + @patch('dovetail.utils.dovetail_utils.add_hosts_info') + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_get_hosts_info_no_valid_hostname(self, mock_path, mock_open, + mock_load, mock_fn): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = True + file_obj = Mock() + mock_open.return_value.__enter__.return_value = file_obj + hosts_info = {'127.0.0.1': [None]} + mock_load.return_value = {'hosts_info': hosts_info} + + expected = '' + result = dovetail_utils.get_hosts_info() + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + mock_open.assert_called_once_with(file_complete_name) + mock_load.assert_called_once_with(file_obj) + mock_fn.assert_called_once_with(hosts_info.keys()[0], + hosts_info.values()[0]) + self.assertEqual(expected, result) + + @patch('dovetail.utils.dovetail_utils.add_hosts_info') + @patch('yaml.safe_load') + @patch('__builtin__.open') + @patch('os.path', autospec=True) + def test_get_hosts_info(self, mock_path, mock_open, mock_load, mock_fn): + file_path = 'file_path' + file_complete_name = '/'.join((file_path, 'hosts.yaml')) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_path.isfile.return_value = True + file_obj = Mock() + mock_open.return_value.__enter__.return_value = file_obj + hosts_ip = '127.0.0.1' + hostnames = ['host_one', 'host_two'] + mock_load.return_value = {'hosts_info': {hosts_ip: hostnames}} + logger = Mock() + + names_str = ' '.join(hostnames) + expected = ' --add-host=\'{}\':{} '.format(names_str, hosts_ip) + result = dovetail_utils.get_hosts_info(logger) + + mock_path.join.assert_called_once_with(file_path, 'hosts.yaml') + mock_path.isfile.assert_called_once_with(file_complete_name) + mock_open.assert_called_once_with(file_complete_name) + mock_load.assert_called_once_with(file_obj) + mock_fn.assert_called_once_with(hosts_ip, hostnames) + logger.debug.assert_called_once_with('Get hosts info {}:{}.' + .format(hosts_ip, names_str)) + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_check_cacert_false(self, mock_path): + file_one_path = 'path_one' + file_two_path = 'path_two' + logger = Mock() + mock_path.isfile.return_value = True + mock_path.dirname.return_value = file_one_path + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_two_path} + + expected = False + result = dovetail_utils.check_cacert_file(file_one_path, logger) + + mock_path.isfile.assert_called_once_with(file_one_path) + mock_path.dirname.assert_called_once_with(file_one_path) + logger.error.assert_called_once_with( + 'Credential file must be put under {}, ' + 'which can be mounted into other container.' + .format(file_two_path)) + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_check_cacert_true(self, mock_path): + file_path = 'path_one' + mock_path.isfile.return_value = True + mock_path.dirname.return_value = file_path + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': file_path} + + expected = True + result = dovetail_utils.check_cacert_file(file_path) + + mock_path.isfile.assert_called_once_with(file_path) + mock_path.dirname.assert_called_once_with(file_path) + self.assertEqual(expected, result) + + @patch('os.path', autospec=True) + def test_check_cacert_not_exist(self, mock_path): + file_path = 'path_one' + mock_path.isfile.return_value = False + logger = Mock() + + expected = False + result = dovetail_utils.check_cacert_file(file_path, logger) + + mock_path.isfile.assert_called_once_with(file_path) + logger.error.assert_called_once_with( + 'OS_CACERT is {}, but the file does not exist.' + .format(file_path)) + self.assertEqual(expected, result) + + @patch('sys.stdout') + def test_show_progress_bar(self, mock_stdout): + length = 99 + max_length = 50 + expect_length = length % max_length + calls = [call('Running ' + ' ' * max_length + '\r'), + call('Running ' + '.' * expect_length + '\r')] + + dovetail_utils.show_progress_bar(length) + mock_stdout.write.assert_has_calls(calls) + mock_stdout.flush.assert_has_calls([call(), call()]) + + def test_get_duration(self): + date = '2018-08-10 05:12:27' + logger = Mock() + + expected = '0m0s' + result = dovetail_utils.get_duration(date, date, logger) + + self.assertEqual(expected, result) + + def test_get_duration_invalid_time(self): + date = 'invalid' + logger = Mock() + + expected = None + result = dovetail_utils.get_duration(date, date, logger) + + logger.exception.assert_called_once() + self.assertEqual(expected, result) + + @patch('os.getenv') + def test_check_https_enabled(self, mock_getenv): + auth_url = 'https://valid-url.com' + mock_getenv.return_value = auth_url + logger = Mock() + + expected = True + result = dovetail_utils.check_https_enabled(logger) + + logger.debug.assert_has_calls( + [call('Checking if https enabled or not...'), + call('https is enabled')]) + self.assertEqual(expected, result) + + @patch('os.getenv') + def test_check_https_disabled(self, mock_getenv): + auth_url = 'invalid' + mock_getenv.return_value = auth_url + logger = Mock() + + expected = False + result = dovetail_utils.check_https_enabled(logger) + + logger.debug.assert_has_calls( + [call('Checking if https enabled or not...'), + call('https is not enabled')]) + self.assertEqual(expected, result) + + @patch('python_hosts.Hosts', autospec=True) + @patch('python_hosts.HostsEntry', autospec=True) + def test_add_hosts_info_no_ip(self, mock_python_h_entry, + mock_python_hosts): + dovetail_utils.add_hosts_info(None, ['host_one']) + + mock_python_hosts.assert_called_once_with(path='/etc/hosts') + mock_python_h_entry.assert_not_called() + + @patch('python_hosts.Hosts', autospec=True) + @patch('python_hosts.HostsEntry', autospec=True) + def test_add_hosts_info_no_hosts(self, mock_python_h_entry, + mock_python_hosts): + dovetail_utils.add_hosts_info('127.0.0.1', []) + + mock_python_hosts.assert_called_once_with(path='/etc/hosts') + mock_python_h_entry.assert_not_called() + + @patch('python_hosts.Hosts', autospec=True) + @patch('python_hosts.HostsEntry', autospec=True) + def test_add_hosts_info(self, mock_python_h_entry, mock_python_hosts): + ip = '127.0.0.1' + hostnames = ['host_one'] + hosts_obj = Mock() + entry_obj = Mock() + mock_python_hosts.return_value = hosts_obj + mock_python_h_entry.return_value = entry_obj + + dovetail_utils.add_hosts_info(ip, hostnames) + + mock_python_hosts.assert_called_once_with(path='/etc/hosts') + mock_python_h_entry.assert_called_once_with(entry_type='ipv4', + address=ip, + names=hostnames) + hosts_obj.add.assert_called_once_with([entry_obj]) + hosts_obj.write.assert_called_once() + + @patch('dovetail.utils.dovetail_utils.objwalk') + def test_get_obj_by_path(self, mock_walk): + path = dist_path = 'path' + obj = 'obj' + mock_walk.return_value = [(path, obj)] + + expected = obj + result = dovetail_utils.get_obj_by_path(obj, dist_path) + + self.assertEqual(expected, result) + + @patch('dovetail.utils.dovetail_utils.objwalk') + def test_get_obj_by_path_none(self, mock_walk): + path = 'path' + dist_path = 'dst_path' + obj = 'obj' + mock_walk.return_value = [(path, obj)] + + expected = None + result = dovetail_utils.get_obj_by_path(obj, dist_path) + + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.environ') + def test_source_env(self, mock_env, mock_open): + file_path = 'file_path' + env_name = 'name' + env_value = 'value' + file_data = u'export %s=%s' % (env_name, env_value) + mock_open.return_value.__enter__.return_value = io.StringIO(file_data) + + dovetail_utils.source_env(file_path) + + 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)] + 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))]) + + @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)] + 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)] + 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('__builtin__.open') + @patch('os.path') + @patch('os.listdir') + @patch('json.load') + @patch('json.dumps') + def test_combine_files(self, mock_dumps, mock_load, mock_listdir, + mock_path, mock_open): + file_path = 'file_path' + file_name = 'file_name' + file_complete_name = '/'.join([file_path, file_name]) + file_content_dict = {'key': 'value'} + file_content_str = '{"key": "value"}' + file_obj = Mock() + mock_listdir.return_value = [file_name] + mock_path.join.return_value = file_complete_name + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = file_content_dict + mock_dumps.return_value = file_content_str + logger = Mock() + + expected = 'result_file' + result = dovetail_utils.combine_files(file_path, expected, logger) + + mock_listdir.assert_called_once_with(file_path) + mock_path.join.assert_called_once_with(file_path, file_name) + mock_open.assert_any_call(file_complete_name, 'r') + mock_load.assert_called_once_with(file_obj) + mock_open.assert_any_call(expected, 'w') + mock_dumps.assert_called_once_with({file_name: file_content_dict}) + file_obj.write.assert_called_once_with(file_content_str) + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path') + @patch('os.listdir') + def test_combine_files_read_exception(self, mock_listdir, mock_path, + mock_open): + file_path = 'file_path' + file_name = 'file_name' + file_complete_name = '/'.join([file_path, file_name]) + mock_listdir.return_value = [file_name] + mock_path.join.return_value = file_complete_name + mock_open.side_effect = Exception() + logger = Mock() + + expected = None + result = dovetail_utils.combine_files(file_path, expected, logger) + + mock_listdir.assert_called_once_with(file_path) + mock_path.join.assert_called_once_with(file_path, file_name) + mock_open.assert_any_call(file_complete_name, 'r') + logger.error.assert_called_once_with( + 'Failed to read file {}.'.format(file_complete_name)) + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path') + @patch('os.listdir') + @patch('json.load') + @patch('json.dumps') + def test_combine_files_write_exception(self, mock_dumps, mock_load, + mock_listdir, mock_path, mock_open): + file_path = 'file_path' + file_name = 'file_name' + file_complete_name = '/'.join([file_path, file_name]) + file_content_dict = {'key': 'value'} + file_content_str = '{"key": "value"}' + file_obj = Mock() + file_obj.write.side_effect = Exception() + mock_listdir.return_value = [file_name] + mock_path.join.return_value = file_complete_name + mock_open.return_value.__enter__.return_value = file_obj + mock_load.return_value = file_content_dict + mock_dumps.return_value = file_content_str + logger = Mock() + + expected = None + result = dovetail_utils.combine_files(file_path, expected, logger) + + mock_listdir.assert_called_once_with(file_path) + mock_path.join.assert_called_once_with(file_path, file_name) + mock_open.assert_any_call(file_complete_name, 'r') + mock_load.assert_called_once_with(file_obj) + mock_open.assert_any_call(expected, 'w') + mock_dumps.assert_called_once_with({file_name: file_content_dict}) + file_obj.write.assert_called_once_with(file_content_str) + logger.exception.assert_called_once_with( + 'Failed to write file {}.'.format(expected)) + self.assertEqual(expected, result) + + @patch('json.dump') + @patch('__builtin__.open') + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.check_https_enabled') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.OS_Utils', autospec=True) + def test_get_openstack_endpoint(self, mock_utils, mock_getenv, + mock_https_check, mock_path, mock_open, + mock_dump): + mock_https_check.return_value = True + mock_getenv.return_value = 'True' + endpoints_ret = True + endpoint_url = 'http://www.abc.com' + endpoint_enabled = True + service_id = '123456789' + service_type = 'type' + service_name = 'name' + endpoints = [{'url': endpoint_url, + 'enabled': endpoint_enabled, + 'service_id': service_id}] + services_ret = True + services = [{'service_type': service_type, + 'name': service_name}] + file_path = 'file_path' + file_name = 'endpoint_info.json' + file_complete_name = '/'.join([file_path, file_name]) + utils_obj = Mock() + file_obj = Mock() + logger = Mock() + mock_utils.return_value = utils_obj + utils_obj.search_endpoints.return_value = (endpoints_ret, endpoints) + utils_obj.search_services.return_value = (services_ret, services) + dovetail_utils.dt_cfg.dovetail_config = {'result_dir': file_path} + mock_path.join.return_value = file_complete_name + mock_open.return_value.__enter__.return_value = file_obj + + expected = [{'Service Type': service_type, + 'URL': endpoint_url, + 'Enabled': endpoint_enabled, + 'Service Name': service_name}] + result = dovetail_utils.get_openstack_endpoint(logger=logger) + + mock_https_check.assert_called_once_with(logger) + mock_getenv.assert_called_once_with('OS_INSECURE') + mock_utils.assert_called_once_with(verify=False) + utils_obj.search_endpoints.assert_called_once() + utils_obj.search_services.assert_called_once_with( + service_id=service_id) + mock_path.join.assert_called_once_with(file_path, file_name) + mock_open.assert_any_call(file_complete_name, 'w') + mock_dump.assert_called_once_with(expected, file_obj) + logger.debug.assert_called_once_with( + 'Record all endpoint info into file {}.' + .format(file_complete_name)) + self.assertEqual(expected, result) + + @patch('dovetail.utils.dovetail_utils.check_https_enabled') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.OS_Utils', autospec=True) + def test_get_openstack_endpoint_no_endpoints(self, mock_utils, mock_getenv, + mock_https_check): + mock_https_check.return_value = True + mock_getenv.return_value = 'True' + endpoints_ret = False + endpoints_exception_msg = 'An exception occured' + utils_obj = Mock() + logger = Mock() + mock_utils.return_value = utils_obj + utils_obj.search_endpoints.return_value = (endpoints_ret, + endpoints_exception_msg) + + expected = None + result = dovetail_utils.get_openstack_endpoint(logger=logger) + + mock_https_check.assert_called_once_with(logger) + mock_getenv.assert_called_once_with('OS_INSECURE') + mock_utils.assert_called_once_with(verify=False) + utils_obj.search_endpoints.assert_called_once() + logger.error.assert_called_once_with( + 'Failed to list endpoints. Exception message, {}' + .format(endpoints_exception_msg)) + self.assertEqual(expected, result) + + @patch('dovetail.utils.dovetail_utils.check_https_enabled') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.OS_Utils', autospec=True) + def test_get_openstack_endpoint_no_services(self, mock_utils, mock_getenv, + mock_https_check): + mock_https_check.return_value = True + mock_getenv.return_value = 'True' + endpoints_ret = True + endpoint_url = 'http://www.abc.com' + endpoint_enabled = True + service_id = '123456789' + endpoints = [{'url': endpoint_url, + 'enabled': endpoint_enabled, + 'service_id': service_id}] + services_ret = False + services_exception_msg = 'An exception occured' + utils_obj = Mock() + logger = Mock() + mock_utils.return_value = utils_obj + utils_obj.search_endpoints.return_value = (endpoints_ret, endpoints) + utils_obj.search_services.return_value = (services_ret, + services_exception_msg) + + expected = None + result = dovetail_utils.get_openstack_endpoint(logger=logger) + + mock_https_check.assert_called_once_with(logger) + mock_getenv.assert_called_once_with('OS_INSECURE') + mock_utils.assert_called_once_with(verify=False) + utils_obj.search_endpoints.assert_called_once() + utils_obj.search_services.assert_called_once_with( + service_id=service_id) + logger.error.assert_called_once_with( + 'Failed to list services. Exception message, {}' + .format(services_exception_msg)) + self.assertEqual(expected, result) + + @patch('__builtin__.open') + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.check_https_enabled') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.OS_Utils', autospec=True) + def test_get_openstack_endpoint_exception(self, mock_utils, mock_getenv, + mock_https_check, mock_path, + mock_open): + mock_https_check.return_value = False + mock_getenv.return_value = 'True' + endpoints_ret = True + endpoint_url = 'http://www.abc.com' + endpoint_enabled = True + service_id = '123456789' + service_type = 'type' + service_name = 'name' + endpoints = [{'url': endpoint_url, + 'enabled': endpoint_enabled, + 'service_id': service_id}] + services_ret = True + services = [{'service_type': service_type, + 'name': service_name}] + file_path = 'file_path' + file_name = 'endpoint_info.json' + file_complete_name = '/'.join([file_path, file_name]) + utils_obj = Mock() + logger = Mock() + mock_utils.return_value = utils_obj + utils_obj.search_endpoints.return_value = (endpoints_ret, endpoints) + utils_obj.search_services.return_value = (services_ret, services) + dovetail_utils.dt_cfg.dovetail_config = {'result_dir': file_path} + mock_path.join.return_value = file_complete_name + errorMSG = 'Exception was raised' + exception = Exception(errorMSG) + mock_open.side_effect = exception + + expected = None + result = dovetail_utils.get_openstack_endpoint(logger=logger) + + mock_https_check.assert_called_once_with(logger) + mock_getenv.assert_called_once_with('OS_INSECURE') + mock_utils.assert_called_once_with() + utils_obj.search_endpoints.assert_called_once() + utils_obj.search_services.assert_called_once_with( + service_id=service_id) + mock_path.join.assert_called_once_with(file_path, file_name) + mock_open.assert_any_call(file_complete_name, 'w') + logger.exception.assert_called_once_with( + 'Failed to write endpoint info into file.') + self.assertEqual(expected, result) + + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.get_inventory_file') + @patch('dovetail.utils.dovetail_utils.exec_cmd') + @patch('dovetail.utils.dovetail_utils.combine_files') + def test_get_hardware_info(self, mock_combine, mock_cmd, mock_inventory, + mock_path): + logger = Mock() + config_dir = 'config_dir' + result_dir = 'result_dir' + pod_file = 'pod_file' + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': config_dir, + 'result_dir': result_dir, + 'pod_file': pod_file} + mock_inventory.return_value = Mock() + ret = 0 + msg = '' + mock_cmd.return_value = (ret, msg) + inventory_file = '/'.join([result_dir, 'inventory.ini']) + info_file_path = '/'.join([result_dir, 'sut_hardware_info']) + all_info_file = '/'.join([result_dir, 'all_hosts_info.json']) + mock_path.join.side_effect = [ + '/'.join([config_dir, pod_file]), + info_file_path, + all_info_file, + inventory_file] + mock_path.exists.return_value = True + mock_combine.return_value = True + + expected = all_info_file + result = dovetail_utils.get_hardware_info(logger=logger) + + join_calls = [call(config_dir, pod_file), + call(result_dir, 'sut_hardware_info'), + call(result_dir, 'all_hosts_info.json'), + call(result_dir, 'inventory.ini')] + mock_path.join.assert_has_calls(join_calls) + log_calls = [ + call('Get hardware info of all nodes list in file {} ...' + .format('/'.join([config_dir, pod_file]))), + call('Hardware info of all nodes are stored in file {}.' + .format(expected))] + logger.info.assert_has_calls(log_calls) + mock_cmd.assert_called_once_with( + 'cd {} && ansible all -m setup -i {} --tree {}' + .format(constants.USERCONF_PATH, inventory_file, info_file_path), + verbose=False) + mock_path.exists.assert_called_once_with(info_file_path) + mock_combine.assert_called_once_with(info_file_path, all_info_file, + logger) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.get_inventory_file') + def test_get_hardware_info_no_inventory(self, mock_inventory, + mock_path): + logger = Mock() + config_dir = 'config_dir' + result_dir = 'result_dir' + pod_file = 'pod_file' + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': config_dir, + 'result_dir': result_dir, + 'pod_file': pod_file} + mock_inventory.return_value = None + mock_path.join.side_effect = [ + '/'.join([config_dir, pod_file]), + '/'.join([result_dir, 'sut_hardware_info']), + '/'.join([result_dir, 'all_hosts_info.json']), + '/'.join([result_dir, 'inventory.ini'])] + + expected = None + result = dovetail_utils.get_hardware_info(logger=logger) + + join_calls = [call(config_dir, pod_file), + call(result_dir, 'sut_hardware_info'), + call(result_dir, 'all_hosts_info.json'), + call(result_dir, 'inventory.ini')] + mock_path.join.assert_has_calls(join_calls) + logger.info.assert_called_once_with( + 'Get hardware info of all nodes list in file {} ...' + .format('/'.join([config_dir, pod_file]))) + logger.error.assert_called_once_with( + 'Failed to get SUT hardware info.') + self.assertEqual(expected, result) + + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.get_inventory_file') + @patch('dovetail.utils.dovetail_utils.exec_cmd') + def test_get_hardware_info_no_info(self, mock_cmd, mock_inventory, + mock_path): + logger = Mock() + config_dir = 'config_dir' + result_dir = 'result_dir' + pod_file = 'pod_file' + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': config_dir, + 'result_dir': result_dir, + 'pod_file': pod_file} + mock_inventory.return_value = Mock() + ret = 0 + msg = '' + mock_cmd.return_value = (ret, msg) + inventory_file = '/'.join([result_dir, 'inventory.ini']) + info_file_path = '/'.join([result_dir, 'sut_hardware_info']) + all_info_file = '/'.join([result_dir, 'all_hosts_info.json']) + mock_path.join.side_effect = [ + '/'.join([config_dir, pod_file]), + info_file_path, + all_info_file, + inventory_file] + mock_path.exists.return_value = False + + expected = None + result = dovetail_utils.get_hardware_info(logger=logger) + + join_calls = [call(config_dir, pod_file), + call(result_dir, 'sut_hardware_info'), + call(result_dir, 'all_hosts_info.json'), + call(result_dir, 'inventory.ini')] + mock_path.join.assert_has_calls(join_calls) + logger.info.assert_called_once_with( + 'Get hardware info of all nodes list in file {} ...' + .format('/'.join([config_dir, pod_file]))) + logger.error.assert_called_once_with( + 'Failed to get SUT hardware info.') + mock_cmd.assert_called_once_with( + 'cd {} && ansible all -m setup -i {} --tree {}' + .format(constants.USERCONF_PATH, inventory_file, info_file_path), + verbose=False) + mock_path.exists.assert_called_once_with(info_file_path) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('dovetail.utils.dovetail_utils.get_inventory_file') + @patch('dovetail.utils.dovetail_utils.exec_cmd') + @patch('dovetail.utils.dovetail_utils.combine_files') + def test_get_hardware_info_no_combine(self, mock_combine, mock_cmd, + mock_inventory, mock_path): + logger = Mock() + config_dir = 'config_dir' + result_dir = 'result_dir' + pod_file = 'pod_file' + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': config_dir, + 'result_dir': result_dir, + 'pod_file': pod_file} + mock_inventory.return_value = Mock() + ret = 0 + msg = '' + mock_cmd.return_value = (ret, msg) + inventory_file = '/'.join([result_dir, 'inventory.ini']) + info_file_path = '/'.join([result_dir, 'sut_hardware_info']) + all_info_file = '/'.join([result_dir, 'all_hosts_info.json']) + mock_path.join.side_effect = [ + '/'.join([config_dir, pod_file]), + info_file_path, + all_info_file, + inventory_file] + mock_path.exists.return_value = True + mock_combine.return_value = False + + expected = None + result = dovetail_utils.get_hardware_info(logger=logger) + + join_calls = [call(config_dir, pod_file), + call(result_dir, 'sut_hardware_info'), + call(result_dir, 'all_hosts_info.json'), + call(result_dir, 'inventory.ini')] + mock_path.join.assert_has_calls(join_calls) + logger.info.assert_called_once_with( + 'Get hardware info of all nodes list in file {} ...' + .format('/'.join([config_dir, pod_file]))) + logger.error.assert_called_once_with( + 'Failed to get all hardware info.') + mock_cmd.assert_called_once_with( + 'cd {} && ansible all -m setup -i {} --tree {}' + .format(constants.USERCONF_PATH, inventory_file, info_file_path), + verbose=False) + mock_path.exists.assert_called_once_with(info_file_path) + mock_combine.assert_called_once_with(info_file_path, all_info_file, + logger) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('__builtin__.open') + @patch('yaml.safe_load') + def test_get_inventory_password(self, mock_load, mock_open, mock_path): + name = 'name' + ip = 'ip' + user = 'user' + password = 'password' + pod_file_data = {'nodes': [{'name': name, + 'ip': ip, + 'user': user, + 'password': password}]} + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + pod_file_obj = Mock() + inventory_file_obj = Mock() + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.side_effect = [pod_file_obj, + inventory_file_obj] + mock_load.return_value = pod_file_data + + expected = True + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + mock_open.assert_any_call(pod_file_name, 'r') + mock_open.assert_any_call(inventory_file_name, 'w') + mock_load.assert_called_once_with(pod_file_obj) + inventory_file_obj.write.assert_called_once_with( + '{name} ansible_host={ip} ansible_user={user} ' + 'ansible_ssh_pass={password}\n' + .format(name=name, ip=ip, user=user, password=password)) + logger.debug.assert_called_once_with( + 'Ansible inventory file is {}.'.format(inventory_file_name)) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('__builtin__.open') + @patch('yaml.safe_load') + def test_get_inventory_key_filename(self, mock_load, mock_open, mock_path): + name = 'name' + ip = 'ip' + user = 'user' + password = 'password' + pod_file_data = {'nodes': [{'name': name, + 'ip': ip, + 'user': user, + 'key_filename': password}]} + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + pod_file_obj = Mock() + inventory_file_obj = Mock() + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.side_effect = [pod_file_obj, + inventory_file_obj] + mock_load.return_value = pod_file_data + config_dir = 'config_dir' + key_file = '/'.join([config_dir, 'id_rsa']) + dovetail_utils.dt_cfg.dovetail_config = {'config_dir': config_dir} + mock_path.join.return_value = key_file + + expected = True + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + mock_open.assert_any_call(pod_file_name, 'r') + mock_open.assert_any_call(inventory_file_name, 'w') + mock_load.assert_called_once_with(pod_file_obj) + mock_path.join.assert_called_once_with(config_dir, 'id_rsa') + inventory_file_obj.write.assert_called_once_with( + '{name} ansible_host={ip} ansible_user={user} ' + 'ansible_ssh_private_key_file={key_file}\n' + .format(name=name, ip=ip, user=user, key_file=key_file)) + logger.debug.assert_called_once_with( + 'Ansible inventory file is {}.'.format(inventory_file_name)) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('__builtin__.open') + @patch('yaml.safe_load') + def test_get_inventory_other(self, mock_load, mock_open, mock_path): + name = 'name' + ip = 'ip' + user = 'user' + pod_file_data = {'nodes': [{'name': name, + 'ip': ip, + 'user': user}]} + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + pod_file_obj = Mock() + inventory_file_obj = Mock() + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.side_effect = [pod_file_obj, + inventory_file_obj] + mock_load.return_value = pod_file_data + + expected = False + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + mock_open.assert_any_call(pod_file_name, 'r') + mock_open.assert_any_call(inventory_file_name, 'w') + mock_load.assert_called_once_with(pod_file_obj) + logger.error.assert_called_once_with( + 'No password or key_filename in file {}.'.format(pod_file_name)) + self.assertEqual(expected, result) + + @patch('os.path') + @patch('__builtin__.open') + @patch('yaml.safe_load') + def test_get_inventory_keyerror(self, mock_load, mock_open, mock_path): + name = 'name' + ip = 'ip' + pod_file_data = {'nodes': [{'name': name, + 'ip': ip}]} + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + pod_file_obj = Mock() + inventory_file_obj = Mock() + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.side_effect = [pod_file_obj, + inventory_file_obj] + mock_load.return_value = pod_file_data + + expected = False + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + mock_open.assert_any_call(pod_file_name, 'r') + mock_open.assert_any_call(inventory_file_name, 'w') + mock_load.assert_called_once_with(pod_file_obj) + logger.exception.assert_called_once_with( + "KeyError 'user'.") + self.assertEqual(expected, result) + + @patch('os.path') + @patch('__builtin__.open') + def test_get_inventory_exception(self, mock_open, mock_path): + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + mock_path.isfile.return_value = True + mock_open.return_value.__enter__.side_effect = Exception() + + expected = False + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + mock_open.assert_called_once_with(pod_file_name, 'r') + logger.exception.assert_called_once_with( + 'Failed to read file {}.'.format(pod_file_name)) + self.assertEqual(expected, result) + + @patch('os.path') + def test_get_inventory_invalid_pod_file(self, mock_path): + inventory_file_name = 'inventory' + pod_file_name = 'pod' + logger = Mock() + mock_path.isfile.return_value = False + + expected = False + result = dovetail_utils.get_inventory_file(pod_file_name, + inventory_file_name, + logger=logger) + + mock_path.isfile.assert_called_once_with(pod_file_name) + logger.error.assert_called_once_with( + "File {} doesn't exist.".format(pod_file_name)) + self.assertEqual(expected, result) + + @patch('subprocess.Popen') + @patch('subprocess.PIPE') + @patch('subprocess.STDOUT') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.exec_log') + @patch('dovetail.utils.dovetail_utils.show_progress_bar') + def test_exec_cmd(self, mock_bar, mock_log, mock_getenv, mock_stdout, + mock_pipe, mock_open): + logger = Mock() + cmd = 'cmd' + verbose = True + subprocess_obj = Mock() + cmd_output = 'line' + mock_open.return_value = subprocess_obj + pip_obj = Mock() + stdout_obj = Mock() + mock_pipe.return_value = pip_obj + mock_stdout.return_value = stdout_obj + subp_stdout = Mock() + subprocess_obj.stdout = subp_stdout + subprocess_obj.wait.return_value = 0 + subp_stdout.readline.side_effect = [cmd_output, ''] + + expected = (0, 'line') + result = dovetail_utils.exec_cmd( + cmd, logger=logger, exit_on_error=True, info=False, + exec_msg_on=True, err_msg='', verbose=verbose, + progress_bar=True) + + log_calls = [ + call(verbose, logger, "Executing command: '%s'" % cmd, 'debug'), + call(verbose, logger, cmd_output, 'debug', True)] + mock_log.assert_has_calls(log_calls) + mock_open.assert_called_once_with(cmd, shell=True, stdout=mock_pipe, + stderr=mock_stdout) + subp_stdout.readline.assert_has_calls([call(), call()]) + subp_stdout.close.assert_called_once_with() + subprocess_obj.wait.assert_called_once_with() + mock_getenv.assert_called_once_with('DEBUG') + mock_bar.assert_called_once_with(1) + self.assertEqual(expected, result) + + @patch('sys.exit') + @patch('subprocess.Popen') + @patch('subprocess.PIPE') + @patch('subprocess.STDOUT') + @patch('os.getenv') + @patch('dovetail.utils.dovetail_utils.exec_log') + @patch('dovetail.utils.dovetail_utils.show_progress_bar') + def test_exec_cmd_error(self, mock_bar, mock_log, mock_getenv, mock_stdout, + mock_pipe, mock_open, mock_exit): + logger = Mock() + cmd = 'cmd' + verbose = True + subprocess_obj = Mock() + cmd_output = 'line' + mock_open.return_value = subprocess_obj + pip_obj = Mock() + stdout_obj = Mock() + mock_pipe.return_value = pip_obj + mock_stdout.return_value = stdout_obj + subp_stdout = Mock() + subprocess_obj.stdout = subp_stdout + subprocess_obj.wait.return_value = 1 + subp_stdout.readline.side_effect = [cmd_output, ''] + + dovetail_utils.exec_cmd( + cmd, logger=logger, exit_on_error=True, info=False, + exec_msg_on=True, err_msg='', verbose=verbose, + progress_bar=True) + + log_calls = [ + call(verbose, logger, "Executing command: '%s'" % cmd, 'debug'), + call(verbose, logger, cmd_output, 'debug', True), + call(verbose, logger, "The command '%s' failed." % cmd, 'error')] + mock_log.assert_has_calls(log_calls) + mock_open.assert_called_once_with(cmd, shell=True, stdout=mock_pipe, + stderr=mock_stdout) + subp_stdout.readline.assert_has_calls([call(), call()]) + subp_stdout.close.assert_called_once_with() + subprocess_obj.wait.assert_called_once_with() + mock_getenv.assert_called_once_with('DEBUG') + mock_bar.assert_called_once_with(1) + mock_exit.assert_called_once_with(1) diff --git a/dovetail/tests/unit/utils/test_openstack_utils.py b/dovetail/tests/unit/utils/test_openstack_utils.py new file mode 100644 index 00000000..73583838 --- /dev/null +++ b/dovetail/tests/unit/utils/test_openstack_utils.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# Copyright (c) 2018 mokats@intracom-telecom.com and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +## + +import unittest + +from mock import patch +import munch +import os_client_config +import shade + +import dovetail.utils.openstack_utils as os_dovetail_utils + +__author__ = 'Stamatis Katsaounis ' + + +class OpenStackUtilsTesting(unittest.TestCase): + + def setUp(self): + self.patcher1 = patch.object(shade, + 'OperatorCloud', autospec=True) + self.patcher2 = patch.object(os_client_config, + 'get_config', autospec=True) + self.cloud = self.patcher1.start().return_value + self.os_client_config = self.patcher2.start().return_value + self.os_dovetail = os_dovetail_utils.OS_Utils() + + def tearDown(self): + self.patcher1.stop() + self.patcher2.stop() + + def test_search_endpoints(self): + endpoint = munch.Munch({u'region_id': u'RegionOne', + u'url': u'http://127.0.0.1:8977/', + u'region': u'RegionOne', + u'enabled': True, + u'interface': u'public', + u'service_id': u'123456', + u'id': u'123456'}) + endpoints = [endpoint] + self.cloud.search_endpoints.return_value = endpoints + + expected = (True, endpoints) + result = self.os_dovetail.search_endpoints() + + self.assertEqual(expected, result) + + def test_search_endpoints_raised_exception(self): + errorMSG = 'Exception was raised' + exception = shade.exc.OpenStackCloudException(errorMSG) + self.cloud.search_endpoints.side_effect = exception + + expected = (False, errorMSG) + result = self.os_dovetail.search_endpoints() + + self.assertEqual(expected, result) + + def test_search_services(self): + service = munch.Munch({'description': u'OpenStack Identity Service', + 'service_type': u'identity', + 'type': u'identity', + 'enabled': True, + 'id': u'1bd26028c8714f3bb726126dc1ea62fc', + 'name': u'keystone'}) + services = [service] + self.cloud.search_services.return_value = services + + expected = (True, services) + result = self.os_dovetail.search_services() + + self.assertEqual(expected, result) + + def test_search_services_raised_exception(self): + errorMSG = 'Exception was raised' + exception = shade.exc.OpenStackCloudException(errorMSG) + self.cloud.search_services.side_effect = exception + + expected = (False, errorMSG) + result = self.os_dovetail.search_services() + + self.assertEqual(expected, result) diff --git a/dovetail/utils/dovetail_utils.py b/dovetail/utils/dovetail_utils.py index 4a8b45ff..50369e62 100644 --- a/dovetail/utils/dovetail_utils.py +++ b/dovetail/utils/dovetail_utils.py @@ -8,6 +8,7 @@ # http://www.apache.org/licenses/LICENSE-2.0 # +from __future__ import print_function import sys import os import re @@ -35,8 +36,6 @@ def exec_log(verbose, logger, msg, level, flush=False): logger.error(msg) elif level == 'debug': logger.debug(msg) - else: - pass else: print(msg) if flush: @@ -44,7 +43,8 @@ def exec_log(verbose, logger, msg, level, flush=False): def exec_cmd(cmd, logger=None, exit_on_error=False, info=False, - exec_msg_on=True, err_msg="", verbose=True): + exec_msg_on=True, err_msg="", verbose=True, + progress_bar=False): msg_err = ("The command '%s' failed." % cmd) if not err_msg else err_msg msg_exec = ("Executing command: '%s'" % cmd) level = 'info' if info else 'debug' @@ -54,14 +54,15 @@ def exec_cmd(cmd, logger=None, exit_on_error=False, info=False, p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = '' - # count = 1 - # DEBUG = os.getenv('DEBUG') + if progress_bar: + count = 1 + DEBUG = os.getenv('DEBUG') for line in iter(p.stdout.readline, b''): exec_log(verbose, logger, line.strip(), level, True) stdout += line - # if DEBUG is None or DEBUG.lower() != "true": - # show_progress_bar(count) - # count += 1 + if progress_bar and (DEBUG is None or DEBUG.lower() != "true"): + show_progress_bar(count) + count += 1 stdout = stdout.strip() returncode = p.wait() p.stdout.close() @@ -109,6 +110,7 @@ def get_obj_by_path(obj, dst_path): for path, obj in objwalk(obj): if path == dst_path: return obj + return None def source_env(env_file): @@ -324,22 +326,21 @@ def get_hosts_info(logger=None): if not hosts_yaml: logger.debug("File {} is empty.".format(hosts_config_file)) return hosts_config - try: - if not hosts_yaml['hosts_info']: - return hosts_config - for ip, hostnames in hosts_yaml['hosts_info'].iteritems(): - if not hostnames: - continue - add_hosts_info(ip, hostnames) - names_str = ' '.join(hostname for hostname in hostnames - if hostname) - if not names_str: - continue - hosts_config += ' --add-host=\'{}\':{} '.format(names_str, ip) - logger.debug('Get hosts info {}:{}.'.format(ip, names_str)) - except KeyError as e: - logger.error("There is no key {} in file {}" - .format(e, hosts_config_file)) + hosts_info = hosts_yaml.get('hosts_info', None) + if not hosts_info: + logger.error("There is no key hosts_info in file {}" + .format(hosts_config_file)) + return hosts_config + for ip, hostnames in hosts_info.iteritems(): + if not hostnames: + continue + add_hosts_info(ip, hostnames) + names_str = ' '.join(hostname for hostname in hostnames + if hostname) + if not names_str: + continue + hosts_config += ' --add-host=\'{}\':{} '.format(names_str, ip) + logger.debug('Get hosts info {}:{}.'.format(ip, names_str)) return hosts_config @@ -377,11 +378,9 @@ def get_value_from_dict(key_path, input_dict): key_path must be given in string format with dots Example: result.dir """ - if not isinstance(key_path, str): + if not isinstance(key_path, str) or not isinstance(input_dict, dict): return None for key in key_path.split("."): - if not isinstance(input_dict, dict): - return None input_dict = input_dict.get(key) if not input_dict: return None -- cgit 1.2.3-korg