summaryrefslogtreecommitdiffstats
path: root/lib/ansible/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:16:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:16:49 +0000
commit48e387c5c12026a567eb7b293a3a590241c0cecb (patch)
tree80f2573be2d7d534b8ac4d2a852fe43f7ac35324 /lib/ansible/plugins
parentReleasing progress-linux version 2.16.6-1~progress7.99u1. (diff)
downloadansible-core-48e387c5c12026a567eb7b293a3a590241c0cecb.tar.xz
ansible-core-48e387c5c12026a567eb7b293a3a590241c0cecb.zip
Merging upstream version 2.17.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/ansible/plugins')
-rw-r--r--lib/ansible/plugins/__init__.py9
-rw-r--r--lib/ansible/plugins/action/__init__.py34
-rw-r--r--lib/ansible/plugins/action/add_host.py6
-rw-r--r--lib/ansible/plugins/action/assemble.py5
-rw-r--r--lib/ansible/plugins/action/assert.py3
-rw-r--r--lib/ansible/plugins/action/async_status.py3
-rw-r--r--lib/ansible/plugins/action/command.py3
-rw-r--r--lib/ansible/plugins/action/copy.py21
-rw-r--r--lib/ansible/plugins/action/debug.py3
-rw-r--r--lib/ansible/plugins/action/dnf.py8
-rw-r--r--lib/ansible/plugins/action/fail.py3
-rw-r--r--lib/ansible/plugins/action/fetch.py5
-rw-r--r--lib/ansible/plugins/action/gather_facts.py7
-rw-r--r--lib/ansible/plugins/action/group_by.py3
-rw-r--r--lib/ansible/plugins/action/include_vars.py3
-rw-r--r--lib/ansible/plugins/action/normal.py3
-rw-r--r--lib/ansible/plugins/action/package.py57
-rw-r--r--lib/ansible/plugins/action/pause.py3
-rw-r--r--lib/ansible/plugins/action/raw.py5
-rw-r--r--lib/ansible/plugins/action/reboot.py45
-rw-r--r--lib/ansible/plugins/action/script.py7
-rw-r--r--lib/ansible/plugins/action/service.py3
-rw-r--r--lib/ansible/plugins/action/set_fact.py3
-rw-r--r--lib/ansible/plugins/action/set_stats.py3
-rw-r--r--lib/ansible/plugins/action/shell.py3
-rw-r--r--lib/ansible/plugins/action/template.py3
-rw-r--r--lib/ansible/plugins/action/unarchive.py3
-rw-r--r--lib/ansible/plugins/action/uri.py5
-rw-r--r--lib/ansible/plugins/action/validate_argument_spec.py3
-rw-r--r--lib/ansible/plugins/action/wait_for_connection.py5
-rw-r--r--lib/ansible/plugins/action/yum.py111
-rw-r--r--lib/ansible/plugins/become/__init__.py3
-rw-r--r--lib/ansible/plugins/become/runas.py3
-rw-r--r--lib/ansible/plugins/become/su.py3
-rw-r--r--lib/ansible/plugins/become/sudo.py3
-rw-r--r--lib/ansible/plugins/cache/__init__.py6
-rw-r--r--lib/ansible/plugins/cache/base.py3
-rw-r--r--lib/ansible/plugins/cache/jsonfile.py4
-rw-r--r--lib/ansible/plugins/cache/memory.py3
-rw-r--r--lib/ansible/plugins/callback/__init__.py6
-rw-r--r--lib/ansible/plugins/callback/default.py5
-rw-r--r--lib/ansible/plugins/callback/junit.py3
-rw-r--r--lib/ansible/plugins/callback/minimal.py4
-rw-r--r--lib/ansible/plugins/callback/oneline.py4
-rw-r--r--lib/ansible/plugins/callback/tree.py3
-rw-r--r--lib/ansible/plugins/cliconf/__init__.py3
-rw-r--r--lib/ansible/plugins/connection/__init__.py9
-rw-r--r--lib/ansible/plugins/connection/local.py7
-rw-r--r--lib/ansible/plugins/connection/paramiko_ssh.py3
-rw-r--r--lib/ansible/plugins/connection/psrp.py5
-rw-r--r--lib/ansible/plugins/connection/ssh.py72
-rw-r--r--lib/ansible/plugins/connection/winrm.py7
-rw-r--r--lib/ansible/plugins/doc_fragments/action_common_attributes.py5
-rw-r--r--lib/ansible/plugins/doc_fragments/action_core.py7
-rw-r--r--lib/ansible/plugins/doc_fragments/backup.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/connection_pipelining.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/constructed.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/decrypt.py5
-rw-r--r--lib/ansible/plugins/doc_fragments/default_callback.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/files.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/inventory_cache.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/result_format_callback.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/return_common.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/shell_common.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/shell_windows.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/template_common.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/url.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/url_windows.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/validate.py3
-rw-r--r--lib/ansible/plugins/doc_fragments/vars_plugin_staging.py3
-rw-r--r--lib/ansible/plugins/filter/__init__.py3
-rw-r--r--lib/ansible/plugins/filter/b64decode.yml16
-rw-r--r--lib/ansible/plugins/filter/b64encode.yml8
-rw-r--r--lib/ansible/plugins/filter/comment.yml2
-rw-r--r--lib/ansible/plugins/filter/core.py20
-rw-r--r--lib/ansible/plugins/filter/encryption.py4
-rw-r--r--lib/ansible/plugins/filter/extract.yml2
-rw-r--r--lib/ansible/plugins/filter/from_yaml_all.yml2
-rw-r--r--lib/ansible/plugins/filter/human_readable.yml6
-rw-r--r--lib/ansible/plugins/filter/human_to_bytes.yml4
-rw-r--r--lib/ansible/plugins/filter/mandatory.yml2
-rw-r--r--lib/ansible/plugins/filter/mathstuff.py4
-rw-r--r--lib/ansible/plugins/filter/password_hash.yml5
-rw-r--r--lib/ansible/plugins/filter/regex_replace.yml15
-rw-r--r--lib/ansible/plugins/filter/regex_search.yml12
-rw-r--r--lib/ansible/plugins/filter/strftime.yml11
-rw-r--r--lib/ansible/plugins/filter/to_datetime.yml19
-rw-r--r--lib/ansible/plugins/filter/to_nice_json.yml4
-rw-r--r--lib/ansible/plugins/filter/union.yml2
-rw-r--r--lib/ansible/plugins/filter/urls.py3
-rw-r--r--lib/ansible/plugins/filter/urlsplit.py3
-rw-r--r--lib/ansible/plugins/filter/zip.yml4
-rw-r--r--lib/ansible/plugins/filter/zip_longest.yml2
-rw-r--r--lib/ansible/plugins/httpapi/__init__.py3
-rw-r--r--lib/ansible/plugins/inventory/__init__.py6
-rw-r--r--lib/ansible/plugins/inventory/advanced_host_list.py3
-rw-r--r--lib/ansible/plugins/inventory/auto.py5
-rw-r--r--lib/ansible/plugins/inventory/constructed.py5
-rw-r--r--lib/ansible/plugins/inventory/generator.py3
-rw-r--r--lib/ansible/plugins/inventory/host_list.py3
-rw-r--r--lib/ansible/plugins/inventory/ini.py3
-rw-r--r--lib/ansible/plugins/inventory/script.py125
-rw-r--r--lib/ansible/plugins/inventory/toml.py3
-rw-r--r--lib/ansible/plugins/inventory/yaml.py7
-rw-r--r--lib/ansible/plugins/list.py5
-rw-r--r--lib/ansible/plugins/loader.py21
-rw-r--r--lib/ansible/plugins/lookup/__init__.py4
-rw-r--r--lib/ansible/plugins/lookup/config.py34
-rw-r--r--lib/ansible/plugins/lookup/csvfile.py23
-rw-r--r--lib/ansible/plugins/lookup/dict.py5
-rw-r--r--lib/ansible/plugins/lookup/env.py8
-rw-r--r--lib/ansible/plugins/lookup/file.py3
-rw-r--r--lib/ansible/plugins/lookup/fileglob.py3
-rw-r--r--lib/ansible/plugins/lookup/first_found.py15
-rw-r--r--lib/ansible/plugins/lookup/indexed_items.py3
-rw-r--r--lib/ansible/plugins/lookup/ini.py9
-rw-r--r--lib/ansible/plugins/lookup/inventory_hostnames.py3
-rw-r--r--lib/ansible/plugins/lookup/items.py3
-rw-r--r--lib/ansible/plugins/lookup/lines.py3
-rw-r--r--lib/ansible/plugins/lookup/list.py4
-rw-r--r--lib/ansible/plugins/lookup/nested.py3
-rw-r--r--lib/ansible/plugins/lookup/password.py30
-rw-r--r--lib/ansible/plugins/lookup/pipe.py3
-rw-r--r--lib/ansible/plugins/lookup/random_choice.py3
-rw-r--r--lib/ansible/plugins/lookup/sequence.py73
-rw-r--r--lib/ansible/plugins/lookup/subelements.py3
-rw-r--r--lib/ansible/plugins/lookup/template.py3
-rw-r--r--lib/ansible/plugins/lookup/together.py3
-rw-r--r--lib/ansible/plugins/lookup/unvault.py3
-rw-r--r--lib/ansible/plugins/lookup/url.py12
-rw-r--r--lib/ansible/plugins/lookup/varnames.py3
-rw-r--r--lib/ansible/plugins/lookup/vars.py3
-rw-r--r--lib/ansible/plugins/netconf/__init__.py3
-rw-r--r--lib/ansible/plugins/shell/__init__.py7
-rw-r--r--lib/ansible/plugins/shell/cmd.py3
-rw-r--r--lib/ansible/plugins/shell/powershell.py3
-rw-r--r--lib/ansible/plugins/shell/sh.py3
-rw-r--r--lib/ansible/plugins/strategy/__init__.py54
-rw-r--r--lib/ansible/plugins/strategy/debug.py3
-rw-r--r--lib/ansible/plugins/strategy/free.py24
-rw-r--r--lib/ansible/plugins/strategy/host_pinned.py4
-rw-r--r--lib/ansible/plugins/strategy/linear.py44
-rw-r--r--lib/ansible/plugins/terminal/__init__.py5
-rw-r--r--lib/ansible/plugins/test/__init__.py3
-rw-r--r--lib/ansible/plugins/test/change.yml2
-rw-r--r--lib/ansible/plugins/test/changed.yml2
-rw-r--r--lib/ansible/plugins/test/contains.yml2
-rw-r--r--lib/ansible/plugins/test/core.py6
-rw-r--r--lib/ansible/plugins/test/exists.yml2
-rw-r--r--lib/ansible/plugins/test/failed.yml2
-rw-r--r--lib/ansible/plugins/test/failure.yml2
-rw-r--r--lib/ansible/plugins/test/files.py4
-rw-r--r--lib/ansible/plugins/test/finished.yml6
-rw-r--r--lib/ansible/plugins/test/issuperset.yml2
-rw-r--r--lib/ansible/plugins/test/match.yml2
-rw-r--r--lib/ansible/plugins/test/mathstuff.py3
-rw-r--r--lib/ansible/plugins/test/reachable.yml2
-rw-r--r--lib/ansible/plugins/test/regex.yml2
-rw-r--r--lib/ansible/plugins/test/search.yml2
-rw-r--r--lib/ansible/plugins/test/skip.yml2
-rw-r--r--lib/ansible/plugins/test/skipped.yml2
-rw-r--r--lib/ansible/plugins/test/started.yml2
-rw-r--r--lib/ansible/plugins/test/succeeded.yml2
-rw-r--r--lib/ansible/plugins/test/success.yml2
-rw-r--r--lib/ansible/plugins/test/successful.yml2
-rw-r--r--lib/ansible/plugins/test/superset.yml2
-rw-r--r--lib/ansible/plugins/test/unreachable.yml2
-rw-r--r--lib/ansible/plugins/test/uri.py4
-rw-r--r--lib/ansible/plugins/vars/__init__.py3
-rw-r--r--lib/ansible/plugins/vars/host_group_vars.py5
170 files changed, 689 insertions, 742 deletions
diff --git a/lib/ansible/plugins/__init__.py b/lib/ansible/plugins/__init__.py
index 0333361..c083dee 100644
--- a/lib/ansible/plugins/__init__.py
+++ b/lib/ansible/plugins/__init__.py
@@ -17,9 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from abc import ABC
@@ -93,7 +91,7 @@ class AnsiblePlugin(ABC):
return options
def set_option(self, option, value):
- self._options[option] = value
+ self._options[option] = C.config.get_config_value(option, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={option: value})
def set_options(self, task_keys=None, var_options=None, direct=None):
'''
@@ -108,7 +106,8 @@ class AnsiblePlugin(ABC):
# allow extras/wildcards from vars that are not directly consumed in configuration
# this is needed to support things like winrm that can have extended protocol options we don't directly handle
if self.allow_extras and var_options and '_extras' in var_options:
- self.set_option('_extras', var_options['_extras'])
+ # these are largely unvalidated passthroughs, either plugin or underlying API will validate
+ self._options['_extras'] = var_options['_extras']
def has_option(self, option):
if not self._options:
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
index 5ba3bd7..7ebfd13 100644
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -3,9 +3,7 @@
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import base64
import json
@@ -102,7 +100,7 @@ class ActionBase(ABC):
etc) associated with this task.
:returns: dictionary of results from the module
- Implementors of action modules may find the following variables especially useful:
+ Implementers of action modules may find the following variables especially useful:
* Module parameters. These are stored in self._task.args
"""
@@ -117,11 +115,9 @@ class ActionBase(ABC):
del tmp
if self._task.async_val and not self._supports_async:
- raise AnsibleActionFail('async is not supported for this task.')
+ raise AnsibleActionFail('This action (%s) does not support async.' % self._task.action)
elif self._task.check_mode and not self._supports_check_mode:
- raise AnsibleActionSkip('check mode is not supported for this task.')
- elif self._task.async_val and self._task.check_mode:
- raise AnsibleActionFail('check mode and async cannot be used on same task.')
+ raise AnsibleActionSkip('This action (%s) does not support check mode.' % self._task.action)
# Error if invalid argument is passed
if self._VALID_ARGS:
@@ -851,10 +847,13 @@ class ActionBase(ABC):
path=path,
follow=follow,
get_checksum=checksum,
+ get_size=False, # ansible.windows.win_stat added this in 1.11.0
checksum_algorithm='sha1',
)
+ # Unknown opts are ignored as module_args could be specific for the
+ # module that is being executed.
mystat = self._execute_module(module_name='ansible.legacy.stat', module_args=module_args, task_vars=all_vars,
- wrap_async=False)
+ wrap_async=False, ignore_unknown_opts=True)
if mystat.get('failed'):
msg = mystat.get('module_stderr')
@@ -938,7 +937,7 @@ class ActionBase(ABC):
data = re.sub(r'^((\r)?\n)?BECOME-SUCCESS.*(\r)?\n', '', data)
return data
- def _update_module_args(self, module_name, module_args, task_vars):
+ def _update_module_args(self, module_name, module_args, task_vars, ignore_unknown_opts: bool = False):
# set check mode in the module arguments, if required
if self._task.check_mode:
@@ -996,7 +995,14 @@ class ActionBase(ABC):
# make sure the remote_tmp value is sent through in case modules needs to create their own
module_args['_ansible_remote_tmp'] = self.get_shell_option('remote_tmp', default='~/.ansible/tmp')
- def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False):
+ # tells the module to ignore options that are not in its argspec.
+ module_args['_ansible_ignore_unknown_opts'] = ignore_unknown_opts
+
+ # allow user to insert string to add context to remote loggging
+ module_args['_ansible_target_log_info'] = C.config.get_config_value('TARGET_LOG_INFO', variables=task_vars)
+
+ def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False,
+ ignore_unknown_opts: bool = False):
'''
Transfer and run a module along with its arguments.
'''
@@ -1032,7 +1038,7 @@ class ActionBase(ABC):
if module_args is None:
module_args = self._task.args
- self._update_module_args(module_name, module_args, task_vars)
+ self._update_module_args(module_name, module_args, task_vars, ignore_unknown_opts=ignore_unknown_opts)
remove_async_dir = None
if wrap_async or self._task.async_val:
@@ -1157,7 +1163,7 @@ class ActionBase(ABC):
if data.pop("_ansible_suppress_tmpdir_delete", False):
self._cleanup_remote_tmp = False
- # NOTE: yum returns results .. but that made it 'compatible' with squashing, so we allow mappings, for now
+ # NOTE: dnf returns results .. but that made it 'compatible' with squashing, so we allow mappings, for now
if 'results' in data and (not isinstance(data['results'], Sequence) or isinstance(data['results'], string_types)):
data['ansible_module_results'] = data['results']
del data['results']
@@ -1339,7 +1345,7 @@ class ActionBase(ABC):
display.debug(u"_low_level_execute_command() done: rc=%d, stdout=%s, stderr=%s" % (rc, out, err))
return dict(rc=rc, stdout=out, stdout_lines=out.splitlines(), stderr=err, stderr_lines=err.splitlines())
- def _get_diff_data(self, destination, source, task_vars, content, source_file=True):
+ def _get_diff_data(self, destination, source, task_vars, content=None, source_file=True):
# Note: Since we do not diff the source and destination before we transform from bytes into
# text the diff between source and destination may not be accurate. To fix this, we'd need
diff --git a/lib/ansible/plugins/action/add_host.py b/lib/ansible/plugins/action/add_host.py
index ede2e05..9039e34 100644
--- a/lib/ansible/plugins/action/add_host.py
+++ b/lib/ansible/plugins/action/add_host.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from collections.abc import Mapping
@@ -51,7 +49,7 @@ class ActionModule(ActionBase):
# TODO: create 'conflict' detection in base class to deal with repeats and aliases and warn user
args = combine_vars(raw, args)
else:
- raise AnsibleActionFail('Invalid raw parameters passed, requires a dictonary/mapping got a %s' % type(raw))
+ raise AnsibleActionFail('Invalid raw parameters passed, requires a dictionary/mapping got a %s' % type(raw))
# Parse out any hostname:port patterns
new_name = args.get('name', args.get('hostname', args.get('host', None)))
diff --git a/lib/ansible/plugins/action/assemble.py b/lib/ansible/plugins/action/assemble.py
index da794ed..6d0634c 100644
--- a/lib/ansible/plugins/action/assemble.py
+++ b/lib/ansible/plugins/action/assemble.py
@@ -16,8 +16,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import codecs
import os
@@ -140,7 +139,7 @@ class ActionModule(ActionBase):
if path_checksum != dest_stat['checksum']:
- if self._play_context.diff:
+ if self._task.diff:
diff = self._get_diff_data(dest, path, task_vars)
remote_path = self._connection._shell.join_path(self._connection._shell.tmpdir, 'src')
diff --git a/lib/ansible/plugins/action/assert.py b/lib/ansible/plugins/action/assert.py
index e2fe329..eb6c646 100644
--- a/lib/ansible/plugins/action/assert.py
+++ b/lib/ansible/plugins/action/assert.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleError
from ansible.playbook.conditional import Conditional
diff --git a/lib/ansible/plugins/action/async_status.py b/lib/ansible/plugins/action/async_status.py
index 4f50fe6..a0fe11e 100644
--- a/lib/ansible/plugins/action/async_status.py
+++ b/lib/ansible/plugins/action/async_status.py
@@ -1,8 +1,7 @@
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins.action import ActionBase
from ansible.utils.vars import merge_hash
diff --git a/lib/ansible/plugins/action/command.py b/lib/ansible/plugins/action/command.py
index 64e1a09..df4dbe9 100644
--- a/lib/ansible/plugins/action/command.py
+++ b/lib/ansible/plugins/action/command.py
@@ -1,8 +1,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins.action import ActionBase
from ansible.utils.vars import merge_hash
diff --git a/lib/ansible/plugins/action/copy.py b/lib/ansible/plugins/action/copy.py
index 048f98d..3799d11 100644
--- a/lib/ansible/plugins/action/copy.py
+++ b/lib/ansible/plugins/action/copy.py
@@ -16,9 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import json
import os
@@ -151,7 +149,7 @@ def _walk_dirs(topdir, base_path=None, local_follow=False, trailing_slash_detect
new_parents.add((parent_stat.st_dev, parent_stat.st_ino))
if (dir_stats.st_dev, dir_stats.st_ino) in new_parents:
- # This was a a circular symlink. So add it as
+ # This was a circular symlink. So add it as
# a symlink
r_files['symlinks'].append((os.readlink(dirpath), dest_dirpath))
else:
@@ -212,7 +210,7 @@ class ActionModule(ActionBase):
# NOTE: do not add to this. This should be made a generic function for action plugins.
# This should also use the same argspec as the module instead of keeping it in sync.
if 'invocation' not in result:
- if self._play_context.no_log:
+ if self._task.no_log:
result['invocation'] = "CENSORED: no_log is set"
else:
# NOTE: Should be removed in the future. For now keep this broken
@@ -285,16 +283,21 @@ class ActionModule(ActionBase):
if local_checksum != dest_status['checksum']:
# The checksums don't match and we will change or error out.
- if self._play_context.diff and not raw:
+ if self._task.diff and not raw:
result['diff'].append(self._get_diff_data(dest_file, source_full, task_vars, content))
- if self._play_context.check_mode:
+ if self._task.check_mode:
self._remove_tempfile_if_content_defined(content, content_tempfile)
result['changed'] = True
return result
# Define a remote directory that we will copy the file to.
- tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, 'source')
+ tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, '.source')
+
+ # ensure we keep suffix for validate
+ suffix = os.path.splitext(dest_file)[1]
+ if suffix:
+ tmp_src += suffix
remote_path = None
@@ -387,7 +390,7 @@ class ActionModule(ActionBase):
def _create_content_tempfile(self, content):
''' Create a tempfile containing defined content '''
- fd, content_tempfile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP)
+ fd, content_tempfile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP, prefix='.')
f = os.fdopen(fd, 'wb')
content = to_bytes(content)
try:
diff --git a/lib/ansible/plugins/action/debug.py b/lib/ansible/plugins/action/debug.py
index 9e23c5f..579ffce 100644
--- a/lib/ansible/plugins/action/debug.py
+++ b/lib/ansible/plugins/action/debug.py
@@ -15,8 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleUndefinedVariable
from ansible.module_utils.six import string_types
diff --git a/lib/ansible/plugins/action/dnf.py b/lib/ansible/plugins/action/dnf.py
index bf8ac3f..52391a4 100644
--- a/lib/ansible/plugins/action/dnf.py
+++ b/lib/ansible/plugins/action/dnf.py
@@ -1,5 +1,6 @@
# Copyright: (c) 2023, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import annotations
from ansible.errors import AnsibleActionFail
from ansible.plugins.action import ActionBase
@@ -7,10 +8,9 @@ from ansible.utils.display import Display
display = Display()
-VALID_BACKENDS = frozenset(("dnf", "dnf4", "dnf5"))
+VALID_BACKENDS = frozenset(("yum", "yum4", "dnf", "dnf4", "dnf5"))
-# FIXME mostly duplicate of the yum action plugin
class ActionModule(ActionBase):
TRANSFERS_FILES = False
@@ -28,7 +28,7 @@ class ActionModule(ActionBase):
module = self._task.args.get('use', self._task.args.get('use_backend', 'auto'))
- if module == 'auto':
+ if module in {'yum', 'auto'}:
try:
if self._task.delegate_to: # if we delegate, we should use delegated host's facts
module = self._templar.template("{{hostvars['%s']['ansible_facts']['pkg_mgr']}}" % self._task.delegate_to)
@@ -56,7 +56,7 @@ class ActionModule(ActionBase):
)
else:
- if module == "dnf4":
+ if module in {"yum4", "dnf4"}:
module = "dnf"
# eliminate collisions with collections search while still allowing local override
diff --git a/lib/ansible/plugins/action/fail.py b/lib/ansible/plugins/action/fail.py
index dedfc8c..998d8a9 100644
--- a/lib/ansible/plugins/action/fail.py
+++ b/lib/ansible/plugins/action/fail.py
@@ -15,8 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins.action import ActionBase
diff --git a/lib/ansible/plugins/action/fetch.py b/lib/ansible/plugins/action/fetch.py
index d057ed2..b7b6f30 100644
--- a/lib/ansible/plugins/action/fetch.py
+++ b/lib/ansible/plugins/action/fetch.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import base64
@@ -42,7 +41,7 @@ class ActionModule(ActionBase):
del tmp # tmp no longer has any effect
try:
- if self._play_context.check_mode:
+ if self._task.check_mode:
raise AnsibleActionSkip('check mode not (yet) supported for this module')
source = self._task.args.get('src', None)
diff --git a/lib/ansible/plugins/action/gather_facts.py b/lib/ansible/plugins/action/gather_facts.py
index 23962c8..31210ec 100644
--- a/lib/ansible/plugins/action/gather_facts.py
+++ b/lib/ansible/plugins/action/gather_facts.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import time
@@ -26,7 +25,7 @@ class ActionModule(ActionBase):
# deal with 'setup specific arguments'
if fact_module not in C._ACTION_SETUP:
- # TODO: remove in favor of controller side argspec detecing valid arguments
+ # TODO: remove in favor of controller side argspec detecting valid arguments
# network facts modules must support gather_subset
try:
name = self._connection.ansible_name.removeprefix('ansible.netcommon.')
@@ -123,7 +122,7 @@ class ActionModule(ActionBase):
mod_args = self._get_module_args(fact_module, task_vars)
# if module does not handle timeout, use timeout to handle module, hijack async_val as this is what async_wrapper uses
- # TODO: make this action compain about async/async settings, use parallel option instead .. or remove parallel in favor of async settings?
+ # TODO: make this action complain about async/async settings, use parallel option instead .. or remove parallel in favor of async settings?
if timeout and 'gather_timeout' not in mod_args:
self._task.async_val = int(timeout)
elif async_val != 0:
diff --git a/lib/ansible/plugins/action/group_by.py b/lib/ansible/plugins/action/group_by.py
index e0c7023..369e89b 100644
--- a/lib/ansible/plugins/action/group_by.py
+++ b/lib/ansible/plugins/action/group_by.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins.action import ActionBase
from ansible.module_utils.six import string_types
diff --git a/lib/ansible/plugins/action/include_vars.py b/lib/ansible/plugins/action/include_vars.py
index 83835b3..c32e622 100644
--- a/lib/ansible/plugins/action/include_vars.py
+++ b/lib/ansible/plugins/action/include_vars.py
@@ -1,8 +1,7 @@
# Copyright: (c) 2016, Allen Sanabria <asanabria@linuxdynasty.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from os import path, walk
import re
diff --git a/lib/ansible/plugins/action/normal.py b/lib/ansible/plugins/action/normal.py
index b2212e6..0476f9a 100644
--- a/lib/ansible/plugins/action/normal.py
+++ b/lib/ansible/plugins/action/normal.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible import constants as C
from ansible.plugins.action import ActionBase
diff --git a/lib/ansible/plugins/action/package.py b/lib/ansible/plugins/action/package.py
index 6c43659..6963b77 100644
--- a/lib/ansible/plugins/action/package.py
+++ b/lib/ansible/plugins/action/package.py
@@ -14,14 +14,14 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleAction, AnsibleActionFail
from ansible.executor.module_common import get_action_args_with_defaults
from ansible.module_utils.facts.system.pkg_mgr import PKG_MGRS
from ansible.plugins.action import ActionBase
from ansible.utils.display import Display
+from ansible.utils.vars import combine_vars
display = Display()
@@ -39,31 +39,46 @@ class ActionModule(ActionBase):
self._supports_async = True
result = super(ActionModule, self).run(tmp, task_vars)
- del tmp # tmp no longer has any effect
module = self._task.args.get('use', 'auto')
- if module == 'auto':
- try:
- if self._task.delegate_to: # if we delegate, we should use delegated host's facts
- module = self._templar.template("{{hostvars['%s']['ansible_facts']['pkg_mgr']}}" % self._task.delegate_to)
- else:
- module = self._templar.template('{{ansible_facts.pkg_mgr}}')
- except Exception:
- pass # could not get it from template!
-
try:
if module == 'auto':
- facts = self._execute_module(
- module_name='ansible.legacy.setup',
- module_args=dict(filter='ansible_pkg_mgr', gather_subset='!all'),
- task_vars=task_vars)
- display.debug("Facts %s" % facts)
- module = facts.get('ansible_facts', {}).get('ansible_pkg_mgr', 'auto')
-
- if module != 'auto':
+
+ if self._task.delegate_to:
+ hosts_vars = task_vars['hostvars'][self._task.delegate_to]
+ tvars = combine_vars(self._task.vars, task_vars.get('delegated_vars', {}))
+ else:
+ hosts_vars = task_vars
+ tvars = task_vars
+
+ # use config
+ module = tvars.get('ansible_package_use', None)
+
+ if not module:
+ # no use, no config, get from facts
+ if hosts_vars.get('ansible_facts', {}).get('pkg_mgr', False):
+ facts = hosts_vars
+ pmgr = 'pkg_mgr'
+ else:
+ # we had no facts, so generate them
+ # very expensive step, we actually run fact gathering because we don't have facts for this host.
+ facts = self._execute_module(
+ module_name='ansible.legacy.setup',
+ module_args=dict(filter='ansible_pkg_mgr', gather_subset='!all'),
+ task_vars=task_vars,
+ )
+ pmgr = 'ansible_pkg_mgr'
+
+ try:
+ # actually get from facts
+ module = facts['ansible_facts'][pmgr]
+ except KeyError:
+ raise AnsibleActionFail('Could not detect a package manager. Try using the "use" option.')
+
+ if module and module != 'auto':
if not self._shared_loader_obj.module_loader.has_plugin(module):
- raise AnsibleActionFail('Could not find a module for %s.' % module)
+ raise AnsibleActionFail('Could not find a matching action for the "%s" package manager.' % module)
else:
# run the 'package' module
new_module_args = self._task.args.copy()
diff --git a/lib/ansible/plugins/action/pause.py b/lib/ansible/plugins/action/pause.py
index d306fbf..d603579 100644
--- a/lib/ansible/plugins/action/pause.py
+++ b/lib/ansible/plugins/action/pause.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import datetime
import time
diff --git a/lib/ansible/plugins/action/raw.py b/lib/ansible/plugins/action/raw.py
index b82ed34..ac337c0 100644
--- a/lib/ansible/plugins/action/raw.py
+++ b/lib/ansible/plugins/action/raw.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins.action import ActionBase
@@ -33,7 +32,7 @@ class ActionModule(ActionBase):
result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect
- if self._play_context.check_mode:
+ if self._task.check_mode:
# in --check mode, always skip this module execution
result['skipped'] = True
return result
diff --git a/lib/ansible/plugins/action/reboot.py b/lib/ansible/plugins/action/reboot.py
index c75fba8..3245716 100644
--- a/lib/ansible/plugins/action/reboot.py
+++ b/lib/ansible/plugins/action/reboot.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2018, Sam Doran <sdoran@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import random
import time
@@ -241,9 +240,13 @@ class ActionModule(ActionBase):
try:
display.debug("{action}: setting connect_timeout to {value}".format(action=self._task.action, value=connect_timeout))
self._connection.set_option("connection_timeout", connect_timeout)
- self._connection.reset()
- except AttributeError:
- display.warning("Connection plugin does not allow the connection timeout to be overridden")
+ except AnsibleError:
+ try:
+ self._connection.set_option("timeout", connect_timeout)
+ except (AnsibleError, AttributeError):
+ display.warning("Connection plugin does not allow the connection timeout to be overridden")
+
+ self._connection.reset()
# try and get boot time
try:
@@ -373,17 +376,25 @@ class ActionModule(ActionBase):
try:
connect_timeout = self._connection.get_option('connection_timeout')
except KeyError:
- pass
+ try:
+ connect_timeout = self._connection.get_option('timeout')
+ except KeyError:
+ pass
else:
if original_connection_timeout != connect_timeout:
try:
- display.debug("{action}: setting connect_timeout back to original value of {value}".format(
- action=self._task.action,
- value=original_connection_timeout))
- self._connection.set_option("connection_timeout", original_connection_timeout)
+ display.debug("{action}: setting connect_timeout/timeout back to original value of {value}".format(action=self._task.action,
+ value=original_connection_timeout))
+ try:
+ self._connection.set_option("connection_timeout", original_connection_timeout)
+ except AnsibleError:
+ try:
+ self._connection.set_option("timeout", original_connection_timeout)
+ except AnsibleError:
+ raise
+ # reset the connection to clear the custom connection timeout
self._connection.reset()
except (AnsibleError, AttributeError) as e:
- # reset the connection to clear the custom connection timeout
display.debug("{action}: failed to reset connection_timeout back to default: {error}".format(action=self._task.action,
error=to_text(e)))
@@ -409,14 +420,13 @@ class ActionModule(ActionBase):
def run(self, tmp=None, task_vars=None):
self._supports_check_mode = True
- self._supports_async = True
# If running with local connection, fail so we don't reboot ourselves
if self._connection.transport == 'local':
msg = 'Running {0} with local connection would reboot the control node.'.format(self._task.action)
return {'changed': False, 'elapsed': 0, 'rebooted': False, 'failed': True, 'msg': msg}
- if self._play_context.check_mode:
+ if self._task.check_mode:
return {'changed': True, 'elapsed': 0, 'rebooted': True}
if task_vars is None:
@@ -442,11 +452,16 @@ class ActionModule(ActionBase):
# Get the original connection_timeout option var so it can be reset after
original_connection_timeout = None
+
+ display.debug("{action}: saving original connect_timeout of {timeout}".format(action=self._task.action, timeout=original_connection_timeout))
try:
original_connection_timeout = self._connection.get_option('connection_timeout')
- display.debug("{action}: saving original connect_timeout of {timeout}".format(action=self._task.action, timeout=original_connection_timeout))
except KeyError:
- display.debug("{action}: connect_timeout connection option has not been set".format(action=self._task.action))
+ try:
+ original_connection_timeout = self._connection.get_option('timeout')
+ except KeyError:
+ display.debug("{action}: connect_timeout connection option has not been set".format(action=self._task.action))
+
# Initiate reboot
reboot_result = self.perform_reboot(task_vars, distribution)
diff --git a/lib/ansible/plugins/action/script.py b/lib/ansible/plugins/action/script.py
index e6ebd09..7830416 100644
--- a/lib/ansible/plugins/action/script.py
+++ b/lib/ansible/plugins/action/script.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import re
@@ -151,11 +150,11 @@ class ActionModule(ActionBase):
# like become and environment args
if getattr(self._connection._shell, "_IS_WINDOWS", False):
# FUTURE: use a more public method to get the exec payload
- pc = self._play_context
+ pc = self._task
exec_data = ps_manifest._create_powershell_wrapper(
to_bytes(script_cmd), source, {}, env_dict, self._task.async_val,
pc.become, pc.become_method, pc.become_user,
- pc.become_pass, pc.become_flags, "script", task_vars, None
+ self._play_context.become_pass, pc.become_flags, "script", task_vars, None
)
# build the necessary exec wrapper command
# FUTURE: this still doesn't let script work on Windows with non-pipelined connections or
diff --git a/lib/ansible/plugins/action/service.py b/lib/ansible/plugins/action/service.py
index c061687..90b0780 100644
--- a/lib/ansible/plugins/action/service.py
+++ b/lib/ansible/plugins/action/service.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleAction, AnsibleActionFail
diff --git a/lib/ansible/plugins/action/set_fact.py b/lib/ansible/plugins/action/set_fact.py
index ee3ceb2..b95ec49 100644
--- a/lib/ansible/plugins/action/set_fact.py
+++ b/lib/ansible/plugins/action/set_fact.py
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleActionFail
from ansible.module_utils.six import string_types
diff --git a/lib/ansible/plugins/action/set_stats.py b/lib/ansible/plugins/action/set_stats.py
index 5c4f005..309180f 100644
--- a/lib/ansible/plugins/action/set_stats.py
+++ b/lib/ansible/plugins/action/set_stats.py
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.plugins.action import ActionBase
diff --git a/lib/ansible/plugins/action/shell.py b/lib/ansible/plugins/action/shell.py
index dd4df46..1b4fbc0 100644
--- a/lib/ansible/plugins/action/shell.py
+++ b/lib/ansible/plugins/action/shell.py
@@ -1,8 +1,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleActionFail
from ansible.plugins.action import ActionBase
diff --git a/lib/ansible/plugins/action/template.py b/lib/ansible/plugins/action/template.py
index 4bfd967..c1cb673 100644
--- a/lib/ansible/plugins/action/template.py
+++ b/lib/ansible/plugins/action/template.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import shutil
diff --git a/lib/ansible/plugins/action/unarchive.py b/lib/ansible/plugins/action/unarchive.py
index 9bce122..bcc152d 100644
--- a/lib/ansible/plugins/action/unarchive.py
+++ b/lib/ansible/plugins/action/unarchive.py
@@ -15,8 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
diff --git a/lib/ansible/plugins/action/uri.py b/lib/ansible/plugins/action/uri.py
index ffd1c89..9860f26 100644
--- a/lib/ansible/plugins/action/uri.py
+++ b/lib/ansible/plugins/action/uri.py
@@ -3,9 +3,7 @@
# (c) 2018, Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
@@ -22,6 +20,7 @@ class ActionModule(ActionBase):
def run(self, tmp=None, task_vars=None):
self._supports_async = True
+ self._supports_check_mode = False
if task_vars is None:
task_vars = dict()
diff --git a/lib/ansible/plugins/action/validate_argument_spec.py b/lib/ansible/plugins/action/validate_argument_spec.py
index b2c1d7b..4d68067 100644
--- a/lib/ansible/plugins/action/validate_argument_spec.py
+++ b/lib/ansible/plugins/action/validate_argument_spec.py
@@ -1,8 +1,7 @@
# Copyright 2021 Red Hat
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase
diff --git a/lib/ansible/plugins/action/wait_for_connection.py b/lib/ansible/plugins/action/wait_for_connection.py
index df549d9..9eb3fac 100644
--- a/lib/ansible/plugins/action/wait_for_connection.py
+++ b/lib/ansible/plugins/action/wait_for_connection.py
@@ -16,8 +16,7 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# CI-required python3 boilerplate
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import time
from datetime import datetime, timedelta, timezone
@@ -69,7 +68,7 @@ class ActionModule(ActionBase):
sleep = int(self._task.args.get('sleep', self.DEFAULT_SLEEP))
timeout = int(self._task.args.get('timeout', self.DEFAULT_TIMEOUT))
- if self._play_context.check_mode:
+ if self._task.check_mode:
display.vvv("wait_for_connection: skipping for check_mode")
return dict(skipped=True)
diff --git a/lib/ansible/plugins/action/yum.py b/lib/ansible/plugins/action/yum.py
deleted file mode 100644
index 9121e81..0000000
--- a/lib/ansible/plugins/action/yum.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# (c) 2018, Ansible Project
-#
-# 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/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-from ansible.errors import AnsibleActionFail
-from ansible.plugins.action import ActionBase
-from ansible.utils.display import Display
-
-display = Display()
-
-VALID_BACKENDS = frozenset(('yum', 'yum4', 'dnf', 'dnf4', 'dnf5'))
-
-
-class ActionModule(ActionBase):
-
- TRANSFERS_FILES = False
-
- def run(self, tmp=None, task_vars=None):
- '''
- Action plugin handler for yum3 vs yum4(dnf) operations.
-
- Enables the yum module to use yum3 and/or yum4. Yum4 is a yum
- command-line compatibility layer on top of dnf. Since the Ansible
- modules for yum(aka yum3) and dnf(aka yum4) call each of yum3 and yum4's
- python APIs natively on the backend, we need to handle this here and
- pass off to the correct Ansible module to execute on the remote system.
- '''
-
- self._supports_check_mode = True
- self._supports_async = True
-
- result = super(ActionModule, self).run(tmp, task_vars)
- del tmp # tmp no longer has any effect
-
- # Carry-over concept from the package action plugin
- if 'use' in self._task.args and 'use_backend' in self._task.args:
- raise AnsibleActionFail("parameters are mutually exclusive: ('use', 'use_backend')")
-
- module = self._task.args.get('use', self._task.args.get('use_backend', 'auto'))
-
- if module == 'dnf':
- module = 'auto'
-
- if module == 'auto':
- try:
- if self._task.delegate_to: # if we delegate, we should use delegated host's facts
- module = self._templar.template("{{hostvars['%s']['ansible_facts']['pkg_mgr']}}" % self._task.delegate_to)
- else:
- module = self._templar.template("{{ansible_facts.pkg_mgr}}")
- except Exception:
- pass # could not get it from template!
-
- if module not in VALID_BACKENDS:
- facts = self._execute_module(
- module_name="ansible.legacy.setup", module_args=dict(filter="ansible_pkg_mgr", gather_subset="!all"),
- task_vars=task_vars)
- display.debug("Facts %s" % facts)
- module = facts.get("ansible_facts", {}).get("ansible_pkg_mgr", "auto")
- if (not self._task.delegate_to or self._task.delegate_facts) and module != 'auto':
- result['ansible_facts'] = {'pkg_mgr': module}
-
- if module not in VALID_BACKENDS:
- result.update(
- {
- 'failed': True,
- 'msg': ("Could not detect which major revision of yum is in use, which is required to determine module backend.",
- "You should manually specify use_backend to tell the module whether to use the yum (yum3) or dnf (yum4) backend})"),
- }
- )
-
- else:
- if module in {"yum4", "dnf4"}:
- module = "dnf"
-
- # eliminate collisions with collections search while still allowing local override
- module = 'ansible.legacy.' + module
-
- if not self._shared_loader_obj.module_loader.has_plugin(module):
- result.update({'failed': True, 'msg': "Could not find a yum module backend for %s." % module})
- else:
- new_module_args = self._task.args.copy()
- if 'use_backend' in new_module_args:
- del new_module_args['use_backend']
- if 'use' in new_module_args:
- del new_module_args['use']
-
- display.vvvv("Running %s as the backend for the yum action plugin" % module)
- result.update(self._execute_module(
- module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
-
- # Cleanup
- if not self._task.async_val:
- # remove a temporary path we created
- self._remove_tmp_path(self._connection._shell.tmpdir)
-
- return result
diff --git a/lib/ansible/plugins/become/__init__.py b/lib/ansible/plugins/become/__init__.py
index 0e4a411..0ac1512 100644
--- a/lib/ansible/plugins/become/__init__.py
+++ b/lib/ansible/plugins/become/__init__.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import shlex
diff --git a/lib/ansible/plugins/become/runas.py b/lib/ansible/plugins/become/runas.py
index 0b7d466..3094c46 100644
--- a/lib/ansible/plugins/become/runas.py
+++ b/lib/ansible/plugins/become/runas.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: runas
diff --git a/lib/ansible/plugins/become/su.py b/lib/ansible/plugins/become/su.py
index 7fa5413..ae2d39a 100644
--- a/lib/ansible/plugins/become/su.py
+++ b/lib/ansible/plugins/become/su.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: su
diff --git a/lib/ansible/plugins/become/sudo.py b/lib/ansible/plugins/become/sudo.py
index fb285f0..6a33c98 100644
--- a/lib/ansible/plugins/become/sudo.py
+++ b/lib/ansible/plugins/become/sudo.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: sudo
diff --git a/lib/ansible/plugins/cache/__init__.py b/lib/ansible/plugins/cache/__init__.py
index 24f4e77..3bc5a16 100644
--- a/lib/ansible/plugins/cache/__init__.py
+++ b/lib/ansible/plugins/cache/__init__.py
@@ -15,8 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import copy
import errno
@@ -29,6 +28,7 @@ from collections.abc import MutableMapping
from ansible import constants as C
from ansible.errors import AnsibleError
+from ansible.module_utils.common.file import S_IRWU_RG_RO
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.plugins import AnsiblePlugin
from ansible.plugins.loader import cache_loader
@@ -165,7 +165,7 @@ class BaseFileCacheModule(BaseCacheModule):
display.warning("error in '%s' cache plugin while trying to write to '%s' : %s" % (self.plugin_name, tmpfile_path, to_bytes(e)))
try:
os.rename(tmpfile_path, cachefile)
- os.chmod(cachefile, mode=0o644)
+ os.chmod(cachefile, mode=S_IRWU_RG_RO)
except (OSError, IOError) as e:
display.warning("error in '%s' cache plugin while trying to move '%s' to '%s' : %s" % (self.plugin_name, tmpfile_path, cachefile, to_bytes(e)))
finally:
diff --git a/lib/ansible/plugins/cache/base.py b/lib/ansible/plugins/cache/base.py
index a947eb7..a7c7468 100644
--- a/lib/ansible/plugins/cache/base.py
+++ b/lib/ansible/plugins/cache/base.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
# moved actual classes to __init__ kept here for backward compat with 3rd parties
from ansible.plugins.cache import BaseCacheModule, BaseFileCacheModule # pylint: disable=unused-import
diff --git a/lib/ansible/plugins/cache/jsonfile.py b/lib/ansible/plugins/cache/jsonfile.py
index a26828a..69fd828 100644
--- a/lib/ansible/plugins/cache/jsonfile.py
+++ b/lib/ansible/plugins/cache/jsonfile.py
@@ -2,9 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: jsonfile
diff --git a/lib/ansible/plugins/cache/memory.py b/lib/ansible/plugins/cache/memory.py
index 59f97b6..59991ac 100644
--- a/lib/ansible/plugins/cache/memory.py
+++ b/lib/ansible/plugins/cache/memory.py
@@ -3,8 +3,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: memory
diff --git a/lib/ansible/plugins/callback/__init__.py b/lib/ansible/plugins/callback/__init__.py
index 4346958..8f9b25e 100644
--- a/lib/ansible/plugins/callback/__init__.py
+++ b/lib/ansible/plugins/callback/__init__.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import difflib
import json
@@ -169,7 +167,7 @@ class CallbackBase(AnsiblePlugin):
_copy_result = deepcopy
def set_option(self, k, v):
- self._plugin_options[k] = v
+ self._plugin_options[k] = C.config.get_config_value(k, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={k: v})
def get_option(self, k):
return self._plugin_options[k]
diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py
index 54ef452..c96d9ab 100644
--- a/lib/ansible/plugins/callback/default.py
+++ b/lib/ansible/plugins/callback/default.py
@@ -2,8 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: default
@@ -166,7 +165,7 @@ class CallbackModule(CallbackBase):
# args can be specified as no_log in several places: in the task or in
# the argument spec. We can check whether the task is no_log but the
# argument spec can't be because that is only run on the target
- # machine and we haven't run it thereyet at this time.
+ # machine and we haven't run it there yet at this time.
#
# So we give people a config option to affect display of the args so
# that they can secure this if they feel that their stdout is insecure
diff --git a/lib/ansible/plugins/callback/junit.py b/lib/ansible/plugins/callback/junit.py
index 92158ef..73db9d5 100644
--- a/lib/ansible/plugins/callback/junit.py
+++ b/lib/ansible/plugins/callback/junit.py
@@ -2,8 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: junit
diff --git a/lib/ansible/plugins/callback/minimal.py b/lib/ansible/plugins/callback/minimal.py
index c4d713f..e316d8f 100644
--- a/lib/ansible/plugins/callback/minimal.py
+++ b/lib/ansible/plugins/callback/minimal.py
@@ -2,9 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: minimal
diff --git a/lib/ansible/plugins/callback/oneline.py b/lib/ansible/plugins/callback/oneline.py
index 556f21c..3a5eb72 100644
--- a/lib/ansible/plugins/callback/oneline.py
+++ b/lib/ansible/plugins/callback/oneline.py
@@ -2,9 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: oneline
diff --git a/lib/ansible/plugins/callback/tree.py b/lib/ansible/plugins/callback/tree.py
index 52a5fee..b7f85f0 100644
--- a/lib/ansible/plugins/callback/tree.py
+++ b/lib/ansible/plugins/callback/tree.py
@@ -2,8 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: tree
diff --git a/lib/ansible/plugins/cliconf/__init__.py b/lib/ansible/plugins/cliconf/__init__.py
index 3201057..9befd36 100644
--- a/lib/ansible/plugins/cliconf/__init__.py
+++ b/lib/ansible/plugins/cliconf/__init__.py
@@ -16,8 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from abc import abstractmethod
from functools import wraps
diff --git a/lib/ansible/plugins/connection/__init__.py b/lib/ansible/plugins/connection/__init__.py
index 5f7e282..e769770 100644
--- a/lib/ansible/plugins/connection/__init__.py
+++ b/lib/ansible/plugins/connection/__init__.py
@@ -2,8 +2,7 @@
# (c) 2015 Toshio Kuratomi <tkuratomi@ansible.com>
# (c) 2017, Peter Sprygada <psprygad@redhat.com>
# (c) 2017 Ansible Project
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import collections.abc as c
import fcntl
@@ -267,19 +266,19 @@ class ConnectionBase(AnsiblePlugin):
# its my cousin ...
value = self._shell._load_name
else:
- # deal with generic options if the plugin supports em (for exmaple not all connections have a remote user)
+ # deal with generic options if the plugin supports em (for example not all connections have a remote user)
options = C.config.get_plugin_options_from_var('connection', self._load_name, varname)
if options:
value = self.get_option(options[0]) # for these variables there should be only one option
elif 'become' not in varname:
- # fallback to play_context, unles becoem related TODO: in the end should come from task/play and not pc
+ # fallback to play_context, unless become related TODO: in the end, should come from task/play and not pc
for prop, var_list in C.MAGIC_VARIABLE_MAPPING.items():
if varname in var_list:
try:
value = getattr(self._play_context, prop)
break
except AttributeError:
- # it was not defined, fine to ignore
+ # It was not defined; fine to ignore
continue
if value is not None:
diff --git a/lib/ansible/plugins/connection/local.py b/lib/ansible/plugins/connection/local.py
index d6dccc7..464d8e8 100644
--- a/lib/ansible/plugins/connection/local.py
+++ b/lib/ansible/plugins/connection/local.py
@@ -2,8 +2,7 @@
# (c) 2015, 2017 Toshio Kuratomi <tkuratomi@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: local
@@ -22,13 +21,13 @@ import fcntl
import getpass
import os
import pty
+import selectors
import shutil
import subprocess
import typing as t
import ansible.constants as C
from ansible.errors import AnsibleError, AnsibleFileNotFound
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import text_type, binary_type
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase
@@ -90,7 +89,7 @@ class Connection(ConnectionBase):
master = None
stdin = subprocess.PIPE
if sudoable and self.become and self.become.expect_prompt() and not self.get_option('pipelining'):
- # Create a pty if sudoable for privlege escalation that needs it.
+ # Create a pty if sudoable for privilege escalation that needs it.
# Falls back to using a standard pipe if this fails, which may
# cause the command to fail in certain situations where we are escalating
# privileges or the command otherwise needs a pty.
diff --git a/lib/ansible/plugins/connection/paramiko_ssh.py b/lib/ansible/plugins/connection/paramiko_ssh.py
index 172dbda..924208b 100644
--- a/lib/ansible/plugins/connection/paramiko_ssh.py
+++ b/lib/ansible/plugins/connection/paramiko_ssh.py
@@ -1,8 +1,7 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
author: Ansible Core Team
diff --git a/lib/ansible/plugins/connection/psrp.py b/lib/ansible/plugins/connection/psrp.py
index 37a4694..b69a1d8 100644
--- a/lib/ansible/plugins/connection/psrp.py
+++ b/lib/ansible/plugins/connection/psrp.py
@@ -1,8 +1,7 @@
# Copyright (c) 2018 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
author: Ansible Core Team
@@ -14,7 +13,7 @@ description:
underlying transport but instead runs in a PowerShell interpreter.
version_added: "2.7"
requirements:
-- pypsrp>=0.4.0 (Python library)
+- pypsrp>=0.4.0, <1.0.0 (Python library)
extends_documentation_fragment:
- connection_pipelining
options:
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 49b2ed2..5c4c28d 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -4,8 +4,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: ssh
@@ -20,6 +19,8 @@ DOCUMENTATION = '''
- connection_pipelining
version_added: historical
notes:
+ - This plugin is mostly a wrapper to the ``ssh`` CLI utility and the exact behavior of the options depends on this tool.
+ This means that the documentation provided here is subject to be overridden by the CLI tool itself.
- Many options default to V(None) here but that only means we do not override the SSH tool's defaults and/or configuration.
For example, if you specify the port in this plugin it will override any C(Port) entry in your C(.ssh/config).
- The ssh CLI tool uses return code 255 as a 'connection error', this can conflict with commands/tools that
@@ -36,7 +37,7 @@ DOCUMENTATION = '''
- name: delegated_vars['ansible_host']
- name: delegated_vars['ansible_ssh_host']
host_key_checking:
- description: Determines if SSH should check host keys.
+ description: Determines if SSH should reject or not a connection after checking host keys.
default: True
type: boolean
ini:
@@ -304,12 +305,13 @@ DOCUMENTATION = '''
- name: ansible_sftp_batch_mode
version_added: '2.7'
ssh_transfer_method:
- description:
- - "Preferred method to use when transferring files over ssh"
- - Setting to 'smart' (default) will try them in order, until one succeeds or they all fail
- - For OpenSSH >=9.0 you must add an additional option to enable scp (scp_extra_args="-O")
- - Using 'piped' creates an ssh pipe with C(dd) on either side to copy the data
- choices: ['sftp', 'scp', 'piped', 'smart']
+ description: Preferred method to use when transferring files over ssh
+ choices:
+ sftp: This is the most reliable way to copy things with SSH.
+ scp: Deprecated in OpenSSH. For OpenSSH >=9.0 you must add an additional option to enable scp C(scp_extra_args="-O").
+ piped: Creates an SSH pipe with C(dd) on either side to copy the data.
+ smart: Tries each method in order (sftp > scp > piped), until one succeeds or they all fail.
+ default: smart
type: string
env: [{name: ANSIBLE_SSH_TRANSFER_METHOD}]
ini:
@@ -317,24 +319,6 @@ DOCUMENTATION = '''
vars:
- name: ansible_ssh_transfer_method
version_added: '2.12'
- scp_if_ssh:
- deprecated:
- why: In favor of the O(ssh_transfer_method) option.
- version: "2.17"
- alternatives: O(ssh_transfer_method)
- default: smart
- description:
- - "Preferred method to use when transferring files over SSH."
- - When set to V(smart), Ansible will try them until one succeeds or they all fail.
- - If set to V(True), it will force 'scp', if V(False) it will use 'sftp'.
- - For OpenSSH >=9.0 you must add an additional option to enable scp (C(scp_extra_args="-O"))
- - This setting will overridden by O(ssh_transfer_method) if set.
- env: [{name: ANSIBLE_SCP_IF_SSH}]
- ini:
- - {key: scp_if_ssh, section: ssh_connection}
- vars:
- - name: ansible_scp_if_ssh
- version_added: '2.7'
use_tty:
version_added: '2.5'
default: true
@@ -389,6 +373,7 @@ import io
import os
import pty
import re
+import selectors
import shlex
import subprocess
import time
@@ -401,11 +386,8 @@ from ansible.errors import (
AnsibleError,
AnsibleFileNotFound,
)
-from ansible.errors import AnsibleOptionsError
-from ansible.module_utils.compat import selectors
from ansible.module_utils.six import PY3, text_type, binary_type
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
-from ansible.module_utils.parsing.convert_bool import BOOLEANS, boolean
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.plugins.shell.powershell import _parse_clixml
from ansible.utils.display import Display
@@ -746,8 +728,8 @@ class Connection(ConnectionBase):
self._add_args(b_command, b_args, u'disable batch mode for sshpass')
b_command += [b'-b', b'-']
- if display.verbosity > 3:
- b_command.append(b'-vvv')
+ if display.verbosity:
+ b_command.append(b'-' + (b'v' * display.verbosity))
# Next, we add ssh_args
ssh_args = self.get_option('ssh_args')
@@ -1240,31 +1222,13 @@ class Connection(ConnectionBase):
# Transfer methods to try
methods = []
- # Use the transfer_method option if set, otherwise use scp_if_ssh
+ # Use the transfer_method option if set
ssh_transfer_method = self.get_option('ssh_transfer_method')
- scp_if_ssh = self.get_option('scp_if_ssh')
- if ssh_transfer_method is None and scp_if_ssh == 'smart':
- ssh_transfer_method = 'smart'
- if ssh_transfer_method is not None:
- if ssh_transfer_method == 'smart':
- methods = smart_methods
- else:
- methods = [ssh_transfer_method]
+ if ssh_transfer_method == 'smart':
+ methods = smart_methods
else:
- # since this can be a non-bool now, we need to handle it correctly
- if not isinstance(scp_if_ssh, bool):
- scp_if_ssh = scp_if_ssh.lower()
- if scp_if_ssh in BOOLEANS:
- scp_if_ssh = boolean(scp_if_ssh, strict=False)
- elif scp_if_ssh != 'smart':
- raise AnsibleOptionsError('scp_if_ssh needs to be one of [smart|True|False]')
- if scp_if_ssh == 'smart':
- methods = smart_methods
- elif scp_if_ssh is True:
- methods = ['scp']
- else:
- methods = ['sftp']
+ methods = [ssh_transfer_method]
for method in methods:
returncode = stdout = stderr = None
diff --git a/lib/ansible/plugins/connection/winrm.py b/lib/ansible/plugins/connection/winrm.py
index b297495..c6a4683 100644
--- a/lib/ansible/plugins/connection/winrm.py
+++ b/lib/ansible/plugins/connection/winrm.py
@@ -2,8 +2,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (annotations, absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
author: Ansible Core Team
@@ -148,8 +147,8 @@ DOCUMENTATION = """
seconds higher than the WS-Man operation timeout, thus make the connection more
robust on networks with long latency and/or many hops between server and client
network wise.
- - Setting the difference bewteen the operation and the read timeout to 10 seconds
- alligns it to the defaults used in the winrm-module and the PSRP-module which also
+ - Setting the difference between the operation and the read timeout to 10 seconds
+ aligns it to the defaults used in the winrm-module and the PSRP-module which also
uses 10 seconds (30 seconds for read timeout and 20 seconds for operation timeout)
- Corresponds to the C(operation_timeout_sec) and
C(read_timeout_sec) args in pywinrm so avoid setting these vars
diff --git a/lib/ansible/plugins/doc_fragments/action_common_attributes.py b/lib/ansible/plugins/doc_fragments/action_common_attributes.py
index c135df5..688d675 100644
--- a/lib/ansible/plugins/doc_fragments/action_common_attributes.py
+++ b/lib/ansible/plugins/doc_fragments/action_common_attributes.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
@@ -11,7 +10,7 @@ class ModuleDocFragment(object):
DOCUMENTATION = r'''
attributes:
check_mode:
- description: Can run in check_mode and return changed status prediction without modifying target
+ description: Can run in check_mode and return changed status prediction without modifying target, if not supported the action will be skipped.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in check_mode), when in diff mode
platform:
diff --git a/lib/ansible/plugins/doc_fragments/action_core.py b/lib/ansible/plugins/doc_fragments/action_core.py
index 931ca14..56214b7 100644
--- a/lib/ansible/plugins/doc_fragments/action_core.py
+++ b/lib/ansible/plugins/doc_fragments/action_core.py
@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright: (c) , Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
-# WARNING: this is mostly here as a convinence for documenting core behaviours, no plugin outside of ansible-core should use this file
+# WARNING: this is mostly here as a convenience for documenting core behaviours, no plugin outside of ansible-core should use this file
class ModuleDocFragment(object):
# requires action_common
@@ -29,7 +28,7 @@ attributes:
support: full
platforms: all
until:
- description: Denotes if this action objeys until/retry/poll keywords
+ description: Denotes if this action obeys until/retry/poll keywords
support: full
tags:
description: Allows for the 'tags' keyword to control the selection of this action for execution
diff --git a/lib/ansible/plugins/doc_fragments/backup.py b/lib/ansible/plugins/doc_fragments/backup.py
index d2e76dc..037df24 100644
--- a/lib/ansible/plugins/doc_fragments/backup.py
+++ b/lib/ansible/plugins/doc_fragments/backup.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2015, Ansible, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/connection_pipelining.py b/lib/ansible/plugins/doc_fragments/connection_pipelining.py
index fa18265..a590be3 100644
--- a/lib/ansible/plugins/doc_fragments/connection_pipelining.py
+++ b/lib/ansible/plugins/doc_fragments/connection_pipelining.py
@@ -1,7 +1,6 @@
# Copyright (c) 2021 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/constructed.py b/lib/ansible/plugins/doc_fragments/constructed.py
index 8e45043..c5d7e0a 100644
--- a/lib/ansible/plugins/doc_fragments/constructed.py
+++ b/lib/ansible/plugins/doc_fragments/constructed.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/decrypt.py b/lib/ansible/plugins/doc_fragments/decrypt.py
index ea7cf59..c2da1cf 100644
--- a/lib/ansible/plugins/doc_fragments/decrypt.py
+++ b/lib/ansible/plugins/doc_fragments/decrypt.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2017, Brian Coca <bcoca@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
@@ -13,7 +12,7 @@ class ModuleDocFragment(object):
options:
decrypt:
description:
- - This option controls the autodecryption of source files using vault.
+ - This option controls the auto-decryption of source files using vault.
type: bool
default: yes
version_added: '2.4'
diff --git a/lib/ansible/plugins/doc_fragments/default_callback.py b/lib/ansible/plugins/doc_fragments/default_callback.py
index 5798334..e206eb3 100644
--- a/lib/ansible/plugins/doc_fragments/default_callback.py
+++ b/lib/ansible/plugins/doc_fragments/default_callback.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/files.py b/lib/ansible/plugins/doc_fragments/files.py
index 3741652..ec76267 100644
--- a/lib/ansible/plugins/doc_fragments/files.py
+++ b/lib/ansible/plugins/doc_fragments/files.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2014, Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/inventory_cache.py b/lib/ansible/plugins/doc_fragments/inventory_cache.py
index 1a0d631..03d6d7c 100644
--- a/lib/ansible/plugins/doc_fragments/inventory_cache.py
+++ b/lib/ansible/plugins/doc_fragments/inventory_cache.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/result_format_callback.py b/lib/ansible/plugins/doc_fragments/result_format_callback.py
index f4f82b7..3ca74aa 100644
--- a/lib/ansible/plugins/doc_fragments/result_format_callback.py
+++ b/lib/ansible/plugins/doc_fragments/result_format_callback.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/return_common.py b/lib/ansible/plugins/doc_fragments/return_common.py
index 6f54288..900e4c0 100644
--- a/lib/ansible/plugins/doc_fragments/return_common.py
+++ b/lib/ansible/plugins/doc_fragments/return_common.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2016, Ansible, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/shell_common.py b/lib/ansible/plugins/doc_fragments/shell_common.py
index 39d8730..a97fa99 100644
--- a/lib/ansible/plugins/doc_fragments/shell_common.py
+++ b/lib/ansible/plugins/doc_fragments/shell_common.py
@@ -1,7 +1,6 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/shell_windows.py b/lib/ansible/plugins/doc_fragments/shell_windows.py
index 0bcc89c..1f25ce0 100644
--- a/lib/ansible/plugins/doc_fragments/shell_windows.py
+++ b/lib/ansible/plugins/doc_fragments/shell_windows.py
@@ -1,7 +1,6 @@
# Copyright (c) 2019 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/template_common.py b/lib/ansible/plugins/doc_fragments/template_common.py
index dbfe482..9795e43 100644
--- a/lib/ansible/plugins/doc_fragments/template_common.py
+++ b/lib/ansible/plugins/doc_fragments/template_common.py
@@ -3,8 +3,7 @@
# Copyright (c) 2019 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/url.py b/lib/ansible/plugins/doc_fragments/url.py
index bafeded..8f90465 100644
--- a/lib/ansible/plugins/doc_fragments/url.py
+++ b/lib/ansible/plugins/doc_fragments/url.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2018, John Barker <gundalow@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/url_windows.py b/lib/ansible/plugins/doc_fragments/url_windows.py
index 7b3e873..4b2c19d 100644
--- a/lib/ansible/plugins/doc_fragments/url_windows.py
+++ b/lib/ansible/plugins/doc_fragments/url_windows.py
@@ -3,8 +3,7 @@
# Copyright (c) 2019 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment:
diff --git a/lib/ansible/plugins/doc_fragments/validate.py b/lib/ansible/plugins/doc_fragments/validate.py
index ac66d25..b71011c 100644
--- a/lib/ansible/plugins/doc_fragments/validate.py
+++ b/lib/ansible/plugins/doc_fragments/validate.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2015, Ansible, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/doc_fragments/vars_plugin_staging.py b/lib/ansible/plugins/doc_fragments/vars_plugin_staging.py
index eacac17..698b7be 100644
--- a/lib/ansible/plugins/doc_fragments/vars_plugin_staging.py
+++ b/lib/ansible/plugins/doc_fragments/vars_plugin_staging.py
@@ -3,8 +3,7 @@
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
+from __future__ import annotations
class ModuleDocFragment(object):
diff --git a/lib/ansible/plugins/filter/__init__.py b/lib/ansible/plugins/filter/__init__.py
index 63b6602..003711f 100644
--- a/lib/ansible/plugins/filter/__init__.py
+++ b/lib/ansible/plugins/filter/__init__.py
@@ -1,8 +1,7 @@
# (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible import constants as C
from ansible.plugins import AnsibleJinja2Plugin
diff --git a/lib/ansible/plugins/filter/b64decode.yml b/lib/ansible/plugins/filter/b64decode.yml
index af8045a..339de3a 100644
--- a/lib/ansible/plugins/filter/b64decode.yml
+++ b/lib/ansible/plugins/filter/b64decode.yml
@@ -2,28 +2,28 @@ DOCUMENTATION:
name: b64decode
author: ansible core team
version_added: 'historical'
- short_description: Decode a base64 string
+ short_description: Decode a Base64 string
description:
- Base64 decoding function.
- The return value is a string.
- - Trying to store a binary blob in a string most likely corrupts the binary. To base64 decode a binary blob,
- use the ``base64`` command and pipe the encoded data through standard input.
- For example, in the ansible.builtin.shell`` module, ``cmd="base64 --decode > myfile.bin" stdin="{{ encoded }}"``.
+ - Trying to store a binary blob in a string most likely corrupts the binary. To Base64 decode a binary blob,
+ use the I(base64) command and pipe the encoded data through standard input.
+ For example, in the M(ansible.builtin.shell) module, ``cmd="base64 --decode > myfile.bin" stdin="{{ encoded }}"``.
positional: _input
options:
_input:
- description: A base64 string to decode.
+ description: A Base64 string to decode.
type: string
required: true
EXAMPLES: |
- # b64 decode a string
+ # Base64 decode a string
lola: "{{ 'bG9sYQ==' | b64decode }}"
- # b64 decode the content of 'b64stuff' variable
+ # Base64 decode the content of 'b64stuff' variable
stuff: "{{ b64stuff | b64decode }}"
RETURN:
_value:
- description: The contents of the base64 encoded string.
+ description: The contents of the Base64 encoded string.
type: string
diff --git a/lib/ansible/plugins/filter/b64encode.yml b/lib/ansible/plugins/filter/b64encode.yml
index 976d1fe..ed32bfb 100644
--- a/lib/ansible/plugins/filter/b64encode.yml
+++ b/lib/ansible/plugins/filter/b64encode.yml
@@ -2,7 +2,7 @@ DOCUMENTATION:
name: b64encode
author: ansible core team
version_added: 'historical'
- short_description: Encode a string as base64
+ short_description: Encode a string as Base64
description:
- Base64 encoding function.
positional: _input
@@ -13,13 +13,13 @@ DOCUMENTATION:
required: true
EXAMPLES: |
- # b64 encode a string
+ # Base64 encode a string
b64lola: "{{ 'lola'| b64encode }}"
- # b64 encode the content of 'stuff' variable
+ # Base64 encode the content of 'stuff' variable
b64stuff: "{{ stuff | b64encode }}"
RETURN:
_value:
- description: A base64 encoded string.
+ description: A Base64 encoded string.
type: string
diff --git a/lib/ansible/plugins/filter/comment.yml b/lib/ansible/plugins/filter/comment.yml
index f1e47e6..c2e4776 100644
--- a/lib/ansible/plugins/filter/comment.yml
+++ b/lib/ansible/plugins/filter/comment.yml
@@ -18,7 +18,7 @@ DOCUMENTATION:
decoration:
description: Indicator for comment or intermediate comment depending on the style.
type: string
- begining:
+ beginning:
description: Indicator of the start of a comment block, only available for styles that support multiline comments.
type: string
end:
diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py
index eee43e6..ef9c09f 100644
--- a/lib/ansible/plugins/filter/core.py
+++ b/lib/ansible/plugins/filter/core.py
@@ -1,9 +1,7 @@
# (c) 2012, Jeroen Hoekx <jeroen@hoekx.be>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import base64
import glob
@@ -46,7 +44,7 @@ UUID_NAMESPACE_ANSIBLE = uuid.UUID('361E6D51-FAEC-444A-9079-341386DA8E2E')
def to_yaml(a, *args, **kw):
- '''Make verbose, human readable yaml'''
+ '''Make verbose, human-readable yaml'''
default_flow_style = kw.pop('default_flow_style', None)
try:
transformed = yaml.dump(a, Dumper=AnsibleDumper, allow_unicode=True, default_flow_style=default_flow_style, **kw)
@@ -56,7 +54,7 @@ def to_yaml(a, *args, **kw):
def to_nice_yaml(a, indent=4, *args, **kw):
- '''Make verbose, human readable yaml'''
+ '''Make verbose, human-readable yaml'''
try:
transformed = yaml.dump(a, Dumper=AnsibleDumper, indent=indent, allow_unicode=True, default_flow_style=False, **kw)
except Exception as e:
@@ -77,7 +75,7 @@ def to_json(a, *args, **kw):
def to_nice_json(a, indent=4, sort_keys=True, *args, **kw):
- '''Make verbose, human readable JSON'''
+ '''Make verbose, human-readable JSON'''
return to_json(a, indent=indent, sort_keys=sort_keys, separators=(',', ': '), *args, **kw)
@@ -122,7 +120,7 @@ def fileglob(pathname):
return [g for g in glob.glob(pathname) if os.path.isfile(g)]
-def regex_replace(value='', pattern='', replacement='', ignorecase=False, multiline=False):
+def regex_replace(value='', pattern='', replacement='', ignorecase=False, multiline=False, count=0, mandatory_count=0):
''' Perform a `re.sub` returning a string '''
value = to_text(value, errors='surrogate_or_strict', nonstring='simplerepr')
@@ -133,7 +131,11 @@ def regex_replace(value='', pattern='', replacement='', ignorecase=False, multil
if multiline:
flags |= re.M
_re = re.compile(pattern, flags=flags)
- return _re.sub(replacement, value)
+ (output, subs) = _re.subn(replacement, value, count=count)
+ if mandatory_count and mandatory_count != subs:
+ raise AnsibleFilterError("'%s' should match %d times, but matches %d times in '%s'"
+ % (pattern, mandatory_count, count, value))
+ return output
def regex_findall(value, regex, multiline=False, ignorecase=False):
@@ -595,7 +597,7 @@ def commonpath(paths):
:rtype: str
"""
if not is_sequence(paths):
- raise AnsibleFilterTypeError("|path_join expects sequence, got %s instead." % type(paths))
+ raise AnsibleFilterTypeError("|commonpath expects sequence, got %s instead." % type(paths))
return os.path.commonpath(paths)
diff --git a/lib/ansible/plugins/filter/encryption.py b/lib/ansible/plugins/filter/encryption.py
index d501879..c6863fd 100644
--- a/lib/ansible/plugins/filter/encryption.py
+++ b/lib/ansible/plugins/filter/encryption.py
@@ -1,8 +1,6 @@
# Copyright: (c) 2021, Ansible Project
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from jinja2.runtime import Undefined
from jinja2.exceptions import UndefinedError
diff --git a/lib/ansible/plugins/filter/extract.yml b/lib/ansible/plugins/filter/extract.yml
index a7c4e91..da1f4c0 100644
--- a/lib/ansible/plugins/filter/extract.yml
+++ b/lib/ansible/plugins/filter/extract.yml
@@ -17,7 +17,7 @@ DOCUMENTATION:
type: raw
required: true
morekeys:
- description: Indicies or keys to extract from the initial result (subkeys/subindices).
+ description: Indices or keys to extract from the initial result (subkeys/subindices).
type: list
elements: dictionary
required: true
diff --git a/lib/ansible/plugins/filter/from_yaml_all.yml b/lib/ansible/plugins/filter/from_yaml_all.yml
index c3dd1f6..f01edbf 100644
--- a/lib/ansible/plugins/filter/from_yaml_all.yml
+++ b/lib/ansible/plugins/filter/from_yaml_all.yml
@@ -5,7 +5,7 @@ DOCUMENTATION:
description:
- Converts a YAML documents in a string representation into an equivalent structured Ansible variable.
- Ansible internally auto-converts YAML strings into variable structures in most contexts, but by default does not handle 'multi document' YAML files or strings.
- - If multiple YAML documents are not supplied, this is the equivalend of using C(from_yaml).
+ - If multiple YAML documents are not supplied, this is the equivalence of using C(from_yaml).
notes:
- This filter functions as a wrapper to the Python C(yaml.safe_load_all) function, part of the L(pyyaml Python library, https://pypi.org/project/PyYAML/).
- Possible conflicts in variable names from the multiple documents are resolved directly by the pyyaml library.
diff --git a/lib/ansible/plugins/filter/human_readable.yml b/lib/ansible/plugins/filter/human_readable.yml
index 2c331b7..b5dd364 100644
--- a/lib/ansible/plugins/filter/human_readable.yml
+++ b/lib/ansible/plugins/filter/human_readable.yml
@@ -1,9 +1,9 @@
DOCUMENTATION:
name: human_redable
version_added: "historical"
- short_description: Make bytes/bits human readable
+ short_description: Make bytes/bits human-readable
description:
- - Convert byte or bit figures to more human readable formats.
+ - Convert byte or bit figures to more human-readable formats.
positional: _input, isbits, unit
options:
_input:
@@ -31,5 +31,5 @@ EXAMPLES: |
RETURN:
_value:
- description: Human readable byte or bit size.
+ description: human-readable byte or bit size.
type: str
diff --git a/lib/ansible/plugins/filter/human_to_bytes.yml b/lib/ansible/plugins/filter/human_to_bytes.yml
index c861350..2739129 100644
--- a/lib/ansible/plugins/filter/human_to_bytes.yml
+++ b/lib/ansible/plugins/filter/human_to_bytes.yml
@@ -3,11 +3,11 @@ DOCUMENTATION:
version_added: "historical"
short_description: Get bytes from string
description:
- - Convert a human readable byte or bit string into a number bytes.
+ - Convert a human-readable byte or bit string into a number bytes.
positional: _input, default_unit, isbits
options:
_input:
- description: Human readable description of a number of bytes.
+ description: human-readable description of a number of bytes.
type: int
required: true
default_unit:
diff --git a/lib/ansible/plugins/filter/mandatory.yml b/lib/ansible/plugins/filter/mandatory.yml
index 1405884..ed3a7dd 100644
--- a/lib/ansible/plugins/filter/mandatory.yml
+++ b/lib/ansible/plugins/filter/mandatory.yml
@@ -1,7 +1,7 @@
DOCUMENTATION:
name: mandatory
version_added: "historical"
- short_description: make a variable's existance mandatory
+ short_description: make a variable's existence mandatory
description:
- Depending on context undefined variables can be ignored or skipped, this ensures they force an error.
positional: _input
diff --git a/lib/ansible/plugins/filter/mathstuff.py b/lib/ansible/plugins/filter/mathstuff.py
index 4ff1118..9772cb5 100644
--- a/lib/ansible/plugins/filter/mathstuff.py
+++ b/lib/ansible/plugins/filter/mathstuff.py
@@ -145,7 +145,7 @@ def inversepower(x, base=2):
def human_readable(size, isbits=False, unit=None):
- ''' Return a human readable string '''
+ ''' Return a human-readable string '''
try:
return formatters.bytes_to_human(size, isbits, unit)
except TypeError as e:
@@ -155,7 +155,7 @@ def human_readable(size, isbits=False, unit=None):
def human_to_bytes(size, default_unit=None, isbits=False):
- ''' Return bytes count from a human readable string '''
+ ''' Return bytes count from a human-readable string '''
try:
return formatters.human_to_bytes(size, default_unit, isbits)
except TypeError as e:
diff --git a/lib/ansible/plugins/filter/password_hash.yml b/lib/ansible/plugins/filter/password_hash.yml
index d12efb4..a9516b7 100644
--- a/lib/ansible/plugins/filter/password_hash.yml
+++ b/lib/ansible/plugins/filter/password_hash.yml
@@ -7,6 +7,7 @@ DOCUMENTATION:
positional: _input
notes:
- Algorithms available might be restricted by the system.
+ - Algorithms may restrict salt length or content. For example, Blowfish/bcrypt requires a 22-character salt.
options:
_input:
description: Secret to hash.
@@ -18,8 +19,8 @@ DOCUMENTATION:
default: sha512
choices: [ md5, blowfish, sha256, sha512 ]
salt:
- description: Secret string that is used for the hashing, if none is provided a random one can be generated.
- type: int
+ description: Secret string used for the hashing. If none is provided a random one can be generated. Use only numbers and letters (characters matching V([./0-9A-Za-z]+)).
+ type: string
rounds:
description: Number of encryption rounds, default varies by algorithm used.
type: int
diff --git a/lib/ansible/plugins/filter/regex_replace.yml b/lib/ansible/plugins/filter/regex_replace.yml
index 8c8d0af..d139e9c 100644
--- a/lib/ansible/plugins/filter/regex_replace.yml
+++ b/lib/ansible/plugins/filter/regex_replace.yml
@@ -6,6 +6,8 @@ DOCUMENTATION:
- Replace a substring defined by a regular expression with another defined by another regular expression based on the first match.
notes:
- Maps to Python's C(re.sub).
+ - 'The substring matched by the group is accessible via the symbolic group name or
+ the ``\{number}`` special sequence. See examples section.'
positional: _input, _regex_match, _regex_replace
options:
_input:
@@ -28,6 +30,16 @@ DOCUMENTATION:
description: Force the search to be case insensitive if V(True), case sensitive otherwise.
type: bool
default: no
+ count:
+ description: Maximum number of pattern occurrences to replace. If zero, replace all occurrences.
+ type: int
+ default: 0
+ version_added: "2.17"
+ mandatory_count:
+ description: Except a certain number of replacements. Raises an error otherwise. If zero, ignore.
+ type: int
+ default: 0
+ version_added: "2.17"
EXAMPLES: |
@@ -46,6 +58,9 @@ EXAMPLES: |
# piratecomment => '#CAR\n#tar\nfoo\n#bar\n'
piratecomment: "{{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('(?im)^(.ar)$', '#\\1') }}"
+ # 'foo=bar=baz' => 'foo:bar=baz'
+ key_value: "{{ 'foo=bar=baz' | regex_replace('=', ':', count=1) }}"
+
RETURN:
_value:
description: String with substitution (or original if no match).
diff --git a/lib/ansible/plugins/filter/regex_search.yml b/lib/ansible/plugins/filter/regex_search.yml
index 970de62..e9ac11d 100644
--- a/lib/ansible/plugins/filter/regex_search.yml
+++ b/lib/ansible/plugins/filter/regex_search.yml
@@ -6,6 +6,8 @@ DOCUMENTATION:
- Search in a string to extract the part that matches the regular expression.
notes:
- Maps to Python's C(re.search).
+ - 'The substring matched by the group is accessible via the symbolic group name or
+ the ``\{number}`` special sequence. See examples section.'
positional: _input, _regex
options:
_input:
@@ -38,6 +40,16 @@ EXAMPLES: |
# drinkat => 'BAR'
drinkat: "{{ 'foo\nBAR' | regex_search('^bar', multiline=True, ignorecase=True) }}"
+ # Extracts server and database id from a string using number
+ # (the substring matched by the group is accessible via the \number special sequence)
+ db: "{{ 'server1/database42' | regex_search('server([0-9]+)/database([0-9]+)', '\\1', '\\2') }}"
+ # => ['1', '42']
+
+ # Extracts dividend and divisor from a division
+ # (the substring matched by the group is accessible via the symbolic group name)
+ db: "{{ '21/42' | regex_search('(?P<dividend>[0-9]+)/(?P<divisor>[0-9]+)', '\\g<dividend>', '\\g<divisor>') }}"
+ # => ['21', '42']
+
RETURN:
_value:
description: Matched string or empty string if no match.
diff --git a/lib/ansible/plugins/filter/strftime.yml b/lib/ansible/plugins/filter/strftime.yml
index a1d8b92..9720729 100644
--- a/lib/ansible/plugins/filter/strftime.yml
+++ b/lib/ansible/plugins/filter/strftime.yml
@@ -21,6 +21,7 @@ DOCUMENTATION:
description: Whether time supplied is in UTC.
type: bool
default: false
+ version_added: '2.14'
EXAMPLES: |
# for a complete set of features go to https://strftime.org/
@@ -39,15 +40,7 @@ EXAMPLES: |
# Use arbitrary epoch value
{{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01
- {{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04
-
- # complex examples
- vars:
- date1: '2022-11-15T03:23:13.686956868Z'
- date2: '2021-12-15T16:06:24.400087Z'
- date_short: '{{ date1|regex_replace("([^.]+)(\.\d{6})(\d*)(.+)", "\1\2\4") }}' #shorten microseconds
- iso8601format: '%Y-%m-%dT%H:%M:%S.%fZ'
- date_diff_isoed: '{{ (date1|to_datetime(isoformat) - date2|to_datetime(isoformat)).total_seconds() }}'
+ {{ '%Y-%m-%d' | strftime(seconds=1441357287, utc=true) }} # => 2015-09-04
RETURN:
_value:
diff --git a/lib/ansible/plugins/filter/to_datetime.yml b/lib/ansible/plugins/filter/to_datetime.yml
index dbd476a..bc50732 100644
--- a/lib/ansible/plugins/filter/to_datetime.yml
+++ b/lib/ansible/plugins/filter/to_datetime.yml
@@ -4,9 +4,14 @@ DOCUMENTATION:
short_description: Get C(datetime) from string
description:
- Using the input string attempt to create a matching Python C(datetime) object.
+ - Adding or Subtracting two datetime objects will result in a Python C(timedelta) object.
notes:
- For a full list of format codes for working with Python date format strings, see
L(the Python documentation, https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior).
+ - The timedelta object produced by the difference of two datetimes store the days, seconds, and microseconds of
+ the delta. This results in the C(seconds) attribute being the total seconds of the minutes and hours of that
+ delta. See L(datatime.timedelta, https://docs.python.org/3/library/datetime.html#timedelta-objects) for more
+ information about how a timedelta works.
positional: _input
options:
_input:
@@ -22,13 +27,23 @@ EXAMPLES: |
# Get total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format
secsdiff: '{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime("%Y-%m-%d"))).total_seconds() }}'
- # Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, and so on to seconds. For that, use total_seconds()
+ # Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years and days to seconds. For that, use total_seconds()
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }}
- # This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds
+ # This expression evaluates to "7212". Delta is 2 hours, 12 seconds
# get amount of days between two dates. This returns only number of days and discards remaining hours, minutes, and seconds
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}
+ # difference between to dotnet (100ns precision) and iso8601 microsecond timestamps
+ # the date1_short regex replace will work for any timestamp that has a higher than microsecond precision
+ # by cutting off anything more precise than microseconds
+ vars:
+ date1: '2022-11-15T03:23:13.6869568Z'
+ date2: '2021-12-15T16:06:24.400087Z'
+ date1_short: '{{ date1|regex_replace("([^.]+)(\.\d{6})(\d*)(.+)", "\1\2\4") }}' # shorten to microseconds
+ iso8601format: '%Y-%m-%dT%H:%M:%S.%fZ'
+ date_diff_isoed: '{{ (date1_short|to_datetime(iso8601format) - date2|to_datetime(iso8601format)).total_seconds() }}'
+
RETURN:
_value:
description: C(datetime) object from the represented value.
diff --git a/lib/ansible/plugins/filter/to_nice_json.yml b/lib/ansible/plugins/filter/to_nice_json.yml
index f40e22c..fa31b26 100644
--- a/lib/ansible/plugins/filter/to_nice_json.yml
+++ b/lib/ansible/plugins/filter/to_nice_json.yml
@@ -39,6 +39,10 @@ DOCUMENTATION:
description: If V(True), keys that are not basic Python types will be skipped.
default: False
type: bool
+ sort_keys:
+ description: Affects sorting of dictionary keys.
+ default: True
+ type: bool
notes:
- Both O(vault_to_text) and O(preprocess_unsafe) defaulted to V(False) between Ansible 2.9 and 2.12.
- 'These parameters to C(json.dumps) will be ignored, they are overridden for internal use: I(cls), I(default), I(indent), I(separators), I(sort_keys).'
diff --git a/lib/ansible/plugins/filter/union.yml b/lib/ansible/plugins/filter/union.yml
index 7ef656d..d5e5c7a 100644
--- a/lib/ansible/plugins/filter/union.yml
+++ b/lib/ansible/plugins/filter/union.yml
@@ -29,7 +29,7 @@ EXAMPLES: |
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | union(list2) }}
- # => [1, 2, 5, 1, 3, 4, 10, 11, 99]
+ # => [1, 2, 5, 3, 4, 10, 11, 99]
RETURN:
_value:
description: A unique list of all the elements from both lists.
diff --git a/lib/ansible/plugins/filter/urls.py b/lib/ansible/plugins/filter/urls.py
index fb7abc6..1f9cde2 100644
--- a/lib/ansible/plugins/filter/urls.py
+++ b/lib/ansible/plugins/filter/urls.py
@@ -3,8 +3,7 @@
# Copyright: (c) 2012, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
+from __future__ import annotations
from functools import partial
diff --git a/lib/ansible/plugins/filter/urlsplit.py b/lib/ansible/plugins/filter/urlsplit.py
index 11c1f11..8963659 100644
--- a/lib/ansible/plugins/filter/urlsplit.py
+++ b/lib/ansible/plugins/filter/urlsplit.py
@@ -2,8 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = r'''
name: urlsplit
diff --git a/lib/ansible/plugins/filter/zip.yml b/lib/ansible/plugins/filter/zip.yml
index 96c307b..bc9e77d 100644
--- a/lib/ansible/plugins/filter/zip.yml
+++ b/lib/ansible/plugins/filter/zip.yml
@@ -5,7 +5,7 @@ DOCUMENTATION:
positional: _input, _additional_lists
description: Iterate over several iterables in parallel, producing tuples with an item from each one.
notes:
- - This is mostly a passhtrough to Python's C(zip) function.
+ - This is mostly a passthrough to Python's C(zip) function.
options:
_input:
description: Original list.
@@ -34,7 +34,7 @@ EXAMPLES: |
shorter: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) }}"
# compose dict from lists of keys and values
- mydcit: "{{ dict(keys_list | zip(values_list)) }}"
+ mydict: "{{ dict(keys_list | zip(values_list)) }}"
RETURN:
_value:
diff --git a/lib/ansible/plugins/filter/zip_longest.yml b/lib/ansible/plugins/filter/zip_longest.yml
index 964e9c2..36e6c2f 100644
--- a/lib/ansible/plugins/filter/zip_longest.yml
+++ b/lib/ansible/plugins/filter/zip_longest.yml
@@ -8,7 +8,7 @@ DOCUMENTATION:
If the iterables are of uneven length, missing values are filled-in with O(fillvalue).
Iteration continues until the longest iterable is exhausted.
notes:
- - This is mostly a passhtrough to Python's C(itertools.zip_longest) function
+ - This is mostly a passthrough to Python's C(itertools.zip_longest) function
options:
_input:
description: Original list.
diff --git a/lib/ansible/plugins/httpapi/__init__.py b/lib/ansible/plugins/httpapi/__init__.py
index 0773921..e6c4f18 100644
--- a/lib/ansible/plugins/httpapi/__init__.py
+++ b/lib/ansible/plugins/httpapi/__init__.py
@@ -1,8 +1,7 @@
# (c) 2018 Red Hat Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from abc import abstractmethod
diff --git a/lib/ansible/plugins/inventory/__init__.py b/lib/ansible/plugins/inventory/__init__.py
index a68f596..f5bfed6 100644
--- a/lib/ansible/plugins/inventory/__init__.py
+++ b/lib/ansible/plugins/inventory/__init__.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <https://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import hashlib
import os
@@ -220,7 +218,7 @@ class BaseInventoryPlugin(AnsiblePlugin):
try:
# avoid loader cache so meta: refresh_inventory can pick up config changes
# if we read more than once, fs cache should be good enough
- config = self.loader.load_from_file(path, cache=False)
+ config = self.loader.load_from_file(path, cache='none')
except Exception as e:
raise AnsibleParserError(to_native(e))
diff --git a/lib/ansible/plugins/inventory/advanced_host_list.py b/lib/ansible/plugins/inventory/advanced_host_list.py
index 3c5f52c..9ca45b6 100644
--- a/lib/ansible/plugins/inventory/advanced_host_list.py
+++ b/lib/ansible/plugins/inventory/advanced_host_list.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: advanced_host_list
diff --git a/lib/ansible/plugins/inventory/auto.py b/lib/ansible/plugins/inventory/auto.py
index 45941ca..9948385 100644
--- a/lib/ansible/plugins/inventory/auto.py
+++ b/lib/ansible/plugins/inventory/auto.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: auto
@@ -37,7 +36,7 @@ class InventoryModule(BaseInventoryPlugin):
return super(InventoryModule, self).verify_file(path)
def parse(self, inventory, loader, path, cache=True):
- config_data = loader.load_from_file(path, cache=False)
+ config_data = loader.load_from_file(path, cache='none')
try:
plugin_name = config_data.get('plugin', None)
diff --git a/lib/ansible/plugins/inventory/constructed.py b/lib/ansible/plugins/inventory/constructed.py
index 76b19e7..98f6178 100644
--- a/lib/ansible/plugins/inventory/constructed.py
+++ b/lib/ansible/plugins/inventory/constructed.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: constructed
@@ -26,6 +25,8 @@ DOCUMENTATION = '''
- The host_group_vars (enabled by default) 'vars plugin' is the one responsible for reading host_vars/ and group_vars/ directories.
- This will execute all vars plugins, even those that are not supposed to execute at the 'inventory' stage.
See vars plugins docs for details on 'stage'.
+ - Implicit groups, such as 'all' or 'ungrouped', need to be explicitly defined in any previous inventory to apply the
+ corresponding group_vars
required: false
default: false
type: boolean
diff --git a/lib/ansible/plugins/inventory/generator.py b/lib/ansible/plugins/inventory/generator.py
index 1955f36..ba697df 100644
--- a/lib/ansible/plugins/inventory/generator.py
+++ b/lib/ansible/plugins/inventory/generator.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: generator
diff --git a/lib/ansible/plugins/inventory/host_list.py b/lib/ansible/plugins/inventory/host_list.py
index d0b2dad..c9ffcc8 100644
--- a/lib/ansible/plugins/inventory/host_list.py
+++ b/lib/ansible/plugins/inventory/host_list.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = r'''
name: host_list
diff --git a/lib/ansible/plugins/inventory/ini.py b/lib/ansible/plugins/inventory/ini.py
index 1ff4bf1..e2efde1 100644
--- a/lib/ansible/plugins/inventory/ini.py
+++ b/lib/ansible/plugins/inventory/ini.py
@@ -1,7 +1,6 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: ini
diff --git a/lib/ansible/plugins/inventory/script.py b/lib/ansible/plugins/inventory/script.py
index 48d9234..d3bfc8e 100644
--- a/lib/ansible/plugins/inventory/script.py
+++ b/lib/ansible/plugins/inventory/script.py
@@ -2,8 +2,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: script
@@ -24,7 +23,7 @@ DOCUMENTATION = '''
- The source provided must be an executable that returns Ansible inventory JSON
- The source must accept C(--list) and C(--host <hostname>) as arguments.
C(--host) will only be used if no C(_meta) key is present.
- This is a performance optimization as the script would be called per host otherwise.
+ This is a performance optimization as the script would be called one additional time per host otherwise.
notes:
- Enabled in configuration by default.
- The plugin does not cache results because external inventory scripts are responsible for their own caching.
@@ -32,6 +31,126 @@ DOCUMENTATION = '''
- To find the scripts that used to be part of the code release, go to U(https://github.com/ansible-community/contrib-scripts/).
'''
+EXAMPLES = r'''# fmt: code
+
+### simple bash script
+
+ #!/usr/bin/env bash
+
+ if [ "$1" == "--list" ]; then
+ cat<<EOF
+ {
+ "bash_hosts": {
+ "hosts": [
+ "myhost.domain.com",
+ "myhost2.domain.com"
+ ],
+ "vars": {
+ "host_test": "test-value"
+ }
+ },
+ "_meta": {
+ "hostvars": {
+ "myhost.domain.com": {
+ "host_specific_test_var": "test-value"
+ }
+ }
+ }
+ }
+ EOF
+ elif [ "$1" == "--host" ]; then
+ # this should not normally be called by Ansible as we return _meta above
+ if [ "$2" == "myhost.domain.com" ]; then
+ echo '{"_meta": {hostvars": {"myhost.domain.com": {"host_specific-test_var": "test-value"}}}}'
+ else
+ echo '{"_meta": {hostvars": {}}}'
+ fi
+ else
+ echo "Invalid option: use --list or --host <hostname>"
+ exit 1
+ fi
+
+
+### python example with ini config
+
+ #!/usr/bin/env python
+ """
+ # ansible_inventory.py
+ """
+ import argparse
+ import json
+ import os.path
+ import sys
+ from configparser import ConfigParser
+ from inventories.custom import MyInventoryAPI
+
+ def load_config() -> ConfigParser:
+ cp = ConfigParser()
+ config_file = os.path.expanduser("~/.config/ansible_inventory_script.cfg")
+ cp.read(config_file)
+ if not cp.has_option('DEFAULT', 'namespace'):
+ raise ValueError("Missing configuration option: DEFAULT -> namespace")
+ return cp
+
+
+ def get_api_data(namespace: str, pretty=False) -> str:
+ """
+ :param namespace: parameter for our custom api
+ :param pretty: Human redable JSON vs machine readable
+ :return: JSON string
+ """
+ found_data = list(MyInventoryAPI(namespace))
+ hostvars = {}
+ data = { '_meta': { 'hostvars': {}},}
+
+ groups = found_data['groups'].keys()
+ for group in groups:
+ groups[group]['hosts'] = found_data[groups].get('host_list', [])
+ if group not in data:
+ data[group] = {}
+ data[group]['hosts'] = found_data[groups].get('host_list', [])
+ data[group]['vars'] = found_data[groups].get('info', [])
+ data[group]['children'] = found_data[group].get('subgroups', [])
+
+ for host_data in found_data['hosts']:
+ for name in host_data.items():
+ # turn info into vars
+ data['_meta'][name] = found_data[name].get('info', {})
+ # set ansible_host if possible
+ if 'address' in found_data[name]:
+ data[name]['_meta']['ansible_host'] = found_data[name]['address']
+ data['_meta']['hostvars'] = hostvars
+
+ return json.dumps(data, indent=pretty)
+
+ if __name__ == '__main__':
+
+ arg_parser = argparse.ArgumentParser( description=__doc__, prog=__file__)
+ arg_parser.add_argument('--pretty', action='store_true', default=False, help="Pretty JSON")
+ mandatory_options = arg_parser.add_mutually_exclusive_group()
+ mandatory_options.add_argument('--list', action='store', nargs="*", help="Get inventory JSON from our API")
+ mandatory_options.add_argument('--host', action='store',
+ help="Get variables for specific host, not used but kept for compatability")
+
+ try:
+ config = load_config()
+ namespace = config.get('DEFAULT', 'namespace')
+
+ args = arg_parser.parse_args()
+ if args.host:
+ print('{"_meta":{}}')
+ sys.stderr.write('This script already provides _meta via --list, so this option is really ignored')
+ elif len(args.list) >= 0:
+ print(get_api_data(namespace, args.pretty))
+ else:
+ raise ValueError("Valid options are --list or --host <HOSTNAME>")
+
+ except ValueError:
+ raise
+
+'''
+
+
import os
import subprocess
diff --git a/lib/ansible/plugins/inventory/toml.py b/lib/ansible/plugins/inventory/toml.py
index 1c2b439..39a3d5c 100644
--- a/lib/ansible/plugins/inventory/toml.py
+++ b/lib/ansible/plugins/inventory/toml.py
@@ -1,8 +1,7 @@
# Copyright (c) 2018 Matt Martz <matt@sivel.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = r'''
name: toml
diff --git a/lib/ansible/plugins/inventory/yaml.py b/lib/ansible/plugins/inventory/yaml.py
index 79af3dc..3625ed4 100644
--- a/lib/ansible/plugins/inventory/yaml.py
+++ b/lib/ansible/plugins/inventory/yaml.py
@@ -1,8 +1,7 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: yaml
@@ -102,7 +101,7 @@ class InventoryModule(BaseFileInventoryPlugin):
self.set_options()
try:
- data = self.loader.load_from_file(path, cache=False)
+ data = self.loader.load_from_file(path, cache='none')
except Exception as e:
raise AnsibleParserError(e)
@@ -114,7 +113,7 @@ class InventoryModule(BaseFileInventoryPlugin):
raise AnsibleParserError('Plugin configuration YAML file, not YAML inventory')
# We expect top level keys to correspond to groups, iterate over them
- # to get host, vars and subgroups (which we iterate over recursivelly)
+ # to get host, vars and subgroups (which we iterate over recursively)
if isinstance(data, MutableMapping):
for group_name in data:
self._parse_group(group_name, data[group_name])
diff --git a/lib/ansible/plugins/list.py b/lib/ansible/plugins/list.py
index cd4d51f..18cbd45 100644
--- a/lib/ansible/plugins/list.py
+++ b/lib/ansible/plugins/list.py
@@ -1,8 +1,7 @@
# (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
@@ -35,7 +34,7 @@ def get_composite_name(collection, name, path, depth):
resolved_collection = 'ansible.builtin'
resource_name = '.'.join(name.split(f"{resolved_collection}.")[1:])
- # collectionize name
+ # create FQCN
composite = [resolved_collection]
if depth:
composite.extend(path.split(os.path.sep)[depth * -1:])
diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py
index 9ff19bb..c865ac4 100644
--- a/lib/ansible/plugins/loader.py
+++ b/lib/ansible/plugins/loader.py
@@ -4,8 +4,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import glob
import os
@@ -15,6 +14,7 @@ import sys
import warnings
from collections import defaultdict, namedtuple
+from importlib import import_module
from traceback import format_exc
import ansible.module_utils.compat.typing as t
@@ -26,7 +26,6 @@ from ansible import __version__ as ansible_version
from ansible import constants as C
from ansible.errors import AnsibleError, AnsiblePluginCircularRedirect, AnsiblePluginRemovedError, AnsibleCollectionUnsupportedVersionError
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
-from ansible.module_utils.compat.importlib import import_module
from ansible.module_utils.six import string_types
from ansible.parsing.utils.yaml import from_yaml
from ansible.parsing.yaml.loader import AnsibleLoader
@@ -1232,24 +1231,22 @@ class Jinja2Loader(PluginLoader):
# check deprecations
deprecation_entry = routing_entry.get('deprecation')
if deprecation_entry:
- warning_text = deprecation_entry.get('warning_text')
+ warning_text = deprecation_entry.get('warning_text') or ''
removal_date = deprecation_entry.get('removal_date')
removal_version = deprecation_entry.get('removal_version')
- if not warning_text:
- warning_text = '{0} "{1}" is deprecated'.format(self.type, key)
+ warning_text = f'{self.type.title()} "{key}" has been deprecated.{" " if warning_text else ""}{warning_text}'
display.deprecated(warning_text, version=removal_version, date=removal_date, collection_name=acr.collection)
# check removal
tombstone_entry = routing_entry.get('tombstone')
if tombstone_entry:
- warning_text = tombstone_entry.get('warning_text')
+ warning_text = tombstone_entry.get('warning_text') or ''
removal_date = tombstone_entry.get('removal_date')
removal_version = tombstone_entry.get('removal_version')
- if not warning_text:
- warning_text = '{0} "{1}" has been removed'.format(self.type, key)
+ warning_text = f'{self.type.title()} "{key}" has been removed.{" " if warning_text else ""}{warning_text}'
exc_msg = display.get_deprecation_message(warning_text, version=removal_version, date=removal_date,
collection_name=acr.collection, removed=True)
@@ -1299,12 +1296,14 @@ class Jinja2Loader(PluginLoader):
fq_name = '.'.join((parent_prefix, func_name))
src_name = f"ansible_collections.{acr.collection}.plugins.{self.type}.{acr.subdirs}.{func_name}"
# TODO: load anyways into CACHE so we only match each at end of loop
- # the files themseves should already be cached by base class caching of modules(python)
+ # the files themselves should already be cached by base class caching of modules(python)
if key in (func_name, fq_name):
plugin = self._plugin_wrapper_type(func)
if plugin:
context = plugin_impl.plugin_load_context
self._update_object(plugin, src_name, plugin_impl.object._original_path, resolved=fq_name)
+ # context will have filename, which for tests/filters might not be correct
+ context._resolved_fqcn = plugin.ansible_name
# FIXME: once we start caching these results, we'll be missing functions that would have loaded later
break # go to next file as it can override if dupe (dont break both loops)
@@ -1448,7 +1447,7 @@ def _load_plugin_filter():
display.warning(u'The plugin filter file, {0} does not exist.'
u' Skipping.'.format(filter_cfg))
- # Specialcase the stat module as Ansible can run very few things if stat is rejected
+ # Special case: the stat module as Ansible can run very few things if stat is rejected
if 'stat' in filters['ansible.modules']:
raise AnsibleError('The stat module was specified in the module reject list file, {0}, but'
' Ansible will not function without the stat module. Please remove stat'
diff --git a/lib/ansible/plugins/lookup/__init__.py b/lib/ansible/plugins/lookup/__init__.py
index c9779d6..bc15943 100644
--- a/lib/ansible/plugins/lookup/__init__.py
+++ b/lib/ansible/plugins/lookup/__init__.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from abc import abstractmethod
diff --git a/lib/ansible/plugins/lookup/config.py b/lib/ansible/plugins/lookup/config.py
index b476b53..4c6b000 100644
--- a/lib/ansible/plugins/lookup/config.py
+++ b/lib/ansible/plugins/lookup/config.py
@@ -1,42 +1,46 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: config
author: Ansible Core Team
version_added: "2.5"
- short_description: Lookup current Ansible configuration values
+ short_description: Display the 'resolved' Ansible option values.
description:
- - Retrieves the value of an Ansible configuration setting.
- - You can use C(ansible-config list) to see all available settings.
+ - Retrieves the value of an Ansible configuration setting, resolving all sources, from defaults, ansible.cfg, envirionmnet,
+ CLI, and variables, but not keywords.
+ - The values returned assume the context of the current host or C(inventory_hostname).
+ - You can use C(ansible-config list) to see the global available settings, add C(-t all) to also show plugin options.
options:
_terms:
- description: The key(s) to look up
+ description: The option(s) to look up.
required: True
on_missing:
- description:
- - action to take if term is missing from config
- - Error will raise a fatal error
- - Skip will just ignore the term
- - Warn will skip over it but issue a warning
+ description: Action to take if term is missing from config
default: error
type: string
- choices: ['error', 'skip', 'warn']
+ choices:
+ error: Issue an error message and raise fatal signal
+ warn: Issue a warning message and continue
+ skip: Silently ignore
plugin_type:
- description: the type of the plugin referenced by 'plugin_name' option.
+ description: The type of the plugin referenced by 'plugin_name' option.
choices: ['become', 'cache', 'callback', 'cliconf', 'connection', 'httpapi', 'inventory', 'lookup', 'netconf', 'shell', 'vars']
type: string
version_added: '2.12'
plugin_name:
- description: name of the plugin for which you want to retrieve configuration settings.
+ description: The name of the plugin for which you want to retrieve configuration settings.
type: string
version_added: '2.12'
show_origin:
- description: toggle the display of what configuration subsystem the value came from
+ description: Set this to return what configuration subsystem the value came from
+ (defaults, config file, environment, CLI, or variables).
type: bool
version_added: '2.16'
+ notes:
+ - Be aware that currently this lookup cannot take keywords nor delegation into account,
+ so for options that support keywords or are affected by delegation, it is at best a good guess or approximation.
"""
EXAMPLES = """
diff --git a/lib/ansible/plugins/lookup/csvfile.py b/lib/ansible/plugins/lookup/csvfile.py
index 76d97ed..9d199d8 100644
--- a/lib/ansible/plugins/lookup/csvfile.py
+++ b/lib/ansible/plugins/lookup/csvfile.py
@@ -1,8 +1,7 @@
# (c) 2013, Jan-Piet Mens <jpmens(at)gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = r"""
name: csvfile
@@ -17,6 +16,11 @@ DOCUMENTATION = r"""
col:
description: column to return (0 indexed).
default: "1"
+ keycol:
+ description: column to search in (0 indexed).
+ default: 0
+ type: int
+ version_added: "2.17"
default:
description: what to return if the value is not found in the file.
delimiter:
@@ -83,7 +87,7 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_
class CSVRecoder:
"""
- Iterator that reads an encoded stream and reencodes the input to UTF-8
+ Iterator that reads an encoded stream and encodes the input to UTF-8
"""
def __init__(self, f, encoding='utf-8'):
self.reader = codecs.getreader(encoding)(f)
@@ -123,14 +127,14 @@ class CSVReader:
class LookupModule(LookupBase):
- def read_csv(self, filename, key, delimiter, encoding='utf-8', dflt=None, col=1):
+ def read_csv(self, filename, key, delimiter, encoding='utf-8', dflt=None, col=1, keycol=0):
try:
f = open(to_bytes(filename), 'rb')
creader = CSVReader(f, delimiter=to_native(delimiter), encoding=encoding)
for row in creader:
- if len(row) and row[0] == key:
+ if len(row) and row[keycol] == key:
return row[int(col)]
except Exception as e:
raise AnsibleError("csvfile: %s" % to_native(e))
@@ -156,6 +160,7 @@ class LookupModule(LookupBase):
# parameters override per term using k/v
try:
+ reset_params = False
for name, value in kv.items():
if name == '_raw_params':
continue
@@ -163,7 +168,11 @@ class LookupModule(LookupBase):
raise AnsibleAssertionError('%s is not a valid option' % name)
self._deprecate_inline_kv()
- paramvals[name] = value
+ self.set_option(name, value)
+ reset_params = True
+
+ if reset_params:
+ paramvals = self.get_options()
except (ValueError, AssertionError) as e:
raise AnsibleError(e)
@@ -173,7 +182,7 @@ class LookupModule(LookupBase):
paramvals['delimiter'] = "\t"
lookupfile = self.find_file_in_search_path(variables, 'files', paramvals['file'])
- var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col'])
+ var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col'], paramvals['keycol'])
if var is not None:
if isinstance(var, MutableSequence):
for v in var:
diff --git a/lib/ansible/plugins/lookup/dict.py b/lib/ansible/plugins/lookup/dict.py
index af9a081..a8c1089 100644
--- a/lib/ansible/plugins/lookup/dict.py
+++ b/lib/ansible/plugins/lookup/dict.py
@@ -1,8 +1,7 @@
# (c) 2014, Kent R. Spillner <kspillner@acm.org>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: dict
@@ -49,7 +48,7 @@ tasks:
RETURN = """
_list:
description:
- - list of composed dictonaries with key and value
+ - list of composed dictionaries with key and value
type: list
"""
diff --git a/lib/ansible/plugins/lookup/env.py b/lib/ansible/plugins/lookup/env.py
index db34d8d..50547a8 100644
--- a/lib/ansible/plugins/lookup/env.py
+++ b/lib/ansible/plugins/lookup/env.py
@@ -1,8 +1,7 @@
# (c) 2012, Jan-Piet Mens <jpmens(at)gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: env
@@ -56,11 +55,12 @@ RETURN = """
type: list
"""
+import os
+
from jinja2.runtime import Undefined
from ansible.errors import AnsibleUndefinedVariable
from ansible.plugins.lookup import LookupBase
-from ansible.utils import py3compat
class LookupModule(LookupBase):
@@ -72,7 +72,7 @@ class LookupModule(LookupBase):
d = self.get_option('default')
for term in terms:
var = term.split()[0]
- val = py3compat.environ.get(var, d)
+ val = os.environ.get(var, d)
if isinstance(val, Undefined):
raise AnsibleUndefinedVariable('The "env" lookup, found an undefined variable: %s' % var)
ret.append(val)
diff --git a/lib/ansible/plugins/lookup/file.py b/lib/ansible/plugins/lookup/file.py
index 25946b2..17338c0 100644
--- a/lib/ansible/plugins/lookup/file.py
+++ b/lib/ansible/plugins/lookup/file.py
@@ -1,8 +1,7 @@
# (c) 2012, Daniel Hokka Zakrisson <daniel@hozac.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: file
diff --git a/lib/ansible/plugins/lookup/fileglob.py b/lib/ansible/plugins/lookup/fileglob.py
index 00d5f09..5ab730d 100644
--- a/lib/ansible/plugins/lookup/fileglob.py
+++ b/lib/ansible/plugins/lookup/fileglob.py
@@ -1,8 +1,7 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: fileglob
diff --git a/lib/ansible/plugins/lookup/first_found.py b/lib/ansible/plugins/lookup/first_found.py
index 6862880..a68791b 100644
--- a/lib/ansible/plugins/lookup/first_found.py
+++ b/lib/ansible/plugins/lookup/first_found.py
@@ -1,8 +1,7 @@
# (c) 2013, seth vidal <skvidal@fedoraproject.org> red hat, inc
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: first_found
@@ -147,6 +146,7 @@ from jinja2.exceptions import UndefinedError
from ansible.errors import AnsibleLookupError, AnsibleUndefinedVariable
from ansible.module_utils.six import string_types
from ansible.plugins.lookup import LookupBase
+from ansible.utils.path import unfrackpath
def _splitter(value, chars):
@@ -198,7 +198,7 @@ class LookupModule(LookupBase):
# NOTE: this is used as 'global' but can be set many times?!?!?
skip = self.get_option('skip')
- # magic extra spliting to create lists
+ # magic extra splitting to create lists
filelist = _split_on(files, ',;')
pathlist = _split_on(paths, ',:;')
@@ -209,7 +209,7 @@ class LookupModule(LookupBase):
f = os.path.join(path, fn)
total_search.append(f)
elif filelist:
- # NOTE: this is now 'extend', previouslly it would clobber all options, but we deemed that a bug
+ # NOTE: this is now 'extend', previously it would clobber all options, but we deemed that a bug
total_search.extend(filelist)
else:
total_search.append(term)
@@ -218,8 +218,9 @@ class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
+ self.set_options(var_options=variables, direct=kwargs)
+
if not terms:
- self.set_options(var_options=variables, direct=kwargs)
terms = self.get_option('files')
total_search, skip = self._process_terms(terms, variables, kwargs)
@@ -246,10 +247,10 @@ class LookupModule(LookupBase):
# exit if we find one!
if path is not None:
- return [path]
+ return [unfrackpath(path, follow=False)]
# if we get here, no file was found
if skip:
- # NOTE: global skip wont matter, only last 'skip' value in dict term
+ # NOTE: global skip won't matter, only last 'skip' value in dict term
return []
raise AnsibleLookupError("No file was found when using first_found.")
diff --git a/lib/ansible/plugins/lookup/indexed_items.py b/lib/ansible/plugins/lookup/indexed_items.py
index f63a895..fe919cd 100644
--- a/lib/ansible/plugins/lookup/indexed_items.py
+++ b/lib/ansible/plugins/lookup/indexed_items.py
@@ -1,8 +1,7 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: indexed_items
diff --git a/lib/ansible/plugins/lookup/ini.py b/lib/ansible/plugins/lookup/ini.py
index 9467676..cdc9a15 100644
--- a/lib/ansible/plugins/lookup/ini.py
+++ b/lib/ansible/plugins/lookup/ini.py
@@ -1,8 +1,7 @@
# (c) 2015, Yannig Perre <yannig.perre(at)gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: ini
@@ -155,16 +154,20 @@ class LookupModule(LookupBase):
params = _parse_params(term, paramvals)
try:
updated_key = False
+ updated_options = False
for param in params:
if '=' in param:
name, value = param.split('=')
if name not in paramvals:
raise AnsibleLookupError('%s is not a valid option.' % name)
- paramvals[name] = value
+ self.set_option(name, value)
+ updated_options = True
elif key == term:
# only take first, this format never supported multiple keys inline
key = param
updated_key = True
+ if updated_options:
+ paramvals = self.get_options()
except ValueError as e:
# bad params passed
raise AnsibleLookupError("Could not use '%s' from '%s': %s" % (param, params, to_native(e)), orig_exc=e)
diff --git a/lib/ansible/plugins/lookup/inventory_hostnames.py b/lib/ansible/plugins/lookup/inventory_hostnames.py
index 4fa1d68..e9ba61b 100644
--- a/lib/ansible/plugins/lookup/inventory_hostnames.py
+++ b/lib/ansible/plugins/lookup/inventory_hostnames.py
@@ -3,8 +3,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: inventory_hostnames
diff --git a/lib/ansible/plugins/lookup/items.py b/lib/ansible/plugins/lookup/items.py
index 162c1e7..058ba97 100644
--- a/lib/ansible/plugins/lookup/items.py
+++ b/lib/ansible/plugins/lookup/items.py
@@ -1,8 +1,7 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: items
diff --git a/lib/ansible/plugins/lookup/lines.py b/lib/ansible/plugins/lookup/lines.py
index 6314e37..7b08acf 100644
--- a/lib/ansible/plugins/lookup/lines.py
+++ b/lib/ansible/plugins/lookup/lines.py
@@ -2,8 +2,7 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: lines
diff --git a/lib/ansible/plugins/lookup/list.py b/lib/ansible/plugins/lookup/list.py
index 6c553ae..a953f68 100644
--- a/lib/ansible/plugins/lookup/list.py
+++ b/lib/ansible/plugins/lookup/list.py
@@ -1,10 +1,8 @@
# (c) 2012-17 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
+from __future__ import annotations
-__metaclass__ = type
DOCUMENTATION = """
name: list
diff --git a/lib/ansible/plugins/lookup/nested.py b/lib/ansible/plugins/lookup/nested.py
index e768dba..097c2a4 100644
--- a/lib/ansible/plugins/lookup/nested.py
+++ b/lib/ansible/plugins/lookup/nested.py
@@ -1,8 +1,7 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: nested
diff --git a/lib/ansible/plugins/lookup/password.py b/lib/ansible/plugins/lookup/password.py
index 1fe97f1..84894e2 100644
--- a/lib/ansible/plugins/lookup/password.py
+++ b/lib/ansible/plugins/lookup/password.py
@@ -3,8 +3,7 @@
# (c) 2013, Maykel Moya <mmoya@speedyrails.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: password
@@ -332,29 +331,32 @@ class LookupModule(LookupBase):
if invalid_params:
raise AnsibleError('Unrecognized parameter(s) given to password lookup: %s' % ', '.join(invalid_params))
- # Set defaults
- params['length'] = int(params.get('length', self.get_option('length')))
- params['encrypt'] = params.get('encrypt', self.get_option('encrypt'))
- params['ident'] = params.get('ident', self.get_option('ident'))
- params['seed'] = params.get('seed', self.get_option('seed'))
+ # update options with what we got
+ if params:
+ self.set_options(direct=params)
- params['chars'] = params.get('chars', self.get_option('chars'))
- if params['chars'] and isinstance(params['chars'], string_types):
+ # chars still might need more
+ chars = params.get('chars', self.get_option('chars'))
+ if chars and isinstance(chars, string_types):
tmp_chars = []
- if u',,' in params['chars']:
+ if u',,' in chars:
tmp_chars.append(u',')
- tmp_chars.extend(c for c in params['chars'].replace(u',,', u',').split(u',') if c)
- params['chars'] = tmp_chars
+ tmp_chars.extend(c for c in chars.replace(u',,', u',').split(u',') if c)
+ self.set_option('chars', tmp_chars)
+
+ # return processed params
+ for field in VALID_PARAMS:
+ params[field] = self.get_option(field)
return relpath, params
def run(self, terms, variables, **kwargs):
ret = []
- self.set_options(var_options=variables, direct=kwargs)
-
for term in terms:
+ self.set_options(var_options=variables, direct=kwargs)
+
changed = None
relpath, params = self._parse_parameters(term)
path = self._loader.path_dwim(relpath)
diff --git a/lib/ansible/plugins/lookup/pipe.py b/lib/ansible/plugins/lookup/pipe.py
index 20e922b..0923f13 100644
--- a/lib/ansible/plugins/lookup/pipe.py
+++ b/lib/ansible/plugins/lookup/pipe.py
@@ -1,8 +1,7 @@
# (c) 2012, Daniel Hokka Zakrisson <daniel@hozac.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = r"""
name: pipe
diff --git a/lib/ansible/plugins/lookup/random_choice.py b/lib/ansible/plugins/lookup/random_choice.py
index 93e6c2e..2e43d2e 100644
--- a/lib/ansible/plugins/lookup/random_choice.py
+++ b/lib/ansible/plugins/lookup/random_choice.py
@@ -1,8 +1,7 @@
# (c) 2013, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: random_choice
diff --git a/lib/ansible/plugins/lookup/sequence.py b/lib/ansible/plugins/lookup/sequence.py
index f4fda43..9efe7ce 100644
--- a/lib/ansible/plugins/lookup/sequence.py
+++ b/lib/ansible/plugins/lookup/sequence.py
@@ -1,8 +1,7 @@
# (c) 2013, Jayson Vantuyl <jayson@aggressive.ly>
# (c) 2012-17 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: sequence
@@ -20,21 +19,21 @@ DOCUMENTATION = """
options:
start:
description: number at which to start the sequence
- default: 0
+ default: 1
type: integer
end:
description: number at which to end the sequence, dont use this with count
type: integer
- default: 0
count:
description: number of elements in the sequence, this is not to be used with end
type: integer
- default: 0
stride:
description: increments between sequence numbers, the default is 1 unless the end is less than the start, then it is -1.
type: integer
+ default: 1
format:
description: return a string with the generated number formatted in
+ default: "%d"
"""
EXAMPLES = """
@@ -98,6 +97,7 @@ SHORTCUT = re_compile(
"(:(.+))?$", # Group 5, Group 6: Format String
IGNORECASE
)
+FIELDS = frozenset(('start', 'end', 'stride', 'count', 'format'))
class LookupModule(LookupBase):
@@ -139,30 +139,12 @@ class LookupModule(LookupBase):
calculating the number of entries in a sequence when a stride is specified.
"""
- def reset(self):
- """set sensible defaults"""
- self.start = 1
- self.count = None
- self.end = None
- self.stride = 1
- self.format = "%d"
-
def parse_kv_args(self, args):
"""parse key-value style arguments"""
- for arg in ["start", "end", "count", "stride"]:
- try:
- arg_raw = args.pop(arg, None)
- if arg_raw is None:
- continue
- arg_cooked = int(arg_raw, 0)
- setattr(self, arg, arg_cooked)
- except ValueError:
- raise AnsibleError(
- "can't parse %s=%s as integer"
- % (arg, arg_raw)
- )
- if 'format' in args:
- self.format = args.pop("format")
+ for arg in FIELDS:
+ value = args.pop(arg, None)
+ if value is not None:
+ self.set_option(arg, value)
if args:
raise AnsibleError(
"unrecognized arguments to with_sequence: %s"
@@ -177,33 +159,17 @@ class LookupModule(LookupBase):
dummy, start, end, dummy, stride, dummy, format = match.groups()
- if start is not None:
- try:
- start = int(start, 0)
- except ValueError:
- raise AnsibleError("can't parse start=%s as integer" % start)
- if end is not None:
- try:
- end = int(end, 0)
- except ValueError:
- raise AnsibleError("can't parse end=%s as integer" % end)
- if stride is not None:
- try:
- stride = int(stride, 0)
- except ValueError:
- raise AnsibleError("can't parse stride=%s as integer" % stride)
-
- if start is not None:
- self.start = start
- if end is not None:
- self.end = end
- if stride is not None:
- self.stride = stride
- if format is not None:
- self.format = format
+ for key in FIELDS:
+ value = locals().get(key, None)
+ if value is not None:
+ self.set_option(key, value)
return True
+ def set_fields(self):
+ for f in FIELDS:
+ setattr(self, f, self.get_option(f))
+
def sanity_check(self):
if self.count is None and self.end is None:
raise AnsibleError("must specify count or end in with_sequence")
@@ -246,7 +212,8 @@ class LookupModule(LookupBase):
for term in terms:
try:
- self.reset() # clear out things for this iteration
+ # set defaults/global
+ self.set_options(direct=kwargs)
try:
if not self.parse_simple_args(term):
self.parse_kv_args(parse_kv(term))
@@ -255,7 +222,9 @@ class LookupModule(LookupBase):
except Exception as e:
raise AnsibleError("unknown error parsing with_sequence arguments: %r. Error was: %s" % (term, e))
+ self.set_fields()
self.sanity_check()
+
if self.stride != 0:
results.extend(self.generate_sequence())
except AnsibleError:
diff --git a/lib/ansible/plugins/lookup/subelements.py b/lib/ansible/plugins/lookup/subelements.py
index f221652..e269be5 100644
--- a/lib/ansible/plugins/lookup/subelements.py
+++ b/lib/ansible/plugins/lookup/subelements.py
@@ -1,8 +1,7 @@
# (c) 2013, Serge van Ginderachter <serge@vanginderachter.be>
# (c) 2012-17 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: subelements
diff --git a/lib/ansible/plugins/lookup/template.py b/lib/ansible/plugins/lookup/template.py
index 358fa1d..b2508d0 100644
--- a/lib/ansible/plugins/lookup/template.py
+++ b/lib/ansible/plugins/lookup/template.py
@@ -2,8 +2,7 @@
# Copyright: (c) 2012-17, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: template
diff --git a/lib/ansible/plugins/lookup/together.py b/lib/ansible/plugins/lookup/together.py
index c990e06..0d0bfd9 100644
--- a/lib/ansible/plugins/lookup/together.py
+++ b/lib/ansible/plugins/lookup/together.py
@@ -1,8 +1,7 @@
# (c) 2013, Bradley Young <young.bradley@gmail.com>
# (c) 2012-17 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: together
diff --git a/lib/ansible/plugins/lookup/unvault.py b/lib/ansible/plugins/lookup/unvault.py
index d7f3cba..f2db18e 100644
--- a/lib/ansible/plugins/lookup/unvault.py
+++ b/lib/ansible/plugins/lookup/unvault.py
@@ -1,7 +1,6 @@
# (c) 2020 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: unvault
diff --git a/lib/ansible/plugins/lookup/url.py b/lib/ansible/plugins/lookup/url.py
index f5c93f2..05ebe6d 100644
--- a/lib/ansible/plugins/lookup/url.py
+++ b/lib/ansible/plugins/lookup/url.py
@@ -1,8 +1,7 @@
# (c) 2015, Brian Coca <bcoca@ansible.com>
# (c) 2012-17 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: url
@@ -88,7 +87,7 @@ options:
- section: url_lookup
key: force_basic_auth
follow_redirects:
- description: String of urllib2, all/yes, safe, none to determine how redirects are followed, see RedirectHandlerFactory for more information
+ description: String of urllib2, all/yes, safe, none to determine how redirects are followed
type: string
version_added: "2.10"
default: 'urllib2'
@@ -99,6 +98,13 @@ options:
ini:
- section: url_lookup
key: follow_redirects
+ choices:
+ all: Will follow all redirects.
+ none: Will not follow any redirects.
+ safe: Only redirects doing GET or HEAD requests will be followed.
+ urllib2: Defer to urllib2 behavior (As of writing this follows HTTP redirects).
+ 'no': (DEPRECATED, will be removed in the future version) alias of V(none).
+ 'yes': (DEPRECATED, will be removed in the future version) alias of V(all).
use_gssapi:
description:
- Use GSSAPI handler of requests
diff --git a/lib/ansible/plugins/lookup/varnames.py b/lib/ansible/plugins/lookup/varnames.py
index 4fd0153..2163ce7 100644
--- a/lib/ansible/plugins/lookup/varnames.py
+++ b/lib/ansible/plugins/lookup/varnames.py
@@ -1,7 +1,6 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: varnames
diff --git a/lib/ansible/plugins/lookup/vars.py b/lib/ansible/plugins/lookup/vars.py
index dd5f763..14cac99 100644
--- a/lib/ansible/plugins/lookup/vars.py
+++ b/lib/ansible/plugins/lookup/vars.py
@@ -1,7 +1,6 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = """
name: vars
diff --git a/lib/ansible/plugins/netconf/__init__.py b/lib/ansible/plugins/netconf/__init__.py
index 1344d63..6887d78 100644
--- a/lib/ansible/plugins/netconf/__init__.py
+++ b/lib/ansible/plugins/netconf/__init__.py
@@ -16,8 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from abc import abstractmethod
from functools import wraps
diff --git a/lib/ansible/plugins/shell/__init__.py b/lib/ansible/plugins/shell/__init__.py
index c9f8add..5aa0a1b 100644
--- a/lib/ansible/plugins/shell/__init__.py
+++ b/lib/ansible/plugins/shell/__init__.py
@@ -14,8 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import os
import os.path
@@ -65,12 +64,12 @@ class ShellBase(AnsiblePlugin):
# TODO: config system should already resolve this so we should be able to just iterate over dicts
env = self.get_option('environment')
if isinstance(env, string_types):
- raise AnsibleError('The "envirionment" keyword takes a list of dictionaries or a dictionary, not a string')
+ raise AnsibleError('The "environment" keyword takes a list of dictionaries or a dictionary, not a string')
if not isinstance(env, Sequence):
env = [env]
for env_dict in env:
if not isinstance(env_dict, Mapping):
- raise AnsibleError('The "envirionment" keyword takes a list of dictionaries (or single dictionary), but got a "%s" instead' % type(env_dict))
+ raise AnsibleError('The "environment" keyword takes a list of dictionaries (or single dictionary), but got a "%s" instead' % type(env_dict))
self.env.update(env_dict)
# We can remove the try: except in the future when we make ShellBase a proper subset of
diff --git a/lib/ansible/plugins/shell/cmd.py b/lib/ansible/plugins/shell/cmd.py
index 152fdd0..db851df 100644
--- a/lib/ansible/plugins/shell/cmd.py
+++ b/lib/ansible/plugins/shell/cmd.py
@@ -1,7 +1,6 @@
# Copyright (c) 2019 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: cmd
diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py
index f2e78cb..405211a 100644
--- a/lib/ansible/plugins/shell/powershell.py
+++ b/lib/ansible/plugins/shell/powershell.py
@@ -1,8 +1,7 @@
# Copyright (c) 2014, Chris Church <chris@ninemoreminutes.com>
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: powershell
diff --git a/lib/ansible/plugins/shell/sh.py b/lib/ansible/plugins/shell/sh.py
index 146c466..e0412b7 100644
--- a/lib/ansible/plugins/shell/sh.py
+++ b/lib/ansible/plugins/shell/sh.py
@@ -1,8 +1,7 @@
# Copyright (c) 2014, Chris Church <chris@ninemoreminutes.com>
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: sh
diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index eb2f76d..efd69ef 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import cmd
import functools
@@ -555,12 +553,19 @@ class StrategyBase:
seen = []
for handler in handlers:
if listeners := handler.listen:
- if notification in handler.get_validated_value(
+ listeners = handler.get_validated_value(
'listen',
handler.fattributes.get('listen'),
listeners,
templar,
- ):
+ )
+ if handler._role is not None:
+ for listener in listeners.copy():
+ listeners.extend([
+ handler._role.get_name(include_role_fqcn=True) + ' : ' + listener,
+ handler._role.get_name(include_role_fqcn=False) + ' : ' + listener
+ ])
+ if notification in listeners:
if handler.name and handler.name in seen:
continue
seen.append(handler.name)
@@ -845,7 +850,7 @@ class StrategyBase:
return ti_copy
- def _load_included_file(self, included_file, iterator, is_handler=False):
+ def _load_included_file(self, included_file, iterator, is_handler=False, handle_stats_and_callbacks=True):
'''
Loads an included YAML file of tasks, applying the optional set of variables.
@@ -853,6 +858,15 @@ class StrategyBase:
in such case the caller is responsible for marking the host(s) as failed
using PlayIterator.mark_host_failed().
'''
+ if handle_stats_and_callbacks:
+ display.deprecated(
+ "Reporting play recap stats and running callbacks functionality for "
+ "``include_tasks`` in ``StrategyBase._load_included_file`` is deprecated. "
+ "See ``https://github.com/ansible/ansible/pull/79260`` for guidance on how to "
+ "move the reporting into specific strategy plugins to account for "
+ "``include_role`` tasks as well.",
+ version="2.21"
+ )
display.debug("loading included file: %s" % included_file._filename)
try:
data = self._loader.load_from_file(included_file._filename)
@@ -872,11 +886,9 @@ class StrategyBase:
loader=self._loader,
variable_manager=self._variable_manager,
)
-
- # since we skip incrementing the stats when the task result is
- # first processed, we do so now for each host in the list
- for host in included_file._hosts:
- self._tqm._stats.increment('ok', host.name)
+ if handle_stats_and_callbacks:
+ for host in included_file._hosts:
+ self._tqm._stats.increment('ok', host.name)
except AnsibleParserError:
raise
except AnsibleError as e:
@@ -884,18 +896,18 @@ class StrategyBase:
reason = "Could not find or access '%s' on the Ansible Controller." % to_text(e.file_name)
else:
reason = to_text(e)
-
- for r in included_file._results:
- r._result['failed'] = True
-
- for host in included_file._hosts:
- tr = TaskResult(host=host, task=included_file._task, return_data=dict(failed=True, reason=reason))
- self._tqm._stats.increment('failures', host.name)
- self._tqm.send_callback('v2_runner_on_failed', tr)
+ if handle_stats_and_callbacks:
+ for r in included_file._results:
+ r._result['failed'] = True
+
+ for host in included_file._hosts:
+ tr = TaskResult(host=host, task=included_file._task, return_data=dict(failed=True, reason=reason))
+ self._tqm._stats.increment('failures', host.name)
+ self._tqm.send_callback('v2_runner_on_failed', tr)
raise AnsibleError(reason) from e
- # finally, send the callback and return the list of blocks loaded
- self._tqm.send_callback('v2_playbook_on_include', included_file)
+ if handle_stats_and_callbacks:
+ self._tqm.send_callback('v2_playbook_on_include', included_file)
display.debug("done processing included file")
return block_list
diff --git a/lib/ansible/plugins/strategy/debug.py b/lib/ansible/plugins/strategy/debug.py
index 0965bb3..6ee294b 100644
--- a/lib/ansible/plugins/strategy/debug.py
+++ b/lib/ansible/plugins/strategy/debug.py
@@ -12,8 +12,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: debug
diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py
index 5e64ef3..6f33a68 100644
--- a/lib/ansible/plugins/strategy/free.py
+++ b/lib/ansible/plugins/strategy/free.py
@@ -14,9 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: free
@@ -250,7 +248,12 @@ class StrategyModule(StrategyBase):
)
else:
is_handler = isinstance(included_file._task, Handler)
- new_blocks = self._load_included_file(included_file, iterator=iterator, is_handler=is_handler)
+ new_blocks = self._load_included_file(
+ included_file,
+ iterator=iterator,
+ is_handler=is_handler,
+ handle_stats_and_callbacks=False,
+ )
# let PlayIterator know about any new handlers included via include_role or
# import_role within include_role/include_taks
@@ -258,13 +261,20 @@ class StrategyModule(StrategyBase):
except AnsibleParserError:
raise
except AnsibleError as e:
- if included_file._is_role:
- # include_role does not have on_include callback so display the error
- display.error(to_text(e), wrap_text=False)
+ display.error(to_text(e), wrap_text=False)
for r in included_file._results:
r._result['failed'] = True
+ r._result['reason'] = str(e)
+ self._tqm._stats.increment('failures', r._host.name)
+ self._tqm.send_callback('v2_runner_on_failed', r)
failed_includes_hosts.add(r._host)
continue
+ else:
+ # since we skip incrementing the stats when the task result is
+ # first processed, we do so now for each host in the list
+ for host in included_file._hosts:
+ self._tqm._stats.increment('ok', host.name)
+ self._tqm.send_callback('v2_playbook_on_include', included_file)
for new_block in new_blocks:
if is_handler:
diff --git a/lib/ansible/plugins/strategy/host_pinned.py b/lib/ansible/plugins/strategy/host_pinned.py
index 70f22eb..f06550f 100644
--- a/lib/ansible/plugins/strategy/host_pinned.py
+++ b/lib/ansible/plugins/strategy/host_pinned.py
@@ -14,9 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: host_pinned
diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py
index f3b117b..29f94c4 100644
--- a/lib/ansible/plugins/strategy/linear.py
+++ b/lib/ansible/plugins/strategy/linear.py
@@ -14,9 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: linear
@@ -33,7 +31,7 @@ DOCUMENTATION = '''
from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleAssertionError, AnsibleParserError
-from ansible.executor.play_iterator import IteratingStates, FailedStates
+from ansible.executor.play_iterator import IteratingStates
from ansible.module_utils.common.text.converters import to_text
from ansible.playbook.handler import Handler
from ansible.playbook.included_file import IncludedFile
@@ -293,7 +291,12 @@ class StrategyModule(StrategyBase):
)
else:
is_handler = isinstance(included_file._task, Handler)
- new_blocks = self._load_included_file(included_file, iterator=iterator, is_handler=is_handler)
+ new_blocks = self._load_included_file(
+ included_file,
+ iterator=iterator,
+ is_handler=is_handler,
+ handle_stats_and_callbacks=False,
+ )
# let PlayIterator know about any new handlers included via include_role or
# import_role within include_role/include_taks
@@ -326,13 +329,19 @@ class StrategyModule(StrategyBase):
except AnsibleParserError:
raise
except AnsibleError as e:
- if included_file._is_role:
- # include_role does not have on_include callback so display the error
- display.error(to_text(e), wrap_text=False)
+ display.error(to_text(e), wrap_text=False)
for r in included_file._results:
r._result['failed'] = True
+ r._result['reason'] = str(e)
+ self._tqm._stats.increment('failures', r._host.name)
+ self._tqm.send_callback('v2_runner_on_failed', r)
failed_includes_hosts.add(r._host)
- continue
+ else:
+ # since we skip incrementing the stats when the task result is
+ # first processed, we do so now for each host in the list
+ for host in included_file._hosts:
+ self._tqm._stats.increment('ok', host.name)
+ self._tqm.send_callback('v2_playbook_on_include', included_file)
for host in failed_includes_hosts:
self._tqm._failed_hosts[host.name] = True
@@ -356,25 +365,16 @@ class StrategyModule(StrategyBase):
failed_hosts = []
unreachable_hosts = []
for res in results:
- # execute_meta() does not set 'failed' in the TaskResult
- # so we skip checking it with the meta tasks and look just at the iterator
- if (res.is_failed() or res._task.action in C._ACTION_META) and iterator.is_failed(res._host):
+ if res.is_failed():
failed_hosts.append(res._host.name)
elif res.is_unreachable():
unreachable_hosts.append(res._host.name)
- # if any_errors_fatal and we had an error, mark all hosts as failed
- if any_errors_fatal and (len(failed_hosts) > 0 or len(unreachable_hosts) > 0):
- dont_fail_states = frozenset([IteratingStates.RESCUE, IteratingStates.ALWAYS])
+ if any_errors_fatal and (failed_hosts or unreachable_hosts):
for host in hosts_left:
- (s, dummy) = iterator.get_next_task_for_host(host, peek=True)
- # the state may actually be in a child state, use the get_active_state()
- # method in the iterator to figure out the true active state
- s = iterator.get_active_state(s)
- if s.run_state not in dont_fail_states or \
- s.run_state == IteratingStates.RESCUE and s.fail_state & FailedStates.RESCUE != 0:
+ if host.name not in failed_hosts:
self._tqm._failed_hosts[host.name] = True
- result |= self._tqm.RUN_FAILED_BREAK_PLAY
+ iterator.mark_host_failed(host)
display.debug("done checking for any_errors_fatal")
display.debug("checking for max_fail_percentage")
diff --git a/lib/ansible/plugins/terminal/__init__.py b/lib/ansible/plugins/terminal/__init__.py
index 2a280a9..fe7dc31 100644
--- a/lib/ansible/plugins/terminal/__init__.py
+++ b/lib/ansible/plugins/terminal/__init__.py
@@ -16,8 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import re
@@ -85,7 +84,7 @@ class TerminalBase(ABC):
This method is called right after the invoke_shell() is called from
the Paramiko SSHClient instance. It provides an opportunity to setup
- terminal parameters such as disbling paging for instance.
+ terminal parameters such as disabling paging for instance.
"""
pass
diff --git a/lib/ansible/plugins/test/__init__.py b/lib/ansible/plugins/test/__init__.py
index 1400316..b0b78d1 100644
--- a/lib/ansible/plugins/test/__init__.py
+++ b/lib/ansible/plugins/test/__init__.py
@@ -1,8 +1,7 @@
# (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins import AnsibleJinja2Plugin
diff --git a/lib/ansible/plugins/test/change.yml b/lib/ansible/plugins/test/change.yml
index 8b3dbe1..ee98fec 100644
--- a/lib/ansible/plugins/test/change.yml
+++ b/lib/ansible/plugins/test/change.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [change]
description:
- Tests if task required changes to complete
- - This test checks for the existance of a C(changed) key in the input dictionary and that it is V(True) if present
+ - This test checks for the existence of a C(changed) key in the input dictionary and that it is V(True) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/changed.yml b/lib/ansible/plugins/test/changed.yml
index 8b3dbe1..ee98fec 100644
--- a/lib/ansible/plugins/test/changed.yml
+++ b/lib/ansible/plugins/test/changed.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [change]
description:
- Tests if task required changes to complete
- - This test checks for the existance of a C(changed) key in the input dictionary and that it is V(True) if present
+ - This test checks for the existence of a C(changed) key in the input dictionary and that it is V(True) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/contains.yml b/lib/ansible/plugins/test/contains.yml
index 6c81a2f..7d936f2 100644
--- a/lib/ansible/plugins/test/contains.yml
+++ b/lib/ansible/plugins/test/contains.yml
@@ -21,7 +21,7 @@ EXAMPLES: |
# as a selector
- action: module=doessomething
- when: lacp_groups|selectattr('interfaces', 'contains', 'em1')|first).master
+ when: (lacp_groups|selectattr('interfaces', 'contains', 'em1')|first).master
vars:
lacp_groups:
- master: lacp0
diff --git a/lib/ansible/plugins/test/core.py b/lib/ansible/plugins/test/core.py
index 498db0e..01e672b 100644
--- a/lib/ansible/plugins/test/core.py
+++ b/lib/ansible/plugins/test/core.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import re
import operator as py_operator
@@ -138,7 +136,7 @@ def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='s
def vault_encrypted(value):
- """Evaulate whether a variable is a single vault encrypted value
+ """Evaluate whether a variable is a single vault encrypted value
.. versionadded:: 2.10
"""
diff --git a/lib/ansible/plugins/test/exists.yml b/lib/ansible/plugins/test/exists.yml
index 6ced0dc..331ce5c 100644
--- a/lib/ansible/plugins/test/exists.yml
+++ b/lib/ansible/plugins/test/exists.yml
@@ -14,7 +14,7 @@ DOCUMENTATION:
EXAMPLES: |
vars:
- my_etc_hosts_exists: "{{ '/etc/hosts' is exist }}"
+ my_etc_hosts_exists: "{{ '/etc/hosts' is exists }}"
list_of_local_files_to_copy_to_remote: "{{ list_of_all_possible_files | select('exists') }}"
RETURN:
diff --git a/lib/ansible/plugins/test/failed.yml b/lib/ansible/plugins/test/failed.yml
index b8cd78b..c880f2e 100644
--- a/lib/ansible/plugins/test/failed.yml
+++ b/lib/ansible/plugins/test/failed.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [failure]
description:
- Tests if task finished in failure, opposite of C(succeeded).
- - This test checks for the existance of a C(failed) key in the input dictionary and that it is V(True) if present.
+ - This test checks for the existence of a C(failed) key in the input dictionary and that it is V(True) if present.
- Tasks that get skipped or not executed due to other failures (syntax, templating, unreachable host, etc) do not return a 'failed' status.
options:
_input:
diff --git a/lib/ansible/plugins/test/failure.yml b/lib/ansible/plugins/test/failure.yml
index b8cd78b..c880f2e 100644
--- a/lib/ansible/plugins/test/failure.yml
+++ b/lib/ansible/plugins/test/failure.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [failure]
description:
- Tests if task finished in failure, opposite of C(succeeded).
- - This test checks for the existance of a C(failed) key in the input dictionary and that it is V(True) if present.
+ - This test checks for the existence of a C(failed) key in the input dictionary and that it is V(True) if present.
- Tasks that get skipped or not executed due to other failures (syntax, templating, unreachable host, etc) do not return a 'failed' status.
options:
_input:
diff --git a/lib/ansible/plugins/test/files.py b/lib/ansible/plugins/test/files.py
index f075cae..fc142b7 100644
--- a/lib/ansible/plugins/test/files.py
+++ b/lib/ansible/plugins/test/files.py
@@ -15,9 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from os.path import isdir, isfile, isabs, exists, lexists, islink, samefile, ismount
diff --git a/lib/ansible/plugins/test/finished.yml b/lib/ansible/plugins/test/finished.yml
index 22bd6e8..c83c5a3 100644
--- a/lib/ansible/plugins/test/finished.yml
+++ b/lib/ansible/plugins/test/finished.yml
@@ -4,8 +4,8 @@ DOCUMENTATION:
version_added: "1.9"
short_description: Did async task finish
description:
- - Used to test if an async task has finished, it will aslo work with normal tasks but will issue a warning.
- - This test checks for the existance of a C(finished) key in the input dictionary and that it is V(1) if present
+ - Used to test if an async task has finished, it will also work with normal tasks but will issue a warning.
+ - This test checks for the existence of a C(finished) key in the input dictionary and that it is V(1) if present
options:
_input:
description: registered result from an Ansible task
@@ -17,5 +17,5 @@ EXAMPLES: |
RETURN:
_value:
- description: Returns V(True) if the aysnc task has finished, V(False) otherwise.
+ description: Returns V(True) if the async task has finished, V(False) otherwise.
type: boolean
diff --git a/lib/ansible/plugins/test/issuperset.yml b/lib/ansible/plugins/test/issuperset.yml
index 7114980..1e16b45 100644
--- a/lib/ansible/plugins/test/issuperset.yml
+++ b/lib/ansible/plugins/test/issuperset.yml
@@ -19,7 +19,7 @@ DOCUMENTATION:
required: True
EXAMPLES: |
big: [1,2,3,4,5]
- sml: [3,4]
+ small: [3,4]
issmallinbig: '{{ big is superset(small) }}'
RETURN:
_value:
diff --git a/lib/ansible/plugins/test/match.yml b/lib/ansible/plugins/test/match.yml
index 76f656b..f1ffc7b 100644
--- a/lib/ansible/plugins/test/match.yml
+++ b/lib/ansible/plugins/test/match.yml
@@ -15,7 +15,7 @@ DOCUMENTATION:
type: string
required: True
ignorecase:
- description: Use case insenstive matching.
+ description: Use case insensitive matching.
type: boolean
default: False
multiline:
diff --git a/lib/ansible/plugins/test/mathstuff.py b/lib/ansible/plugins/test/mathstuff.py
index 9a3f467..4bf33e8 100644
--- a/lib/ansible/plugins/test/mathstuff.py
+++ b/lib/ansible/plugins/test/mathstuff.py
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
import math
diff --git a/lib/ansible/plugins/test/reachable.yml b/lib/ansible/plugins/test/reachable.yml
index bddd860..3f9a01e 100644
--- a/lib/ansible/plugins/test/reachable.yml
+++ b/lib/ansible/plugins/test/reachable.yml
@@ -5,7 +5,7 @@ DOCUMENTATION:
short_description: Task did not end due to unreachable host
description:
- Tests if task was able to reach the host for execution
- - This test checks for the existance of a C(unreachable) key in the input dictionary and that it is V(False) if present
+ - This test checks for the existence of a C(unreachable) key in the input dictionary and that it is V(False) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/regex.yml b/lib/ansible/plugins/test/regex.yml
index 1b2cd69..d80ca85 100644
--- a/lib/ansible/plugins/test/regex.yml
+++ b/lib/ansible/plugins/test/regex.yml
@@ -14,7 +14,7 @@ DOCUMENTATION:
type: string
required: True
ignorecase:
- description: Use case insenstive matching.
+ description: Use case insensitive matching.
type: boolean
default: False
multiline:
diff --git a/lib/ansible/plugins/test/search.yml b/lib/ansible/plugins/test/search.yml
index 9a7551c..0348353 100644
--- a/lib/ansible/plugins/test/search.yml
+++ b/lib/ansible/plugins/test/search.yml
@@ -14,7 +14,7 @@ DOCUMENTATION:
type: string
required: True
ignorecase:
- description: Use case insenstive matching.
+ description: Use case insensitive matching.
type: boolean
default: False
multiline:
diff --git a/lib/ansible/plugins/test/skip.yml b/lib/ansible/plugins/test/skip.yml
index 2aad3a3..808f067 100644
--- a/lib/ansible/plugins/test/skip.yml
+++ b/lib/ansible/plugins/test/skip.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [skip]
description:
- Tests if task was skipped
- - This test checks for the existance of a C(skipped) key in the input dictionary and that it is V(True) if present
+ - This test checks for the existence of a C(skipped) key in the input dictionary and that it is V(True) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/skipped.yml b/lib/ansible/plugins/test/skipped.yml
index 2aad3a3..808f067 100644
--- a/lib/ansible/plugins/test/skipped.yml
+++ b/lib/ansible/plugins/test/skipped.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [skip]
description:
- Tests if task was skipped
- - This test checks for the existance of a C(skipped) key in the input dictionary and that it is V(True) if present
+ - This test checks for the existence of a C(skipped) key in the input dictionary and that it is V(True) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/started.yml b/lib/ansible/plugins/test/started.yml
index 23a6cb5..34a28b6 100644
--- a/lib/ansible/plugins/test/started.yml
+++ b/lib/ansible/plugins/test/started.yml
@@ -5,7 +5,7 @@ DOCUMENTATION:
short_description: Was async task started
description:
- Used to check if an async task has started, will also work with non async tasks but will issue a warning.
- - This test checks for the existance of a C(started) key in the input dictionary and that it is V(1) if present
+ - This test checks for the existence of a C(started) key in the input dictionary and that it is V(1) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/succeeded.yml b/lib/ansible/plugins/test/succeeded.yml
index 97105c8..753869f 100644
--- a/lib/ansible/plugins/test/succeeded.yml
+++ b/lib/ansible/plugins/test/succeeded.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [succeeded, successful]
description:
- Tests if task finished successfully, opposite of C(failed).
- - This test checks for the existance of a C(failed) key in the input dictionary and that it is V(False) if present
+ - This test checks for the existence of a C(failed) key in the input dictionary and that it is V(False) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/success.yml b/lib/ansible/plugins/test/success.yml
index 97105c8..753869f 100644
--- a/lib/ansible/plugins/test/success.yml
+++ b/lib/ansible/plugins/test/success.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [succeeded, successful]
description:
- Tests if task finished successfully, opposite of C(failed).
- - This test checks for the existance of a C(failed) key in the input dictionary and that it is V(False) if present
+ - This test checks for the existence of a C(failed) key in the input dictionary and that it is V(False) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/successful.yml b/lib/ansible/plugins/test/successful.yml
index 97105c8..753869f 100644
--- a/lib/ansible/plugins/test/successful.yml
+++ b/lib/ansible/plugins/test/successful.yml
@@ -6,7 +6,7 @@ DOCUMENTATION:
aliases: [succeeded, successful]
description:
- Tests if task finished successfully, opposite of C(failed).
- - This test checks for the existance of a C(failed) key in the input dictionary and that it is V(False) if present
+ - This test checks for the existence of a C(failed) key in the input dictionary and that it is V(False) if present
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/superset.yml b/lib/ansible/plugins/test/superset.yml
index 7114980..1e16b45 100644
--- a/lib/ansible/plugins/test/superset.yml
+++ b/lib/ansible/plugins/test/superset.yml
@@ -19,7 +19,7 @@ DOCUMENTATION:
required: True
EXAMPLES: |
big: [1,2,3,4,5]
- sml: [3,4]
+ small: [3,4]
issmallinbig: '{{ big is superset(small) }}'
RETURN:
_value:
diff --git a/lib/ansible/plugins/test/unreachable.yml b/lib/ansible/plugins/test/unreachable.yml
index 52e2730..018bee6 100644
--- a/lib/ansible/plugins/test/unreachable.yml
+++ b/lib/ansible/plugins/test/unreachable.yml
@@ -5,7 +5,7 @@ DOCUMENTATION:
short_description: Did task end due to the host was unreachable
description:
- Tests if task was not able to reach the host for execution
- - This test checks for the existance of a C(unreachable) key in the input dictionary and that it's value is V(True)
+ - This test checks for the existence of a C(unreachable) key in the input dictionary and that it's value is V(True)
options:
_input:
description: registered result from an Ansible task
diff --git a/lib/ansible/plugins/test/uri.py b/lib/ansible/plugins/test/uri.py
index 7ef3381..b9679d0 100644
--- a/lib/ansible/plugins/test/uri.py
+++ b/lib/ansible/plugins/test/uri.py
@@ -1,8 +1,6 @@
# (c) Ansible Project
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from urllib.parse import urlparse
diff --git a/lib/ansible/plugins/vars/__init__.py b/lib/ansible/plugins/vars/__init__.py
index 4f9045b..12b52d9 100644
--- a/lib/ansible/plugins/vars/__init__.py
+++ b/lib/ansible/plugins/vars/__init__.py
@@ -15,8 +15,7 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
from ansible.plugins import AnsiblePlugin
from ansible.utils.path import basedir
diff --git a/lib/ansible/plugins/vars/host_group_vars.py b/lib/ansible/plugins/vars/host_group_vars.py
index 28b4213..cd02cc5 100644
--- a/lib/ansible/plugins/vars/host_group_vars.py
+++ b/lib/ansible/plugins/vars/host_group_vars.py
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#############################################
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
+from __future__ import annotations
DOCUMENTATION = '''
name: host_group_vars
@@ -74,7 +73,7 @@ class VarsModule(BaseVarsPlugin):
def load_found_files(self, loader, data, found_files):
for found in found_files:
- new_data = loader.load_from_file(found, cache=True, unsafe=True)
+ new_data = loader.load_from_file(found, cache='all', unsafe=True)
if new_data: # ignore empty files
data = combine_vars(data, new_data)
return data