An Ansible aggregate callback plugin that prints a dynamic-width host summary
table after the PLAY RECAP. Columns are driven by whatever facts your playbook
publishes via ansible.builtin.set_stats — the callback just formats them.
Runs alongside Ansible's default stdout callback (it does not replace it).
Pure Python standard library: no tabulate, no pip install anywhere.
Tested on default Ansible from distro packages on RHEL 8, Ubuntu 22.04, and Ubuntu 24.04 (Ansible 2.12+).
ansible-galaxy collection install .
Then in ansible.cfg:
[defaults]
callbacks_enabled = angel.summary.tableClone this repo into your project (or copy plugins/callback/table.py):
[defaults]
callback_plugins = ./callback_plugins
callbacks_enabled = tableYour playbook publishes per-host summary values using set_stats:
- name: Publish summary facts
ansible.builtin.set_stats:
data:
uptime_min: "{{ (ansible_uptime_seconds | int) // 60 }}"
kernel: "{{ ansible_kernel }}"
secureboot: "{{ secureboot | default('unknown') }}"
fips: "{{ fips | default('unknown') }}"
per_host: true
aggregate: trueEvery key you publish becomes a column. Hostname is always the first column;
Status (OK / CHANGED / FAILED / UNREACHABLE) is always the last.
| Variable | Purpose |
|---|---|
ANSIBLE_TABLE_COLUMNS |
Comma-separated stat keys; filters and orders the user columns. Keys that aren't published show as —. |
ANSIBLE_TABLE_HEADERS |
key=Display Name pairs. Overrides the auto-derived header text. |
ANSIBLE_TABLE_LOG_FILE |
Path to write a plain-text copy of the table. Overwrite mode. |
NO_COLOR |
Disable ANSI color on the Status column. |
The Status column is colored when stdout is a TTY and NO_COLOR is unset
(green OK, cyan CHANGED, red FAILED, yellow UNREACHABLE). Color is
never written to the log file.
=== Host Summary ===
+----------+--------------+--------------------+-------------+------+-------------+
| Hostname | Uptime (min) | Kernel | Secure Boot | FIPS | Status |
+----------+--------------+--------------------+-------------+------+-------------+
| db01 | — | — | — | — | UNREACHABLE |
| web01 | 1432 | 5.15.0-119-generic | yes | no | OK |
| web02 | 287 | 5.15.0-119-generic | yes | no | CHANGED |
+----------+--------------+--------------------+-------------+------+-------------+
Run unit tests:
python3 -m unittest tests/test_render.py -v
Run the integration smoke test:
./tests/test_integration.sh