From c6d428de119b6883c823a1bcf53307a83cc4f644 Mon Sep 17 00:00:00 2001 From: Ruediger Pluem Date: Mon, 20 Jan 2025 16:54:52 +0100 Subject: [PATCH] Add 'report_diff' key in result data in case of available diff data In case that a 'diff' key with 'before' and 'after' data is available create a unified diff from 'before' and 'after' and store it below the 'report_diff' key as a string for later consumption in config reports on the Foreman server. Remove the 'diff' key afterwards to save space as in case of a file the content is probably big and it is stored twice (before and after). * plugins/callback/foreman.py: Implement feature. * tests/callback/three_hosts.yml: Add tasks to generate diffs when running in diff mode. * tests/conftest.py: Allow to run the playbook in diff mode. * tests/test_callback.py: - Run the playbook for the foreman report type in diff mode - Ignore further items when comparing the json data that depend on the local environment - Remove additional strings that are different in each run - Transform the json string into json data to improve data handling * tests/fixtures/callback/dir_store/*: Adjust fixtures to new tests and changes in format. Signed-off-by: Ruediger Pluem --- plugins/callback/foreman.py | 73 +-- tests/callback/three_hosts.yml | 12 + tests/conftest.py | 9 +- .../dir_store/foreman/testhost-report.json | 467 +++++++++++++++++- .../dir_store/foreman/testhostA-report.json | 75 ++- .../dir_store/foreman/testhostB-report.json | 75 ++- .../dir_store/proxy/testhost-report.json | 92 +++- .../dir_store/proxy/testhostA-report.json | 6 - .../dir_store/proxy/testhostB-report.json | 6 - tests/test_callback.py | 12 +- 10 files changed, 741 insertions(+), 86 deletions(-) diff --git a/plugins/callback/foreman.py b/plugins/callback/foreman.py index 2af963238d..2a92913d01 100644 --- a/plugins/callback/foreman.py +++ b/plugins/callback/foreman.py @@ -110,6 +110,7 @@ from collections import defaultdict import json import time +import re try: import requests @@ -123,36 +124,6 @@ from ansible.plugins.callback import CallbackBase -def build_log_foreman(data_list): - """ - Transform the internal log structure to one accepted by Foreman's - config_report API. - """ - for data in data_list: - result = data.pop('result') - task = data.pop('task') - result['failed'] = data.get('failed') - result['module'] = task.get('action') - if data.get('failed'): - level = 'err' - elif result.get('changed'): - level = 'notice' - else: - level = 'info' - - yield { - "log": { - 'sources': { - 'source': task.get('name'), - }, - 'messages': { - 'message': json.dumps(result, sort_keys=True, cls=AnsibleNoVaultJSONEncoder), - }, - 'level': level, - } - } - - def get_time(): """ Return the time for measuring duration. Prefers monotonic time but @@ -269,6 +240,46 @@ def _send_data(self, data_type, report_type, host, data): self._display.warning(u'Sending data to Foreman at {url} failed for {host}: {err}'.format( host=to_text(host), err=to_text(err), url=to_text(self.foreman_url))) + def _build_log_foreman(self, data_list): + """ + Transform the internal log structure to one accepted by Foreman's + config_report API. + """ + for data in data_list: + result = data.pop('result') + task = data.pop('task') + result['failed'] = data.get('failed') + result['module'] = task.get('action') + if data.get('failed'): + level = 'err' + elif result.get('changed'): + level = 'notice' + else: + level = 'info' + + # Check if the 'diff' key is set and transform the state before + # and after the change into a unified diff string and store it + # below the 'report_diff' key. Remove the 'diff' key afterwards + # as in case of a file the content is probably big and it is + # stored twice (before and after). + if 'diff' in result: + diff = self._get_diff(result['diff']) + # Remove color escape sequences for terminal output + result['report_diff'] = re.sub(u'\033\\[0.*?m', '', diff) + del result['diff'] + + yield { + "log": { + 'sources': { + 'source': task.get('name'), + }, + 'messages': { + 'message': json.dumps(result, sort_keys=True, cls=AnsibleNoVaultJSONEncoder), + }, + 'level': level, + } + } + def send_facts(self): """ Sends facts to Foreman, to be parsed by foreman_ansible fact @@ -336,7 +347,7 @@ def send_reports_foreman(self, stats): "failed": total['failures'] + total['unreachable'], "skipped": total['skipped'], }, - "logs": list(build_log_foreman(self.items[host])), + "logs": list(self._build_log_foreman(self.items[host])), "reporter": "ansible", "check_mode": self.check_mode, } diff --git a/tests/callback/three_hosts.yml b/tests/callback/three_hosts.yml index 5843292191..0ad4b5dc33 100644 --- a/tests/callback/three_hosts.yml +++ b/tests/callback/three_hosts.yml @@ -57,6 +57,18 @@ unsafe: !unsafe | THIS IS {{ crypt }} + - name: Generate a diff + copy: + content: Test + dest: "/tmp/test_{{ lookup('env', 'FOREMAN_REPORT_TYPE') }}.txt" + remote_src: false + mode: '0644' + + - name: Remove file + file: + path: "/tmp/test_{{ lookup('env', 'FOREMAN_REPORT_TYPE') }}.txt" + state: absent + handlers: - name: Test handler 1 command: echo foo diff --git a/tests/conftest.py b/tests/conftest.py index 8a5b958303..38d32e7dfe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -47,7 +47,7 @@ def get_foreman_url(): return server_yml_content['foreman_server_url'] -def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode=False, extra_env=None): +def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode=False, diff_mode=False, extra_env=None): # Assemble parameters for playbook call os.environ['ANSIBLE_CONFIG'] = os.path.join(os.getcwd(), 'ansible.cfg') if extra_env is not None: @@ -62,8 +62,13 @@ def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode kwargs['extravars'] = extra_vars if limit: kwargs['limit'] = limit + cmdline = [] if check_mode: - kwargs['cmdline'] = "--check" + cmdline.append("--check") + if diff_mode: + cmdline.append("--diff") + if len(cmdline) > 0: + kwargs['cmdline'] = ' '.join(cmdline) return ansible_runner.run(**kwargs) diff --git a/tests/fixtures/callback/dir_store/foreman/testhost-report.json b/tests/fixtures/callback/dir_store/foreman/testhost-report.json index e4b2bdad98..354c9f519e 100644 --- a/tests/fixtures/callback/dir_store/foreman/testhost-report.json +++ b/tests/fixtures/callback/dir_store/foreman/testhost-report.json @@ -7,7 +7,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Changed task" @@ -18,7 +50,39 @@ "log": { "level": "info", "messages": { - "message": "{\"changed\": false, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": false, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Ok task" @@ -29,7 +93,12 @@ "log": { "level": "err", "messages": { - "message": "{\"changed\": false, \"failed\": true, \"module\": \"fail\", \"msg\": \"no reason\"}" + "message": { + "changed": false, + "failed": true, + "module": "fail", + "msg": "no reason" + } }, "sources": { "source": "Failed task" @@ -40,7 +109,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Task with var in name (foo bar)" @@ -51,7 +152,119 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"failed\": false, \"module\": \"command\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"foo-1\", \"ansible_loop_var\": \"item\", \"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"item\": 1, \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}, {\"_ansible_item_label\": \"foo-2\", \"ansible_loop_var\": \"item\", \"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"item\": 2, \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}, {\"_ansible_item_label\": \"foo-3\", \"ansible_loop_var\": \"item\", \"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"item\": 3, \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}]}" + "message": { + "changed": true, + "failed": false, + "module": "command", + "msg": "All items completed", + "results": [ + { + "_ansible_item_label": "foo-1", + "ansible_loop_var": "item", + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "item": 1, + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + }, + { + "_ansible_item_label": "foo-2", + "ansible_loop_var": "item", + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "item": 2, + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + }, + { + "_ansible_item_label": "foo-3", + "ansible_loop_var": "item", + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "item": 3, + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } + ] + } }, "sources": { "source": "Loop task" @@ -62,7 +275,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"admin\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo admin\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"admin\", \"stdout_lines\": [\"admin\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "admin" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo admin", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "admin", + "stdout_lines": [ + "admin" + ] + } }, "sources": { "source": "Vault command" @@ -73,7 +318,16 @@ "log": { "level": "info", "messages": { - "message": "{\"ansible_facts\": {\"crypt\": \"ENCRYPTED_VAULT_VALUE_NOT_REPORTED\", \"geheim\": \"admin\", \"unsafe\": \"THIS IS {{ crypt }}\\n\"}, \"changed\": false, \"failed\": false, \"module\": \"set_fact\"}" + "message": { + "ansible_facts": { + "crypt": "ENCRYPTED_VAULT_VALUE_NOT_REPORTED", + "geheim": "admin", + "unsafe": "THIS IS {{ crypt }}\n" + }, + "changed": false, + "failed": false, + "module": "set_fact" + } }, "sources": { "source": "Vault fact" @@ -84,7 +338,134 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "checksum": "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa", + "dest": "/tmp/test_foreman.txt", + "failed": false, + "gid": 1000, + "group": "devil", + "invocation": { + "module_args": { + "_original_basename": "tmp2zc1vj45", + "attributes": null, + "backup": false, + "checksum": "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa", + "content": null, + "dest": "/tmp/test_foreman.txt", + "directory_mode": null, + "follow": false, + "force": true, + "group": null, + "local_follow": null, + "mode": "0644", + "owner": null, + "remote_src": false, + "selevel": null, + "serole": null, + "setype": null, + "seuser": null, + "src": "/home/devil/.ansible/tmp/ansible-tmp-1738151543.3063104-35413-153104140441724/source", + "unsafe_writes": false, + "validate": null + } + }, + "md5sum": "0cbc6611f5540bd0809a388dc95a615b", + "mode": "0644", + "module": "copy", + "owner": "devil", + "report_diff": "--- before\n+++ after:\n@@ -0,0 +1 @@\n+Test\n\\ No newline at end of file\n\n", + "size": 4, + "src": "/home/devil/.ansible/tmp/ansible-tmp-1738151543.3063104-35413-153104140441724/source", + "state": "file", + "uid": 1000 + } + }, + "sources": { + "source": "Generate a diff" + } + } + }, + { + "log": { + "level": "notice", + "messages": { + "message": { + "changed": true, + "failed": false, + "invocation": { + "module_args": { + "_diff_peek": null, + "_original_basename": null, + "access_time": null, + "access_time_format": "%Y%m%d%H%M.%S", + "attributes": null, + "follow": true, + "force": false, + "group": null, + "mode": null, + "modification_time": null, + "modification_time_format": "%Y%m%d%H%M.%S", + "owner": null, + "path": "/tmp/test_foreman.txt", + "recurse": false, + "selevel": null, + "serole": null, + "setype": null, + "seuser": null, + "src": null, + "state": "absent", + "unsafe_writes": false + } + }, + "module": "file", + "path": "/tmp/test_foreman.txt", + "report_diff": "--- before\n+++ after\n@@ -1,4 +1,4 @@\n {\n \"path\": \"/tmp/test_foreman.txt\",\n- \"state\": \"file\"\n+ \"state\": \"absent\"\n }\n\n", + "state": "absent" + } + }, + "sources": { + "source": "Remove file" + } + } + }, + { + "log": { + "level": "notice", + "messages": { + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Test handler 1" @@ -95,7 +476,39 @@ "log": { "level": "info", "messages": { - "message": "{\"changed\": false, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": false, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Test handler 2" @@ -106,7 +519,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Test handler 3" @@ -122,7 +567,7 @@ "reported_at": "2000-01-01 12:00:00+00:00", "reporter": "ansible", "status": { - "applied": 6, + "applied": 8, "failed": 0, "skipped": 1 } diff --git a/tests/fixtures/callback/dir_store/foreman/testhostA-report.json b/tests/fixtures/callback/dir_store/foreman/testhostA-report.json index 6e68c3ca8d..edb622a8dd 100644 --- a/tests/fixtures/callback/dir_store/foreman/testhostA-report.json +++ b/tests/fixtures/callback/dir_store/foreman/testhostA-report.json @@ -7,7 +7,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Changed task" @@ -18,7 +50,39 @@ "log": { "level": "info", "messages": { - "message": "{\"changed\": false, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": false, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Ok task" @@ -29,7 +93,12 @@ "log": { "level": "err", "messages": { - "message": "{\"changed\": false, \"failed\": true, \"module\": \"fail\", \"msg\": \"no reason\"}" + "message": { + "changed": false, + "failed": true, + "module": "fail", + "msg": "no reason" + } }, "sources": { "source": "Failed task" diff --git a/tests/fixtures/callback/dir_store/foreman/testhostB-report.json b/tests/fixtures/callback/dir_store/foreman/testhostB-report.json index 9a796252d9..e1d9637c48 100644 --- a/tests/fixtures/callback/dir_store/foreman/testhostB-report.json +++ b/tests/fixtures/callback/dir_store/foreman/testhostB-report.json @@ -7,7 +7,39 @@ "log": { "level": "notice", "messages": { - "message": "{\"changed\": true, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": true, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Changed task" @@ -18,7 +50,39 @@ "log": { "level": "info", "messages": { - "message": "{\"changed\": false, \"cmd\": [\"echo\", \"foo\"], \"delta\": \"12:00:00.0000\", \"end\": \"2000-01-01 12:00:00.0000\", \"failed\": false, \"invocation\": {\"module_args\": {\"_raw_params\": \"echo foo\", \"_uses_shell\": false, \"argv\": null, \"chdir\": null, \"creates\": null, \"executable\": null, \"removes\": null, \"stdin\": null, \"stdin_add_newline\": true, \"strip_empty_ends\": true}}, \"module\": \"command\", \"rc\": 0, \"start\": \"2000-01-01 12:00:00.0000\", \"stderr\": \"\", \"stderr_lines\": [], \"stdout\": \"foo\", \"stdout_lines\": [\"foo\"]}" + "message": { + "changed": false, + "cmd": [ + "echo", + "foo" + ], + "delta": "12:00:00.0000", + "end": "2000-01-01 12:00:00.0000", + "failed": false, + "invocation": { + "module_args": { + "_raw_params": "echo foo", + "_uses_shell": false, + "argv": null, + "chdir": null, + "creates": null, + "executable": null, + "removes": null, + "stdin": null, + "stdin_add_newline": true, + "strip_empty_ends": true + } + }, + "module": "command", + "rc": 0, + "start": "2000-01-01 12:00:00.0000", + "stderr": "", + "stderr_lines": [], + "stdout": "foo", + "stdout_lines": [ + "foo" + ] + } }, "sources": { "source": "Ok task" @@ -29,7 +93,12 @@ "log": { "level": "err", "messages": { - "message": "{\"changed\": false, \"failed\": true, \"module\": \"fail\", \"msg\": \"no reason\"}" + "message": { + "changed": false, + "failed": true, + "module": "fail", + "msg": "no reason" + } }, "sources": { "source": "Failed task" diff --git a/tests/fixtures/callback/dir_store/proxy/testhost-report.json b/tests/fixtures/callback/dir_store/proxy/testhost-report.json index 61de85ab84..17b6f1649d 100644 --- a/tests/fixtures/callback/dir_store/proxy/testhost-report.json +++ b/tests/fixtures/callback/dir_store/proxy/testhost-report.json @@ -42,8 +42,6 @@ true ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Changed task", "notify": [ "test handlers" @@ -86,8 +84,6 @@ false ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Ok task", "poll": 15, "throttle": 0 @@ -106,8 +102,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "ignore_errors": true, "name": "Failed task", "poll": 15, @@ -145,8 +139,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "name": "Task with var in name (foo bar)", "poll": 15, "throttle": 0 @@ -242,8 +234,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "loop": [ 1, 2, @@ -285,8 +275,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "name": "Vault command", "poll": 15, "throttle": 0 @@ -310,13 +298,79 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "name": "Vault fact", "poll": 15, "throttle": 0 } }, + { + "failed": false, + "result": { + "changed": true, + "checksum": "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa", + "dest": "/tmp/test_proxy.txt", + "invocation": { + "module_args": { + "backup": false, + "checksum": "640ab2bae07bedc4c163f679a746f7ab7fb5d1fa", + "dest": "/tmp/test_proxy.txt", + "follow": false, + "force": true, + "mode": "0644", + "remote_src": false, + "unsafe_writes": false + } + }, + "md5sum": "0cbc6611f5540bd0809a388dc95a615b", + "mode": "0644", + "size": 4, + "state": "file" + }, + "task": { + "action": "copy", + "any_errors_fatal": false, + "async": 0, + "async_val": 0, + "become": false, + "become_method": "sudo", + "check_mode": false, + "name": "Generate a diff", + "poll": 15, + "throttle": 0 + } + }, + { + "failed": false, + "result": { + "changed": true, + "invocation": { + "module_args": { + "access_time_format": "%Y%m%d%H%M.%S", + "follow": true, + "force": false, + "modification_time_format": "%Y%m%d%H%M.%S", + "path": "/tmp/test_proxy.txt", + "recurse": false, + "state": "absent", + "unsafe_writes": false + } + }, + "path": "/tmp/test_proxy.txt", + "state": "absent" + }, + "task": { + "action": "file", + "any_errors_fatal": false, + "async": 0, + "async_val": 0, + "become": false, + "become_method": "sudo", + "check_mode": false, + "name": "Remove file", + "poll": 15, + "throttle": 0 + } + }, { "failed": false, "result": { @@ -348,8 +402,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "is_handler": true, "name": "Test handler 1", "poll": 15, @@ -390,8 +442,6 @@ false ], "check_mode": false, - "delay": 5, - "diff": false, "is_handler": true, "name": "Test handler 2", "poll": 15, @@ -429,8 +479,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "is_handler": true, "name": "Test handler 3", "poll": 15, @@ -439,10 +487,10 @@ } ], "summary": { - "changed": 6, + "changed": 8, "failures": 0, "ignored": 1, - "ok": 10, + "ok": 12, "rescued": 0, "skipped": 1, "unreachable": 0 diff --git a/tests/fixtures/callback/dir_store/proxy/testhostA-report.json b/tests/fixtures/callback/dir_store/proxy/testhostA-report.json index b7c34c6d65..a67e5bc9b4 100644 --- a/tests/fixtures/callback/dir_store/proxy/testhostA-report.json +++ b/tests/fixtures/callback/dir_store/proxy/testhostA-report.json @@ -42,8 +42,6 @@ true ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Changed task", "poll": 15, "throttle": 0 @@ -83,8 +81,6 @@ false ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Ok task", "poll": 15, "throttle": 0 @@ -103,8 +99,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "ignore_errors": true, "name": "Failed task", "poll": 15, diff --git a/tests/fixtures/callback/dir_store/proxy/testhostB-report.json b/tests/fixtures/callback/dir_store/proxy/testhostB-report.json index 66f597eb25..270a28764a 100644 --- a/tests/fixtures/callback/dir_store/proxy/testhostB-report.json +++ b/tests/fixtures/callback/dir_store/proxy/testhostB-report.json @@ -42,8 +42,6 @@ true ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Changed task", "poll": 15, "throttle": 0 @@ -83,8 +81,6 @@ false ], "check_mode": false, - "delay": 5, - "diff": false, "name": "Ok task", "poll": 15, "throttle": 0 @@ -103,8 +99,6 @@ "become": false, "become_method": "sudo", "check_mode": false, - "delay": 5, - "diff": false, "ignore_errors": true, "name": "Failed task", "poll": 15, diff --git a/tests/test_callback.py b/tests/test_callback.py index 790fc303ef..e459181bfe 100644 --- a/tests/test_callback.py +++ b/tests/test_callback.py @@ -12,6 +12,7 @@ def run_playbook_callback(tmpdir, report_type): extra_env = {} + diff_mode = True ansible_version = get_ansible_version() if LooseVersion(ansible_version) < LooseVersion('2.11'): extra_env['ANSIBLE_CALLBACK_WHITELIST'] = "theforeman.foreman.foreman" @@ -23,13 +24,14 @@ def run_playbook_callback(tmpdir, report_type): extra_env['FOREMAN_URL'] = "http://localhost" if report_type == "proxy": extra_env['FOREMAN_PROXY_URL'] = "http://localhost" + diff_mode = False extra_env['FOREMAN_SSL_CERT'] = "/dev/zero" extra_env['FOREMAN_SSL_KEY'] = "/dev/zero" extra_env['FOREMAN_DIR_STORE'] = tmpdir.strpath extra_env['ANSIBLE_VAULT_PASSWORD_FILE'] = os.path.join(os.getcwd(), 'tests', 'callback', 'vault-pass') playbook = os.path.join('..', 'callback', 'three_hosts') inventory = os.path.join(os.getcwd(), 'tests', 'callback', 'three_hosts') - return run_playbook(playbook, inventory=inventory, extra_env=extra_env) + return run_playbook(playbook, inventory=inventory, diff_mode=diff_mode, extra_env=extra_env) def drop_incompatible_items(d): @@ -41,7 +43,7 @@ def drop_incompatible_items(d): for k, v in d.items(): if k in ['msg', 'start', 'end', 'delta', 'uuid', 'timeout', '_ansible_no_log', 'warn', 'connection', 'extended_allitems', 'loop_control', 'expand_argument_vars', 'retries', 'parent', 'parent_type', 'finalized', 'squashed', 'no_log', - 'listen', '_ansible_internal_redirect_list']: + 'listen', '_ansible_internal_redirect_list', '_original_basename', 'src', 'owner', 'group', 'uid', 'gid', 'delay', 'diff']: continue if isinstance(v, dict): @@ -72,10 +74,16 @@ def run_callback(tmpdir, report_type, vcrmode): contents = re.sub(r"\\\"_ansible_no_log\\\": [^,]+, ", "", contents) contents = re.sub(r", \\\"warn\\\": false", "", contents) contents = re.sub(r", \\\"expand_argument_vars\\\": true", "", contents) + contents = re.sub(r"(\+\+\+ after:)[^\\]*", "\\1", contents) real_contents = json.loads(contents) if report_type == "foreman": try: real_contents['config_report']['metrics']['time']['total'] = 1 + logs = [] + for log in real_contents['config_report']['logs']: + log['log']['messages']['message'] = json.loads(log['log']['messages']['message']) + logs.append(log) + real_contents['config_report']['logs'] = logs except KeyError: pass else: