make callbacks modular

This commit is contained in:
Michael DeHaan 2012-08-18 19:43:08 -04:00
parent 798c35d83e
commit 3017dc92d9
3 changed files with 143 additions and 42 deletions

View file

@ -127,6 +127,7 @@ def main(args):
pb.run() pb.run()
hosts = sorted(pb.stats.processed.keys()) hosts = sorted(pb.stats.processed.keys())
print callbacks.banner("PLAY RECAP") print callbacks.banner("PLAY RECAP")
playbook_cb.on_stats(pb.stats)
for h in hosts: for h in hosts:
t = pb.stats.summarize(h) t = pb.stats.summarize(h)
print "%-30s : %s %s %s %s " % ( print "%-30s : %s %s %s %s " % (

View file

@ -20,14 +20,30 @@ import sys
import getpass import getpass
import os import os
import subprocess import subprocess
import os.path
from ansible.color import stringc from ansible.color import stringc
dirname = os.path.dirname(__file__)
callbacks = utils.import_plugins(os.path.join(dirname, 'callbacks'))
callbacks = [ c.CallbackModule() for c in callbacks.values() ]
cowsay = None cowsay = None
if os.path.exists("/usr/bin/cowsay"): if os.path.exists("/usr/bin/cowsay"):
cowsay = "/usr/bin/cowsay" cowsay = "/usr/bin/cowsay"
elif os.path.exists("/usr/games/cowsay"): elif os.path.exists("/usr/games/cowsay"):
cowsay = "/usr/games/cowsay" cowsay = "/usr/games/cowsay"
def call_callback_module(method_name, *args, **kwargs):
for callback_plugin in callbacks:
methods = [
getattr(callback_plugin, method_name, None),
getattr(callback_plugin, 'on_any', None)
]
for method in methods:
if method is not None:
method(*args, **kwargs)
def vv(msg, host=None): def vv(msg, host=None):
return verbose(msg, host=host, caplevel=1) return verbose(msg, host=host, caplevel=1)
@ -161,31 +177,31 @@ class DefaultRunnerCallbacks(object):
pass pass
def on_failed(self, host, res, ignore_errors=False): def on_failed(self, host, res, ignore_errors=False):
pass call_callback_module('runner_on_failed', host, res, ignore_errors=ignore_errors)
def on_ok(self, host, res): def on_ok(self, host, res):
pass call_callback_module('runner_on_ok', host, res)
def on_error(self, host, msg): def on_error(self, host, msg):
pass call_callback_module('runner_on_error', host, msg)
def on_skipped(self, host): def on_skipped(self, host, item=None):
pass call_callback_module('runner_on_skipped', host, item=item)
def on_unreachable(self, host, res): def on_unreachable(self, host, res):
pass call_callback_module('runner_on_unreachable', host, res)
def on_no_hosts(self): def on_no_hosts(self):
pass call_callback_module('runner_on_no_hosts')
def on_async_poll(self, host, res, jid, clock): def on_async_poll(self, host, res, jid, clock):
pass call_callback_module('runner_on_async_poll', host, res, jid, clock)
def on_async_ok(self, host, res, jid): def on_async_ok(self, host, res, jid):
pass call_callback_module('runner_on_async_ok', host, res, jid)
def on_async_failed(self, host, res, jid): def on_async_failed(self, host, res, jid):
pass call_callback_module('runner_on_async_failed', host, res, jid)
######################################################################## ########################################################################
@ -193,21 +209,19 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
''' callbacks for use by /usr/bin/ansible ''' ''' callbacks for use by /usr/bin/ansible '''
def __init__(self): def __init__(self):
# set by /usr/bin/ansible later # set by /usr/bin/ansible later
self.options = None self.options = None
self._async_notified = {} self._async_notified = {}
def on_failed(self, host, res, ignore_errors=False): def on_failed(self, host, res, ignore_errors=False):
self._on_any(host,res) self._on_any(host,res)
super(CliRunnerCallbacks, self).on_failed(host, res, ignore_errors=ignore_errors)
def on_ok(self, host, res): def on_ok(self, host, res):
self._on_any(host,res) self._on_any(host,res)
super(CliRunnerCallbacks, self).on_ok(host, res)
def on_unreachable(self, host, res): def on_unreachable(self, host, res):
if type(res) == dict: if type(res) == dict:
res = res.get('msg','') res = res.get('msg','')
print "%s | FAILED => %s" % (host, res) print "%s | FAILED => %s" % (host, res)
@ -216,36 +230,36 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
self.options.tree, host, self.options.tree, host,
utils.jsonify(dict(failed=True, msg=res),format=True) utils.jsonify(dict(failed=True, msg=res),format=True)
) )
super(CliRunnerCallbacks, self).on_unreachable(host, res)
def on_skipped(self, host): def on_skipped(self, host):
pass super(CliRunnerCallbacks, self).on_skipped(host, res)
def on_error(self, host, err): def on_error(self, host, err):
print >>sys.stderr, "err: [%s] => %s\n" % (host, err) print >>sys.stderr, "err: [%s] => %s\n" % (host, err)
super(CliRunnerCallbacks, self).on_error(host, err)
def on_no_hosts(self): def on_no_hosts(self):
print >>sys.stderr, "no hosts matched\n" print >>sys.stderr, "no hosts matched\n"
super(CliRunnerCallbacks, self).on_no_hosts()
def on_async_poll(self, host, res, jid, clock): def on_async_poll(self, host, res, jid, clock):
if jid not in self._async_notified: if jid not in self._async_notified:
self._async_notified[jid] = clock + 1 self._async_notified[jid] = clock + 1
if self._async_notified[jid] > clock: if self._async_notified[jid] > clock:
self._async_notified[jid] = clock self._async_notified[jid] = clock
print "<job %s> polling, %ss remaining"%(jid, clock) print "<job %s> polling, %ss remaining"%(jid, clock)
super(CliRunnerCallbacks, self).on_async_poll(host, res, jid, clock)
def on_async_ok(self, host, res, jid): def on_async_ok(self, host, res, jid):
print "<job %s> finished on %s => %s"%(jid, host, utils.jsonify(res,format=True)) print "<job %s> finished on %s => %s"%(jid, host, utils.jsonify(res,format=True))
super(CliRunnerCallbacks, self).on_async_poll(host, res, jid)
def on_async_failed(self, host, res, jid): def on_async_failed(self, host, res, jid):
print "<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True)) print "<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True))
super(CliRunnerCallbacks, self).on_async_failed(host,res,jid)
def _on_any(self, host, result): def _on_any(self, host, result):
print host_report_msg(host, self.options.module_name, result, self.options.one_line) print host_report_msg(host, self.options.module_name, result, self.options.one_line)
if self.options.tree: if self.options.tree:
utils.write_tree_file(self.options.tree, host, utils.jsonify(result,format=True)) utils.write_tree_file(self.options.tree, host, utils.jsonify(result,format=True))
@ -256,37 +270,32 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
''' callbacks used for Runner() from /usr/bin/ansible-playbook ''' ''' callbacks used for Runner() from /usr/bin/ansible-playbook '''
def __init__(self, stats, verbose=utils.VERBOSITY): def __init__(self, stats, verbose=utils.VERBOSITY):
self.verbose = verbose self.verbose = verbose
self.stats = stats self.stats = stats
self._async_notified = {} self._async_notified = {}
def on_unreachable(self, host, msg): def on_unreachable(self, host, msg):
item = None item = None
if type(msg) == dict: if type(msg) == dict:
item = msg.get('item', None) item = msg.get('item', None)
if item: if item:
print "fatal: [%s] => (item=%s) => %s" % (host, item, msg) print "fatal: [%s] => (item=%s) => %s" % (host, item, msg)
else: else:
print "fatal: [%s] => %s" % (host, msg) print "fatal: [%s] => %s" % (host, msg)
super(PlaybookRunnerCallbacks, self).on_unreachable(host, msg)
def on_failed(self, host, results, ignore_errors=False): def on_failed(self, host, results, ignore_errors=False):
item = results.get('item', None) item = results.get('item', None)
if item: if item:
msg = "failed: [%s] => (item=%s) => %s" % (host, item, utils.jsonify(results)) msg = "failed: [%s] => (item=%s) => %s" % (host, item, utils.jsonify(results))
else: else:
msg = "failed: [%s] => %s" % (host, utils.jsonify(results)) msg = "failed: [%s] => %s" % (host, utils.jsonify(results))
print stringc(msg, 'red') print stringc(msg, 'red')
if ignore_errors: if ignore_errors:
print stringc("...ignoring", 'yellow') print stringc("...ignoring", 'yellow')
super(PlaybookRunnerCallbacks, self).on_failed(host, results, ignore_errors=ignore_errors)
def on_ok(self, host, host_result): def on_ok(self, host, host_result):
item = host_result.get('item', None) item = host_result.get('item', None)
# show verbose output for non-setup module results if --verbose is used # show verbose output for non-setup module results if --verbose is used
@ -310,6 +319,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
print stringc(msg, 'green') print stringc(msg, 'green')
else: else:
print stringc(msg, 'yellow') print stringc(msg, 'yellow')
super(PlaybookRunnerCallbacks, self).on_ok(host, host_result)
def on_error(self, host, err): def on_error(self, host, err):
@ -322,38 +332,39 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
msg = stringc(msg, 'red') msg = stringc(msg, 'red')
print >>sys.stderr, msg print >>sys.stderr, msg
super(PlaybookRunnerCallbacks, self).on_error(host, err)
def on_skipped(self, host, item=None): def on_skipped(self, host, item=None):
msg = '' msg = ''
if item: if item:
msg = "skipping: [%s] => (item=%s)" % (host, item) msg = "skipping: [%s] => (item=%s)" % (host, item)
else: else:
msg = "skipping: [%s]" % host msg = "skipping: [%s]" % host
print stringc(msg, 'yellow') print stringc(msg, 'yellow')
super(PlaybookRunnerCallbacks, self).on_skipped(host, item=None)
def on_no_hosts(self): def on_no_hosts(self):
print stringc("no hosts matched or remaining\n", 'red') print stringc("no hosts matched or remaining\n", 'red')
super(PlaybookRunnerCallbacks, self).on_no_hosts()
def on_async_poll(self, host, res, jid, clock): def on_async_poll(self, host, res, jid, clock):
if jid not in self._async_notified: if jid not in self._async_notified:
self._async_notified[jid] = clock + 1 self._async_notified[jid] = clock + 1
if self._async_notified[jid] > clock: if self._async_notified[jid] > clock:
self._async_notified[jid] = clock self._async_notified[jid] = clock
msg = "<job %s> polling, %ss remaining"%(jid, clock) msg = "<job %s> polling, %ss remaining"%(jid, clock)
print stringc(msg, 'cyan') print stringc(msg, 'cyan')
super(PlaybookRunnerCallbacks, self).on_async_poll(host,res,jid,clock)
def on_async_ok(self, host, res, jid): def on_async_ok(self, host, res, jid):
msg = "<job %s> finished on %s"%(jid, host) msg = "<job %s> finished on %s"%(jid, host)
print stringc(msg, 'cyan') print stringc(msg, 'cyan')
super(PlaybookRunnerCallbacks, self).on_async_ok(host, res, jid)
def on_async_failed(self, host, res, jid): def on_async_failed(self, host, res, jid):
msg = "<job %s> FAILED on %s"%(jid, host) msg = "<job %s> FAILED on %s"%(jid, host)
print stringc(msg, 'red') print stringc(msg, 'red')
super(PlaybookRunnerCallbacks, self).on_async_failed(host,res,jid)
######################################################################## ########################################################################
@ -366,19 +377,17 @@ class PlaybookCallbacks(object):
self.verbose = verbose self.verbose = verbose
def on_start(self): def on_start(self):
call_callback_module('playbook_on_start')
pass
def on_notify(self, host, handler): def on_notify(self, host, handler):
call_callback_module('playbook_on_notify', host, handler)
pass
def on_task_start(self, name, is_conditional): def on_task_start(self, name, is_conditional):
msg = "TASK: [%s]" % name msg = "TASK: [%s]" % name
if is_conditional: if is_conditional:
msg = "NOTIFIED: [%s]" % name msg = "NOTIFIED: [%s]" % name
print banner(msg) print banner(msg)
call_callback_module('playbook_on_task_start', name, is_conditional)
def on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None): def on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None):
@ -406,24 +415,29 @@ class PlaybookCallbacks(object):
if encrypt: if encrypt:
result = utils.do_encrypt(result,encrypt,salt_size,salt) result = utils.do_encrypt(result,encrypt,salt_size,salt)
call_callback_module('playbook_on_vars_prompt', varname, private=private, prompt=prompt, encrypt=encrypt, confirm=confirm, salt_size=salt_size, salt=None)
return result return result
def on_setup(self): def on_setup(self):
print banner("GATHERING FACTS") print banner("GATHERING FACTS")
call_callback_module('playbook_on_setup')
def on_import_for_host(self, host, imported_file): def on_import_for_host(self, host, imported_file):
msg = "%s: importing %s" % (host, imported_file) msg = "%s: importing %s" % (host, imported_file)
print stringc(msg, 'cyan') print stringc(msg, 'cyan')
call_callback_module('playbook_on_import_for_host', host, imported_file)
def on_not_import_for_host(self, host, missing_file): def on_not_import_for_host(self, host, missing_file):
msg = "%s: not importing file: %s" % (host, missing_file) msg = "%s: not importing file: %s" % (host, missing_file)
print stringc(msg, 'cyan') print stringc(msg, 'cyan')
call_callback_module('playbook_on_not_import_for_host', host, missing_file)
def on_play_start(self, pattern): def on_play_start(self, pattern):
print banner("PLAY [%s]" % pattern) print banner("PLAY [%s]" % pattern)
call_callback_module('playbook_on_play_start', pattern)
def on_stats(self, stats):
call_callback_module('playbook_on_stats', stats)

View file

@ -0,0 +1,86 @@
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
class CallbackModule(object):
"""
this is an example ansible callback file that does nothing. You can drop
other classes in the same directory to define your own handlers. Methods
you do not use can be omitted.
example uses include: logging, emailing, storing info, etc
"""
def on_any(self, *args, **kwargs):
pass
def runner_on_failed(self, host, res, ignore_errors=False):
pass
def runner_on_ok(self, host, res):
pass
def runner_on_error(self, host, msg):
pass
def runner_on_skipped(self, host, item=None):
pass
def runner_on_unreachable(self, host, res):
pass
def runner_on_no_hosts(self):
pass
def runner_on_async_poll(self, host, res, jid, clock):
pass
def runner_on_async_ok(self, host, res, jid):
pass
def runner_on_async_failed(self, host, res, jid):
pass
def playbook_on_start(self):
pass
def playbook_on_notify(self, host, handler):
pass
def playbook_on_task_start(self, name, is_conditional):
pass
def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None):
pass
def playbook_on_setup(self):
pass
def playbook_on_import_for_host(self, host, imported_file):
pass
def playbook_on_not_import_for_host(self, host, missing_file):
pass
def playbook_on_play_start(self, pattern):
pass
def playbook_on_stats(self, stats):
pass