mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-01-11 15:15:26 +01:00
Fix json callback for non-lockstep strategy plugins such as free. Fixes #65931
This commit is contained in:
parent
6b347f3725
commit
23d3703a84
2 changed files with 50 additions and 7 deletions
|
|
@ -0,0 +1,4 @@
|
||||||
|
bugfixes:
|
||||||
|
- json callback - Fix host result to task references in the resultant JSON
|
||||||
|
output for non-lockstep strategy plugins such as free
|
||||||
|
(https://github.com/ansible/ansible/issues/65931)
|
||||||
|
|
@ -25,6 +25,11 @@ DOCUMENTATION = '''
|
||||||
- key: show_custom_stats
|
- key: show_custom_stats
|
||||||
section: defaults
|
section: defaults
|
||||||
type: bool
|
type: bool
|
||||||
|
notes:
|
||||||
|
- When using a strategy such as free, host_pinned, or a custom strategy, host results will
|
||||||
|
be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate
|
||||||
|
task objects indicated by duplicate task IDs at ``.plays[].tasks[].task.id``, each with an
|
||||||
|
individual host result for the task.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
@ -33,10 +38,14 @@ import json
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from ansible.inventory.host import Host
|
from ansible.inventory.host import Host
|
||||||
|
from ansible.module_utils._text import to_text
|
||||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||||
from ansible.plugins.callback import CallbackBase
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
|
||||||
|
LOCKSTEP_CALLBACKS = frozenset(('linear', 'debug'))
|
||||||
|
|
||||||
|
|
||||||
def current_time():
|
def current_time():
|
||||||
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
||||||
|
|
||||||
|
|
@ -49,12 +58,15 @@ class CallbackModule(CallbackBase):
|
||||||
def __init__(self, display=None):
|
def __init__(self, display=None):
|
||||||
super(CallbackModule, self).__init__(display)
|
super(CallbackModule, self).__init__(display)
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self._task_map = {}
|
||||||
|
self._is_lockstep = False
|
||||||
|
|
||||||
def _new_play(self, play):
|
def _new_play(self, play):
|
||||||
|
self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS
|
||||||
return {
|
return {
|
||||||
'play': {
|
'play': {
|
||||||
'name': play.get_name(),
|
'name': play.get_name(),
|
||||||
'id': str(play._uuid),
|
'id': to_text(play._uuid),
|
||||||
'duration': {
|
'duration': {
|
||||||
'start': current_time()
|
'start': current_time()
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +78,7 @@ class CallbackModule(CallbackBase):
|
||||||
return {
|
return {
|
||||||
'task': {
|
'task': {
|
||||||
'name': task.get_name(),
|
'name': task.get_name(),
|
||||||
'id': str(task._uuid),
|
'id': to_text(task._uuid),
|
||||||
'duration': {
|
'duration': {
|
||||||
'start': current_time()
|
'start': current_time()
|
||||||
}
|
}
|
||||||
|
|
@ -74,13 +86,32 @@ class CallbackModule(CallbackBase):
|
||||||
'hosts': {}
|
'hosts': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _find_result_task(self, host, task):
|
||||||
|
key = (host.get_name(), task._uuid)
|
||||||
|
return self._task_map.get(
|
||||||
|
key,
|
||||||
|
self.results[-1]['tasks'][-1]
|
||||||
|
)
|
||||||
|
|
||||||
def v2_playbook_on_play_start(self, play):
|
def v2_playbook_on_play_start(self, play):
|
||||||
self.results.append(self._new_play(play))
|
self.results.append(self._new_play(play))
|
||||||
|
|
||||||
|
def v2_runner_on_start(self, host, task):
|
||||||
|
if self._is_lockstep:
|
||||||
|
return
|
||||||
|
key = (host.get_name(), task._uuid)
|
||||||
|
task_result = self._new_task(task)
|
||||||
|
self._task_map[key] = task_result
|
||||||
|
self.results[-1]['tasks'].append(task_result)
|
||||||
|
|
||||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||||
|
if not self._is_lockstep:
|
||||||
|
return
|
||||||
self.results[-1]['tasks'].append(self._new_task(task))
|
self.results[-1]['tasks'].append(self._new_task(task))
|
||||||
|
|
||||||
def v2_playbook_on_handler_task_start(self, task):
|
def v2_playbook_on_handler_task_start(self, task):
|
||||||
|
if not self._is_lockstep:
|
||||||
|
return
|
||||||
self.results[-1]['tasks'].append(self._new_task(task))
|
self.results[-1]['tasks'].append(self._new_task(task))
|
||||||
|
|
||||||
def _convert_host_to_name(self, key):
|
def _convert_host_to_name(self, key):
|
||||||
|
|
@ -118,14 +149,22 @@ class CallbackModule(CallbackBase):
|
||||||
"""This function is used as a partial to add failed/skipped info in a single method"""
|
"""This function is used as a partial to add failed/skipped info in a single method"""
|
||||||
host = result._host
|
host = result._host
|
||||||
task = result._task
|
task = result._task
|
||||||
task_result = result._result.copy()
|
|
||||||
task_result.update(on_info)
|
result_copy = result._result.copy()
|
||||||
task_result['action'] = task.action
|
result_copy.update(on_info)
|
||||||
self.results[-1]['tasks'][-1]['hosts'][host.name] = task_result
|
result_copy['action'] = task.action
|
||||||
|
|
||||||
|
task_result = self._find_result_task(host, task)
|
||||||
|
|
||||||
|
task_result['hosts'][host.name] = result_copy
|
||||||
end_time = current_time()
|
end_time = current_time()
|
||||||
self.results[-1]['tasks'][-1]['task']['duration']['end'] = end_time
|
task_result['task']['duration']['end'] = end_time
|
||||||
self.results[-1]['play']['duration']['end'] = end_time
|
self.results[-1]['play']['duration']['end'] = end_time
|
||||||
|
|
||||||
|
if not self._is_lockstep:
|
||||||
|
key = (host.get_name(), task._uuid)
|
||||||
|
del self._task_map[key]
|
||||||
|
|
||||||
def __getattribute__(self, name):
|
def __getattribute__(self, name):
|
||||||
"""Return ``_record_task_result`` partial with a dict containing skipped/failed if necessary"""
|
"""Return ``_record_task_result`` partial with a dict containing skipped/failed if necessary"""
|
||||||
if name not in ('v2_runner_on_ok', 'v2_runner_on_failed', 'v2_runner_on_unreachable', 'v2_runner_on_skipped'):
|
if name not in ('v2_runner_on_ok', 'v2_runner_on_failed', 'v2_runner_on_unreachable', 'v2_runner_on_skipped'):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue