Merge "Validation actions refactor" into stable/1.6

This commit is contained in:
Zuul
2022-03-17 21:24:44 +00:00
committed by Gerrit Code Review
14 changed files with 348 additions and 281 deletions

View File

@@ -62,11 +62,10 @@ class ListHistory(BaseLister):
("Limiting output to the maximum of "
"{} last validations.").format(history_limit))
actions = ValidationActions()
actions = ValidationActions(log_path=parsed_args.validation_log_dir)
return actions.show_history(
validation_ids=parsed_args.validation,
log_path=parsed_args.validation_log_dir,
history_limit=history_limit)

View File

@@ -171,7 +171,8 @@ class Run(BaseCommand):
# Get config:
config = self.base.config
v_actions = ValidationActions(parsed_args.validation_dir)
v_actions = ValidationActions(
parsed_args.validation_dir, log_path=parsed_args.validation_log_dir)
# Ansible execution should be quiet while using the validations_json
# default callback and be verbose while passing ANSIBLE_SDTOUT_CALLBACK
# environment variable to Ansible through the --extra-env-vars argument
@@ -213,7 +214,6 @@ class Run(BaseCommand):
python_interpreter=parsed_args.python_interpreter,
quiet=quiet_mode,
ssh_user=parsed_args.ssh_user,
log_path=parsed_args.validation_log_dir,
validation_config=config,
skip_list=skip_list)
except RuntimeError as e:

View File

@@ -71,8 +71,8 @@ class ShowGroup(BaseLister):
self.base.set_argument_parser(self, parsed_args)
v_actions = ValidationActions(parsed_args.validation_dir)
return v_actions.group_information(
constants.VALIDATION_GROUPS_INFO,
validation_config=self.base.config)

View File

@@ -68,10 +68,10 @@ class Group(object):
return self.data
@property
def get_formated_group(self):
"""Get a formated content for output display
def get_formated_groups(self):
"""Get a formated list of groups for output display
:return:
:return: information about parsed groups
:rtype: `list` of `tuples`
:Example:

View File

@@ -29,7 +29,8 @@ class TestListHistory(BaseCommand):
self.cmd = history.ListHistory(self.app, None)
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'show_history')
'show_history',
autospec=True)
def test_list_history(self, mock_history):
arglist = ['--validation-log-dir', '/foo/log/dir']
verifylist = [('validation_log_dir', '/foo/log/dir')]
@@ -80,7 +81,8 @@ class TestGetHistory(BaseCommand):
@mock.patch('validations_libs.validation_logs.ValidationLogs.'
'get_logfile_content_by_uuid',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST)
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST,
autospec=True)
def test_get_history(self, mock_logs):
arglist = ['123']
verifylist = [('uuid', '123')]
@@ -91,7 +93,8 @@ class TestGetHistory(BaseCommand):
@mock.patch('validations_libs.validation_logs.ValidationLogs.'
'get_logfile_content_by_uuid',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST)
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST,
autospec=True)
def test_get_history_from_log_dir(self, mock_logs):
arglist = ['123', '--validation-log-dir', '/foo/log/dir']
verifylist = [('uuid', '123'), ('validation_log_dir', '/foo/log/dir')]
@@ -102,7 +105,8 @@ class TestGetHistory(BaseCommand):
@mock.patch('validations_libs.validation_logs.ValidationLogs.'
'get_logfile_content_by_uuid',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST)
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST,
autospec=True)
def test_get_history_full_arg(self, mock_logs):
arglist = ['123', '--full']
verifylist = [('uuid', '123'), ('full', True)]

View File

@@ -30,7 +30,8 @@ class TestList(BaseCommand):
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'list_validations',
return_value=fakes.VALIDATIONS_LIST)
return_value=fakes.VALIDATIONS_LIST,
autospec=True)
def test_list_validations(self, mock_list):
arglist = ['--validation-dir', 'foo']
verifylist = [('validation_dir', 'foo')]
@@ -59,7 +60,8 @@ class TestList(BaseCommand):
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'list_validations',
return_value=[])
return_value=[],
autospec=True)
def test_list_validations_empty(self, mock_list):
arglist = ['--validation-dir', 'foo']
verifylist = [('validation_dir', 'foo')]
@@ -69,7 +71,8 @@ class TestList(BaseCommand):
self.assertEqual(result, [])
@mock.patch('validations_libs.utils.parse_all_validations_on_disk',
return_value=fakes.VALIDATIONS_LIST_GROUP)
return_value=fakes.VALIDATIONS_LIST_GROUP,
autospec=True)
def test_list_validations_group(self, mock_list):
arglist = ['--validation-dir', 'foo', '--group', 'prep']
verifylist = [('validation_dir', 'foo'),
@@ -82,7 +85,8 @@ class TestList(BaseCommand):
self.assertEqual(result, val_list)
@mock.patch('validations_libs.utils.parse_all_validations_on_disk',
return_value=fakes.VALIDATIONS_LIST_GROUP)
return_value=fakes.VALIDATIONS_LIST_GROUP,
autospec=True)
def test_list_validations_by_category(self, mock_list):
arglist = ['--validation-dir', 'foo', '--category', 'networking']
verifylist = [('validation_dir', 'foo'),
@@ -95,7 +99,8 @@ class TestList(BaseCommand):
self.assertEqual(result, val_list)
@mock.patch('validations_libs.utils.parse_all_validations_on_disk',
return_value=fakes.VALIDATIONS_LIST_GROUP)
return_value=fakes.VALIDATIONS_LIST_GROUP,
autospec=True)
def test_list_validations_by_product(self, mock_list):
arglist = ['--validation-dir', 'foo', '--product', 'product1']
verifylist = [('validation_dir', 'foo'),

View File

@@ -32,7 +32,8 @@ class TestRun(BaseCommand):
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=None)
return_value=None,
autospec=True)
def test_run_command_return_none(self, mock_run):
args = self._set_args(['--validation', 'foo'])
verifylist = [('validation_name', ['foo'])]
@@ -43,7 +44,8 @@ class TestRun(BaseCommand):
@mock.patch('validations_libs.cli.common.open')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
def test_run_command_success(self, mock_run, mock_open):
args = self._set_args(['--validation', 'foo'])
verifylist = [('validation_name', ['foo'])]
@@ -65,10 +67,12 @@ class TestRun(BaseCommand):
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_vars(self, mock_config, mock_run,
mock_user, mock_print, mock_log_dir):
def test_run_command_extra_vars(self, mock_config,
mock_run, mock_user,
mock_print, mock_log_dir):
run_called_args = {
'inventory': 'localhost',
'limit_hosts': None,
@@ -83,7 +87,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -95,7 +98,9 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('validations_libs.cli.common.print_dict')
@@ -103,10 +108,11 @@ class TestRun(BaseCommand):
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_vars_twice(self, mock_config,
mock_run, mock_user, mock_print,
def test_run_command_extra_vars_twice(self, mock_config, mock_run,
mock_user, mock_print,
mock_log_dir):
run_called_args = {
'inventory': 'localhost',
@@ -122,7 +128,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -135,7 +140,9 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
def test_run_command_exclusive_vars(self):
arglist = ['--validation', 'foo',
@@ -154,7 +161,8 @@ class TestRun(BaseCommand):
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_vars_file(self, mock_config, mock_run,
mock_user, mock_open,
@@ -174,7 +182,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -186,14 +193,17 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_env_vars(self, mock_config, mock_run,
mock_user, mock_log_dir):
@@ -211,7 +221,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -223,14 +232,17 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_env_vars_with_custom_callback(self,
mock_config,
@@ -240,7 +252,6 @@ class TestRun(BaseCommand):
run_called_args = {
'inventory': 'localhost',
'limit_hosts': None,
'log_path': mock_log_dir,
'quiet': False,
'group': [],
'category': [],
@@ -264,14 +275,17 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_env_vars_twice(self, mock_config,
mock_run, mock_user,
@@ -290,7 +304,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -303,14 +316,17 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN))
return_value=copy.deepcopy(fakes.FAKE_SUCCESS_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_extra_env_vars_and_extra_vars(self,
mock_config,
@@ -331,7 +347,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -345,7 +360,9 @@ class TestRun(BaseCommand):
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_run.assert_called_with(**run_called_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
def test_run_command_exclusive_wrong_extra_vars(self):
arglist = ['--validation', 'foo',
@@ -356,50 +373,18 @@ class TestRun(BaseCommand):
self.assertRaises(Exception, self.check_parser, self.cmd,
arglist, verifylist)
@mock.patch('validations_libs.utils.find_config_file',
return_value="/etc/validations_foo.cfg")
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=copy.deepcopy(fakes.FAKE_FAILED_RUN))
return_value=copy.deepcopy(fakes.FAKE_FAILED_RUN),
autospec=True)
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_failed_validation(self, mock_config,
mock_run, mock_user, mock_log_dir):
run_called_args = {
'inventory': 'localhost',
'limit_hosts': None,
'group': [],
'category': [],
'product': [],
'extra_vars': None,
'validations_dir': '/usr/share/ansible/validation-playbooks',
'base_dir': '/usr/share/ansible',
'validation_name': ['foo'],
'extra_env_vars': None,
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
arglist = ['--validation', 'foo']
verifylist = [('validation_name', ['foo'])]
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(RuntimeError, self.cmd.take_action, parsed_args)
mock_run.assert_called_with(**run_called_args)
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=[])
@mock.patch('validations_libs.utils.load_config', return_value={})
def test_run_command_no_validation(self, mock_config, mock_run,
mock_user):
def test_run_command_failed_validation(self, mock_config, mock_run, mock_user,
mock_log_dir, mock_config_file):
run_called_args = {
'inventory': 'localhost',
'limit_hosts': None,
@@ -418,8 +403,56 @@ class TestRun(BaseCommand):
'skip_list': None
}
arglist = ['--validation', 'foo']
verifylist = [('validation_name', ['foo'])]
arglist = [
'--validation', 'foo',
'--extra-vars', 'key=value',
'--extra-env-vars', 'key2=value2']
verifylist = [
('validation_name', ['foo']),
('extra_vars', {'key': 'value'}),
('extra_env_vars', {'key2': 'value2'})]
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(RuntimeError, self.cmd.take_action, parsed_args)
call_args = mock_run.mock_calls[0][2]
self.assertDictEqual(call_args, run_called_args)
@mock.patch('validations_libs.constants.VALIDATIONS_LOG_BASEDIR')
@mock.patch('getpass.getuser',
return_value='doe')
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'run_validations',
return_value=[],
autospec=True)
def test_run_command_no_validation(self, mock_run, mock_user, mock_log_dir):
run_called_args = {
'inventory': 'localhost',
'limit_hosts': None,
'group': [],
'category': [],
'product': [],
'extra_vars': {'key': 'value'},
'validations_dir': '/usr/share/ansible/validation-playbooks',
'base_dir': '/usr/share/ansible',
'validation_name': ['foo'],
'extra_env_vars': {'key2': 'value2'},
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'validation_config': {},
'skip_list': None,
'log_path': mock_log_dir}
arglist = [
'--validation', 'foo',
'--extra-vars', 'key=value',
'--extra-env-vars', 'key2=value2']
verifylist = [
('validation_name', ['foo']),
('extra_vars', {'key': 'value'}),
('extra_env_vars', {'key2': 'value2'})]
self._set_args(arglist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -451,7 +484,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -489,7 +521,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': None
}
@@ -526,7 +557,6 @@ class TestRun(BaseCommand):
'python_interpreter': sys.executable,
'quiet': True,
'ssh_user': 'doe',
'log_path': mock_log_dir,
'validation_config': {},
'skip_list': {'key': 'value'}
}

View File

@@ -17,6 +17,8 @@ try:
except ImportError:
import mock
from validations_libs import group
from validations_libs.validation_actions import ValidationActions
from validations_libs.cli import show
from validations_libs.tests import fakes
from validations_libs.tests.cli.fakes import BaseCommand
@@ -48,22 +50,17 @@ class TestShowGroup(BaseCommand):
@mock.patch('yaml.safe_load', return_value=fakes.GROUP)
@mock.patch('six.moves.builtins.open')
def test_show_validations_group_info(self, mock_open, mock_yaml, mock_actions):
arglist = []
mock_info = mock.MagicMock()
mock_info.group_information = mock.MagicMock(return_value='foo')
mock_actions.return_value = mock_info
method_calls = [
mock.call(fakes.FAKE_VALIDATIONS_PATH),
mock.call().group_information(validation_config={})]
arglist = []
parsed_args = self.check_parser(self.cmd, arglist, [])
group_info = self.cmd.take_action(parsed_args)
mock_actions.assert_called_once_with(
validation_path=fakes.FAKE_VALIDATIONS_PATH)
mock_info.group_information.assert_called_once()
self.assertEqual('foo', group_info)
self.cmd.take_action(parsed_args)
mock_actions.assert_called_with(fakes.FAKE_VALIDATIONS_PATH)
class TestShowParameter(BaseCommand):
@@ -73,10 +70,15 @@ class TestShowParameter(BaseCommand):
self.cmd = show.ShowParameter(self.app, None)
@mock.patch('six.moves.builtins.open')
def test_show_validations_parameters_by_group(self, mock_open):
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'show_validations_parameters', autospec=True)
def test_show_validations_parameters_by_group(self, mock_show, mock_open):
arglist = ['--group', 'prep']
verifylist = [('group', ['prep'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_show.assert_called_once()
def test_show_parameter_exclusive_group(self):
arglist = ['--validation', 'foo', '--group', 'bar']
@@ -86,23 +88,32 @@ class TestShowParameter(BaseCommand):
arglist, verifylist)
@mock.patch('six.moves.builtins.open')
def test_show_validations_parameters_by_validations(self, mock_open):
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'show_validations_parameters', autospec=True)
def test_show_validations_parameters_by_validations(self, mock_show, mock_open):
arglist = ['--group', 'prep']
verifylist = [('group', ['prep'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_show.assert_called_once()
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'show_validations_parameters')
'show_validations_parameters', autospec=True)
def test_show_validations_parameters_by_categories(self, mock_show):
arglist = ['--category', 'os']
verifylist = [('category', ['os'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_show.assert_called_once()
@mock.patch('validations_libs.validation_actions.ValidationActions.'
'show_validations_parameters')
'show_validations_parameters', autospec=True)
def test_show_validations_parameters_by_products(self, mock_show):
arglist = ['--product', 'product1']
verifylist = [('product', ['product1'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_show.assert_called_once()

View File

@@ -57,9 +57,9 @@ VALIDATION_LIST_RESULT = (('ID', 'Name', 'Groups', 'Categories', 'Products'),
['product1'])])
GROUPS_LIST = [
('group1', 'Group1 description'),
('group2', 'Group2 description'),
('group3', 'Group3 description'),
'group1',
'group2',
'group3'
]
BAD_VALIDATIONS_LOGS_CONTENTS_LIST = [{

View File

@@ -40,7 +40,7 @@ class TestGroup(TestCase):
def test_get_formated_group(self, mock_open, mock_yaml):
grp = Group('/tmp/foo')
ret = [('no-op', 'noop-foo'), ('post', 'post-foo'), ('pre', 'pre-foo')]
data = grp.get_formated_group
data = grp.get_formated_groups
self.assertEqual(data, ret)
@mock.patch('yaml.safe_load', return_value=fakes.GROUP)

View File

@@ -354,22 +354,6 @@ class TestUtils(TestCase):
result = utils.get_validation_group_name_list('/foo/groups.yaml')
self.assertEqual(result, ['no-op', 'post', 'pre'])
@mock.patch('validations_libs.utils.parse_all_validations_on_disk',
return_value=[fakes.FAKE_METADATA])
@mock.patch('yaml.safe_load', return_value=fakes.GROUP)
@mock.patch('six.moves.builtins.open')
def test_get_validations_details(self, mock_open, mock_load, mock_parse):
result = utils.get_validations_details('foo')
self.assertEqual(result, fakes.FAKE_METADATA)
@mock.patch('six.moves.builtins.open')
def test_get_validations_details_wrong_type(self, mock_open):
validation = ['foo']
self.assertRaises(TypeError,
utils.get_validations_details,
validation=validation)
def test_get_validations_parameters_wrong_validations_data_type(self):
self.assertRaises(TypeError,
utils.get_validations_parameters,

View File

@@ -20,8 +20,6 @@ except ImportError:
import mock
from mock import ANY
import json
from unittest import TestCase
from validations_libs.tests import fakes
@@ -62,8 +60,7 @@ class TestValidationActions(TestCase):
skip_list = {'fake': {'hosts': 'ALL',
'reason': None,
'lp': None
}
}
}}
run = ValidationActions()
run_return = run.run_validations(playbook, inventory,
@@ -124,9 +121,8 @@ class TestValidationActions(TestCase):
}
}
run = ValidationActions()
run = ValidationActions(log_path='/var/log/validations')
run_return = run.run_validations(playbook, inventory,
log_path='/var/log/validations',
validations_dir='/tmp/foo',
skip_list=skip_list,
limit_hosts='!cloud1')
@@ -185,9 +181,8 @@ class TestValidationActions(TestCase):
}
}
run = ValidationActions()
run = ValidationActions(log_path='/var/log/validations')
run_return = run.run_validations(playbook, inventory,
log_path='/var/log/validations',
validations_dir='/tmp/foo',
skip_list=skip_list,
limit_hosts='cloud,cloud1,!cloud2')
@@ -256,9 +251,8 @@ class TestValidationActions(TestCase):
run = ValidationActions()
self.assertRaises(RuntimeError, run.run_validations,
validation_name='fake.yaml',
validations_dir='/tmp/foo'
)
validation_name=['fake'],
validations_dir='/tmp/foo')
@mock.patch('validations_libs.utils.get_validations_playbook')
def test_validation_run_not_all_found(self, mock_validation_play):
@@ -271,7 +265,7 @@ class TestValidationActions(TestCase):
validations_dir='/tmp/foo')
except RuntimeError as runtime_error:
self.assertEqual(
"Validation ['foo'] not found in /tmp/foo.",
"Following validations were not found in '/tmp/foo': foo",
str(runtime_error))
else:
self.fail("Runtime error exception should have been raised")
@@ -435,7 +429,7 @@ class TestValidationActions(TestCase):
@mock.patch('six.moves.builtins.open')
def test_group_information(self, mock_open, mock_yaml, mock_data):
v_actions = ValidationActions()
col, values = v_actions.group_information('512e')
col, values = v_actions.group_information()
self.assertEqual(col, ('Groups', 'Description',
'Number of Validations'))
self.assertEqual(values, [('no-op', 'noop-foo', 1),

View File

@@ -382,36 +382,6 @@ def get_validation_group_name_list(groups_path=None):
return gp.get_groups_keys_list
def get_validations_details(validation):
"""Return information details for a validation
:param validation: Name of the validation
:type validation: `string`
:return: The information of the validation
:rtype: `dict`
:raises: a `TypeError` exception if `validation` is not a string
:Example:
>>> validation = "check-something"
>>> get_validations_details(validation)
{'description': 'Verify that the server has enough something.',
'groups': ['group1', 'group2'],
'categories': ['category1', 'category2'],
'products': ['product1', 'product2'],
'id': 'check-something',
'name': 'Verify the server fits the something requirements'}
"""
if not isinstance(validation, six.string_types):
raise TypeError("The 'validation' argument must be a String")
results = parse_all_validations_on_disk(constants.ANSIBLE_VALIDATION_DIR)
for r in results:
if r['id'] == validation:
return r
return {}
def get_validations_data(
validation,
path=constants.ANSIBLE_VALIDATION_DIR,

View File

@@ -42,10 +42,23 @@ class ValidationActions(object):
"""
def __init__(self, validation_path=constants.ANSIBLE_VALIDATION_DIR):
def __init__(self, validation_path=constants.ANSIBLE_VALIDATION_DIR,
groups_path=constants.VALIDATION_GROUPS_INFO,
log_path=constants.VALIDATIONS_LOG_BASEDIR):
"""
:param groups_path: The absolute path to the validation groups
definition file.
(Defaults to ``constants.VALIDATION_GROUPS_INFO``)
:type groups_path: ``string``
:param log_path: The absolute path of the validations logs directory
(Defaults to ``constants.VALIDATIONS_LOG_BASEDIR``)
:type log_path: ``string``
"""
self.log = logging.getLogger(__name__ + ".ValidationActions")
self.validation_path = validation_path
self.log_path = log_path
self.groups_path = groups_path
def list_validations(self,
groups=None,
@@ -84,7 +97,7 @@ class ValidationActions(object):
| val3 | val_name3 | ['group4'] | ['category3'] | ['product3'] |
+------+-----------+----------------------+---------------+--------------+
:Example:
:example:
>>> path = "/foo/bar"
>>> groups = ['group1']
@@ -130,13 +143,16 @@ class ValidationActions(object):
return (column_names, return_values)
def show_validations(self, validation,
log_path=constants.VALIDATIONS_LOG_BASEDIR,
log_path=None,
validation_config=None):
"""Display detailed information about a Validation
:param validation: The name of the validation
:type validation: `string`
:param log_path: The absolute path of the validations logs
:param log_path: The absolute path of the validations logs.
The 'log_path' argument is deprecated and
will be removed in the next release.
Use the 'log_path' argument of the init method.
:type log_path: `string`
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
@@ -146,7 +162,7 @@ class ValidationActions(object):
:return: The detailed information for a validation
:rtype: `dict`
:Example:
:example:
>>> path = "/foo/bar"
>>> validation = 'foo'
@@ -166,7 +182,14 @@ class ValidationActions(object):
"""
self.log = logging.getLogger(__name__ + ".show_validations")
# Get validation data:
vlog = ValidationLogs(log_path)
if log_path:
self.log.warning((
"The 'log_path' argument is deprecated and"
" will be removed in the next release. "
"Use the 'log_path' argument of the init method."))
vlog = ValidationLogs(log_path)
else:
vlog = ValidationLogs(self.log_path)
data = v_utils.get_validations_data(
validation,
self.validation_path,
@@ -185,75 +208,91 @@ class ValidationActions(object):
data.update(data_format)
return data
def _skip_hosts(self, skip_list, playbook, limit_hosts=None):
def _skip_hosts(self, skip_list, limit_hosts=None):
"""Check Ansible Hosts and return an updated limit_hosts
:param skip_list: The list of the validation to skip
:type validation_name: ``dict``
:param playbook: The name of the playbook
:type base_dir: ``string``
:param skip_list: list of hosts to skip with reasons why
:type skip_list: `dict`
:param limit_hosts: Limit the execution to the hosts.
:type limit_hosts: ``string``
:return the limit hosts according the skip_list or None if the
validation should be skipped on ALL hosts.
:example
limit_hosts = 'cloud1,cloud2'
skip_list = {'xyz': {'hosts': 'cloud1',
'reason': None,
'lp': None}
}
>>> _skip_hosts(skip_list, playbook, limit_hosts='cloud1,cloud2')
'cloud2,!cloud1'
:example:
>>> v_actions = ValidationActions()
>>> limit_hosts = 'cloud1,cloud2'
>>> skip_list = {
... 'xyz': {
... 'hosts': 'cloud1',
... 'reason': None,
... 'lp': None}}
>>> v_actions._skip_hosts(skip_list, validation, limit_hosts='cloud1,cloud2')
'!cloud1,cloud2'
"""
hosts = skip_list[playbook].get('hosts', 'all')
hosts = skip_list.get('hosts', 'all')
if hosts.lower() == 'all':
return None
else:
_hosts = ['!{}'.format(hosts)]
if limit_hosts:
# check if skipped hosts is already in limit host
_hosts.extend([limit for limit in limit_hosts.split(',')
if hosts not in limit])
return ','.join(_hosts)
_hosts = ['!{}'.format(hosts)]
if limit_hosts:
# check if skipped hosts is already in limit host
_hosts.extend([limit for limit in limit_hosts.split(',')
if hosts not in limit])
return ','.join(_hosts)
def _skip_playbook(self, skip_list, playbook, limit_hosts=None):
"""Check if playbook is in the ski plist
:param skip_list: The list of the validation to skip
:type validation_name: ``dict``
"""Check if playbook is in the skiplist
:param skip_list: Dictionary of validations to skip.
:type skip_list: `dictionary`
:param playbook: The name of the playbook
:type base_dir: ``string``
:type playbook: `string`
:param limit_hosts: Limit the execution to the hosts.
:type limit_hosts: ``string``
:type limit_hosts: `string`
:return a tuple of playbook and hosts
:example
skip_list = {'xyz': {'hosts': 'cloud1',
'reason': None,
'lp': None}
}
If playbook not in skip list:
>>> _skip_playbook(skip_list, 'foo', None)
('foo', None)
:rtype: `tuple`
If playbook in the skip list, but with restriction only on
host cloud1:
>>> _skip_playbook(skip_list, 'xyz', None)
('xyz', '!cloud1')
:example:
If playbook in the skip list, and should be skip on ALL hosts:
skip_list = {'xyz': {'hosts': 'ALL',
'reason': None,
'lp': None}
}
>>> _skip_playbook(skip_list, 'xyz', None)
(None, None)
>>> skip_list = {
... 'xyz': {
... 'hosts': 'cloud1',
... 'reason': None,
... 'lp': None}}
If playbook is not in skip list:
>>> v_actions = ValidationActions()
>>> v_actions._skip_playbook(skip_list, 'foo', None)
('foo', None)
If playbook is in the skip list, but with restriction only on
host cloud1:
>>> v_actions = ValidationActions()
>>> v_actions._skip_playbook(skip_list, 'xyz', None)
('xyz', '!cloud1')
If playbook in the skip list, and should be skip on ALL hosts:
>>> skip_list = {
... 'xyz': {
... 'hosts': 'ALL',
... 'reason': None,
... 'lp': None}}
>>> v_actions = ValidationActions()
>>> v_actions._skip_playbook(skip_list, 'xyz', None)
(None, None)
"""
if skip_list:
if playbook in skip_list.keys():
_hosts = self._skip_hosts(skip_list, playbook,
limit_hosts)
if playbook in skip_list:
self.log.info((
"Validation '{}' skipped on following hosts '{}' "
"with reason: '{}'.").format(
playbook,
skip_list[playbook].get('hosts', 'All'),
skip_list[playbook].get('reason', None)))
_hosts = self._skip_hosts(
skip_list[playbook],
limit_hosts)
if _hosts:
return playbook, _hosts
else:
@@ -266,6 +305,14 @@ class ValidationActions(object):
with the last time the file was modified serving as a key.
Finally we take the last `n` logs, where `n` == `history_limit`
and return them while discarding the time information.
:param logs: List of validation log file paths
:type logs: `list`
:param history_limit: number of entries to display
:type history_limit: `int`
:return: List of time-modified, path tuples of length =< history_limit
:rtype: `list`
"""
history_limit = min(history_limit, len(logs))
@@ -280,17 +327,16 @@ class ValidationActions(object):
group=None, category=None, product=None,
extra_vars=None, validations_dir=None,
extra_env_vars=None, ansible_cfg=None, quiet=True,
workdir=None, limit_hosts=None, run_async=False,
limit_hosts=None, run_async=False,
base_dir=constants.DEFAULT_VALIDATIONS_BASEDIR,
log_path=constants.VALIDATIONS_LOG_BASEDIR,
python_interpreter=None, skip_list=None,
log_path=None, python_interpreter=None, skip_list=None,
callback_whitelist=None,
output_callback='validation_stdout', ssh_user=None,
validation_config=None):
"""Run one or multiple validations by name(s), by group(s) or by
product(s)
:param validation_name: A list of validation names
:param validation_name: A list of validation names.
:type validation_name: ``list``
:param inventory: Either proper inventory file, or a comma-separated
list. (Defaults to ``localhost``)
@@ -315,8 +361,6 @@ class ValidationActions(object):
:type ansible_cfg: ``string``
:param quiet: Disable all output (Defaults to ``True``)
:type quiet: ``Boolean``
:param workdir: Location of the working directory
:type workdir: ``string``
:param limit_hosts: Limit the execution to the hosts.
:type limit_hosts: ``string``
:param run_async: Enable the Ansible asynchronous mode
@@ -329,6 +373,9 @@ class ValidationActions(object):
:param log_path: The absolute path of the validations logs directory
(Defaults to
``constants.VALIDATIONS_LOG_BASEDIR``)
The absolute path of the validations logs directory.
The 'log_path' argument is deprecated and will be removed in the next release.
Use the 'log_path' argument of the init method.
:type log_path: ``string``
:param python_interpreter: Path to the Python interpreter to be
used for module execution on remote targets,
@@ -359,7 +406,7 @@ class ValidationActions(object):
Status, Status_by_Host, UUID and Unreachable_Hosts)
:rtype: ``list``
:Example:
:example:
>>> path = "/u/s/a"
>>> validation_name = ['foo', 'bar']
@@ -402,27 +449,45 @@ class ValidationActions(object):
for val in validations:
playbooks.append("{path}/{id}.yaml".format(**val))
elif validation_name:
self.log.debug(
"Getting the {} validation.".format(
validation_name))
playbooks = v_utils.get_validations_playbook(
validations_dir,
validation_name,
validation_config=validation_config)
if not playbooks or len(validation_name) != len(playbooks):
p = []
found_playbooks = []
for play in playbooks:
p.append(os.path.basename(os.path.splitext(play)[0]))
found_playbooks.append(
os.path.basename(os.path.splitext(play)[0]))
unknown_validation = list(set(validation_name) - set(p))
unknown_validations = list(
set(validation_name) - set(found_playbooks))
msg = "Validation {} not found in {}.".format(
unknown_validation, validations_dir)
msg = (
"Following validations were not found in '{}': {}"
).format(validations_dir, ', '.join(unknown_validations))
raise RuntimeError(msg)
else:
raise RuntimeError("No validations found")
if log_path:
self.log.warning((
"The 'log_path' argument is deprecated and"
" will be removed in the next release. "
"Use the 'log_path' argument of the init method."))
log_path = v_utils.create_log_dir(log_path)
else:
log_path = v_utils.create_log_dir(self.log_path)
self.log.debug((
'Running the validations with Ansible.\n'
'Gathered playbooks:\n -{}').format(
'\n -'.join(playbooks)))
log_path = v_utils.create_log_dir(log_path)
self.log.debug('Running the validations with Ansible')
results = []
for playbook in playbooks:
# Check if playbook should be skipped and on which hosts
@@ -485,17 +550,6 @@ class ValidationActions(object):
'validations': _playbook.split('.')[0],
'UUID': validation_uuid,
})
# Print hosts which has been skipped:
if _hosts:
skipped_hosts = [h.replace('!', '')
for h in _hosts.split(',') if '!' in h]
if skipped_hosts:
msg = ("Validation {} has been skipped "
"on hosts: {}").format(_play,
','.join(skipped_hosts))
self.log.info(msg)
else:
self.log.info('Skipping Validations: {}'.format(playbook))
if run_async:
return results
@@ -504,7 +558,7 @@ class ValidationActions(object):
vlog = ValidationLogs(log_path)
return vlog.get_results(uuid)
def group_information(self, groups, validation_config=None):
def group_information(self, groups=None, validation_config=None):
"""Get Information about Validation Groups
This is used to print table from python ``Tuple`` with ``PrettyTable``.
@@ -519,7 +573,10 @@ class ValidationActions(object):
| group3 | Description of group3 | 1 |
+----------+--------------------------+-----------------------+
:param groups: The absolute path of the groups.yaml file
:param groups: The absolute path of the groups.yaml file.
The argument is deprecated and will be removed
in the next release.
Use the 'groups_path' argument of the init method.
:type groups: ``string``
:param validation_config: A dictionary of configuration for Validation
loaded from an validation.cfg file.
@@ -529,19 +586,27 @@ class ValidationActions(object):
the numbers of validation belonging to them.
:rtype: ``tuple``
:Example:
:example:
>>> groups = "/foo/bar/groups.yaml"
>>> actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
>>> group_info = actions.group_information(groups)
>>> actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR, groups)
>>> group_info = actions.group_information()
>>> print(group_info)
(('Groups', 'Desciption', 'Number of Validations'),
[('group1', 'Description of group1', 3),
('group2', 'Description of group2', 12),
('group3', 'Description of group3', 1)])
"""
val_gp = Group(groups)
group_definitions = val_gp.get_formated_group
if groups:
self.log.warning((
"The 'groups' argument is deprecated and"
" will be removed in the next release. "
"Use the 'groups_path' argument of the init method."))
val_group = Group(groups)
else:
val_group = Group(self.groups_path)
group_definitions = val_group.get_formated_groups
group_info = []
@@ -599,7 +664,7 @@ class ValidationActions(object):
:return: A JSON or a YAML dump (By default, JSON).
if `download_file` is used, a file containing only the
parameters will be created in the file system.
:exemple:
:example:
>>> validations = ['check-cpu', 'check-ram']
>>> groups = None
@@ -620,26 +685,8 @@ class ValidationActions(object):
}
}
}
"""
if not validations:
validations = []
elif not isinstance(validations, list):
raise TypeError("The 'validations' argument must be a List")
if not groups:
groups = []
elif not isinstance(groups, list):
raise TypeError("The 'groups' argument must be a List")
if not categories:
categories = []
elif not isinstance(categories, list):
raise TypeError("The 'categories' argument must be a List")
if not products:
products = []
elif not isinstance(products, list):
raise TypeError("The 'products' argument must be a List")
supported_format = ['json', 'yaml']
@@ -667,7 +714,7 @@ class ValidationActions(object):
params_only = {}
try:
with open(download_file, 'w') as parameters_file:
for val_name in params.keys():
for val_name in params:
params_only.update(params[val_name].get('parameters'))
if output_format == 'json':
@@ -698,7 +745,7 @@ class ValidationActions(object):
return params
def show_history(self, validation_ids=None, extension='json',
log_path=constants.VALIDATIONS_LOG_BASEDIR,
log_path=None,
history_limit=None):
"""Return validation executions history
@@ -706,7 +753,10 @@ class ValidationActions(object):
:type validation_ids: a list of strings
:param extension: The log file extension (Defaults to ``json``)
:type extension: ``string``
:param log_path: The absolute path of the validations logs directory
:param log_path: The absolute path of the validations logs directory.
The 'log_path' argument is deprecated and will
be removed in the next release.
Use the 'log_path' argument of the init method.
:type log_path: ``string``
:param history_limit: The number of most recent history logs
to be displayed.
@@ -716,7 +766,7 @@ class ValidationActions(object):
history
:rtype: ``tuple``
:Example:
:example:
>>> actions = ValidationActions(constants.ANSIBLE_VALIDATION_DIR)
>>> print(actions.show_history())
@@ -754,8 +804,17 @@ class ValidationActions(object):
'PASSED',
'2020-11-13T11:47:50.279662Z',
'0:00:02.237')])
"""
vlogs = ValidationLogs(log_path)
if log_path:
self.log.warning((
"The 'log_path' argument is deprecated and"
" will be removed in the next release. "
"Use the 'log_path' argument of the init method."))
vlogs = ValidationLogs(log_path)
else:
vlogs = ValidationLogs(self.log_path)
if validation_ids:
if not isinstance(validation_ids, list):
validation_ids = [validation_ids]
@@ -794,13 +853,16 @@ class ValidationActions(object):
:type uuid: ``string``
:param status: The status of the execution (Defaults to FAILED)
:type status: ``string``
:param log_path: The absolute path of the validations logs directory
:param log_path: The absolute path of the validations logs directory.
The 'log_path' argument is deprecated and will
be removed in the next release.
Use the 'log_path' argument of the init method.
:type log_path: ``string``
:return: A list of validations execution with details and by status
:rtype: ``tuple``
:Example:
:example:
>>> actions = ValidationActions(validation_path='/foo/bar')
>>> status = actions.get_status(validation_id='foo'))
@@ -831,7 +893,15 @@ class ValidationActions(object):
'failed': True,
'msg': 'Debug mode is not disabled.'})])
"""
vlogs = ValidationLogs(log_path)
if log_path:
self.log.warning((
"The 'log_path' argument is deprecated and"
" will be removed in the next release. "
"Use the 'log_path' argument of the init method."))
vlogs = ValidationLogs(log_path)
else:
vlogs = ValidationLogs(self.log_path)
if validation_id:
logs = vlogs.get_logfile_by_validation(validation_id)
elif uuid: