diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py
index 545cb7c19b..5270a4fa66 100644
--- a/lib/ansible/config/manager.py
+++ b/lib/ansible/config/manager.py
@@ -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)))
diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py
index 6dbf51c716..faea2aff7f 100644
--- a/lib/ansible/executor/task_queue_manager.py
+++ b/lib/ansible/executor/task_queue_manager.py
@@ -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])))
diff --git a/lib/ansible/plugins/__init__.py b/lib/ansible/plugins/__init__.py
index c89ffd7ef7..cee24dad58 100644
--- a/lib/ansible/plugins/__init__.py
+++ b/lib/ansible/plugins/__init__.py
@@ -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
diff --git a/lib/ansible/plugins/callback/__init__.py b/lib/ansible/plugins/callback/__init__.py
index 48a68a25ac..739fb24e48 100644
--- a/lib/ansible/plugins/callback/__init__.py
+++ b/lib/ansible/plugins/callback/__init__.py
@@ -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"))
diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py
index 34f4180f54..033cbbde44 100644
--- a/lib/ansible/plugins/callback/default.py
+++ b/lib/ansible/plugins/callback/default.py
@@ -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))
diff --git a/lib/ansible/plugins/callback/logentries.py b/lib/ansible/plugins/callback/logentries.py
index ddb3f096bf..8f0d73b878 100644
--- a/lib/ansible/plugins/callback/logentries.py
+++ b/lib/ansible/plugins/callback/logentries.py
@@ -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)
diff --git a/lib/ansible/plugins/callback/profile_roles.py b/lib/ansible/plugins/callback/profile_roles.py
index 26d86595ac..fd4333ff4d 100644
--- a/lib/ansible/plugins/callback/profile_roles.py
+++ b/lib/ansible/plugins/callback/profile_roles.py
@@ -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 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)
diff --git a/lib/ansible/plugins/callback/profile_tasks.py b/lib/ansible/plugins/callback/profile_tasks.py
index e5b57a6275..cb97a2d356 100644
--- a/lib/ansible/plugins/callback/profile_tasks.py
+++ b/lib/ansible/plugins/callback/profile_tasks.py
@@ -2,29 +2,60 @@
# (C) 2015, Tom Paine,
# (C) 2014, Jharrod LaFon, @JharrodLaFon
# (C) 2012-2013, Michael DeHaan,
-#
-# 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 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: `` () ``"
+ - 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'],
diff --git a/lib/ansible/plugins/callback/profile_tasks.rst b/lib/ansible/plugins/callback/profile_tasks.rst
deleted file mode 100644
index aad84774ea..0000000000
--- a/lib/ansible/plugins/callback/profile_tasks.rst
+++ /dev/null
@@ -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 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
-
- 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+
diff --git a/lib/ansible/plugins/callback/selective.py b/lib/ansible/plugins/callback/selective.py
index d0edf46055..d61eb06764 100644
--- a/lib/ansible/plugins/callback/selective.py
+++ b/lib/ansible/plugins/callback/selective.py
@@ -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 .
+# (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
diff --git a/lib/ansible/plugins/callback/skippy.py b/lib/ansible/plugins/callback/skippy.py
index 856b424894..522233fe34 100644
--- a/lib/ansible/plugins/callback/skippy.py
+++ b/lib/ansible/plugins/callback/skippy.py
@@ -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:
diff --git a/lib/ansible/plugins/callback/slack.py b/lib/ansible/plugins/callback/slack.py
index 13c5b1183f..a4bf4ef8ad 100644
--- a/lib/ansible/plugins/callback/slack.py
+++ b/lib/ansible/plugins/callback/slack.py
@@ -1,20 +1,45 @@
# (C) 2014-2015, Matt Martz
+# (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 .
-
+'''
+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)]
diff --git a/lib/ansible/plugins/callback/stderr.py b/lib/ansible/plugins/callback/stderr.py
index 98e469a0e3..5afde9f92d 100644
--- a/lib/ansible/plugins/callback/stderr.py
+++ b/lib/ansible/plugins/callback/stderr.py
@@ -1,20 +1,19 @@
# (c) 2017, Frederic Van Espen
-#
-# 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 .
+# (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
diff --git a/lib/ansible/plugins/callback/syslog_json.py b/lib/ansible/plugins/callback/syslog_json.py
index 912a64222f..6d0d1446a7 100644
--- a/lib/ansible/plugins/callback/syslog_json.py
+++ b/lib/ansible/plugins/callback/syslog_json.py
@@ -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 =
- and put the plugin in
-
- 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'
diff --git a/lib/ansible/plugins/callback/timer.py b/lib/ansible/plugins/callback/timer.py
index ce444badf7..f2657e0b53 100644
--- a/lib/ansible/plugins/callback/timer.py
+++ b/lib/ansible/plugins/callback/timer.py
@@ -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
diff --git a/lib/ansible/plugins/callback/tree.py b/lib/ansible/plugins/callback/tree.py
index e403d99fd2..a0918357b4 100644
--- a/lib/ansible/plugins/callback/tree.py
+++ b/lib/ansible/plugins/callback/tree.py
@@ -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 .
-
+# (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
diff --git a/lib/ansible/plugins/connection/network_cli.py b/lib/ansible/plugins/connection/network_cli.py
index 365d045c75..0525809f57 100644
--- a/lib/ansible/plugins/connection/network_cli.py
+++ b/lib/ansible/plugins/connection/network_cli.py
@@ -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:
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 7f6a236359..7280489168 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -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)
diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py
index 1302bfbf5d..61721d086f 100644
--- a/lib/ansible/plugins/loader.py
+++ b/lib/ansible/plugins/loader.py
@@ -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 '''