1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-06-14 03:55:37 +00:00

[PR #12067/f677c2ab backport][stable-13] counter_enabled callback: display output for looped tasks (#12241)

counter_enabled callback: display output for looped tasks (#12067)

* fix(counter_enabled): display output for looped tasks with delegate_to

Implement v2_runner_item_on_ok, v2_runner_item_on_failed, and
v2_runner_item_on_skipped so that looped tasks (including those
using delegate_to: localhost) produce visible output.

Also extract _host_label, _display_result_ok, _display_result_failed,
and _display_result_skipped helpers to eliminate repeated delegation
and message-building logic across the callback methods.

Fixes #8187



* changelog(counter_enabled): add fragment for PR #12067



* test(counter_enabled): add integration tests, adjust _host_label



* test(counter_enabled): migrate integration tests to callback test framework



* test(counter_enabled): fix integration tests to use set_fact instead of debug



---------


(cherry picked from commit f677c2ab7d)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
patchback[bot] 2026-06-13 17:33:53 +02:00 committed by GitHub
parent c14620fcf6
commit 5bb9c6c805
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 165 additions and 56 deletions

View file

@ -140,69 +140,83 @@ class CallbackModule(CallbackBase):
self._host_counter = self._previous_batch_total
self._task_counter += 1
def v2_runner_on_ok(self, result):
self._host_counter += 1
delegated_vars = result._result.get("_ansible_delegated_vars", None)
if self._play.strategy == "free" and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
def _host_label(self, result):
host_name = result._host.get_name()
delegate_to = result._task.delegate_to
if delegate_to and delegate_to != host_name:
delegated_vars = result._result.get("_ansible_delegated_vars", {})
ahost = delegated_vars.get("ansible_host", delegate_to)
label = f"{host_name} -> {delegate_to}"
if ahost != delegate_to:
label += f"({ahost})"
return f"[{label}]"
return f"[{host_name}]"
def _display_result_ok(self, result, counter_str="", item_suffix=""):
if isinstance(result._task, TaskInclude):
return
elif result._result.get("changed", False):
if delegated_vars:
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
else:
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
msg = f"changed: {counter_str}{self._host_label(result)}{item_suffix}"
color = C.COLOR_CHANGED
else:
if not self._plugin_options.get("display_ok_hosts", True):
return
if delegated_vars:
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
else:
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
msg = f"ok: {counter_str}{self._host_label(result)}{item_suffix}"
color = C.COLOR_OK
self._handle_warnings(result._result)
self._clean_results(result._result, result._task.action)
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
self._display.display(msg, color=color)
def v2_runner_on_ok(self, result):
self._host_counter += 1
if self._play.strategy == "free" and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
if result._task.loop and "results" in result._result:
self._process_items(result)
else:
self._clean_results(result._result, result._task.action)
self._display_result_ok(result, f"{self._host_counter}/{self._host_total} ")
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
self._display.display(msg, color=color)
def v2_runner_item_on_ok(self, result):
self._display_result_ok(result, item_suffix=f" => (item={self._get_item_label(result._result)})")
def _display_result_failed(self, result, counter_str="", item_suffix=""):
self._clean_results(result._result, result._task.action)
self._handle_warnings(result._result)
prefix = "failed" if item_suffix else "fatal"
msg = f"{prefix}: {counter_str}{self._host_label(result)}{item_suffix}"
self._display.display(f"{msg} => {self._dump_results(result._result)}", color=C.COLOR_ERROR)
def v2_runner_item_on_failed(self, result):
self._display_result_failed(result, item_suffix=f" (item={self._get_item_label(result._result)})")
def _display_result_skipped(self, result, counter_str="", item_suffix=""):
self._clean_results(result._result, result._task.action)
msg = f"skipping: {counter_str}[{result._host.get_name()}]{item_suffix}"
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
self._display.display(msg, color=C.COLOR_SKIP)
def v2_runner_item_on_skipped(self, result):
if self._plugin_options.get("show_skipped_hosts", C.DISPLAY_SKIPPED_HOSTS):
self._display_result_skipped(result, item_suffix=f" => (item={self._get_item_label(result._result)})")
def v2_runner_on_failed(self, result, ignore_errors=False):
self._host_counter += 1
delegated_vars = result._result.get("_ansible_delegated_vars", None)
self._clean_results(result._result, result._task.action)
if self._play.strategy == "free" and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
self._handle_exception(result._result)
self._handle_warnings(result._result)
if result._task.loop and "results" in result._result:
self._process_items(result)
else:
if delegated_vars:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
f"{delegated_vars['ansible_host']}]: FAILED! => {self._dump_results(result._result)}",
color=C.COLOR_ERROR,
)
else:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: FAILED! => {self._dump_results(result._result)}",
color=C.COLOR_ERROR,
)
self._display_result_failed(result, f"{self._host_counter}/{self._host_total} ", ": FAILED!")
if ignore_errors:
self._display.display("...ignoring", color=C.COLOR_SKIP)
@ -210,21 +224,14 @@ class CallbackModule(CallbackBase):
def v2_runner_on_skipped(self, result):
self._host_counter += 1
if self._plugin_options.get(
"show_skipped_hosts", C.DISPLAY_SKIPPED_HOSTS
): # fallback on constants for inherited plugins missing docs
self._clean_results(result._result, result._task.action)
if self._plugin_options.get("show_skipped_hosts", C.DISPLAY_SKIPPED_HOSTS):
if self._play.strategy == "free" and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
if result._task.loop and "results" in result._result:
self._process_items(result)
else:
msg = f"skipping: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
self._display.display(msg, color=C.COLOR_SKIP)
self._display_result_skipped(result, f"{self._host_counter}/{self._host_total} ")
def v2_runner_on_unreachable(self, result):
self._host_counter += 1
@ -232,15 +239,7 @@ class CallbackModule(CallbackBase):
if self._play.strategy == "free" and self._last_task_banner != result._task._uuid:
self._print_task_banner(result._task)
delegated_vars = result._result.get("_ansible_delegated_vars", None)
if delegated_vars:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
f"{delegated_vars['ansible_host']}]: UNREACHABLE! => {self._dump_results(result._result)}",
color=C.COLOR_UNREACHABLE,
)
else:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: UNREACHABLE! => {self._dump_results(result._result)}",
color=C.COLOR_UNREACHABLE,
)
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} {self._host_label(result)}: UNREACHABLE! => {self._dump_results(result._result)}",
color=C.COLOR_UNREACHABLE,
)