diff --git a/validations_libs/tests/fakes.py b/validations_libs/tests/fakes.py index 05c5232b..bc836eb8 100644 --- a/validations_libs/tests/fakes.py +++ b/validations_libs/tests/fakes.py @@ -159,7 +159,10 @@ VALIDATIONS_LOGS_CONTENTS_LIST = [{ 'unreachable': 0 } }, - 'validation_output': [] + 'validation_output': [{'task': { + 'hosts': {u'foo': {}}, + 'name': u'Check if iscsi.service is enabled', + 'status': u'FAILED'}}] }] VALIDATIONS_DATA = {'Description': 'My Validation One Description', @@ -169,7 +172,7 @@ VALIDATIONS_DATA = {'Description': 'My Validation One Description', 'parameters': {}} VALIDATIONS_STATS = {'Last execution date': '2019-11-25 13:40:14', - 'Number of execution': 'Total: 1, Passed: 1, Failed: 0'} + 'Number of execution': 'Total: 1, Passed: 0, Failed: 1'} FAKE_PLAYBOOK = [{'hosts': 'undercloud', 'roles': ['advanced_format_512e_support'], diff --git a/validations_libs/tests/test_validation_actions.py b/validations_libs/tests/test_validation_actions.py index 44ecf2bb..556b074e 100644 --- a/validations_libs/tests/test_validation_actions.py +++ b/validations_libs/tests/test_validation_actions.py @@ -154,11 +154,20 @@ class TestValidationActions(TestCase): 'Description': 'foo', 'Groups': ['prep', 'pre-deployment'], 'ID': '512e'} data.update({'Last execution date': '2019-11-25 13:40:14', - 'Number of execution': 'Total: 1, Passed: 1, Failed: 0'}) + 'Number of execution': 'Total: 1, Passed: 0, Failed: 1'}) validations_show = ValidationActions() out = validations_show.show_validations('512e') self.assertEqual(out, data) + @mock.patch('os.path.exists', return_value=False) + def test_validation_show_not_found(self, mock_exists): + validations_show = ValidationActions() + self.assertRaises( + RuntimeError, + validations_show.show_validations, + '512e' + ) + @mock.patch('validations_libs.utils.parse_all_validations_on_disk', return_value=fakes.VALIDATIONS_LIST) @mock.patch('yaml.safe_load', return_value=fakes.GROUP) @@ -193,11 +202,12 @@ class TestValidationActions(TestCase): @mock.patch('six.moves.builtins.open') def test_show_history(self, mock_open, mock_load, mock_get_log): v_actions = ValidationActions() - col, values = v_actions.show_history('foo') + col, values = v_actions.show_history('512e') self.assertEqual(col, ('UUID', 'Validations', 'Status', 'Execution at', 'Duration')) - self.assertEqual(values, [('123', 'foo', 'PASSED', + self.assertEqual(values, [('008886df-d297-1eaa-2a74-000000000008', + '512e', 'PASSED', '2019-11-25T13:40:14.404623Z', '0:00:03.753')]) @@ -213,6 +223,24 @@ class TestValidationActions(TestCase): self.assertEqual(col, ('UUID', 'Validations', 'Status', 'Execution at', 'Duration')) - self.assertEqual(values, [('123', 'foo', 'PASSED', + self.assertEqual(values, [('008886df-d297-1eaa-2a74-000000000008', + '512e', 'PASSED', '2019-11-25T13:40:14.404623Z', '0:00:03.753')]) + + @mock.patch('validations_libs.validation_logs.ValidationLogs.' + 'get_logfile_by_validation', + return_value=['/tmp/123_foo_2020-03-30T13:17:22.447857Z.json']) + @mock.patch('json.load', + return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]) + @mock.patch('six.moves.builtins.open') + def test_get_status(self, mock_open, mock_load, mock_get_log): + v_actions = ValidationActions() + col, values = v_actions.get_status('foo') + self.assertEqual(col, ['name', 'host', 'status', 'task_data']) + self.assertEqual(values, [('Check if iscsi.service is enabled', 'foo', + 'FAILED', {})]) + + def test_get_status_no_param(self): + v_actions = ValidationActions() + self.assertRaises(RuntimeError, v_actions.get_status) diff --git a/validations_libs/tests/test_validation_log.py b/validations_libs/tests/test_validation_log.py index ee629003..7aeb981a 100644 --- a/validations_libs/tests/test_validation_log.py +++ b/validations_libs/tests/test_validation_log.py @@ -224,3 +224,26 @@ class TestValidationLog(TestCase): val = ValidationLog( logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json') self.assertTrue(val.is_valid_format()) + + @mock.patch('json.load', + return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]) + @mock.patch('six.moves.builtins.open') + def test_get_plays(self, mock_open, mock_json): + val = ValidationLog( + logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json') + plays = val.get_plays + self.assertEquals( + plays, + [fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]['plays'][0]['play']]) + + @mock.patch('json.load', + return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]) + @mock.patch('six.moves.builtins.open') + def test_get_tasks_data(self, mock_open, mock_json): + val = ValidationLog( + logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json') + tasks_data = val.get_tasks_data + self.assertEquals( + tasks_data, + [fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0] + ['validation_output'][0]['task']]) diff --git a/validations_libs/validation_actions.py b/validations_libs/validation_actions.py index 3f177e7b..b0366956 100644 --- a/validations_libs/validation_actions.py +++ b/validations_libs/validation_actions.py @@ -53,6 +53,11 @@ class ValidationActions(object): # Get validation data: vlog = ValidationLogs(log_path) data = v_utils.get_validations_data(validation, self.validation_path) + if not data: + msg = "Validation {} not found in the path: {}".format( + validation, + self.validation_path) + raise RuntimeError(msg) logfiles = vlog.get_all_logfiles_content() format = vlog.get_validations_stats(logfiles) data.update(format) @@ -148,9 +153,10 @@ class ValidationActions(object): f.write(params) return params - def show_history(self, validation_id=None, extension='json'): + def show_history(self, validation_id=None, extension='json', + log_path=constants.VALIDATIONS_LOG_BASEDIR): """Return validations history""" - vlogs = ValidationLogs(self.validation_path) + vlogs = ValidationLogs(log_path) logs = (vlogs.get_logfile_by_validation(validation_id) if validation_id else vlogs.get_all_logfiles(extension)) @@ -158,11 +164,35 @@ class ValidationActions(object): column_name = ('UUID', 'Validations', 'Status', 'Execution at', 'Duration') - for log in logs: vlog = ValidationLog(logfile=log) if vlog.is_valid_format(): - values.append((vlog.get_uuid, vlog.validation_id, - vlog.get_status, vlog.get_start_time, - vlog.get_duration)) + for play in vlog.get_plays: + values.append((play['id'], play['validation_id'], + vlog.get_status, + play['duration'].get('start'), + play['duration'].get('time_elapsed'))) + return (column_name, values) + + def get_status(self, validation_id=None, uuid=None, status='FAILED', + log_path=constants.VALIDATIONS_LOG_BASEDIR): + """Return validations execution details by status""" + vlogs = ValidationLogs(log_path) + if validation_id: + logs = vlogs.get_logfile_by_validation(validation_id) + elif uuid: + logs = vlogs.get_logfile_by_uuid(uuid) + else: + raise RuntimeError("You need to provide a validation_id or a uuid") + + values = [] + column_name = ['name', 'host', 'status', 'task_data'] + for log in logs: + vlog = ValidationLog(logfile=log) + if vlog.is_valid_format(): + for task in vlog.get_tasks_data: + if task['status'] == status: + for host in task['hosts']: + values.append((task['name'], host, task['status'], + task['hosts'][host])) return (column_name, values) diff --git a/validations_libs/validation_logs.py b/validations_libs/validation_logs.py index 23a33226..f9601c72 100644 --- a/validations_libs/validation_logs.py +++ b/validations_libs/validation_logs.py @@ -150,6 +150,16 @@ class ValidationLog(object): play in self.content['plays']] return ', '.join(filter(None, start_time)) + @property + def get_plays(self): + """Return a list of Playbook data""" + return [play['play'] for play in self.content['plays']] + + @property + def get_tasks_data(self): + """Return a list of task from validation output""" + return [output['task'] for output in self.content['validation_output']] + class ValidationLogs(object):