# Copyright (c) 2013 AnsibleWorks, Inc. # This file is a utility Ansible plugin that is not part of the AnsibleWorks # or Ansible packages. It does not import any code from either package, nor # does its license apply to Ansible or AnsibleWorks. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of the nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import json import os import subprocess import sys class CallbackModule(object): ''' Callback module for logging ansible-playbook events to the database. ''' # These events should never have an associated play. EVENTS_WITHOUT_PLAY = [ 'playbook_on_start', 'playbook_on_stats', ] # These events should never have an associated task. EVENTS_WITHOUT_TASK = EVENTS_WITHOUT_PLAY + [ 'playbook_on_setup', 'playbook_on_notify', 'playbook_on_import_for_host', 'playbook_on_not_import_for_host', 'playbook_on_no_hosts_matched', 'playbook_on_no_hosts_remaining', ] def __init__(self): self.callback_script = os.getenv('ACOM_CALLBACK_EVENT_SCRIPT') def _log_event(self, event, **event_data): play = getattr(getattr(self, 'play', None), 'name', '') if play and event not in self.EVENTS_WITHOUT_PLAY: event_data['play'] = play task = getattr(getattr(self, 'task', None), 'name', '') if task and event not in self.EVENTS_WITHOUT_TASK: event_data['task'] = task event_data_json = json.dumps(event_data) cmdline = [self.callback_script, '-e', event, '-d', event_data_json] subprocess.check_call(cmdline) def on_any(self, *args, **kwargs): pass def runner_on_failed(self, host, res, ignore_errors=False): self._log_event('runner_on_failed', host=host, res=res, ignore_errors=ignore_errors) def runner_on_ok(self, host, res): self._log_event('runner_on_ok', host=host, res=res) def runner_on_error(self, host, msg): self._log_event('runner_on_error', host=host, msg=msg) def runner_on_skipped(self, host, item=None): self._log_event('runner_on_skipped', host=host, item=item) def runner_on_unreachable(self, host, res): self._log_event('runner_on_unreachable', host=host, res=res) def runner_on_no_hosts(self): self._log_event('runner_on_no_hosts') 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 runner_on_file_diff(self, host, diff): self._log_event('runner_on_file_diff', host=host, diff=diff) def playbook_on_start(self): self._log_event('playbook_on_start') def playbook_on_notify(self, host, handler): self._log_event('playbook_on_notify', host=host, handler=handler) def playbook_on_no_hosts_matched(self): self._log_event('playbook_on_no_hosts_matched') def playbook_on_no_hosts_remaining(self): self._log_event('playbook_on_no_hosts_remaining') def playbook_on_task_start(self, name, is_conditional): self._log_event('playbook_on_task_start', name=name, is_conditional=is_conditional) def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None): self._log_event('playbook_on_vars_prompt', varname=varname, private=private, prompt=prompt, encrypt=encrypt, confirm=confirm, salt_size=salt_size, salt=salt, default=default) def playbook_on_setup(self): self._log_event('playbook_on_setup') def playbook_on_import_for_host(self, host, imported_file): self._log_event('playbook_on_import_for_host', host=host, imported_file=imported_file) def playbook_on_not_import_for_host(self, host, missing_file): self._log_event('playbook_on_not_import_for_host', host=host, missing_file=missing_file) def playbook_on_play_start(self, pattern): self._log_event('playbook_on_play_start', pattern=pattern) def playbook_on_stats(self, stats): d = {} for attr in ('changed', 'dark', 'failures', 'ok', 'processed', 'skipped'): d[attr] = getattr(stats, attr) self._log_event('playbook_on_stats', **d)