From 26ea5efffce9a80ab7a9df78faac0bd61b3db38e Mon Sep 17 00:00:00 2001 From: Jiri Podivin Date: Fri, 9 Jul 2021 11:03:37 +0200 Subject: [PATCH] callback adjustment for no hosts matched The validation_json callback will now consider the no hosts being matched case as a validation failure. And record the result as such. To prevent unnecessary confusion, the host indicated in the created log file will be designated as 'No host matched'. And will be displayed as such on the VF stdout. Basic test coverage included. Signed-off-by: Jiri Podivin Change-Id: I613203698b726941162239185de77949bf30254c --- .../callback_plugins/validation_json.py | 35 +++++++++ .../callback_plugins/test_validation_json.py | 78 ++++++++++++++++++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/validations_common/callback_plugins/validation_json.py b/validations_common/callback_plugins/validation_json.py index bd2d9c3..5b1c2d6 100644 --- a/validations_common/callback_plugins/validation_json.py +++ b/validations_common/callback_plugins/validation_json.py @@ -187,6 +187,41 @@ class CallbackModule(CallbackBase): result['play']['duration']['end'] = end_time result['play']['duration']['time_elapsed'] = time_elapsed + def v2_playbook_on_no_hosts_matched(self): + no_match_result = self._val_task('No tasks run') + no_match_result['task']['status'] = "FAILED" + no_match_result['task']['info'] = ( + "None of the hosts specified" + " were matched in the inventory file") + + output = { + 'plays': self.results, + 'stats': { + 'No host matched':{ + 'changed': 0, + 'failures': 1, + 'ignored': 0, + 'ok': 0, + 'rescued': 0, + 'skipped': 0, + 'unreachable': 0}}, + 'validation_output': self.simple_results + [no_match_result] + } + + log_file = "{}/{}_{}_{}.json".format( + VALIDATIONS_LOG_DIR, + os.getenv( + 'ANSIBLE_UUID', + self.results[0].get('play').get('id')), + self.env['playbook_name'], + self.current_time) + + with open(log_file, 'w') as js: + js.write(json.dumps(output, + cls=AnsibleJSONEncoder, + indent=4, + sort_keys=True)) + def __getattribute__(self, name): """Return ``_record_task_result`` partial with a dict containing skipped/failed if necessary diff --git a/validations_common/tests/callback_plugins/test_validation_json.py b/validations_common/tests/callback_plugins/test_validation_json.py index ccc2308..882e6f1 100644 --- a/validations_common/tests/callback_plugins/test_validation_json.py +++ b/validations_common/tests/callback_plugins/test_validation_json.py @@ -290,7 +290,10 @@ class TestValidationJson(base.TestCase): mock_new_task.assert_called_once_with(callback, mock_task) self.assertIn({'task': {'host': 'foo'}}, callback.results[0]['tasks']) - @mock.patch('json.dumps', return_value='json_dump_foo') + @mock.patch( + 'json.dumps', + return_value='json_dump_foo', + autospec=True) @mock.patch( 'validations_common.callback_plugins.validation_json.open', create=True) @@ -349,6 +352,79 @@ class TestValidationJson(base.TestCase): mock_json_dumps.assert_called_once_with(output, **kwargs) mock_write.assert_called_once_with('json_dump_foo') + @mock.patch( + 'json.dumps', + return_value='json_dump_foo', + autospec=True) + @mock.patch( + 'validations_common.callback_plugins.validation_json.open', + create=True) + def test_v2_playbook_on_no_hosts_matched(self, mock_open, + mock_json_dumps): + + results = [ + { + 'play': { + 'id': 'fizz' + } + } + ] + validation_task = { + 'task': { + 'name': 'No tasks run', + 'hosts': {}}} + + validation_json.VALIDATIONS_LOG_DIR = '/home/foo/validations' + + callback = validation_json.CallbackModule() + dummy_stats = AggregateStats() + + callback.results = results + callback.simple_results = results + callback.env['playbook_name'] = 'foo' + callback.current_time = 'foo-bar-fooTfoo:bar:foo.fizz' + + dummy_stats.processed['foohost'] = 5 + + no_match_result = validation_task + no_match_result['task']['status'] = "FAILED" + no_match_result['task']['info'] = ( + "None of the hosts specified" + " were matched in the inventory file") + + output = { + 'plays': results, + 'stats': { + 'No host matched':{ + 'changed': 0, + 'failures': 1, + 'ignored': 0, + 'ok': 0, + 'rescued': 0, + 'skipped': 0, + 'unreachable': 0}}, + 'validation_output': results + [no_match_result] + } + + log_file = "{}/{}_{}_{}.json".format( + "/home/foo/validations", + 'fizz', + 'foo', + 'foo-bar-fooTfoo:bar:foo.fizz') + + kwargs = { + 'cls': AnsibleJSONEncoder, + 'indent': 4, + 'sort_keys': True + } + + callback.v2_playbook_on_no_hosts_matched() + mock_write = mock_open.return_value.__enter__.return_value.write + + mock_open.assert_called_once_with(log_file, 'w') + mock_json_dumps.assert_called_once_with(output, **kwargs) + mock_write.assert_called_once_with('json_dump_foo') + @mock.patch('time.time', return_value=99.99) @mock.patch( 'validations_common.callback_plugins.validation_json.secondsToStr',