commit
d81ea397aa
62 changed files with 259 additions and 357 deletions
|
@ -10,9 +10,10 @@ include examples/ansible.cfg
|
||||||
include lib/ansible/module_utils/powershell.ps1
|
include lib/ansible/module_utils/powershell.ps1
|
||||||
recursive-include lib/ansible/modules *
|
recursive-include lib/ansible/modules *
|
||||||
recursive-include docs *
|
recursive-include docs *
|
||||||
recursive-include plugins *
|
|
||||||
include Makefile
|
include Makefile
|
||||||
include VERSION
|
include VERSION
|
||||||
include MANIFEST.in
|
include MANIFEST.in
|
||||||
|
include contrib/README.md
|
||||||
|
include contrib/inventory *
|
||||||
prune lib/ansible/modules/core/.git
|
prune lib/ansible/modules/core/.git
|
||||||
prune lib/ansible/modules/extras/.git
|
prune lib/ansible/modules/extras/.git
|
||||||
|
|
17
contrib/README.md
Normal file
17
contrib/README.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
inventory
|
||||||
|
=========
|
||||||
|
|
||||||
|
Inventory scripts allow you to store your hosts, groups, and variables in any way
|
||||||
|
you like. Examples include discovering inventory from EC2 or pulling it from
|
||||||
|
Cobbler. These could also be used to interface with LDAP or database.
|
||||||
|
|
||||||
|
chmod +x an inventory plugin and either name it /etc/ansible/hosts or use ansible
|
||||||
|
with -i to designate the path to the script. You might also need to copy a configuration
|
||||||
|
file with the same name and/or set environment variables, the scripts or configuration
|
||||||
|
files have more details.
|
||||||
|
|
||||||
|
contributions welcome
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Send in pull requests to add plugins of your own. The sky is the limit!
|
||||||
|
|
0
plugins/inventory/nova.py → contrib/inventory/nova.py
Normal file → Executable file
0
plugins/inventory/nova.py → contrib/inventory/nova.py
Normal file → Executable file
0
plugins/inventory/rax.py → contrib/inventory/rax.py
Normal file → Executable file
0
plugins/inventory/rax.py → contrib/inventory/rax.py
Normal file → Executable file
|
@ -296,7 +296,7 @@ class TaskQueueManager:
|
||||||
continue
|
continue
|
||||||
methods = [
|
methods = [
|
||||||
getattr(callback_plugin, method_name, None),
|
getattr(callback_plugin, method_name, None),
|
||||||
getattr(callback_plugin, 'on_any', None)
|
getattr(callback_plugin, 'v2_on_any', None)
|
||||||
]
|
]
|
||||||
for method in methods:
|
for method in methods:
|
||||||
if method is not None:
|
if method is not None:
|
||||||
|
|
|
@ -36,9 +36,7 @@ class CallbackBase:
|
||||||
self._display = display
|
self._display = display
|
||||||
|
|
||||||
def set_connection_info(self, conn_info):
|
def set_connection_info(self, conn_info):
|
||||||
# FIXME: this is a temporary hack, as the connection info object
|
pass
|
||||||
# should be created early and passed down through objects
|
|
||||||
self._display._verbosity = conn_info.verbosity
|
|
||||||
|
|
||||||
def on_any(self, *args, **kwargs):
|
def on_any(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -100,3 +98,90 @@ class CallbackBase:
|
||||||
def playbook_on_stats(self, stats):
|
def playbook_on_stats(self, stats):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
####### V2 METHODS, by default they call v1 counterparts if possible ######
|
||||||
|
def v2_on_any(self, *args, **kwargs):
|
||||||
|
self.on_any(args, kwargs)
|
||||||
|
|
||||||
|
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.runner_on_failed(host, result._result, ignore_errors)
|
||||||
|
|
||||||
|
def v2_runner_on_ok(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.runner_on_ok(host, result._result)
|
||||||
|
|
||||||
|
def v2_runner_on_skipped(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
#FIXME, get item to pass through
|
||||||
|
item = None
|
||||||
|
self.runner_on_skipped(host, item)
|
||||||
|
|
||||||
|
def v2_runner_on_unreachable(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.runner_on_unreachable(host, result._result)
|
||||||
|
|
||||||
|
def v2_runner_on_no_hosts(self, task):
|
||||||
|
self.runner_on_no_hosts()
|
||||||
|
|
||||||
|
def v2_runner_on_async_poll(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
jid = result._result.get('ansible_job_id')
|
||||||
|
#FIXME, get real clock
|
||||||
|
clock = 0
|
||||||
|
self.runner_on_async_poll(host, result._result, jid, clock)
|
||||||
|
|
||||||
|
def v2_runner_on_async_ok(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
jid = result._result.get('ansible_job_id')
|
||||||
|
self.runner_on_async_ok(host, result._result, jid)
|
||||||
|
|
||||||
|
def v2_runner_on_async_failed(self, result):
|
||||||
|
host = result._host.get_name()
|
||||||
|
jid = result._result.get('ansible_job_id')
|
||||||
|
self.runner_on_async_failed(host, result._result, jid)
|
||||||
|
|
||||||
|
def v2_runner_on_file_diff(self, result, diff):
|
||||||
|
pass #no v1 correspondance
|
||||||
|
|
||||||
|
def v2_playbook_on_start(self):
|
||||||
|
self.playbook_on_start()
|
||||||
|
|
||||||
|
def v2_playbook_on_notify(self, result, handler):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.playbook_on_notify(host, handler)
|
||||||
|
|
||||||
|
def v2_playbook_on_no_hosts_matched(self):
|
||||||
|
self.playbook_on_no_hosts_matched()
|
||||||
|
|
||||||
|
def v2_playbook_on_no_hosts_remaining(self):
|
||||||
|
self.playbook_on_no_hosts_remaining()
|
||||||
|
|
||||||
|
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||||
|
self.playbook_on_task_start(task, is_conditional)
|
||||||
|
|
||||||
|
def v2_playbook_on_cleanup_task_start(self, task):
|
||||||
|
pass #no v1 correspondance
|
||||||
|
|
||||||
|
def v2_playbook_on_handler_task_start(self, task):
|
||||||
|
pass #no v1 correspondance
|
||||||
|
|
||||||
|
def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
|
||||||
|
self.playbook_on_vars_prompt(varname, private, prompt, encrypt, confirm, salt_size, salt, default)
|
||||||
|
|
||||||
|
def v2_playbook_on_setup(self):
|
||||||
|
self.playbook_on_setup()
|
||||||
|
|
||||||
|
def v2_playbook_on_import_for_host(self, result, imported_file):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.playbook_on_import_for_host(host, imported_file)
|
||||||
|
|
||||||
|
def v2_playbook_on_not_import_for_host(self, result, missing_file):
|
||||||
|
host = result._host.get_name()
|
||||||
|
self.playbook_on_not_import_for_host(host, missing_file)
|
||||||
|
|
||||||
|
def v2_playbook_on_play_start(self, play):
|
||||||
|
self.playbook_on_play_start(play.name)
|
||||||
|
|
||||||
|
def v2_playbook_on_stats(self, stats):
|
||||||
|
self.playbook_on_stats(stats)
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,23 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
from ansible.plugins.callback import CallbackBase
|
||||||
import time
|
|
||||||
import json
|
|
||||||
|
|
||||||
class CallbackModule(object):
|
class CallbackModule(CallbackBase):
|
||||||
"""
|
"""
|
||||||
This is a very trivial example of how any callback function can get at play and task objects.
|
This is a very trivial example of how any callback function can get at play and task objects.
|
||||||
play will be 'None' for runner invocations, and task will be None for 'setup' invocations.
|
play will be 'None' for runner invocations, and task will be None for 'setup' invocations.
|
||||||
"""
|
"""
|
||||||
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = 'aggregate'
|
||||||
|
|
||||||
def on_any(self, *args, **kwargs):
|
def v2_on_any(self, *args, **kwargs):
|
||||||
play = getattr(self, 'play', None)
|
i = 0
|
||||||
task = getattr(self, 'task', None)
|
self._display.display(" --- ARGS ")
|
||||||
print "play = %s, task = %s, args = %s, kwargs = %s" % (play,task,args,kwargs)
|
for a in args:
|
||||||
|
self._display.display(' %s: %s' % (i, a))
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self._display.display(" --- KWARGS ")
|
||||||
|
for k in kwargs:
|
||||||
|
self._display.display(' %s: %s' % (k, kwargs[k]))
|
|
@ -33,9 +33,6 @@ class CallbackModule(CallbackBase):
|
||||||
CALLBACK_VERSION = 2.0
|
CALLBACK_VERSION = 2.0
|
||||||
CALLBACK_TYPE = 'stdout'
|
CALLBACK_TYPE = 'stdout'
|
||||||
|
|
||||||
def v2_on_any(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||||
if 'exception' in result._result:
|
if 'exception' in result._result:
|
||||||
if self._display.verbosity < 3:
|
if self._display.verbosity < 3:
|
||||||
|
@ -67,7 +64,7 @@ class CallbackModule(CallbackBase):
|
||||||
msg = "ok: [%s]" % result._host.get_name()
|
msg = "ok: [%s]" % result._host.get_name()
|
||||||
color = 'green'
|
color = 'green'
|
||||||
|
|
||||||
if (self._display._verbosity > 0 or 'verbose_always' in result._result) and result._task.action not in ('setup', 'include'):
|
if (self._display.verbosity > 0 or 'verbose_always' in result._result) and result._task.action not in ('setup', 'include'):
|
||||||
indent = None
|
indent = None
|
||||||
if 'verbose_always' in result._result:
|
if 'verbose_always' in result._result:
|
||||||
indent = 4
|
indent = 4
|
||||||
|
@ -77,7 +74,7 @@ class CallbackModule(CallbackBase):
|
||||||
|
|
||||||
def v2_runner_on_skipped(self, result):
|
def v2_runner_on_skipped(self, result):
|
||||||
msg = "skipping: [%s]" % result._host.get_name()
|
msg = "skipping: [%s]" % result._host.get_name()
|
||||||
if self._display._verbosity > 0 or 'verbose_always' in result._result:
|
if self._display.verbosity > 0 or 'verbose_always' in result._result:
|
||||||
indent = None
|
indent = None
|
||||||
if 'verbose_always' in result._result:
|
if 'verbose_always' in result._result:
|
||||||
indent = 4
|
indent = 4
|
||||||
|
@ -88,27 +85,6 @@ class CallbackModule(CallbackBase):
|
||||||
def v2_runner_on_unreachable(self, result):
|
def v2_runner_on_unreachable(self, result):
|
||||||
self._display.display("fatal: [%s]: UNREACHABLE! => %s" % (result._host.get_name(), result._result), color='red')
|
self._display.display("fatal: [%s]: UNREACHABLE! => %s" % (result._host.get_name(), result._result), color='red')
|
||||||
|
|
||||||
def v2_runner_on_no_hosts(self, task):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_runner_on_async_poll(self, result):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_runner_on_async_ok(self, result):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_runner_on_async_failed(self, result):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_runner_on_file_diff(self, result, diff):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_notify(self, result, handler):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_no_hosts_matched(self):
|
def v2_playbook_on_no_hosts_matched(self):
|
||||||
self._display.display("skipping: no hosts matched", color='cyan')
|
self._display.display("skipping: no hosts matched", color='cyan')
|
||||||
|
|
||||||
|
@ -124,18 +100,6 @@ class CallbackModule(CallbackBase):
|
||||||
def v2_playbook_on_handler_task_start(self, task):
|
def v2_playbook_on_handler_task_start(self, task):
|
||||||
self._display.banner("RUNNING HANDLER [%s]" % task.get_name().strip())
|
self._display.banner("RUNNING HANDLER [%s]" % task.get_name().strip())
|
||||||
|
|
||||||
#def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
|
|
||||||
# pass
|
|
||||||
|
|
||||||
def v2_playbook_on_setup(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_import_for_host(self, result, imported_file):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_not_import_for_host(self, result, missing_file):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def v2_playbook_on_play_start(self, play):
|
def v2_playbook_on_play_start(self, play):
|
||||||
name = play.get_name().strip()
|
name = play.get_name().strip()
|
||||||
if not name:
|
if not name:
|
||||||
|
@ -144,7 +108,3 @@ class CallbackModule(CallbackBase):
|
||||||
msg = "PLAY [%s]" % name
|
msg = "PLAY [%s]" % name
|
||||||
|
|
||||||
self._display.banner(name)
|
self._display.banner(name)
|
||||||
|
|
||||||
def v2_playbook_on_stats(self, stats):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,15 @@ import os
|
||||||
import urllib
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
from ansible import utils
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import prettytable
|
import prettytable
|
||||||
HAS_PRETTYTABLE = True
|
HAS_PRETTYTABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_PRETTYTABLE = False
|
HAS_PRETTYTABLE = False
|
||||||
|
|
||||||
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
class CallbackModule(object):
|
class CallbackModule(CallbackBase):
|
||||||
"""This is an example ansible callback plugin that sends status
|
"""This is an example ansible callback plugin that sends status
|
||||||
updates to a HipChat channel during playbook execution.
|
updates to a HipChat channel during playbook execution.
|
||||||
|
|
||||||
|
@ -42,11 +41,16 @@ class CallbackModule(object):
|
||||||
prettytable
|
prettytable
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = 'notification'
|
||||||
|
|
||||||
|
def __init__(self, display):
|
||||||
|
|
||||||
|
super(CallbackModule, self).__init__(display)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if not HAS_PRETTYTABLE:
|
if not HAS_PRETTYTABLE:
|
||||||
self.disabled = True
|
self.disabled = True
|
||||||
utils.warning('The `prettytable` python module is not installed. '
|
self.display.warning('The `prettytable` python module is not installed. '
|
||||||
'Disabling the HipChat callback plugin.')
|
'Disabling the HipChat callback plugin.')
|
||||||
|
|
||||||
self.msg_uri = 'https://api.hipchat.com/v1/rooms/message'
|
self.msg_uri = 'https://api.hipchat.com/v1/rooms/message'
|
||||||
|
@ -57,7 +61,7 @@ class CallbackModule(object):
|
||||||
|
|
||||||
if self.token is None:
|
if self.token is None:
|
||||||
self.disabled = True
|
self.disabled = True
|
||||||
utils.warning('HipChat token could not be loaded. The HipChat '
|
self.display.warning('HipChat token could not be loaded. The HipChat '
|
||||||
'token can be provided using the `HIPCHAT_TOKEN` '
|
'token can be provided using the `HIPCHAT_TOKEN` '
|
||||||
'environment variable.')
|
'environment variable.')
|
||||||
|
|
||||||
|
@ -80,63 +84,8 @@ class CallbackModule(object):
|
||||||
response = urllib2.urlopen(url, urllib.urlencode(params))
|
response = urllib2.urlopen(url, urllib.urlencode(params))
|
||||||
return response.read()
|
return response.read()
|
||||||
except:
|
except:
|
||||||
utils.warning('Could not submit message to hipchat')
|
self.display.warning('Could not submit message to hipchat')
|
||||||
|
|
||||||
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_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_no_hosts_matched(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_remaining(self):
|
|
||||||
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, default=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, name):
|
def playbook_on_play_start(self, name):
|
||||||
"""Display Playbook and play start messages"""
|
"""Display Playbook and play start messages"""
|
85
lib/ansible/plugins/callback/log_plays.py
Normal file
85
lib/ansible/plugins/callback/log_plays.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# (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/>.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
# NOTE: in Ansible 1.2 or later general logging is available without
|
||||||
|
# this plugin, just set ANSIBLE_LOG_PATH as an environment variable
|
||||||
|
# or log_path in the DEFAULTS section of your ansible configuration
|
||||||
|
# file. This callback is an example of per hosts logging for those
|
||||||
|
# that want it.
|
||||||
|
|
||||||
|
|
||||||
|
class CallbackModule(CallbackBase):
|
||||||
|
"""
|
||||||
|
logs playbook results, per host, in /var/log/ansible/hosts
|
||||||
|
"""
|
||||||
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = 'notification'
|
||||||
|
|
||||||
|
TIME_FORMAT="%b %d %Y %H:%M:%S"
|
||||||
|
MSG_FORMAT="%(now)s - %(category)s - %(data)s\n\n"
|
||||||
|
|
||||||
|
def __init__(self, display):
|
||||||
|
|
||||||
|
super(CallbackModule, self).__init__(display)
|
||||||
|
|
||||||
|
if not os.path.exists("/var/log/ansible/hosts"):
|
||||||
|
os.makedirs("/var/log/ansible/hosts")
|
||||||
|
|
||||||
|
def log(self, host, category, data):
|
||||||
|
if type(data) == dict:
|
||||||
|
if 'verbose_override' in data:
|
||||||
|
# avoid logging extraneous data from facts
|
||||||
|
data = 'omitted'
|
||||||
|
else:
|
||||||
|
data = data.copy()
|
||||||
|
invocation = data.pop('invocation', None)
|
||||||
|
data = json.dumps(data)
|
||||||
|
if invocation is not None:
|
||||||
|
data = json.dumps(invocation) + " => %s " % data
|
||||||
|
|
||||||
|
path = os.path.join("/var/log/ansible/hosts", host)
|
||||||
|
now = time.strftime(self.TIME_FORMAT, time.localtime())
|
||||||
|
fd = open(path, "a")
|
||||||
|
fd.write(self.MSG_FORMAT % dict(now=now, category=category, data=data))
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||||
|
self.log(host, 'FAILED', res)
|
||||||
|
|
||||||
|
def runner_on_ok(self, host, res):
|
||||||
|
self.log(host, 'OK', res)
|
||||||
|
|
||||||
|
def runner_on_skipped(self, host, item=None):
|
||||||
|
self.log(host, 'SKIPPED', '...')
|
||||||
|
|
||||||
|
def runner_on_unreachable(self, host, res):
|
||||||
|
self.log(host, 'UNREACHABLE', res)
|
||||||
|
|
||||||
|
def runner_on_async_failed(self, host, res, jid):
|
||||||
|
self.log(host, 'ASYNC_FAILED', res)
|
||||||
|
|
||||||
|
def playbook_on_import_for_host(self, host, imported_file):
|
||||||
|
self.log(host, 'IMPORTED', imported_file)
|
||||||
|
|
||||||
|
def playbook_on_not_import_for_host(self, host, missing_file):
|
||||||
|
self.log(host, 'NOTIMPORTED', missing_file)
|
|
@ -19,87 +19,69 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
FAILED_VOICE="Zarvox"
|
FAILED_VOICE="Zarvox"
|
||||||
REGULAR_VOICE="Trinoids"
|
REGULAR_VOICE="Trinoids"
|
||||||
HAPPY_VOICE="Cellos"
|
HAPPY_VOICE="Cellos"
|
||||||
LASER_VOICE="Princess"
|
LASER_VOICE="Princess"
|
||||||
SAY_CMD="/usr/bin/say"
|
SAY_CMD="/usr/bin/say"
|
||||||
|
|
||||||
def say(msg, voice):
|
class CallbackModule(CallbackBase):
|
||||||
subprocess.call([SAY_CMD, msg, "--voice=%s" % (voice)])
|
|
||||||
|
|
||||||
class CallbackModule(object):
|
|
||||||
"""
|
"""
|
||||||
makes Ansible much more exciting on OS X.
|
makes Ansible much more exciting on OS X.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = 'notification'
|
||||||
|
|
||||||
|
def __init__(self, display):
|
||||||
|
|
||||||
|
super(CallbackModule, self).__init__(display)
|
||||||
|
|
||||||
# plugin disable itself if say is not present
|
# plugin disable itself if say is not present
|
||||||
# ansible will not call any callback if disabled is set to True
|
# ansible will not call any callback if disabled is set to True
|
||||||
if not os.path.exists(SAY_CMD):
|
if not os.path.exists(SAY_CMD):
|
||||||
self.disabled = True
|
self.disabled = True
|
||||||
print "%s does not exist, plugin %s disabled" % \
|
self._display.warning("%s does not exist, plugin %s disabled" % (SAY_CMD, os.path.basename(__file__)) )
|
||||||
(SAY_CMD, os.path.basename(__file__))
|
|
||||||
|
|
||||||
def on_any(self, *args, **kwargs):
|
def say(self, msg, voice):
|
||||||
pass
|
subprocess.call([SAY_CMD, msg, "--voice=%s" % (voice)])
|
||||||
|
|
||||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||||
say("Failure on host %s" % host, FAILED_VOICE)
|
self.say("Failure on host %s" % host, FAILED_VOICE)
|
||||||
|
|
||||||
def runner_on_ok(self, host, res):
|
def runner_on_ok(self, host, res):
|
||||||
say("pew", LASER_VOICE)
|
self.say("pew", LASER_VOICE)
|
||||||
|
|
||||||
def runner_on_skipped(self, host, item=None):
|
def runner_on_skipped(self, host, item=None):
|
||||||
say("pew", LASER_VOICE)
|
self.say("pew", LASER_VOICE)
|
||||||
|
|
||||||
def runner_on_unreachable(self, host, res):
|
def runner_on_unreachable(self, host, res):
|
||||||
say("Failure on host %s" % host, FAILED_VOICE)
|
self.say("Failure on host %s" % host, FAILED_VOICE)
|
||||||
|
|
||||||
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):
|
def runner_on_async_ok(self, host, res, jid):
|
||||||
say("pew", LASER_VOICE)
|
self.say("pew", LASER_VOICE)
|
||||||
|
|
||||||
def runner_on_async_failed(self, host, res, jid):
|
def runner_on_async_failed(self, host, res, jid):
|
||||||
say("Failure on host %s" % host, FAILED_VOICE)
|
self.say("Failure on host %s" % host, FAILED_VOICE)
|
||||||
|
|
||||||
def playbook_on_start(self):
|
def playbook_on_start(self):
|
||||||
say("Running Playbook", REGULAR_VOICE)
|
self.say("Running Playbook", REGULAR_VOICE)
|
||||||
|
|
||||||
def playbook_on_notify(self, host, handler):
|
def playbook_on_notify(self, host, handler):
|
||||||
say("pew", LASER_VOICE)
|
self.say("pew", LASER_VOICE)
|
||||||
|
|
||||||
def playbook_on_no_hosts_matched(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_remaining(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_task_start(self, name, is_conditional):
|
def playbook_on_task_start(self, name, is_conditional):
|
||||||
if not is_conditional:
|
if not is_conditional:
|
||||||
say("Starting task: %s" % name, REGULAR_VOICE)
|
self.say("Starting task: %s" % name, REGULAR_VOICE)
|
||||||
else:
|
else:
|
||||||
say("Notifying task: %s" % name, REGULAR_VOICE)
|
self.say("Notifying task: %s" % name, REGULAR_VOICE)
|
||||||
|
|
||||||
def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_setup(self):
|
def playbook_on_setup(self):
|
||||||
say("Gathering facts", REGULAR_VOICE)
|
self.say("Gathering facts", REGULAR_VOICE)
|
||||||
|
|
||||||
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, name):
|
def playbook_on_play_start(self, name):
|
||||||
say("Starting play: %s" % name, HAPPY_VOICE)
|
self.say("Starting play: %s" % name, HAPPY_VOICE)
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
def playbook_on_stats(self, stats):
|
||||||
say("Play complete", HAPPY_VOICE)
|
self.say("Play complete", HAPPY_VOICE)
|
||||||
|
|
|
@ -6,7 +6,9 @@ import logging.handlers
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
class CallbackModule(object):
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
class CallbackModule(CallbackBase):
|
||||||
"""
|
"""
|
||||||
logs ansible-playbook and ansible runs to a syslog server in json format
|
logs ansible-playbook and ansible runs to a syslog server in json format
|
||||||
make sure you have in ansible.cfg:
|
make sure you have in ansible.cfg:
|
||||||
|
@ -17,8 +19,13 @@ class CallbackModule(object):
|
||||||
SYSLOG_SERVER (optional): defaults to localhost
|
SYSLOG_SERVER (optional): defaults to localhost
|
||||||
SYSLOG_PORT (optional): defaults to 514
|
SYSLOG_PORT (optional): defaults to 514
|
||||||
"""
|
"""
|
||||||
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = 'aggregate'
|
||||||
|
|
||||||
|
def __init__(self, display):
|
||||||
|
|
||||||
|
super(CallbackModule, self).__init__(display)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.logger = logging.getLogger('ansible logger')
|
self.logger = logging.getLogger('ansible logger')
|
||||||
self.logger.setLevel(logging.DEBUG)
|
self.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
@ -30,8 +37,6 @@ class CallbackModule(object):
|
||||||
self.logger.addHandler(self.handler)
|
self.logger.addHandler(self.handler)
|
||||||
self.hostname = socket.gethostname()
|
self.hostname = socket.gethostname()
|
||||||
|
|
||||||
def on_any(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||||
self.logger.error('%s ansible-command: task execution FAILED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.error('%s ansible-command: task execution FAILED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
@ -40,52 +45,16 @@ class CallbackModule(object):
|
||||||
self.logger.info('%s ansible-command: task execution OK; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.info('%s ansible-command: task execution OK; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
|
||||||
def runner_on_skipped(self, host, item=None):
|
def runner_on_skipped(self, host, item=None):
|
||||||
self.logger.info('%s ansible-command: task execution SKIPPED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.info('%s ansible-command: task execution SKIPPED; host: %s; message: %s' % (self.hostname,host, 'skipped'))
|
||||||
|
|
||||||
def runner_on_unreachable(self, host, res):
|
def runner_on_unreachable(self, host, res):
|
||||||
self.logger.error('%s ansible-command: task execution UNREACHABLE; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.error('%s ansible-command: task execution UNREACHABLE; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
|
||||||
def runner_on_no_hosts(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def runner_on_async_poll(self, host, res):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def runner_on_async_ok(self, host, res):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def runner_on_async_failed(self, host, res):
|
def runner_on_async_failed(self, host, res):
|
||||||
self.logger.error('%s ansible-command: task execution FAILED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.error('%s ansible-command: task execution FAILED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
|
||||||
def playbook_on_start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_notify(self, host, handler):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_matched(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_remaining(self):
|
|
||||||
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, default=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_setup(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_import_for_host(self, host, imported_file):
|
def playbook_on_import_for_host(self, host, imported_file):
|
||||||
self.logger.info('%s ansible-command: playbook IMPORTED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.info('%s ansible-command: playbook IMPORTED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
|
||||||
def playbook_on_not_import_for_host(self, host, missing_file):
|
def playbook_on_not_import_for_host(self, host, missing_file):
|
||||||
self.logger.info('%s ansible-command: playbook NOT IMPORTED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
self.logger.info('%s ansible-command: playbook NOT IMPORTED; host: %s; message: %s' % (self.hostname,host,json.dumps(res, sort_keys=True)))
|
||||||
|
|
||||||
def playbook_on_play_start(self, name):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
|
||||||
pass
|
|
|
@ -12,13 +12,12 @@ class CallbackModule(CallbackBase):
|
||||||
CALLBACK_TYPE = 'aggregate'
|
CALLBACK_TYPE = 'aggregate'
|
||||||
|
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
|
|
||||||
def __init__(self, display):
|
def __init__(self, display):
|
||||||
|
|
||||||
super(CallbackModule, self).__init__(display)
|
super(CallbackModule, self).__init__(display)
|
||||||
|
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
self._display.warning("Timerv2 plugin is active from included callbacks.")
|
|
||||||
|
|
||||||
def days_hours_minutes_seconds(self, timedelta):
|
def days_hours_minutes_seconds(self, timedelta):
|
||||||
minutes = (timedelta.seconds//60)%60
|
minutes = (timedelta.seconds//60)%60
|
||||||
|
@ -27,7 +26,7 @@ class CallbackModule(CallbackBase):
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
def playbook_on_stats(self, stats):
|
||||||
self.v2_playbook_on_stats(stats)
|
self.v2_playbook_on_stats(stats)
|
||||||
|
|
||||||
def v2_playbook_on_stats(self, stats):
|
def v2_playbook_on_stats(self, stats):
|
||||||
end_time = datetime.now()
|
end_time = datetime.now()
|
||||||
timedelta = end_time - self.start_time
|
timedelta = end_time - self.start_time
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
ansible-plugins
|
|
||||||
===============
|
|
||||||
|
|
||||||
You can extend ansible with optional callback and connection plugins.
|
|
||||||
|
|
||||||
callbacks
|
|
||||||
=========
|
|
||||||
|
|
||||||
Callbacks can be used to add logging or monitoring capability, or just make
|
|
||||||
interesting sound effects.
|
|
||||||
|
|
||||||
Drop callback plugins in your ansible/lib/callback_plugins/ directory.
|
|
||||||
|
|
||||||
connections
|
|
||||||
===========
|
|
||||||
|
|
||||||
Connection plugins allow ansible to talk over different protocols.
|
|
||||||
|
|
||||||
Drop connection plugins in your ansible/lib/runner/connection_plugins/ directory.
|
|
||||||
|
|
||||||
inventory
|
|
||||||
=========
|
|
||||||
|
|
||||||
Inventory plugins allow you to store your hosts, groups, and variables in any way
|
|
||||||
you like. Examples include discovering inventory from EC2 or pulling it from
|
|
||||||
Cobbler. These could also be used to interface with LDAP or database.
|
|
||||||
|
|
||||||
chmod +x an inventory plugin and either name it /etc/ansible/hosts or use ansible
|
|
||||||
with -i to designate the path to the plugin.
|
|
||||||
|
|
||||||
contributions welcome
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Send in pull requests to add plugins of your own. The sky is the limit!
|
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
# (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/>.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
|
|
||||||
# NOTE: in Ansible 1.2 or later general logging is available without
|
|
||||||
# this plugin, just set ANSIBLE_LOG_PATH as an environment variable
|
|
||||||
# or log_path in the DEFAULTS section of your ansible configuration
|
|
||||||
# file. This callback is an example of per hosts logging for those
|
|
||||||
# that want it.
|
|
||||||
|
|
||||||
TIME_FORMAT="%b %d %Y %H:%M:%S"
|
|
||||||
MSG_FORMAT="%(now)s - %(category)s - %(data)s\n\n"
|
|
||||||
|
|
||||||
if not os.path.exists("/var/log/ansible/hosts"):
|
|
||||||
os.makedirs("/var/log/ansible/hosts")
|
|
||||||
|
|
||||||
def log(host, category, data):
|
|
||||||
if type(data) == dict:
|
|
||||||
if 'verbose_override' in data:
|
|
||||||
# avoid logging extraneous data from facts
|
|
||||||
data = 'omitted'
|
|
||||||
else:
|
|
||||||
data = data.copy()
|
|
||||||
invocation = data.pop('invocation', None)
|
|
||||||
data = json.dumps(data)
|
|
||||||
if invocation is not None:
|
|
||||||
data = json.dumps(invocation) + " => %s " % data
|
|
||||||
|
|
||||||
path = os.path.join("/var/log/ansible/hosts", host)
|
|
||||||
now = time.strftime(TIME_FORMAT, time.localtime())
|
|
||||||
fd = open(path, "a")
|
|
||||||
fd.write(MSG_FORMAT % dict(now=now, category=category, data=data))
|
|
||||||
fd.close()
|
|
||||||
|
|
||||||
class CallbackModule(object):
|
|
||||||
"""
|
|
||||||
logs playbook results, per host, in /var/log/ansible/hosts
|
|
||||||
"""
|
|
||||||
|
|
||||||
def on_any(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
|
||||||
log(host, 'FAILED', res)
|
|
||||||
|
|
||||||
def runner_on_ok(self, host, res):
|
|
||||||
log(host, 'OK', res)
|
|
||||||
|
|
||||||
def runner_on_skipped(self, host, item=None):
|
|
||||||
log(host, 'SKIPPED', '...')
|
|
||||||
|
|
||||||
def runner_on_unreachable(self, host, res):
|
|
||||||
log(host, 'UNREACHABLE', res)
|
|
||||||
|
|
||||||
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):
|
|
||||||
log(host, 'ASYNC_FAILED', res)
|
|
||||||
|
|
||||||
def playbook_on_start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_notify(self, host, handler):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_matched(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_no_hosts_remaining(self):
|
|
||||||
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, default=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_setup(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_import_for_host(self, host, imported_file):
|
|
||||||
log(host, 'IMPORTED', imported_file)
|
|
||||||
|
|
||||||
def playbook_on_not_import_for_host(self, host, missing_file):
|
|
||||||
log(host, 'NOTIMPORTED', missing_file)
|
|
||||||
|
|
||||||
def playbook_on_play_start(self, name):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
|
||||||
pass
|
|
||||||
|
|
Loading…
Reference in a new issue