I want to pretty-print each item of a list of results that a previous task has registered. My naïve approach was this:
- hosts: localhost
gather_facts: no
tasks:
- command: "echo number {{item}}"
with_items: [1, 2, 3]
register: result
- debug:
msg: "=== {{ item.cmd|join(' ') }} ===\n{{ item.stdout|indent(first=true) }}"
with_items: "{{ result.results }}"
I'm using stdout_callback = debug in ansible.cfg to make the output more human-readable.
The above works almost. The output looks as expected, but unfortunately the debug module logs the full loop item in addition to my msg expression, which garbles the output quite a bit:
TASK [debug] ************************************************************************************************************************************************************
ok: [localhost] => (item={'cmd': ['echo', 'number', '1'], 'stdout': 'number 1', 'stderr': '', 'rc': 0, 'start': '2021-12-20 13:36:29.488443', 'end': '2021-12-20 13:36:29.490032', 'delta': '0:00:00.001589', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'echo number 1', 'warn': True, '_uses_shell': False, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['number 1'], 'stderr_lines': [], 'failed': False, 'item': 1, 'ansible_loop_var': 'item'}) => {}
MSG:
=== echo number 1 ===
number 1
Setting no_log: true suppresses all output and is thus not helpful. Is there any way to print just the msg?
I've experimented quite a bit, but the only solution that came close to what I had in mind was to do away with the loop in the debug task and use a custom filter plugin to print the items.
- debug:
msg: "{{ result.results | map('format_result', '=== {} ===\n{}') | join('\n\n') }}"
where filter_plugins/format_result.py looks like this:
def format_result(res, pattern):
return (pattern.format(" ".join(res['cmd']), res['stdout']))
class FilterModule(object):
def filters(self):
return {
'format_result': format_result,
}
The filter plugin approach does what I want but I was asking myself whether there's no simpler solution.
Update 2021-12-21:
Thanks to @Zeitounator's comment below, I came up with this:
- hosts: localhost
gather_facts: no
tasks:
- command: "echo number {{item}}"
with_items: [1, 2, 3]
register: result
loop_control:
label: "\nThe item is: {{ result.stdout|indent(first=true) }}\n"
I haven't yet figured out how to access the loop result inside the loop without using register.
Update 2022-02-03:
Trying to summarize what I've learned (thanks everyone and forgive my ignorance, as you could see I'm pretty much an ansible noob):
- The mistake of my first approach was to ignore that ansible prints a result line for every simple task, and for every iteration for tasks with a loop.
- A
debugtask with loop will print a result line for every iteration, followed by the actual message. - The lengthy ouput that I wanted to suppress is just this result line, not the output of the debug module itself.
- For the reasons above,
debugtasks with a loop generally don't cut it for pretty-printed output. - Thus the loop has to be moved into the template, like in β.εηοιτ.βε's solution, or in my original "custom filter" approach.