Implement async callbacks (#74953)
* add changelog and output from default callback * add test * add comments about TE task
This commit is contained in:
parent
ca6123e0ee
commit
703cb79442
6 changed files with 59 additions and 4 deletions
4
changelogs/fragments/74953-implement-async-callbacks.yml
Normal file
4
changelogs/fragments/74953-implement-async-callbacks.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
minor_changes:
|
||||||
|
- callback API - implemented ``v2_runner_on_async_ok`` and ``v2_runner_on_async_failed`` callbacks
|
||||||
|
(https://github.com/ansible/ansible/pull/74953).
|
||||||
|
- default callback plugin - displays output for ``v2_runner_on_async_ok`` and ``v2_runner_on_async_failed`` callbacks.
|
|
@ -617,6 +617,20 @@ class TaskExecutor:
|
||||||
if self._task.async_val > 0:
|
if self._task.async_val > 0:
|
||||||
if self._task.poll > 0 and not result.get('skipped') and not result.get('failed'):
|
if self._task.poll > 0 and not result.get('skipped') and not result.get('failed'):
|
||||||
result = self._poll_async_result(result=result, templar=templar, task_vars=vars_copy)
|
result = self._poll_async_result(result=result, templar=templar, task_vars=vars_copy)
|
||||||
|
if result.get('failed'):
|
||||||
|
self._final_q.send_callback(
|
||||||
|
'v2_runner_on_async_failed',
|
||||||
|
TaskResult(self._host.name,
|
||||||
|
self._task, # We send the full task here, because the controller knows nothing about it, the TE created it
|
||||||
|
result,
|
||||||
|
task_fields=self._task.dump_attrs()))
|
||||||
|
else:
|
||||||
|
self._final_q.send_callback(
|
||||||
|
'v2_runner_on_async_ok',
|
||||||
|
TaskResult(self._host.name,
|
||||||
|
self._task, # We send the full task here, because the controller knows nothing about it, the TE created it
|
||||||
|
result,
|
||||||
|
task_fields=self._task.dump_attrs()))
|
||||||
|
|
||||||
# ensure no log is preserved
|
# ensure no log is preserved
|
||||||
result["_ansible_no_log"] = self._play_context.no_log
|
result["_ansible_no_log"] = self._play_context.no_log
|
||||||
|
@ -831,7 +845,7 @@ class TaskExecutor:
|
||||||
|
|
||||||
if int(async_result.get('finished', 0)) != 1:
|
if int(async_result.get('finished', 0)) != 1:
|
||||||
if async_result.get('_ansible_parsed'):
|
if async_result.get('_ansible_parsed'):
|
||||||
return dict(failed=True, msg="async task did not complete within the requested time - %ss" % self._task.async_val)
|
return dict(failed=True, msg="async task did not complete within the requested time - %ss" % self._task.async_val, async_result=async_result)
|
||||||
else:
|
else:
|
||||||
return dict(failed=True, msg="async task produced unparseable results", async_result=async_result)
|
return dict(failed=True, msg="async task produced unparseable results", async_result=async_result)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -364,7 +364,6 @@ class CallbackBase(AnsiblePlugin):
|
||||||
host = result._host.get_name()
|
host = result._host.get_name()
|
||||||
self.runner_on_unreachable(host, result._result)
|
self.runner_on_unreachable(host, result._result)
|
||||||
|
|
||||||
# FIXME: not called
|
|
||||||
def v2_runner_on_async_poll(self, result):
|
def v2_runner_on_async_poll(self, result):
|
||||||
host = result._host.get_name()
|
host = result._host.get_name()
|
||||||
jid = result._result.get('ansible_job_id')
|
jid = result._result.get('ansible_job_id')
|
||||||
|
@ -372,16 +371,18 @@ class CallbackBase(AnsiblePlugin):
|
||||||
clock = 0
|
clock = 0
|
||||||
self.runner_on_async_poll(host, result._result, jid, clock)
|
self.runner_on_async_poll(host, result._result, jid, clock)
|
||||||
|
|
||||||
# FIXME: not called
|
|
||||||
def v2_runner_on_async_ok(self, result):
|
def v2_runner_on_async_ok(self, result):
|
||||||
host = result._host.get_name()
|
host = result._host.get_name()
|
||||||
jid = result._result.get('ansible_job_id')
|
jid = result._result.get('ansible_job_id')
|
||||||
self.runner_on_async_ok(host, result._result, jid)
|
self.runner_on_async_ok(host, result._result, jid)
|
||||||
|
|
||||||
# FIXME: not called
|
|
||||||
def v2_runner_on_async_failed(self, result):
|
def v2_runner_on_async_failed(self, result):
|
||||||
host = result._host.get_name()
|
host = result._host.get_name()
|
||||||
|
# Attempt to get the async job ID. If the job does not finish before the
|
||||||
|
# async timeout value, the ID may be within the unparsed 'async_result' dict.
|
||||||
jid = result._result.get('ansible_job_id')
|
jid = result._result.get('ansible_job_id')
|
||||||
|
if not jid and 'async_result' in result._result:
|
||||||
|
jid = result._result['async_result'].get('ansible_job_id')
|
||||||
self.runner_on_async_failed(host, result._result, jid)
|
self.runner_on_async_failed(host, result._result, jid)
|
||||||
|
|
||||||
def v2_playbook_on_start(self, playbook):
|
def v2_playbook_on_start(self, playbook):
|
||||||
|
|
|
@ -413,6 +413,21 @@ class CallbackModule(CallbackBase):
|
||||||
color=C.COLOR_DEBUG
|
color=C.COLOR_DEBUG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def v2_runner_on_async_ok(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
jid = result._result.get('ansible_job_id')
|
||||||
|
self._display.display("ASYNC OK on %s: jid=%s" % (host, jid), color=C.COLOR_DEBUG)
|
||||||
|
|
||||||
|
def v2_runner_on_async_failed(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
|
||||||
|
# Attempt to get the async job ID. If the job does not finish before the
|
||||||
|
# async timeout value, the ID may be within the unparsed 'async_result' dict.
|
||||||
|
jid = result._result.get('ansible_job_id')
|
||||||
|
if not jid and 'async_result' in result._result:
|
||||||
|
jid = result._result['async_result'].get('ansible_job_id')
|
||||||
|
self._display.display("ASYNC FAILED on %s: jid=%s" % (host, jid), color=C.COLOR_DEBUG)
|
||||||
|
|
||||||
def v2_playbook_on_notify(self, handler, host):
|
def v2_playbook_on_notify(self, handler, host):
|
||||||
if self._display.verbosity > 1:
|
if self._display.verbosity > 1:
|
||||||
self._display.display("NOTIFIED HANDLER %s for %s" % (handler.get_name(), host), color=C.COLOR_VERBOSE, screen_only=True)
|
self._display.display("NOTIFIED HANDLER %s for %s" % (handler.get_name(), host), color=C.COLOR_VERBOSE, screen_only=True)
|
||||||
|
|
|
@ -125,6 +125,13 @@ export ANSIBLE_CHECK_MODE_MARKERS=0
|
||||||
|
|
||||||
run_test default
|
run_test default
|
||||||
|
|
||||||
|
# Check for async output
|
||||||
|
# NOTE: regex to match 1 or more digits works for both BSD and GNU grep
|
||||||
|
ansible-playbook -i inventory test_async.yml 2>&1 | tee async_test.out
|
||||||
|
grep "ASYNC OK .* jid=[0-9]\{1,\}" async_test.out
|
||||||
|
grep "ASYNC FAILED .* jid=[0-9]\{1,\}" async_test.out
|
||||||
|
rm -f async_test.out
|
||||||
|
|
||||||
# Hide skipped
|
# Hide skipped
|
||||||
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=0
|
export ANSIBLE_DISPLAY_SKIPPED_HOSTS=0
|
||||||
|
|
||||||
|
|
14
test/integration/targets/callback_default/test_async.yml
Normal file
14
test/integration/targets/callback_default/test_async.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
- hosts: testhost
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- name: test success async output
|
||||||
|
command: sleep 1
|
||||||
|
async: 10
|
||||||
|
poll: 1
|
||||||
|
|
||||||
|
- name: test failure async output
|
||||||
|
command: sleep 10
|
||||||
|
async: 1
|
||||||
|
poll: 1
|
||||||
|
ignore_errors: yes
|
Loading…
Reference in a new issue