allow config for callbaks and some fixes
* only complain about ini deprecation if value is set * set plugin config for stdout and other types * updated plugin docs, moved several plugins to new config * finished ssh docs * fixed some issues seen in plugins while modifying docs * placeholder for 'required' * callbacks must use _plugin_options as _options already in use
This commit is contained in:
parent
942b6fb9bc
commit
869a318492
19 changed files with 480 additions and 402 deletions
|
@ -215,6 +215,15 @@ class ConfigManager(object):
|
|||
''' Load YAML Config Files in order, check merge flags, keep origin of settings'''
|
||||
pass
|
||||
|
||||
def get_plugin_options(self, plugin_type, name, variables=None):
|
||||
|
||||
options = {}
|
||||
defs = self.get_configuration_definitions(plugin_type, name)
|
||||
for option in defs:
|
||||
options[option] = self.get_config_value(option, plugin_type=plugin_type, plugin_name=name, variables=variables)
|
||||
|
||||
return options
|
||||
|
||||
def get_configuration_definitions(self, plugin_type=None, name=None):
|
||||
''' just list the possible settings, either base or for specific plugins or plugin '''
|
||||
|
||||
|
@ -224,7 +233,7 @@ class ConfigManager(object):
|
|||
elif name is None:
|
||||
ret = self._plugins.get(plugin_type, {})
|
||||
else:
|
||||
ret = {name: self._plugins.get(plugin_type, {}).get(name, {})}
|
||||
ret = self._plugins.get(plugin_type, {}).get(name, {})
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -287,7 +296,7 @@ class ConfigManager(object):
|
|||
for ini_entry in defs[config]['ini']:
|
||||
value = get_ini_config_value(self._parser, ini_entry)
|
||||
origin = cfile
|
||||
if 'deprecated' in ini_entry:
|
||||
if value is not None and 'deprecated' in ini_entry:
|
||||
self.DEPRECATED.append(('[%s]%s' % (ini_entry['section'], ini_entry['key']), ini_entry['deprecated']))
|
||||
except Exception as e:
|
||||
sys.stderr.write("Error while loading ini config %s: %s" % (cfile, to_native(e)))
|
||||
|
|
|
@ -176,6 +176,7 @@ class TaskQueueManager:
|
|||
raise AnsibleError("Invalid callback for stdout specified: %s" % self._stdout_callback)
|
||||
else:
|
||||
self._stdout_callback = callback_loader.get(self._stdout_callback)
|
||||
self._stdout_callback.set_options(C.config.get_plugin_options('callback', self._stdout_callback._load_name))
|
||||
stdout_callback_loaded = True
|
||||
else:
|
||||
raise AnsibleError("callback must be an instance of CallbackBase or the name of a callback plugin")
|
||||
|
@ -198,7 +199,9 @@ class TaskQueueManager:
|
|||
C.DEFAULT_CALLBACK_WHITELIST is None or callback_name not in C.DEFAULT_CALLBACK_WHITELIST)):
|
||||
continue
|
||||
|
||||
self._callback_plugins.append(callback_plugin())
|
||||
callback_obj = callback_plugin()
|
||||
callback_obj .set_options(C.config.get_plugin_options('callback', callback_plugin._load_name))
|
||||
self._callback_plugins.append(callback_obj)
|
||||
|
||||
self._callbacks_loaded = True
|
||||
|
||||
|
@ -366,4 +369,4 @@ class TaskQueueManager:
|
|||
display.warning(u"Failure using method (%s) in callback plugin (%s): %s" % (to_text(method_name), to_text(callback_plugin), to_text(e)))
|
||||
from traceback import format_tb
|
||||
from sys import exc_info
|
||||
display.debug('Callback Exception: \n' + ' '.join(format_tb(exc_info()[2])))
|
||||
display.vvv('Callback Exception: \n' + ' '.join(format_tb(exc_info()[2])))
|
||||
|
|
|
@ -24,7 +24,7 @@ __metaclass__ = type
|
|||
from abc import ABCMeta
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils.six import with_metaclass
|
||||
from ansible.module_utils.six import with_metaclass, string_types
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
|
@ -39,22 +39,29 @@ PLUGIN_PATH_CACHE = {}
|
|||
|
||||
|
||||
def get_plugin_class(obj):
|
||||
return obj.__class__.__name__.lower().replace('module', '')
|
||||
if isinstance(obj, string_types):
|
||||
return obj.lower().replace('module', '')
|
||||
else:
|
||||
return obj.__class__.__name__.lower().replace('module', '')
|
||||
|
||||
|
||||
class AnsiblePlugin(with_metaclass(ABCMeta, object)):
|
||||
|
||||
def __init__(self):
|
||||
self.options = {}
|
||||
self._options = {}
|
||||
|
||||
def get_option(self, option, hostvars=None):
|
||||
if option not in self.options:
|
||||
if option not in self._options:
|
||||
option_value = C.config.get_config_value(option, plugin_type=get_plugin_class(self), plugin_name=self.name, variables=hostvars)
|
||||
self.set_option(option, option_value)
|
||||
return self.options.get(option)
|
||||
return self._options.get(option)
|
||||
|
||||
def set_option(self, option, value):
|
||||
self.options[option] = value
|
||||
self._options[option] = value
|
||||
|
||||
def set_options(self, options):
|
||||
self.options = options
|
||||
self._options = options
|
||||
|
||||
def _check_required(self):
|
||||
# FIXME: standarize required check based on config
|
||||
pass
|
||||
|
|
|
@ -26,6 +26,7 @@ import warnings
|
|||
from copy import deepcopy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.plugins import AnsiblePlugin
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.utils.color import stringc
|
||||
from ansible.vars.manager import strip_internal_keys
|
||||
|
@ -45,7 +46,7 @@ except ImportError:
|
|||
__all__ = ["CallbackBase"]
|
||||
|
||||
|
||||
class CallbackBase:
|
||||
class CallbackBase(AnsiblePlugin):
|
||||
|
||||
'''
|
||||
This is a base ansible callback class that does nothing. New callbacks should
|
||||
|
@ -53,7 +54,8 @@ class CallbackBase:
|
|||
custom actions.
|
||||
'''
|
||||
|
||||
def __init__(self, display=None):
|
||||
def __init__(self, display=None, options=None):
|
||||
|
||||
if display:
|
||||
self._display = display
|
||||
else:
|
||||
|
@ -70,9 +72,18 @@ class CallbackBase:
|
|||
version = getattr(self, 'CALLBACK_VERSION', '1.0')
|
||||
self._display.vvvv('Loading callback plugin %s of type %s, v%s from %s' % (name, ctype, version, __file__))
|
||||
|
||||
self.disabled = False
|
||||
|
||||
self._plugin_options = {}
|
||||
if options is not None:
|
||||
self.set_options(options)
|
||||
|
||||
''' helper for callbacks, so they don't all have to include deepcopy '''
|
||||
_copy_result = deepcopy
|
||||
|
||||
def set_options(self, options):
|
||||
self._plugin_options = options
|
||||
|
||||
def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):
|
||||
if result.get('_ansible_no_log', False):
|
||||
return json.dumps(dict(censored="the output has been hidden due to the fact that 'no_log: true' was specified for this result"))
|
||||
|
|
|
@ -14,17 +14,17 @@ DOCUMENTATION:
|
|||
show_skipped_hosts:
|
||||
name: Show skipped hosts
|
||||
description: "Toggle to control displaying skipped task/host results in a task"
|
||||
default: True
|
||||
env:
|
||||
- name: DISPLAY_SKIPPED_HOSTS
|
||||
ini:
|
||||
- key: display_skipped_hosts
|
||||
section: defaults
|
||||
type: boolean
|
||||
default: True
|
||||
show_custom_stats:
|
||||
name: Show custom stats
|
||||
default: False
|
||||
description: 'This adds the custom stats set via the set_stats plugin to the play recap'
|
||||
default: False
|
||||
env:
|
||||
- name: ANSIBLE_SHOW_CUSTOM_STATS
|
||||
ini:
|
||||
|
@ -119,7 +119,7 @@ class CallbackModule(CallbackBase):
|
|||
self._display.display(msg, color=color)
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
if C.DISPLAY_SKIPPED_HOSTS:
|
||||
if self._plugin_options['show_skipped_hosts']:
|
||||
|
||||
delegated_vars = result._result.get('_ansible_delegated_vars', None)
|
||||
self._clean_results(result._result, result._task.action)
|
||||
|
@ -248,7 +248,7 @@ class CallbackModule(CallbackBase):
|
|||
self._display.display(msg + " (item=%s) => %s" % (self._get_item(result._result), self._dump_results(result._result)), color=C.COLOR_ERROR)
|
||||
|
||||
def v2_runner_item_on_skipped(self, result):
|
||||
if C.DISPLAY_SKIPPED_HOSTS:
|
||||
if self._plugin_options['show_skipped_hosts']:
|
||||
self._clean_results(result._result, result._task.action)
|
||||
msg = "skipping: [%s] => (item=%s) " % (result._host.get_name(), self._get_item(result._result))
|
||||
if (self._display.verbosity > 0 or '_ansible_verbose_always' in result._result) and '_ansible_verbose_override' not in result._result:
|
||||
|
@ -287,7 +287,7 @@ class CallbackModule(CallbackBase):
|
|||
self._display.display("", screen_only=True)
|
||||
|
||||
# print custom stats
|
||||
if C.SHOW_CUSTOM_STATS and stats.custom:
|
||||
if self._plugin_options['show_custom_stats'] and stats.custom:
|
||||
self._display.banner("CUSTOM STATS: ")
|
||||
# per host
|
||||
# TODO: come up with 'pretty format'
|
||||
|
@ -308,11 +308,11 @@ class CallbackModule(CallbackBase):
|
|||
self._display.banner("PLAYBOOK: %s" % basename(playbook._file_name))
|
||||
|
||||
if self._display.verbosity > 3:
|
||||
if self._options is not None:
|
||||
for option in dir(self._options):
|
||||
if self._plugin_options is not None:
|
||||
for option in dir(self._plugin_options):
|
||||
if option.startswith('_') or option in ['read_file', 'ensure_value', 'read_module']:
|
||||
continue
|
||||
val = getattr(self._options, option)
|
||||
val = getattr(self._plugin_options, option)
|
||||
if val:
|
||||
self._display.vvvv('%s: %s' % (option, val))
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ DOCUMENTATION:
|
|||
type: notification
|
||||
short_description: Sends events to Logentries
|
||||
description:
|
||||
- This callback plugin will generate JSON objects and send them to Logentries for auditing/debugging purposes.
|
||||
- If you want to use an ini configuration, the file must be placed in the same directory as this plugin and named logentries.ini
|
||||
- This callback plugin will generate JSON objects and send them to Logentries via TCP for auditing/debugging purposes.
|
||||
- Before 2.4, if you wanted to use an ini configuration, the file must be placed in the same directory as this plugin and named logentries.ini
|
||||
- In 2.4 and above you can just put it in the main Ansible configuration file.
|
||||
version_added: "2.0"
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- certifi (python library)
|
||||
- flatdict (pytnon library)
|
||||
- flatdict (pytnon library), if you want to use the 'flatten' option
|
||||
options:
|
||||
api:
|
||||
description: URI to the Logentries API
|
||||
|
@ -22,7 +23,7 @@ DOCUMENTATION:
|
|||
- name: LOGENTRIES_API
|
||||
default: data.logentries.com
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: api
|
||||
port:
|
||||
description: Http port to use when connecting to the API
|
||||
|
@ -30,7 +31,7 @@ DOCUMENTATION:
|
|||
- name: LOGENTRIES_PORT
|
||||
default: 80
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: port
|
||||
tls_port:
|
||||
description: Port to use when connecting to the API when TLS is enabled
|
||||
|
@ -38,15 +39,15 @@ DOCUMENTATION:
|
|||
- name: LOGENTRIES_TLS_PORT
|
||||
default: 443
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: tls_port
|
||||
token:
|
||||
description: the authentication token
|
||||
description: The logentries "TCP token"
|
||||
env:
|
||||
- name: LOGENTRIES_ANSIBLE_TOKEN
|
||||
required: True
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: token
|
||||
use_tls:
|
||||
description:
|
||||
|
@ -56,7 +57,7 @@ DOCUMENTATION:
|
|||
default: False
|
||||
type: boolean
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: use_tls
|
||||
flatten:
|
||||
description: flatten complex data structures into a single dictionary with complex keys
|
||||
|
@ -65,7 +66,7 @@ DOCUMENTATION:
|
|||
env:
|
||||
- name: LOGENTRIES_FLATTEN
|
||||
ini:
|
||||
- section: defaults
|
||||
- section: callback_logentries
|
||||
key: flatten
|
||||
EXAMPLES: >
|
||||
To enable, add this to your ansible.cfg file in the defaults block
|
||||
|
@ -78,8 +79,8 @@ EXAMPLES: >
|
|||
export LOGENTRIES_PORT=10000
|
||||
export LOGENTRIES_ANSIBLE_TOKEN=dd21fc88-f00a-43ff-b977-e3a4233c53af
|
||||
|
||||
Or create a logentries.ini config file that sites next to the plugin with the following contents
|
||||
[logentries]
|
||||
Or in the main Ansible config file
|
||||
[callback_logentries]
|
||||
api = data.logentries.com
|
||||
port = 10000
|
||||
tls_port = 20000
|
||||
|
@ -108,8 +109,8 @@ try:
|
|||
except ImportError:
|
||||
HAS_FLATDICT = False
|
||||
|
||||
from ansible.module_utils.six.moves import configparser
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_bytes, to_text, to_native
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
"""
|
||||
Todo:
|
||||
|
@ -118,11 +119,7 @@ Todo:
|
|||
|
||||
|
||||
class PlainTextSocketAppender(object):
|
||||
def __init__(self,
|
||||
verbose=True,
|
||||
LE_API='data.logentries.com',
|
||||
LE_PORT=80,
|
||||
LE_TLS_PORT=443):
|
||||
def __init__(self, display, LE_API='data.logentries.com', LE_PORT=80, LE_TLS_PORT=443):
|
||||
|
||||
self.LE_API = LE_API
|
||||
self.LE_PORT = LE_PORT
|
||||
|
@ -134,7 +131,7 @@ class PlainTextSocketAppender(object):
|
|||
# Unicode Line separator character \u2028
|
||||
self.LINE_SEP = u'\u2028'
|
||||
|
||||
self.verbose = verbose
|
||||
self._display = display
|
||||
self._conn = None
|
||||
|
||||
def open_connection(self):
|
||||
|
@ -149,9 +146,8 @@ class PlainTextSocketAppender(object):
|
|||
try:
|
||||
self.open_connection()
|
||||
return
|
||||
except Exception:
|
||||
if self.verbose:
|
||||
self._display.warning("Unable to connect to Logentries")
|
||||
except Exception as e:
|
||||
self._display.vvvv("Unable to connect to Logentries: %s" % str(e))
|
||||
|
||||
root_delay *= 2
|
||||
if (root_delay > self.MAX_DELAY):
|
||||
|
@ -160,6 +156,7 @@ class PlainTextSocketAppender(object):
|
|||
wait_for = root_delay + random.uniform(0, root_delay)
|
||||
|
||||
try:
|
||||
self._display.vvvv("sleeping %s before retry" % wait_for)
|
||||
time.sleep(wait_for)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
|
@ -221,91 +218,76 @@ class CallbackModule(CallbackBase):
|
|||
CALLBACK_NEEDS_WHITELIST = True
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# TODO: allow for alternate posting methods (REST/UDP/agent/etc)
|
||||
super(CallbackModule, self).__init__()
|
||||
|
||||
# verify dependencies
|
||||
if not HAS_SSL:
|
||||
self._display.warning("Unable to import ssl module. Will send over port 80.")
|
||||
|
||||
warn = ''
|
||||
if not HAS_CERTIFI:
|
||||
self.disabled = True
|
||||
warn += 'The `certifi` python module is not installed.'
|
||||
self._display.warning('The `certifi` python module is not installed.\nDisabling the Logentries callback plugin.')
|
||||
|
||||
if not HAS_FLATDICT:
|
||||
self.disabled = True
|
||||
warn += 'The `flatdict` python module is not installed.'
|
||||
|
||||
if warn:
|
||||
self._display.warning('%s\nDisabling the Logentries callback plugin.' % warn)
|
||||
|
||||
config_path = os.path.abspath(os.path.dirname(__file__))
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.readfp(open(os.path.join(config_path, 'logentries.ini')))
|
||||
if config.has_option('logentries', 'api'):
|
||||
self.api_uri = config.get('logentries', 'api')
|
||||
if config.has_option('logentries', 'port'):
|
||||
self.api_port = config.getint('logentries', 'port')
|
||||
if config.has_option('logentries', 'tls_port'):
|
||||
self.api_tls_port = config.getint('logentries', 'tls_port')
|
||||
if config.has_option('logentries', 'use_tls'):
|
||||
self.use_tls = config.getboolean('logentries', 'use_tls')
|
||||
if config.has_option('logentries', 'token'):
|
||||
self.token = config.get('logentries', 'token')
|
||||
if config.has_option('logentries', 'flatten'):
|
||||
self.flatten = config.getboolean('logentries', 'flatten')
|
||||
|
||||
except:
|
||||
self.api_uri = os.getenv('LOGENTRIES_API')
|
||||
if self.api_uri is None:
|
||||
self.api_uri = 'data.logentries.com'
|
||||
|
||||
try:
|
||||
self.api_port = int(os.getenv('LOGENTRIES_PORT'))
|
||||
if self.api_port is None:
|
||||
self.api_port = 80
|
||||
except TypeError:
|
||||
self.api_port = 80
|
||||
|
||||
try:
|
||||
self.api_tls_port = int(os.getenv('LOGENTRIES_TLS_PORT'))
|
||||
if self.api_tls_port is None:
|
||||
self.api_tls_port = 443
|
||||
except TypeError:
|
||||
self.api_tls_port = 443
|
||||
|
||||
# this just needs to be set to use TLS
|
||||
self.use_tls = os.getenv('LOGENTRIES_USE_TLS')
|
||||
if self.use_tls is None:
|
||||
self.use_tls = False
|
||||
elif self.use_tls.lower() in ['yes', 'true']:
|
||||
self.use_tls = True
|
||||
|
||||
self.token = os.getenv('LOGENTRIES_ANSIBLE_TOKEN')
|
||||
if self.token is None:
|
||||
self.disabled = True
|
||||
self._display.warning('Logentries token could not be loaded. The logentries token can be provided using the `LOGENTRIES_TOKEN` environment '
|
||||
'variable')
|
||||
|
||||
self.flatten = os.getenv('LOGENTRIES_FLATTEN')
|
||||
if self.flatten is None:
|
||||
self.flatten = False
|
||||
elif self.flatten.lower() in ['yes', 'true']:
|
||||
self.flatten = True
|
||||
|
||||
self.verbose = False
|
||||
self.timeout = 10
|
||||
self.le_jobid = str(uuid.uuid4())
|
||||
|
||||
if self.use_tls:
|
||||
self._appender = TLSSocketAppender(verbose=self.verbose,
|
||||
LE_API=self.api_uri,
|
||||
LE_TLS_PORT=self.api_tls_port)
|
||||
else:
|
||||
self._appender = PlainTextSocketAppender(verbose=self.verbose,
|
||||
LE_API=self.api_uri,
|
||||
LE_PORT=self.api_port)
|
||||
self._appender.reopen_connection()
|
||||
# FIXME: remove when done testing
|
||||
# initialize configurable
|
||||
self.api_url = 'data.logentries.com'
|
||||
self.api_port = 80
|
||||
self.api_tls_port = 443
|
||||
self.use_tls = False
|
||||
self.flatten = False
|
||||
self.token = None
|
||||
|
||||
# FIXME: make configurable, move to options
|
||||
self.timeout = 10
|
||||
|
||||
# FIXME: remove testing
|
||||
# self.set_options({'api': 'data.logentries.com', 'port': 80,
|
||||
# 'tls_port': 10000, 'use_tls': True, 'flatten': False, 'token': 'ae693734-4c5b-4a44-8814-1d2feb5c8241'})
|
||||
|
||||
def set_option(self, name, value):
|
||||
raise AnsibleError("The Logentries callabck plugin does not suport setting individual options.")
|
||||
|
||||
def set_options(self, options):
|
||||
|
||||
super(CallbackModule, self).set_options(options)
|
||||
|
||||
# get options
|
||||
try:
|
||||
self.api_url = self._plugin_options['api']
|
||||
self.api_port = self._plugin_options['port']
|
||||
self.api_tls_port = self._plugin_options['tls_port']
|
||||
self.use_tls = self._plugin_options['use_tls']
|
||||
self.flatten = self._plugin_options['flatten']
|
||||
except KeyError as e:
|
||||
self._display.warning("Missing option for Logentries callback plugin: %s" % to_native(e))
|
||||
self.disabled = True
|
||||
|
||||
try:
|
||||
self.token = self._plugin_options['token']
|
||||
except KeyError as e:
|
||||
self._display.warning('Logentries token was not provided, this is required for this callback to operate, disabling')
|
||||
self.disabled = True
|
||||
|
||||
if self.flatten and not HAS_FLATDICT:
|
||||
self.disabled = True
|
||||
self._display.warning('You have chosen to flatten and the `flatdict` python module is not installed.\nDisabling the Logentries callback plugin.')
|
||||
|
||||
self._initialize_connections()
|
||||
|
||||
def _initialize_connections(self):
|
||||
|
||||
if not self.disabled:
|
||||
if self.use_tls:
|
||||
self._display.vvvv("Connecting to %s:%s with TLS" % (self.api_url, self.api_tls_port))
|
||||
self._appender = TLSSocketAppender(display=self._display, LE_API=self.api_url, LE_TLS_PORT=self.api_tls_port)
|
||||
else:
|
||||
self._display.vvvv("Connecting to %s:%s" % (self.api_url, self.api_port))
|
||||
self._appender = PlainTextSocketAppender(display=self._display, LE_API=self.api_url, LE_PORT=self.api_port)
|
||||
self._appender.reopen_connection()
|
||||
|
||||
def emit_formatted(self, record):
|
||||
if self.flatten:
|
||||
|
@ -318,43 +300,34 @@ class CallbackModule(CallbackBase):
|
|||
msg = record.rstrip('\n')
|
||||
msg = "{} {}".format(self.token, msg)
|
||||
self._appender.put(msg)
|
||||
self._display.vvvv("Sent event to logentries")
|
||||
|
||||
def _set_info(self, host, res):
|
||||
return {'le_jobid': self.le_jobid, 'hostname': host, 'results': res}
|
||||
|
||||
def runner_on_ok(self, host, res):
|
||||
results = {}
|
||||
results['le_jobid'] = self.le_jobid
|
||||
results['hostname'] = host
|
||||
results['results'] = res
|
||||
results = self._set_info(host, res)
|
||||
results['status'] = 'OK'
|
||||
self.emit_formatted(results)
|
||||
|
||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||
results = {}
|
||||
results['le_jobid'] = self.le_jobid
|
||||
results['hostname'] = host
|
||||
results['results'] = res
|
||||
results = self._set_info(host, res)
|
||||
results['status'] = 'FAILED'
|
||||
self.emit_formatted(results)
|
||||
|
||||
def runner_on_skipped(self, host, item=None):
|
||||
results = {}
|
||||
results['le_jobid'] = self.le_jobid
|
||||
results['hostname'] = host
|
||||
results = self._set_info(host, item)
|
||||
del results['results']
|
||||
results['status'] = 'SKIPPED'
|
||||
self.emit_formatted(results)
|
||||
|
||||
def runner_on_unreachable(self, host, res):
|
||||
results = {}
|
||||
results['le_jobid'] = self.le_jobid
|
||||
results['hostname'] = host
|
||||
results['results'] = res
|
||||
results = self._set_info(host, res)
|
||||
results['status'] = 'UNREACHABLE'
|
||||
self.emit_formatted(results)
|
||||
|
||||
def runner_on_async_failed(self, host, res, jid):
|
||||
results = {}
|
||||
results['le_jobid'] = self.le_jobid
|
||||
results['hostname'] = host
|
||||
results['results'] = res
|
||||
results = self._set_info(host, res)
|
||||
results['jid'] = jid
|
||||
results['status'] = 'ASYNC_FAILED'
|
||||
self.emit_formatted(results)
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
# (C) 2017, Tennis Smith, http://github.com/gamename
|
||||
#
|
||||
# This file 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.
|
||||
#
|
||||
# File 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.
|
||||
#
|
||||
# See <http://www.gnu.org/licenses/> for a copy of the
|
||||
# GNU General Public License
|
||||
# (c) 2017, Tennis Smith, http://github.com/gamename
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#
|
||||
# This will track the use of each role during the life of a playbook's
|
||||
# execution. The total time spent in each role will be printed at the
|
||||
# end.
|
||||
#
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: profile_roles
|
||||
type: aggregate
|
||||
short_description: adds timing information to roles
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- This callback module provides profiling for ansible roles.
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
'''
|
||||
|
||||
# Make coding more python3-ish
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
|
@ -117,11 +114,9 @@ class CallbackModule(CallbackBase):
|
|||
|
||||
# Print the timings starting with the largest one
|
||||
for result in self.totals.most_common():
|
||||
msg = u"{0:-<70}{1:->9}".format(result[0] + u' ',
|
||||
u' {0:.02f}s'.format(result[1]))
|
||||
msg = u"{0:-<70}{1:->9}".format(result[0] + u' ', u' {0:.02f}s'.format(result[1]))
|
||||
self._display.display(msg)
|
||||
|
||||
msg_total = u"{0:-<70}{1:->9}".format(u'total ',
|
||||
u' {0:.02f}s'.format(total_time))
|
||||
msg_total = u"{0:-<70}{1:->9}".format(u'total ', u' {0:.02f}s'.format(total_time))
|
||||
self._display.display(filled("", fchar="~"))
|
||||
self._display.display(msg_total)
|
||||
|
|
|
@ -2,29 +2,60 @@
|
|||
# (C) 2015, Tom Paine, <github@aioue.net>
|
||||
# (C) 2014, Jharrod LaFon, @JharrodLaFon
|
||||
# (C) 2012-2013, Michael DeHaan, <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file 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.
|
||||
#
|
||||
# File 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.
|
||||
#
|
||||
# See <http://www.gnu.org/licenses/> for a copy of the
|
||||
# GNU General Public License
|
||||
# (C) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Provides per-task timing, ongoing playbook elapsed time and
|
||||
# ordered list of top 20 longest running tasks at end
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: profile_tasks
|
||||
type: aggregate
|
||||
short_description: adds time information to tasks
|
||||
version_added: "2.0"
|
||||
description:
|
||||
- Ansible callback plugin for timing individual tasks and overall execution time.
|
||||
- "Mashup of 2 excellent original works: https://github.com/jlafon/ansible-profile,
|
||||
https://github.com/junaid18183/ansible_home/blob/master/ansible_plugins/callback_plugins/timestamp.py.old"
|
||||
- "Format: ``<task start timestamp> (<length of previous task>) <current elapsed playbook execution time>``"
|
||||
- It also lists the top/bottom time consuming tasks in the summary (configurable)
|
||||
- Before 2.4 only the environment variables were available for configuration.
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
options:
|
||||
output_limit:
|
||||
description: Number of tasks to display in the summary
|
||||
default: 20
|
||||
env:
|
||||
- name: PROFILE_TASKS_TASK_OUTPUT_LIMIT
|
||||
ini:
|
||||
- section: callback_profile_tasks
|
||||
key: task_output_limit
|
||||
sort_order:
|
||||
description: Adjust the sorting output of summary tasks
|
||||
choices: ['descending', 'ascending', 'none']
|
||||
default: 'descending'
|
||||
env:
|
||||
- name: PROFILE_TASKS_SORT_ORDER
|
||||
ini:
|
||||
- section: callback_profile_tasks
|
||||
key: sort_order
|
||||
#EXAMPLES: > '
|
||||
#
|
||||
# TASK: [ensure messaging security group exists] ********************************
|
||||
# Thursday 11 June 2017 22:50:53 +0100 (0:00:00.721) 0:00:05.322 *********
|
||||
# ok: [localhost]
|
||||
#
|
||||
# TASK: [ensure db security group exists] ***************************************
|
||||
# Thursday 11 June 2017 22:50:54 +0100 (0:00:00.558) 0:00:05.880 *********
|
||||
# changed: [localhost]
|
||||
# '
|
||||
'''
|
||||
|
||||
# Make coding more python3-ish
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import collections
|
||||
import os
|
||||
import time
|
||||
|
||||
from ansible.module_utils.six.moves import reduce
|
||||
|
@ -82,19 +113,32 @@ class CallbackModule(CallbackBase):
|
|||
def __init__(self):
|
||||
self.stats = collections.OrderedDict()
|
||||
self.current = None
|
||||
self.sort_order = os.getenv('PROFILE_TASKS_SORT_ORDER', True)
|
||||
self.task_output_limit = os.getenv('PROFILE_TASKS_TASK_OUTPUT_LIMIT', 20)
|
||||
|
||||
if self.sort_order == 'ascending':
|
||||
self.sort_order = False
|
||||
|
||||
if self.task_output_limit == 'all':
|
||||
self.task_output_limit = None
|
||||
else:
|
||||
self.task_output_limit = int(self.task_output_limit)
|
||||
self.sort_order = None
|
||||
self.task_output_limit = None
|
||||
|
||||
super(CallbackModule, self).__init__()
|
||||
|
||||
def set_options(self, options):
|
||||
|
||||
super(CallbackModule, self).set_options(options)
|
||||
|
||||
self.sort_order = self._plugin_options['sort_order']
|
||||
if self.sort_order is not None:
|
||||
if self.sort_order == 'ascending':
|
||||
self.sort_order = False
|
||||
elif self.sort_order == 'descending':
|
||||
self.sort_order = True
|
||||
elif self.sort_order == 'none':
|
||||
self.sort_order = None
|
||||
|
||||
self.task_output_limit = self._plugin_options['output_limit']
|
||||
if self.task_output_limit is not None:
|
||||
if self.task_output_limit == 'all':
|
||||
self.task_output_limit = None
|
||||
else:
|
||||
self.task_output_limit = int(self.task_output_limit)
|
||||
|
||||
def _record_task(self, task):
|
||||
"""
|
||||
Logs the start of each task
|
||||
|
@ -126,7 +170,7 @@ class CallbackModule(CallbackBase):
|
|||
results = self.stats.items()
|
||||
|
||||
# Sort the tasks by the specified sort
|
||||
if self.sort_order != 'none':
|
||||
if self.sort_order is not None:
|
||||
results = sorted(
|
||||
self.stats.items(),
|
||||
key=lambda x: x[1]['time'],
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
profile\_tasks.py
|
||||
=================
|
||||
|
||||
Ansible plugin for timing individual tasks and overall execution time.
|
||||
|
||||
Mashup of 2 excellent original works:
|
||||
|
||||
- https://github.com/jlafon/ansible-profile
|
||||
- https://github.com/junaid18183/ansible_home/blob/master/ansible_plugins/callback_plugins/timestamp.py.old
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Add ``profile_tasks`` to the ``callback_whitelist`` in ``ansible.cfg``.
|
||||
|
||||
Run playbooks as normal.
|
||||
|
||||
Certain options are configurable using environment variables. You can specify ``ascending`` or ``none`` for
|
||||
the environment variable ``PROFILE_TASKS_SORT_ORDER`` to adjust sorting output. If you want to see more than
|
||||
20 tasks in the output you can set ``PROFILE_TASKS_TASK_OUTPUT_LIMIT`` to any number, or the special value
|
||||
``all`` to get a list of all tasks.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Tasks
|
||||
~~~~~
|
||||
|
||||
Ongoing timing of each task as it happens.
|
||||
|
||||
| Format:
|
||||
| ``<task start timestamp> (<length of previous task>) <current elapsed playbook execution time>``
|
||||
|
||||
Task output example:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
TASK: [ensure messaging security group exists] ********************************
|
||||
Thursday 11 June 2017 22:50:53 +0100 (0:00:00.721) 0:00:05.322 *********
|
||||
ok: [localhost]
|
||||
|
||||
TASK: [ensure db security group exists] ***************************************
|
||||
Thursday 11 June 2017 22:50:54 +0100 (0:00:00.558) 0:00:05.880 *********
|
||||
changed: [localhost]
|
||||
|
||||
Play Recap
|
||||
~~~~~~~~~~
|
||||
|
||||
Recap includes ending timestamp, total playbook execution time and a
|
||||
sorted list of the top longest running tasks.
|
||||
|
||||
No more wondering how old the results in a terminal window are.
|
||||
|
||||
.. code:: shell
|
||||
|
||||
ansible <args here>
|
||||
<normal output here>
|
||||
PLAY RECAP ********************************************************************
|
||||
Thursday 11 June 2016 22:51:00 +0100 (0:00:01.011) 0:00:43.247 *********
|
||||
===============================================================================
|
||||
old_and_slow : install tons of packages -------------------------------- 20.03s
|
||||
/home/bob/ansible/roles/old_and_slow/tasks/main.yml:4 -------------------------
|
||||
db : second task to run ------------------------------------------------- 2.03s
|
||||
/home/bob/ansible/roles/db/tasks/main.yml:4 -----------------------------------
|
||||
setup ------------------------------------------------------------------- 0.42s
|
||||
None --------------------------------------------------------------------------
|
||||
www : first task to run ------------------------------------------------- 0.03s
|
||||
/home/bob/ansible/roles/www/tasks/main.yml:1 ----------------------------------
|
||||
fast_task : first task to run ------------------------------------------- 0.01s
|
||||
/home/bob/ansible/roles/fast_task.yml:1 ---------------------------------------
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
Ansible 2.0+
|
|
@ -1,61 +1,59 @@
|
|||
# (c) Fastly, inc 2016
|
||||
#
|
||||
# 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/>.
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""
|
||||
selective.py callback plugin.
|
||||
|
||||
This callback only prints tasks that have been tagged with `print_action` or that have failed.
|
||||
Tasks that are not printed are placed with a '.'.
|
||||
|
||||
For example:
|
||||
|
||||
- debug: msg="This will not be printed"
|
||||
- debug: msg="But this will"
|
||||
tags: [print_action]"
|
||||
|
||||
This allows operators to focus on the tasks that provide value only.
|
||||
|
||||
If you increase verbosity all tasks are printed.
|
||||
DOCUMENTATION:
|
||||
callback: selective
|
||||
callback_type: stdout
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: only print certain tasks
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- This callback only prints tasks that have been tagged with `print_action` or that have failed.
|
||||
This allows operators to focus on the tasks that provide value only.
|
||||
- Tasks that are not printed are placed with a '.'.
|
||||
- If you increase verbosity all tasks are printed.
|
||||
options:
|
||||
nocolor:
|
||||
default: False
|
||||
description: This setting allows suppressing colorizing output
|
||||
env:
|
||||
- name: ANSIBLE_NOCOLOR
|
||||
- name: ANSIBLE_SELECTIVE_DONT_COLORIZE
|
||||
ini:
|
||||
- section: defaults
|
||||
- key: nocolor
|
||||
type: boolean
|
||||
EXAMPLES:
|
||||
- debug: msg="This will not be printed"
|
||||
- debug: msg="But this will"
|
||||
tags: [print_action]
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
import difflib
|
||||
import os
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DONT_COLORIZE = False
|
||||
COLORS = {
|
||||
'normal': '\033[0m',
|
||||
'ok': '\033[92m',
|
||||
'ok': C.COLOR_OK,
|
||||
'bold': '\033[1m',
|
||||
'not_so_bold': '\033[1m\033[34m',
|
||||
'changed': '\033[93m',
|
||||
'failed': '\033[91m',
|
||||
'changed': C.COLOR_CHANGED,
|
||||
'failed': C.COLOR_ERROR,
|
||||
'endc': '\033[0m',
|
||||
'skipped': '\033[96m',
|
||||
'skipped': C.COLOR_SKIP,
|
||||
}
|
||||
|
||||
DONT_COLORIZE = os.getenv('ANSIBLE_SELECTIVE_DONT_COLORIZE', default=False)
|
||||
|
||||
|
||||
def dict_diff(prv, nxt):
|
||||
"""Return a dict of keys that differ with another config object."""
|
||||
|
@ -89,6 +87,13 @@ class CallbackModule(CallbackBase):
|
|||
self.last_task_name = None
|
||||
self.printed_last_task = False
|
||||
|
||||
def set_options(self, options):
|
||||
|
||||
super(CallbackModule, self).set_options(options)
|
||||
|
||||
global DONT_COLORIZE
|
||||
DONT_COLORIZE = self._plugin_options['nocolor']
|
||||
|
||||
def _print_task(self, task_name=None):
|
||||
if task_name is None:
|
||||
task_name = self.last_task_name
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
DOCUMENTATION:
|
||||
callback: skippy
|
||||
callback_type: stdout
|
||||
requires: set as display
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: Ansible screen output that ignores skipped status
|
||||
version_added: "2.0"
|
||||
description:
|
||||
|
|
|
@ -1,20 +1,45 @@
|
|||
# (C) 2014-2015, Matt Martz <matt@sivel.net>
|
||||
# (C) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# 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/>.
|
||||
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: slack
|
||||
callback_type: notification
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- prettytable (python library)
|
||||
short_description: Sends play events to a Slack channel
|
||||
version_added: "2.1"
|
||||
description:
|
||||
- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
|
||||
- Before 2.4 only environment variables were available for configuring this plugin
|
||||
options:
|
||||
webhook_url:
|
||||
required: True
|
||||
description: Slack Webhook URL
|
||||
env:
|
||||
- name: SLACK_WEBHOOK_URL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: webhook_url
|
||||
channel:
|
||||
default: "#ansible"
|
||||
description: Slack room to post in.
|
||||
env:
|
||||
- name: SLACK_CHANNEL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: channel
|
||||
username:
|
||||
description: Username to post as.
|
||||
env:
|
||||
- name: SLACK_USERNAME
|
||||
default: ansible
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: username
|
||||
'''
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
@ -42,17 +67,6 @@ except ImportError:
|
|||
class CallbackModule(CallbackBase):
|
||||
"""This is an ansible callback plugin that sends status
|
||||
updates to a Slack channel during playbook execution.
|
||||
|
||||
This plugin makes use of the following environment variables:
|
||||
SLACK_WEBHOOK_URL (required): Slack Webhook URL
|
||||
SLACK_CHANNEL (optional): Slack room to post in. Default: #ansible
|
||||
SLACK_USERNAME (optional): Username to post as. Default: ansible
|
||||
SLACK_INVOCATION (optional): Show command line invocation
|
||||
details. Default: False
|
||||
|
||||
Requires:
|
||||
prettytable
|
||||
|
||||
"""
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'notification'
|
||||
|
@ -64,9 +78,9 @@ class CallbackModule(CallbackBase):
|
|||
self.disabled = False
|
||||
|
||||
if cli:
|
||||
self._options = cli.options
|
||||
self._plugin_options = cli.options
|
||||
else:
|
||||
self._options = None
|
||||
self._plugin_options = None
|
||||
|
||||
super(CallbackModule, self).__init__(display=display)
|
||||
|
||||
|
@ -76,13 +90,10 @@ class CallbackModule(CallbackBase):
|
|||
'installed. Disabling the Slack callback '
|
||||
'plugin.')
|
||||
|
||||
self.webhook_url = os.getenv('SLACK_WEBHOOK_URL')
|
||||
self.channel = os.getenv('SLACK_CHANNEL', '#ansible')
|
||||
self.username = os.getenv('SLACK_USERNAME', 'ansible')
|
||||
self.show_invocation = boolean(
|
||||
os.getenv('SLACK_INVOCATION', self._display.verbosity > 1),
|
||||
strict=False
|
||||
)
|
||||
self.webhook_url = self._plugin_options['webook_url']
|
||||
self.channel = self._plugin_options['channel']
|
||||
self.username = self._plugin_options['username']
|
||||
self.show_invocation = (self._display.verbosity > 1)
|
||||
|
||||
if self.webhook_url is None:
|
||||
self.disabled = True
|
||||
|
@ -91,12 +102,13 @@ class CallbackModule(CallbackBase):
|
|||
'the `SLACK_WEBHOOK_URL` environment '
|
||||
'variable.')
|
||||
|
||||
self.playbook_name = None
|
||||
else:
|
||||
self.playbook_name = None
|
||||
|
||||
# This is a 6 character identifier provided with each message
|
||||
# This makes it easier to correlate messages when there are more
|
||||
# than 1 simultaneous playbooks running
|
||||
self.guid = uuid.uuid4().hex[:6]
|
||||
# This is a 6 character identifier provided with each message
|
||||
# This makes it easier to correlate messages when there are more
|
||||
# than 1 simultaneous playbooks running
|
||||
self.guid = uuid.uuid4().hex[:6]
|
||||
|
||||
def send_msg(self, attachments):
|
||||
payload = {
|
||||
|
@ -125,13 +137,13 @@ class CallbackModule(CallbackBase):
|
|||
'*Playbook initiated* (_%s_)' % self.guid
|
||||
]
|
||||
invocation_items = []
|
||||
if self._options and self.show_invocation:
|
||||
tags = self._options.tags
|
||||
skip_tags = self._options.skip_tags
|
||||
extra_vars = self._options.extra_vars
|
||||
subset = self._options.subset
|
||||
if self._plugin_options and self.show_invocation:
|
||||
tags = self._plugin_options.tags
|
||||
skip_tags = self._plugin_options.skip_tags
|
||||
extra_vars = self._plugin_options.extra_vars
|
||||
subset = self._plugin_options.subset
|
||||
inventory = os.path.basename(
|
||||
os.path.realpath(self._options.inventory)
|
||||
os.path.realpath(self._plugin_options.inventory)
|
||||
)
|
||||
|
||||
invocation_items.append('Inventory: %s' % inventory)
|
||||
|
@ -145,7 +157,7 @@ class CallbackModule(CallbackBase):
|
|||
invocation_items.append('Extra Vars: %s' %
|
||||
' '.join(extra_vars))
|
||||
|
||||
title.append('by *%s*' % self._options.remote_user)
|
||||
title.append('by *%s*' % self._plugin_options.remote_user)
|
||||
|
||||
title.append('\n\n*%s*' % self.playbook_name)
|
||||
msg_items = [' '.join(title)]
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
# (c) 2017, Frederic Van Espen <github@freh.be>
|
||||
#
|
||||
# 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/>.
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: stderr
|
||||
callback_type: stdout
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: Splits output, sending failed tasks to stderr
|
||||
version_added: "2.4"
|
||||
description:
|
||||
- This is the stderr callback plugin, it behaves like the default callback plugin but sends error output to stderr.
|
||||
- Also it does not output skipped host/task/item status
|
||||
'''
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
|
|
@ -1,3 +1,44 @@
|
|||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: syslog_json
|
||||
callback_type: notification
|
||||
requirements:
|
||||
- whietlist in configuration
|
||||
short_description: sends JSON events to syslog
|
||||
version_added: "1.9"
|
||||
description:
|
||||
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format
|
||||
- Before 2.4 only environment variables were available for configuration
|
||||
options:
|
||||
server:
|
||||
description: syslog server that will recieve the event
|
||||
env:
|
||||
- name: SYSLOG_SERVER
|
||||
default: localhost
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_server
|
||||
port:
|
||||
description: prot on which the syslog server is listening
|
||||
env:
|
||||
- name: SYSLOG_PORT
|
||||
default: 514
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_port
|
||||
facility:
|
||||
description: syslog facitliy to log as
|
||||
env:
|
||||
- name: SYSLOG_FACILITY
|
||||
default: user
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_facility
|
||||
'''
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
@ -16,15 +57,8 @@ from ansible.plugins.callback import CallbackBase
|
|||
class CallbackModule(CallbackBase):
|
||||
"""
|
||||
logs ansible-playbook and ansible runs to a syslog server in json format
|
||||
make sure you have in ansible.cfg:
|
||||
callback_plugins = <path_to_callback_plugins_folder>
|
||||
and put the plugin in <path_to_callback_plugins_folder>
|
||||
|
||||
This plugin makes use of the following environment variables:
|
||||
SYSLOG_SERVER (optional): defaults to localhost
|
||||
SYSLOG_PORT (optional): defaults to 514
|
||||
SYSLOG_FACILITY (optional): defaults to user
|
||||
"""
|
||||
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'aggregate'
|
||||
CALLBACK_NAME = 'syslog_json'
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: timer
|
||||
callback_type: aggregate
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
short_description: Adds time to play stats
|
||||
version_added: "2.0"
|
||||
description:
|
||||
- This callback just adds total play duration to the play stats.
|
||||
'''
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
# (c) 2012-2014, Ansible, Inc
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
'''
|
||||
DOCUMENTATION:
|
||||
callback: tree
|
||||
callback_type: notification
|
||||
requirements:
|
||||
- invoked in the command line
|
||||
short_description: Save host events to files
|
||||
version_added: "2.0"
|
||||
description:
|
||||
- "This callback is used by the Ansible (adhoc) command line option `-t|--tree`"
|
||||
- This produces a JSON dump of events in a directory, a file for each host, the directory used MUST be passed as a commadn line option.
|
||||
'''
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
|
|
@ -28,6 +28,11 @@ DOCUMENTATION:
|
|||
private_key_file:
|
||||
description:
|
||||
- Key or certificate file used for authentication
|
||||
ini:
|
||||
- section: defaults
|
||||
key: private_key_file
|
||||
env:
|
||||
- name: ANSIBLE_PRIVATE_KEY_FILE
|
||||
vars:
|
||||
- name: ansible_private_key_file
|
||||
timeout:
|
||||
|
|
|
@ -69,10 +69,11 @@ DOCUMENTATION:
|
|||
description: Extra exclusive to the 'ssh' CLI
|
||||
vars:
|
||||
- name: ansible_ssh_extra_args
|
||||
ssh_retries:
|
||||
retries:
|
||||
# constant: ANSIBLE_SSH_RETRIES
|
||||
description: Number of attempts to connect.
|
||||
default: 3
|
||||
type: integer
|
||||
env:
|
||||
- name: ANSIBLE_SSH_RETRIES
|
||||
ini:
|
||||
|
@ -118,14 +119,54 @@ DOCUMENTATION:
|
|||
- {key: pipelining, section: ssh_connection}
|
||||
type: boolean
|
||||
vars: [{name: ansible_ssh_pipelining}]
|
||||
# TODO:
|
||||
# ANSIBLE_SSH_RETRIES
|
||||
private_key_file:
|
||||
description:
|
||||
- Path to private key file to use for authentication
|
||||
ini:
|
||||
- section: defaults
|
||||
key: private_key_file
|
||||
env:
|
||||
- name: ANSIBLE_PRIVATE_KEY_FILE
|
||||
vars:
|
||||
- name: ansible_private_key_file
|
||||
- name: ansible_ssh_private_key_file
|
||||
|
||||
# self._play_context.private_key_file
|
||||
# ANSIBLE_SSH_CONTROL_PATH
|
||||
# ANSIBLE_SSH_CONTROL_PATH_DIR
|
||||
# DEFAULT_SFTP_BATCH_MODE
|
||||
# DEFAULT_SCP_IF_SSH
|
||||
control_path:
|
||||
default: null
|
||||
description:
|
||||
- This is the location to save ssh's ControlPath sockets, it uses ssh's variable substitution.
|
||||
- Since 2.3, if null, ansible will generate a unique hash. Use `%(directory)s` to indicate where to use the control dir path setting.
|
||||
env:
|
||||
- name: ANSIBLE_SSH_CONTROL_PATH
|
||||
ini:
|
||||
- key: control_path
|
||||
section: ssh_connection
|
||||
control_path_dir:
|
||||
default: ~/.ansible/cp
|
||||
description:
|
||||
- This sets the directory to use for ssh control path if the control path setting is null.
|
||||
- Also, provides the `%(directory)s` variable for the control path setting.
|
||||
env:
|
||||
- name: ANSIBLE_SSH_CONTROL_PATH_DIR
|
||||
ini:
|
||||
- section: ssh_connection
|
||||
key: control_path_dir
|
||||
sftp_batch_mode:
|
||||
default: True
|
||||
description: 'TODO: write it'
|
||||
env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
|
||||
ini:
|
||||
- {key: sftp_batch_mode, section: ssh_connection}
|
||||
type: boolean
|
||||
scp_if_ssh:
|
||||
default: smart
|
||||
description:
|
||||
- "Prefered method to use when transfering files over ssh"
|
||||
- When set to smart, Ansible will try them until one succeeds or they all fail
|
||||
- If set to True, it will force 'scp', if False it will use 'sftp'
|
||||
env: [{name: ANSIBLE_SCP_IF_SSH}]
|
||||
ini:
|
||||
- {key: scp_if_ssh, section: ssh_connection}
|
||||
'''
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
|
|
@ -205,14 +205,17 @@ class PluginLoader:
|
|||
''' Reads plugin docs to find configuration setting definitions, to push to config manager for later use '''
|
||||
|
||||
# plugins w/o class name don't support config
|
||||
if self.class_name and self.class_name in ('Connection'):
|
||||
# FIXME: expand from just connection
|
||||
type_name = get_plugin_class(self)
|
||||
dstring = read_docstring(path, verbose=False, ignore_errors=False)
|
||||
if dstring.get('doc', False):
|
||||
if 'options' in dstring['doc'] and isinstance(dstring['doc']['options'], dict):
|
||||
C.config.initialize_plugin_configuration_definitions(type_name, name, dstring['doc']['options'])
|
||||
display.debug('Loaded config def from plugin (%s/%s)' % (type_name, name))
|
||||
if self.class_name:
|
||||
type_name = get_plugin_class(self.class_name)
|
||||
|
||||
# FIXME: expand from just connection and callback
|
||||
if type_name in ('connection', 'callback'):
|
||||
dstring = read_docstring(path, verbose=False, ignore_errors=False)
|
||||
|
||||
if dstring.get('doc', False):
|
||||
if 'options' in dstring['doc'] and isinstance(dstring['doc']['options'], dict):
|
||||
C.config.initialize_plugin_configuration_definitions(type_name, name, dstring['doc']['options'])
|
||||
display.debug('Loaded config def from plugin (%s/%s)' % (type_name, name))
|
||||
|
||||
def add_directory(self, directory, with_subdir=False):
|
||||
''' Adds an additional directory to the search path '''
|
||||
|
|
Loading…
Reference in a new issue