diff --git a/awx/lib/tower_display_callback/module.py b/awx/lib/tower_display_callback/module.py index e5b4e21713..02b30ef2bc 100644 --- a/awx/lib/tower_display_callback/module.py +++ b/awx/lib/tower_display_callback/module.py @@ -196,7 +196,6 @@ class BaseCallbackModule(CallbackBase): def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None): - return # not currently used in v2 (yet) - FIXME: Confirm this is still the case? event_data = dict( varname=varname, private=private, @@ -239,17 +238,17 @@ class BaseCallbackModule(CallbackBase): super(BaseCallbackModule, self).v2_playbook_on_play_start(play) def v2_playbook_on_import_for_host(self, result, imported_file): - return # not currently used in v2 (yet) / don't care about recording this one + # NOTE: Not used by Ansible 2.x. with self.capture_event_data('playbook_on_import_for_host'): super(BaseCallbackModule, self).v2_playbook_on_import_for_host(result, imported_file) def v2_playbook_on_not_import_for_host(self, result, missing_file): - return # not currently used in v2 (yet) / don't care about recording this one + # NOTE: Not used by Ansible 2.x. with self.capture_event_data('playbook_on_not_import_for_host'): super(BaseCallbackModule, self).v2_playbook_on_not_import_for_host(result, missing_file) def v2_playbook_on_setup(self): - return # not currently used in v2 (yet) + # NOTE: Not used by Ansible 2.x. with self.capture_event_data('playbook_on_setup'): super(BaseCallbackModule, self).v2_playbook_on_setup() @@ -257,6 +256,9 @@ class BaseCallbackModule(CallbackBase): # FIXME: Flag task path output as vv. task_uuid = str(task._uuid) if task_uuid in self.task_uuids: + # FIXME: When this task UUID repeats, it means the play is using the + # free strategy, so different hosts may be running different tasks + # within a play. return self.task_uuids.add(task_uuid) self.set_task(task) @@ -270,27 +272,27 @@ class BaseCallbackModule(CallbackBase): super(BaseCallbackModule, self).v2_playbook_on_task_start(task, is_conditional) def v2_playbook_on_cleanup_task_start(self, task): - # re-using playbook_on_task_start event here for this v2-specific - # event, though we may consider any changes necessary to distinguish - # this from a normal task FIXME! + # NOTE: Not used by Ansible 2.x. self.set_task(task) event_data = dict( task=task, name=task.get_name(), uuid=str(task._uuid), + is_conditional=True, ) with self.capture_event_data('playbook_on_task_start', **event_data): super(BaseCallbackModule, self).v2_playbook_on_cleanup_task_start(task) def v2_playbook_on_handler_task_start(self, task): - # re-using playbook_on_task_start event here for this v2-specific - # event, though we may consider any changes necessary to distinguish - # this from a normal task FIXME! + # NOTE: Re-using playbook_on_task_start event for this v2-specific + # event, but setting is_conditional=True, which is how v1 identified a + # task run as a handler. self.set_task(task) event_data = dict( task=task, name=task.get_name(), uuid=str(task._uuid), + is_conditional=True, ) with self.capture_event_data('playbook_on_task_start', **event_data): super(BaseCallbackModule, self).v2_playbook_on_handler_task_start(task) @@ -304,8 +306,9 @@ class BaseCallbackModule(CallbackBase): super(BaseCallbackModule, self).v2_playbook_on_no_hosts_remaining() def v2_playbook_on_notify(self, result, handler): + # NOTE: Not used by Ansible 2.x. event_data = dict( - host=result._host.name, + host=result._host.get_name(), task=result._task, handler=handler, ) @@ -329,7 +332,7 @@ class BaseCallbackModule(CallbackBase): def v2_runner_on_ok(self, result): # FIXME: Display detailed results or not based on verbosity. event_data = dict( - host=result._host.name, + host=result._host.get_name(), remote_addr=result._host.address, task=result._task, res=result._result, @@ -341,7 +344,8 @@ class BaseCallbackModule(CallbackBase): def v2_runner_on_failed(self, result, ignore_errors=False): # FIXME: Add verbosity for exception/results output. event_data = dict( - host=result._host.name, + host=result._host.get_name(), + remote_addr=result._host.address, res=result._result, task=result._task, ignore_errors=ignore_errors, @@ -350,12 +354,10 @@ class BaseCallbackModule(CallbackBase): with self.capture_event_data('runner_on_failed', **event_data): super(BaseCallbackModule, self).v2_runner_on_failed(result, ignore_errors) - def v2_runner_on_error(self, result): - pass # Not implemented in v2. - def v2_runner_on_skipped(self, result): event_data = dict( - host=result._host.name, + host=result._host.get_name(), + remote_addr=result._host.address, task=result._task, event_loop=result._task.loop if hasattr(result._task, 'loop') else None, ) @@ -364,7 +366,8 @@ class BaseCallbackModule(CallbackBase): def v2_runner_on_unreachable(self, result): event_data = dict( - host=result._host.name, + host=result._host.get_name(), + remote_addr=result._host.address, task=result._task, res=result._result, ) @@ -372,25 +375,69 @@ class BaseCallbackModule(CallbackBase): super(BaseCallbackModule, self).v2_runner_on_unreachable(result) def v2_runner_on_no_hosts(self, task): + # NOTE: Not used by Ansible 2.x. event_data = dict( task=task, ) with self.capture_event_data('runner_on_no_hosts', **event_data): super(BaseCallbackModule, self).v2_runner_on_no_hosts(task) - def v2_runner_on_file_diff(self, result, diff): - # FIXME: Ignore file diff for ad hoc commands? + def v2_runner_on_async_poll(self, result): + # NOTE: Not used by Ansible 2.x. event_data = dict( - host=result._host.name, + host=result._host.get_name(), + task=result._task, + res=result._result, + jid=result._result.get('ansible_job_id'), + ) + with self.capture_event_data('runner_on_async_poll', **event_data): + super(BaseCallbackModule, self).v2_runner_on_async_poll(result) + + def v2_runner_on_async_ok(self, result): + # NOTE: Not used by Ansible 2.x. + event_data = dict( + host=result._host.get_name(), + task=result._task, + res=result._result, + jid=result._result.get('ansible_job_id'), + ) + with self.capture_event_data('runner_on_async_ok', **event_data): + super(BaseCallbackModule, self).v2_runner_on_async_ok(result) + + def v2_runner_on_async_failed(self, result): + # NOTE: Not used by Ansible 2.x. + event_data = dict( + host=result._host.get_name(), + task=result._task, + res=result._result, + jid=result._result.get('ansible_job_id'), + ) + with self.capture_event_data('runner_on_async_failed', **event_data): + super(BaseCallbackModule, self).v2_runner_on_async_failed(result) + + def v2_runner_on_file_diff(self, result, diff): + # NOTE: Not used by Ansible 2.x. + event_data = dict( + host=result._host.get_name(), task=result._task, diff=diff, ) with self.capture_event_data('runner_on_file_diff', **event_data): super(BaseCallbackModule, self).v2_runner_on_file_diff(result, diff) + def v2_on_file_diff(self, result): + # NOTE: Logged as runner_on_file_diff. + event_data = dict( + host=result._host.get_name(), + task=result._task, + diff=result._result.get('diff'), + ) + with self.capture_event_data('runner_on_file_diff', **event_data): + super(BaseCallbackModule, self).v2_on_file_diff(result) + def v2_runner_item_on_ok(self, result): event_data = dict( - host=result._host.name, + host=result._host.get_name(), task=result._task, res=result._result, ) @@ -399,7 +446,7 @@ class BaseCallbackModule(CallbackBase): def v2_runner_item_on_failed(self, result): event_data = dict( - host=result._host.name, + host=result._host.get_name(), task=result._task, res=result._result, ) @@ -408,24 +455,21 @@ class BaseCallbackModule(CallbackBase): def v2_runner_item_on_skipped(self, result): event_data = dict( - host=result._host.name, + host=result._host.get_name(), task=result._task, res=result._result, ) with self.capture_event_data('runner_item_on_skipped', **event_data): super(BaseCallbackModule, self).v2_runner_item_on_skipped(result) - # V2 does not use the _on_async callbacks (yet). - - def runner_on_async_poll(self, host, res, jid, clock): - self._log_event('runner_on_async_poll', host=host, res=res, jid=jid, - clock=clock) - - def runner_on_async_ok(self, host, res, jid): - self._log_event('runner_on_async_ok', host=host, res=res, jid=jid) - - def runner_on_async_failed(self, host, res, jid): - self._log_event('runner_on_async_failed', host=host, res=res, jid=jid) + def v2_runner_retry(self, result): + event_data = dict( + host=result._host.get_name(), + task=result._task, + res=result._result, + ) + with self.capture_event_data('runner_retry', **event_data): + super(BaseCallbackModule, self).v2_runner_retry(result) class TowerDefaultCallbackModule(BaseCallbackModule, DefaultCallbackModule): diff --git a/awx/main/migrations/0045_v310_job_event_stdout.py b/awx/main/migrations/0045_v310_job_event_stdout.py index 27bce05632..e3325ddb6b 100644 --- a/awx/main/migrations/0045_v310_job_event_stdout.py +++ b/awx/main/migrations/0045_v310_job_event_stdout.py @@ -79,7 +79,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='jobevent', name='event', - field=models.CharField(max_length=100, choices=[(b'runner_on_failed', 'Host Failed'), (b'runner_on_ok', 'Host OK'), (b'runner_on_error', 'Host Failure'), (b'runner_on_skipped', 'Host Skipped'), (b'runner_on_unreachable', 'Host Unreachable'), (b'runner_on_no_hosts', 'No Hosts Remaining'), (b'runner_on_async_poll', 'Host Polling'), (b'runner_on_async_ok', 'Host Async OK'), (b'runner_on_async_failed', 'Host Async Failure'), (b'runner_on_file_diff', 'File Difference'), (b'playbook_on_start', 'Playbook Started'), (b'playbook_on_notify', 'Running Handlers'), (b'playbook_on_no_hosts_matched', 'No Hosts Matched'), (b'playbook_on_no_hosts_remaining', 'No Hosts Remaining'), (b'playbook_on_task_start', 'Task Started'), (b'playbook_on_vars_prompt', 'Variables Prompted'), (b'playbook_on_setup', 'Gathering Facts'), (b'playbook_on_import_for_host', 'internal: on Import for Host'), (b'playbook_on_not_import_for_host', 'internal: on Not Import for Host'), (b'playbook_on_play_start', 'Play Started'), (b'playbook_on_stats', 'Playbook Complete'), (b'debug', 'Debug'), (b'verbose', 'Verbose'), (b'deprecated', 'Deprecated'), (b'warning', 'Warning'), (b'system_warning', 'System Warning'), (b'error', 'Error')]), + field=models.CharField(max_length=100, choices=[(b'runner_on_failed', 'Host Failed'), (b'runner_on_ok', 'Host OK'), (b'runner_on_error', 'Host Failure'), (b'runner_on_skipped', 'Host Skipped'), (b'runner_on_unreachable', 'Host Unreachable'), (b'runner_on_no_hosts', 'No Hosts Remaining'), (b'runner_on_async_poll', 'Host Polling'), (b'runner_on_async_ok', 'Host Async OK'), (b'runner_on_async_failed', 'Host Async Failure'), (b'runner_item_on_ok', 'Item OK'), (b'runner_item_on_failed', 'Item Failed'), (b'runner_item_on_skipped', 'Item Skipped'), (b'runner_retry', 'Host Retry'), (b'runner_on_file_diff', 'File Difference'), (b'playbook_on_start', 'Playbook Started'), (b'playbook_on_notify', 'Running Handlers'), (b'playbook_on_include', 'Including File'), (b'playbook_on_no_hosts_matched', 'No Hosts Matched'), (b'playbook_on_no_hosts_remaining', 'No Hosts Remaining'), (b'playbook_on_task_start', 'Task Started'), (b'playbook_on_vars_prompt', 'Variables Prompted'), (b'playbook_on_setup', 'Gathering Facts'), (b'playbook_on_import_for_host', 'internal: on Import for Host'), (b'playbook_on_not_import_for_host', 'internal: on Not Import for Host'), (b'playbook_on_play_start', 'Play Started'), (b'playbook_on_stats', 'Playbook Complete'), (b'debug', 'Debug'), (b'verbose', 'Verbose'), (b'deprecated', 'Deprecated'), (b'warning', 'Warning'), (b'system_warning', 'System Warning'), (b'error', 'Error')]), ), migrations.AlterUniqueTogether( name='adhoccommandevent', diff --git a/awx/main/models/ad_hoc_commands.py b/awx/main/models/ad_hoc_commands.py index c81531d22c..65f40427b0 100644 --- a/awx/main/models/ad_hoc_commands.py +++ b/awx/main/models/ad_hoc_commands.py @@ -260,16 +260,16 @@ class AdHocCommandEvent(CreatedModifiedModel): ('runner_on_ok', _('Host OK'), False), ('runner_on_unreachable', _('Host Unreachable'), True), # Tower won't see no_hosts (check is done earlier without callback). - #('runner_on_no_hosts', _('No Hosts Matched'), False), + # ('runner_on_no_hosts', _('No Hosts Matched'), False), # Tower will see skipped (when running in check mode for a module that # does not support check mode). ('runner_on_skipped', _('Host Skipped'), False), - # Tower does not support async for ad hoc commands. - #('runner_on_async_poll', _('Host Polling'), False), - #('runner_on_async_ok', _('Host Async OK'), False), - #('runner_on_async_failed', _('Host Async Failure'), True), - # Tower does not yet support --diff mode - #('runner_on_file_diff', _('File Difference'), False), + # Tower does not support async for ad hoc commands (not used in v2). + # ('runner_on_async_poll', _('Host Polling'), False), + # ('runner_on_async_ok', _('Host Async OK'), False), + # ('runner_on_async_failed', _('Host Async Failure'), True), + # Tower does not yet support --diff mode. + # ('runner_on_file_diff', _('File Difference'), False), # Additional event types for captured stdout not directly related to # runner events. diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 74a8395a2a..a15e291d78 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -936,11 +936,12 @@ class JobEvent(CreatedModifiedModel): # - playbook_on_vars_prompt (for each play, but before play starts, we # currently don't handle responding to these prompts) # - playbook_on_play_start (once for each play) - # - playbook_on_import_for_host - # - playbook_on_not_import_for_host + # - playbook_on_import_for_host (not logged, not used for v2) + # - playbook_on_not_import_for_host (not logged, not used for v2) # - playbook_on_no_hosts_matched # - playbook_on_no_hosts_remaining - # - playbook_on_setup + # - playbook_on_include (only v2 - only used for handlers?) + # - playbook_on_setup (not used for v2) # - runner_on* # - playbook_on_task_start (once for each task within a play) # - runner_on_failed @@ -948,12 +949,16 @@ class JobEvent(CreatedModifiedModel): # - runner_on_error (not used for v2) # - runner_on_skipped # - runner_on_unreachable - # - runner_on_no_hosts - # - runner_on_async_poll - # - runner_on_async_ok - # - runner_on_async_failed - # - runner_on_file_diff - # - playbook_on_notify (once for each notification from the play) + # - runner_on_no_hosts (not used for v2) + # - runner_on_async_poll (not used for v2) + # - runner_on_async_ok (not used for v2) + # - runner_on_async_failed (not used for v2) + # - runner_on_file_diff (v2 event is v2_on_file_diff) + # - runner_item_on_ok (v2 only) + # - runner_item_on_failed (v2 only) + # - runner_item_on_skipped (v2 only) + # - runner_retry (v2 only) + # - playbook_on_notify (once for each notification from the play, not used for v2) # - playbook_on_stats EVENT_TYPES = [ @@ -967,19 +972,22 @@ class JobEvent(CreatedModifiedModel): (3, 'runner_on_async_poll', _('Host Polling'), False), (3, 'runner_on_async_ok', _('Host Async OK'), False), (3, 'runner_on_async_failed', _('Host Async Failure'), True), - # Tower does not yet support --diff mode + (3, 'runner_item_on_ok', _('Item OK'), False), + (3, 'runner_item_on_failed', _('Item Failed'), True), + (3, 'runner_item_on_skipped', _('Item Skipped'), False), + (3, 'runner_retry', _('Host Retry'), False), + # Tower does not yet support --diff mode. (3, 'runner_on_file_diff', _('File Difference'), False), (0, 'playbook_on_start', _('Playbook Started'), False), (2, 'playbook_on_notify', _('Running Handlers'), False), + (2, 'playbook_on_include', _('Including File'), False), (2, 'playbook_on_no_hosts_matched', _('No Hosts Matched'), False), (2, 'playbook_on_no_hosts_remaining', _('No Hosts Remaining'), False), (2, 'playbook_on_task_start', _('Task Started'), False), # Tower does not yet support vars_prompt (and will probably hang :) (1, 'playbook_on_vars_prompt', _('Variables Prompted'), False), (2, 'playbook_on_setup', _('Gathering Facts'), False), - # callback will not record this (2, 'playbook_on_import_for_host', _('internal: on Import for Host'), False), - # callback will not record this (2, 'playbook_on_not_import_for_host', _('internal: on Not Import for Host'), False), (1, 'playbook_on_play_start', _('Play Started'), False), (1, 'playbook_on_stats', _('Playbook Complete'), False),