Skip to content

Commit 2cd1b97

Browse files
committed
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 <ruediger.pluem@vodafone.com>
1 parent af10b05 commit 2cd1b97

File tree

10 files changed

+741
-86
lines changed

10 files changed

+741
-86
lines changed

plugins/callback/foreman.py

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
from collections import defaultdict
111111
import json
112112
import time
113+
import re
113114

114115
try:
115116
import requests
@@ -123,36 +124,6 @@
123124
from ansible.plugins.callback import CallbackBase
124125

125126

126-
def build_log_foreman(data_list):
127-
"""
128-
Transform the internal log structure to one accepted by Foreman's
129-
config_report API.
130-
"""
131-
for data in data_list:
132-
result = data.pop('result')
133-
task = data.pop('task')
134-
result['failed'] = data.get('failed')
135-
result['module'] = task.get('action')
136-
if data.get('failed'):
137-
level = 'err'
138-
elif result.get('changed'):
139-
level = 'notice'
140-
else:
141-
level = 'info'
142-
143-
yield {
144-
"log": {
145-
'sources': {
146-
'source': task.get('name'),
147-
},
148-
'messages': {
149-
'message': json.dumps(result, sort_keys=True, cls=AnsibleNoVaultJSONEncoder),
150-
},
151-
'level': level,
152-
}
153-
}
154-
155-
156127
def get_time():
157128
"""
158129
Return the time for measuring duration. Prefers monotonic time but
@@ -269,6 +240,46 @@ def _send_data(self, data_type, report_type, host, data):
269240
self._display.warning(u'Sending data to Foreman at {url} failed for {host}: {err}'.format(
270241
host=to_text(host), err=to_text(err), url=to_text(self.foreman_url)))
271242

243+
def _build_log_foreman(self, data_list):
244+
"""
245+
Transform the internal log structure to one accepted by Foreman's
246+
config_report API.
247+
"""
248+
for data in data_list:
249+
result = data.pop('result')
250+
task = data.pop('task')
251+
result['failed'] = data.get('failed')
252+
result['module'] = task.get('action')
253+
if data.get('failed'):
254+
level = 'err'
255+
elif result.get('changed'):
256+
level = 'notice'
257+
else:
258+
level = 'info'
259+
260+
# Check if the 'diff' key is set and transform the state before
261+
# and after the change into a unified diff string and store it
262+
# below the 'report_diff' key. Remove the 'diff' key afterwards
263+
# as in case of a file the content is probably big and it is
264+
# stored twice (before and after).
265+
if 'diff' in result:
266+
diff = self._get_diff(result['diff'])
267+
# Remove color escape sequences for terminal output
268+
result['report_diff'] = re.sub(u'\033\\[0.*?m', '', diff)
269+
del result['diff']
270+
271+
yield {
272+
"log": {
273+
'sources': {
274+
'source': task.get('name'),
275+
},
276+
'messages': {
277+
'message': json.dumps(result, sort_keys=True, cls=AnsibleNoVaultJSONEncoder),
278+
},
279+
'level': level,
280+
}
281+
}
282+
272283
def send_facts(self):
273284
"""
274285
Sends facts to Foreman, to be parsed by foreman_ansible fact
@@ -336,7 +347,7 @@ def send_reports_foreman(self, stats):
336347
"failed": total['failures'] + total['unreachable'],
337348
"skipped": total['skipped'],
338349
},
339-
"logs": list(build_log_foreman(self.items[host])),
350+
"logs": list(self._build_log_foreman(self.items[host])),
340351
"reporter": "ansible",
341352
"check_mode": self.check_mode,
342353
}

tests/callback/three_hosts.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@
5757
unsafe: !unsafe |
5858
THIS IS {{ crypt }}
5959
60+
- name: Generate a diff
61+
copy:
62+
content: Test
63+
dest: "/tmp/test_{{ lookup('env', 'FOREMAN_REPORT_TYPE') }}.txt"
64+
remote_src: false
65+
mode: '0644'
66+
67+
- name: Remove file
68+
file:
69+
path: "/tmp/test_{{ lookup('env', 'FOREMAN_REPORT_TYPE') }}.txt"
70+
state: absent
71+
6072
handlers:
6173
- name: Test handler 1
6274
command: echo foo

tests/conftest.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def get_foreman_url():
4747
return server_yml_content['foreman_server_url']
4848

4949

50-
def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode=False, extra_env=None):
50+
def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode=False, diff_mode=False, extra_env=None):
5151
# Assemble parameters for playbook call
5252
os.environ['ANSIBLE_CONFIG'] = os.path.join(os.getcwd(), 'ansible.cfg')
5353
if extra_env is not None:
@@ -62,8 +62,13 @@ def run_playbook(module, extra_vars=None, limit=None, inventory=None, check_mode
6262
kwargs['extravars'] = extra_vars
6363
if limit:
6464
kwargs['limit'] = limit
65+
cmdline = []
6566
if check_mode:
66-
kwargs['cmdline'] = "--check"
67+
cmdline.append("--check")
68+
if diff_mode:
69+
cmdline.append("--diff")
70+
if len(cmdline) > 0:
71+
kwargs['cmdline'] = ' '.join(cmdline)
6772
return ansible_runner.run(**kwargs)
6873

6974

0 commit comments

Comments
 (0)