diff --git a/validations_libs/tests/fakes.py b/validations_libs/tests/fakes.py index 514becc6..fe5d9d34 100644 --- a/validations_libs/tests/fakes.py +++ b/validations_libs/tests/fakes.py @@ -111,7 +111,24 @@ FAILED_VALIDATIONS_LOGS_CONTENTS_LIST = [{ 'unreachable': 0 } }, - 'validation_output': [] + 'validation_output': [ + { + "task": { + "hosts": { + "localhost": { + "_ansible_no_log": False, + "action": "fail", + "changed": False, + "failed": True, + "failed_when_result": True, + "msg": "Fake Failed" + } + }, + "name": "Verify Fake requirements", + "status": "FAILED" + } + } + ] }] VALIDATIONS_LOGS_CONTENTS_LIST = [{ diff --git a/validations_libs/tests/test_validation_logs.py b/validations_libs/tests/test_validation_logs.py index d9e467de..29ed3245 100644 --- a/validations_libs/tests/test_validation_logs.py +++ b/validations_libs/tests/test_validation_logs.py @@ -199,6 +199,7 @@ class TestValidationLogs(TestCase): self.assertEqual(content, [{ 'UUID': '123', 'Validations': 'foo', + 'Reasons': '', 'Status': 'PASSED', 'Status_by_Host': 'undercloud,PASSED', 'Host_Group': 'undercloud', @@ -206,6 +207,28 @@ class TestValidationLogs(TestCase): 'Duration': '0:00:03.753', 'Validations': 'foo'}]) + @mock.patch('validations_libs.validation_logs.ValidationLogs.' + 'get_logfile_by_uuid_validation_id') + @mock.patch('json.load', + return_value=fakes.FAILED_VALIDATIONS_LOGS_CONTENTS_LIST[0]) + @mock.patch('six.moves.builtins.open') + def test_get_failed_results(self, mock_open, mock_json, + mock_get_validation): + mock_get_validation.return_value = \ + ['/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'] + vlogs = ValidationLogs('/tmp/foo') + content = vlogs.get_results(uuid='123', validation_id='foo') + self.assertEqual(content, [{ + 'UUID': '123', + 'Validations': 'foo', + 'Status': 'FAILED', + 'Status_by_Host': 'undercloud,FAILED', + 'Host_Group': 'undercloud', + 'Unreachable_Hosts': '', + 'Duration': '', + 'Validations': 'foo', + 'Reasons': "localhost: Fake Failed\n"}]) + def test_get_results_none(self): vlogs = ValidationLogs('/tmp/foo') self.assertRaises(RuntimeError, vlogs.get_results, uuid=None) @@ -224,6 +247,7 @@ class TestValidationLogs(TestCase): { 'UUID': '123', 'Validations': 'foo', + 'Reasons': '', 'Status': 'PASSED', 'Status_by_Host': 'undercloud,PASSED', 'Host_Group': 'undercloud', @@ -233,6 +257,7 @@ class TestValidationLogs(TestCase): { 'UUID': '123', 'Validations': 'foo', + 'Reasons': '', 'Status': 'PASSED', 'Status_by_Host': 'undercloud,PASSED', 'Host_Group': 'undercloud', diff --git a/validations_libs/validation_logs.py b/validations_libs/validation_logs.py index 8cdcfcb1..fda5b762 100644 --- a/validations_libs/validation_logs.py +++ b/validations_libs/validation_logs.py @@ -286,6 +286,26 @@ class ValidationLog: play in self.content['plays']] return ', '.join(filter(None, duration)) + @property + def get_reason(self): + """Return validation reason + + :return: hostname: reason of the failure + :rtype: ``string`` + """ + reason = [] + if self.get_status == 'FAILED': + for v_output in self.content['validation_output']: + for h in v_output['task']['hosts']: + msg = v_output['task']['hosts'][h].get('msg', + 'Unknown') + msg = msg[:50] + '\n' + msg[50:] + reason.append('{}: {}'.format(h, msg)) + if not self.content['validation_output']: + if self.get_unreachable_hosts: + reason.append('Unreachable') + return ',\n'.join(reason) + @property def get_start_time(self): """Return Ansible start time @@ -538,5 +558,6 @@ class ValidationLogs(object): data['Status_by_Host'] = vlog.get_hosts_status data['Unreachable_Hosts'] = vlog.get_unreachable_hosts data['Duration'] = vlog.get_duration + data['Reasons'] = vlog.get_reason res.append(data) return res