ansible-core 2.17.4__py3-none-any.whl → 2.18.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/__main__.py +2 -17
- ansible/cli/__init__.py +3 -15
- ansible/cli/config.py +187 -24
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +38 -16
- ansible/cli/galaxy.py +30 -53
- ansible/cli/inventory.py +2 -2
- ansible/cli/pull.py +2 -2
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -10
- ansible/config/base.yml +127 -57
- ansible/config/manager.py +89 -11
- ansible/constants.py +32 -9
- ansible/errors/__init__.py +5 -0
- ansible/executor/interpreter_discovery.py +1 -1
- ansible/executor/play_iterator.py +16 -0
- ansible/executor/playbook_executor.py +1 -4
- ansible/executor/powershell/become_wrapper.ps1 +4 -5
- ansible/executor/powershell/bootstrap_wrapper.ps1 +2 -3
- ansible/executor/powershell/exec_wrapper.ps1 +1 -1
- ansible/executor/powershell/module_manifest.py +2 -2
- ansible/executor/task_executor.py +50 -39
- ansible/executor/task_queue_manager.py +1 -1
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +3 -4
- ansible/galaxy/collection/__init__.py +21 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
- ansible/galaxy/collection/galaxy_api_proxy.py +10 -16
- ansible/galaxy/collection/gpg.py +17 -23
- ansible/galaxy/data/COPYING +7 -0
- ansible/galaxy/data/apb/Dockerfile.j2 +1 -0
- ansible/galaxy/data/apb/Makefile.j2 +1 -0
- ansible/galaxy/data/apb/README.md +7 -3
- ansible/galaxy/data/apb/apb.yml.j2 +1 -0
- ansible/galaxy/data/apb/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/deprovision.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/provision.yml.j2 +1 -0
- ansible/galaxy/data/apb/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/tests/ansible.cfg +1 -0
- ansible/galaxy/data/apb/tests/inventory +1 -0
- ansible/galaxy/data/apb/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/apb/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/collections_galaxy_meta.yml +1 -0
- ansible/galaxy/data/container/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/container/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/container.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tests/ansible.cfg +1 -0
- ansible/galaxy/data/container/tests/inventory +1 -0
- ansible/galaxy/data/container/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/container/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/README.md.j2 +1 -0
- ansible/galaxy/data/default/collection/galaxy.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/meta/runtime.yml +1 -0
- ansible/galaxy/data/default/collection/plugins/README.md.j2 +1 -0
- ansible/galaxy/data/default/role/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tests/inventory +1 -0
- ansible/galaxy/data/default/role/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/default/role/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/network/library/example_command.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_config.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_facts.py.j2 +1 -0
- ansible/galaxy/data/network/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/network/module_utils/example.py.j2 +1 -0
- ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tests/inventory +1 -0
- ansible/galaxy/data/network/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/network/vars/main.yml.j2 +1 -0
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/galaxy/role.py +1 -1
- ansible/galaxy/token.py +20 -8
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +28 -0
- ansible/module_utils/_internal/_concurrent/_futures.py +21 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +2 -2
- ansible/module_utils/basic.py +14 -11
- ansible/module_utils/common/collections.py +1 -1
- ansible/module_utils/common/file.py +0 -6
- ansible/module_utils/common/process.py +22 -9
- ansible/module_utils/common/text/converters.py +5 -8
- ansible/module_utils/common/text/formatters.py +20 -4
- ansible/module_utils/common/validation.py +33 -25
- ansible/module_utils/compat/paramiko.py +6 -1
- ansible/module_utils/compat/selinux.py +2 -2
- ansible/module_utils/connection.py +8 -24
- ansible/module_utils/csharp/Ansible.Become.cs +14 -25
- ansible/module_utils/csharp/Ansible.Process.cs +1 -1
- ansible/module_utils/distro/__init__.py +1 -1
- ansible/module_utils/distro/_distro.py +8 -4
- ansible/module_utils/facts/collector.py +2 -0
- ansible/module_utils/facts/default_collectors.py +3 -1
- ansible/module_utils/facts/hardware/aix.py +54 -52
- ansible/module_utils/facts/hardware/darwin.py +37 -34
- ansible/module_utils/facts/hardware/freebsd.py +55 -15
- ansible/module_utils/facts/hardware/hpux.py +3 -0
- ansible/module_utils/facts/hardware/linux.py +101 -57
- ansible/module_utils/facts/hardware/netbsd.py +3 -0
- ansible/module_utils/facts/hardware/openbsd.py +4 -1
- ansible/module_utils/facts/hardware/sunos.py +7 -1
- ansible/module_utils/facts/network/aix.py +16 -17
- ansible/module_utils/facts/network/fc_wwn.py +4 -1
- ansible/module_utils/facts/network/hpux.py +21 -4
- ansible/module_utils/facts/network/iscsi.py +7 -8
- ansible/module_utils/facts/network/linux.py +0 -2
- ansible/module_utils/facts/other/facter.py +9 -4
- ansible/module_utils/facts/other/ohai.py +5 -5
- ansible/module_utils/facts/packages.py +49 -7
- ansible/module_utils/facts/sysctl.py +33 -31
- ansible/module_utils/facts/system/distribution.py +1 -1
- ansible/module_utils/facts/system/local.py +12 -22
- ansible/module_utils/facts/system/service_mgr.py +3 -1
- ansible/module_utils/facts/system/systemd.py +47 -0
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/splitter.py +1 -1
- ansible/modules/add_host.py +1 -1
- ansible/modules/apt.py +43 -32
- ansible/modules/apt_key.py +6 -6
- ansible/modules/apt_repository.py +23 -14
- ansible/modules/assemble.py +7 -2
- ansible/modules/assert.py +4 -4
- ansible/modules/blockinfile.py +3 -6
- ansible/modules/command.py +1 -1
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +13 -10
- ansible/modules/deb822_repository.py +16 -17
- ansible/modules/debconf.py +9 -9
- ansible/modules/debug.py +1 -1
- ansible/modules/dnf.py +79 -164
- ansible/modules/dnf5.py +48 -31
- ansible/modules/dpkg_selections.py +2 -2
- ansible/modules/expect.py +2 -2
- ansible/modules/fetch.py +2 -2
- ansible/modules/file.py +5 -3
- ansible/modules/find.py +40 -12
- ansible/modules/gather_facts.py +4 -2
- ansible/modules/get_url.py +29 -24
- ansible/modules/git.py +35 -35
- ansible/modules/group.py +71 -1
- ansible/modules/hostname.py +2 -4
- ansible/modules/include_vars.py +5 -5
- ansible/modules/iptables.py +13 -16
- ansible/modules/known_hosts.py +16 -13
- ansible/modules/lineinfile.py +1 -4
- ansible/modules/meta.py +6 -1
- ansible/modules/mount_facts.py +651 -0
- ansible/modules/package_facts.py +63 -80
- ansible/modules/pause.py +4 -3
- ansible/modules/pip.py +14 -14
- ansible/modules/replace.py +1 -4
- ansible/modules/rpm_key.py +31 -11
- ansible/modules/service.py +8 -8
- ansible/modules/service_facts.py +20 -5
- ansible/modules/set_stats.py +1 -1
- ansible/modules/setup.py +3 -3
- ansible/modules/stat.py +3 -3
- ansible/modules/subversion.py +1 -1
- ansible/modules/systemd.py +16 -10
- ansible/modules/systemd_service.py +16 -10
- ansible/modules/sysvinit.py +4 -4
- ansible/modules/unarchive.py +35 -22
- ansible/modules/uri.py +24 -18
- ansible/modules/user.py +145 -12
- ansible/modules/validate_argument_spec.py +3 -3
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +136 -179
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/mod_args.py +11 -10
- ansible/parsing/vault/__init__.py +8 -3
- ansible/parsing/yaml/constructor.py +10 -8
- ansible/parsing/yaml/objects.py +1 -1
- ansible/playbook/base.py +12 -23
- ansible/playbook/helpers.py +4 -0
- ansible/playbook/loop_control.py +8 -0
- ansible/playbook/play.py +4 -22
- ansible/playbook/play_context.py +0 -16
- ansible/playbook/playbook_include.py +2 -2
- ansible/playbook/role/__init__.py +2 -2
- ansible/playbook/task.py +1 -1
- ansible/plugins/__init__.py +2 -0
- ansible/plugins/action/__init__.py +7 -9
- ansible/plugins/action/reboot.py +2 -2
- ansible/plugins/become/__init__.py +1 -1
- ansible/plugins/callback/__init__.py +44 -3
- ansible/plugins/callback/default.py +1 -1
- ansible/plugins/cliconf/__init__.py +1 -1
- ansible/plugins/connection/paramiko_ssh.py +2 -80
- ansible/plugins/connection/psrp.py +33 -82
- ansible/plugins/connection/ssh.py +0 -8
- ansible/plugins/connection/winrm.py +46 -1
- ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
- ansible/plugins/doc_fragments/constructed.py +10 -10
- ansible/plugins/doc_fragments/default_callback.py +8 -8
- ansible/plugins/doc_fragments/files.py +5 -5
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +6 -6
- ansible/plugins/doc_fragments/return_common.py +1 -1
- ansible/plugins/doc_fragments/shell_common.py +2 -10
- ansible/plugins/doc_fragments/shell_windows.py +0 -9
- ansible/plugins/doc_fragments/url.py +2 -2
- ansible/plugins/doc_fragments/url_windows.py +4 -5
- ansible/plugins/doc_fragments/validate.py +1 -1
- ansible/plugins/filter/core.py +2 -0
- ansible/plugins/filter/human_to_bytes.yml +9 -0
- ansible/plugins/filter/password_hash.yml +1 -1
- ansible/plugins/filter/strftime.yml +1 -1
- ansible/plugins/filter/to_nice_json.yml +7 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +1 -1
- ansible/plugins/loader.py +0 -11
- ansible/plugins/lookup/config.py +1 -1
- ansible/plugins/lookup/csvfile.py +21 -9
- ansible/plugins/lookup/env.py +8 -9
- ansible/plugins/lookup/ini.py +10 -1
- ansible/plugins/lookup/random_choice.py +2 -2
- ansible/plugins/lookup/url.py +7 -2
- ansible/plugins/shell/__init__.py +15 -20
- ansible/plugins/shell/powershell.py +9 -6
- ansible/plugins/strategy/__init__.py +16 -7
- ansible/plugins/test/core.py +23 -1
- ansible/plugins/test/issubset.yml +1 -1
- ansible/plugins/test/subset.yml +1 -1
- ansible/plugins/test/timedout.yml +20 -0
- ansible/plugins/test/vault_encrypted.yml +6 -6
- ansible/plugins/test/vaulted_file.yml +19 -0
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -8
- ansible/utils/collection_loader/_collection_finder.py +23 -55
- ansible/utils/display.py +44 -31
- ansible/utils/jsonrpc.py +1 -1
- ansible/utils/listify.py +1 -5
- ansible/utils/path.py +3 -0
- ansible/utils/vars.py +18 -27
- ansible/vars/manager.py +7 -150
- ansible/vars/plugins.py +1 -1
- ansible_core-2.18.0b1.dist-info/Apache-License.txt +202 -0
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/METADATA +36 -23
- ansible_core-2.18.0b1.dist-info/MIT-license.txt +14 -0
- ansible_core-2.18.0b1.dist-info/PSF-license.txt +48 -0
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/RECORD +311 -306
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/WHEEL +1 -1
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/entry_points.txt +1 -1
- ansible_core-2.18.0b1.dist-info/simplified_bsd.txt +8 -0
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +5 -4
- ansible_test/_data/completion/windows.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -2
- ansible_test/_data/requirements/constraints.txt +1 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.in +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +4 -4
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.import.txt +1 -1
- ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +5 -7
- ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -2
- ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
- ansible_test/_data/requirements/sanity.yamllint.in +1 -0
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/ansible_util.py +8 -35
- ansible_test/_internal/ci/azp.py +1 -1
- ansible_test/_internal/classification/__init__.py +0 -2
- ansible_test/_internal/cli/parsers/key_value_parsers.py +3 -0
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -1
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
- ansible_test/_internal/commands/sanity/__init__.py +96 -19
- ansible_test/_internal/commands/sanity/pylint.py +20 -24
- ansible_test/_internal/completion.py +2 -0
- ansible_test/_internal/constants.py +0 -1
- ansible_test/_internal/coverage_util.py +1 -2
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/host_configs.py +10 -0
- ansible_test/_internal/host_profiles.py +9 -13
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/python_requirements.py +5 -14
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -0
- ansible_test/_internal/util_common.py +5 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
- ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
- ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/injector/python.py +5 -0
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
- ansible_test/_util/target/sanity/import/importer.py +1 -1
- ansible_test/_util/target/setup/bootstrap.sh +6 -17
- ansible_test/_util/target/setup/requirements.py +14 -20
- ansible_test/config/config.yml +1 -1
- ansible_core-2.17.4.data/scripts/ansible-test +0 -44
- ansible_test/_data/requirements/sanity.mypy.in +0 -10
- ansible_test/_data/requirements/sanity.mypy.txt +0 -18
- ansible_test/_internal/commands/sanity/mypy.py +0 -274
- ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
- ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
- ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/COPYING +0 -0
- {ansible_core-2.17.4.dist-info → ansible_core-2.18.0b1.dist-info}/top_level.txt +0 -0
|
@@ -9,17 +9,14 @@ from __future__ import annotations
|
|
|
9
9
|
import itertools
|
|
10
10
|
import os
|
|
11
11
|
import os.path
|
|
12
|
-
import pkgutil
|
|
13
12
|
import re
|
|
14
13
|
import sys
|
|
15
14
|
from keyword import iskeyword
|
|
16
|
-
from tokenize import Name as _VALID_IDENTIFIER_REGEX
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
# DO NOT add new non-stdlib import deps here, this loader is used by external tools (eg ansible-test import sanity)
|
|
20
18
|
# that only allow stdlib and module_utils
|
|
21
19
|
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
|
|
22
|
-
from ansible.module_utils.six import string_types, PY3
|
|
23
20
|
from ._collection_config import AnsibleCollectionConfig
|
|
24
21
|
|
|
25
22
|
from contextlib import contextmanager
|
|
@@ -32,11 +29,7 @@ except ImportError:
|
|
|
32
29
|
__import__(name)
|
|
33
30
|
return sys.modules[name]
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
from importlib import reload as reload_module
|
|
37
|
-
except ImportError:
|
|
38
|
-
# 2.7 has a global reload function instead...
|
|
39
|
-
reload_module = reload # type: ignore[name-defined] # pylint:disable=undefined-variable
|
|
32
|
+
from importlib import reload as reload_module
|
|
40
33
|
|
|
41
34
|
try:
|
|
42
35
|
try:
|
|
@@ -77,26 +70,7 @@ try:
|
|
|
77
70
|
except ImportError:
|
|
78
71
|
_meta_yml_to_dict = None
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
if not hasattr(__builtins__, 'ModuleNotFoundError'):
|
|
82
|
-
# this was introduced in Python 3.6
|
|
83
|
-
ModuleNotFoundError = ImportError
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
_VALID_IDENTIFIER_STRING_REGEX = re.compile(
|
|
87
|
-
''.join((_VALID_IDENTIFIER_REGEX, r'\Z')),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
try: # NOTE: py3/py2 compat
|
|
92
|
-
# py2 mypy can't deal with try/excepts
|
|
93
|
-
is_python_identifier = str.isidentifier # type: ignore[attr-defined]
|
|
94
|
-
except AttributeError: # Python 2
|
|
95
|
-
def is_python_identifier(self): # type: (str) -> bool
|
|
96
|
-
"""Determine whether the given string is a Python identifier."""
|
|
97
|
-
# Ref: https://stackoverflow.com/a/55802320/595220
|
|
98
|
-
return bool(re.match(_VALID_IDENTIFIER_STRING_REGEX, self))
|
|
99
|
-
|
|
73
|
+
is_python_identifier = str.isidentifier # type: ignore[attr-defined]
|
|
100
74
|
|
|
101
75
|
PB_EXTENSIONS = ('.yml', '.yaml')
|
|
102
76
|
SYNTHETIC_PACKAGE_NAME = '<ansible_synthetic_collection_package>'
|
|
@@ -219,7 +193,7 @@ class _AnsibleTraversableResources(TraversableResources):
|
|
|
219
193
|
parts = package.split('.')
|
|
220
194
|
is_ns = parts[0] == 'ansible_collections' and len(parts) < 3
|
|
221
195
|
|
|
222
|
-
if isinstance(package,
|
|
196
|
+
if isinstance(package, str):
|
|
223
197
|
if is_ns:
|
|
224
198
|
# Don't use ``spec_from_loader`` here, because that will point
|
|
225
199
|
# to exactly 1 location for a namespace. Use ``find_spec``
|
|
@@ -241,7 +215,7 @@ class _AnsibleCollectionFinder:
|
|
|
241
215
|
# TODO: accept metadata loader override
|
|
242
216
|
self._ansible_pkg_path = to_native(os.path.dirname(to_bytes(sys.modules['ansible'].__file__)))
|
|
243
217
|
|
|
244
|
-
if isinstance(paths,
|
|
218
|
+
if isinstance(paths, str):
|
|
245
219
|
paths = [paths]
|
|
246
220
|
elif paths is None:
|
|
247
221
|
paths = []
|
|
@@ -326,7 +300,7 @@ class _AnsibleCollectionFinder:
|
|
|
326
300
|
return paths
|
|
327
301
|
|
|
328
302
|
def set_playbook_paths(self, playbook_paths):
|
|
329
|
-
if isinstance(playbook_paths,
|
|
303
|
+
if isinstance(playbook_paths, str):
|
|
330
304
|
playbook_paths = [playbook_paths]
|
|
331
305
|
|
|
332
306
|
# track visited paths; we have to preserve the dir order as-passed in case there are duplicate collections (first one wins)
|
|
@@ -412,19 +386,17 @@ class _AnsiblePathHookFinder:
|
|
|
412
386
|
# when called from a path_hook, find_module doesn't usually get the path arg, so this provides our context
|
|
413
387
|
self._pathctx = to_native(pathctx)
|
|
414
388
|
self._collection_finder = collection_finder
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
self._file_finder = None
|
|
389
|
+
# cache the native FileFinder (take advantage of its filesystem cache for future find/load requests)
|
|
390
|
+
self._file_finder = None
|
|
418
391
|
|
|
419
392
|
# class init is fun- this method has a self arg that won't get used
|
|
420
393
|
def _get_filefinder_path_hook(self=None):
|
|
421
394
|
_file_finder_hook = None
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
_file_finder_hook = _file_finder_hook[0]
|
|
395
|
+
# try to find the FileFinder hook to call for fallback path-based imports in Py3
|
|
396
|
+
_file_finder_hook = [ph for ph in sys.path_hooks if 'FileFinder' in repr(ph)]
|
|
397
|
+
if len(_file_finder_hook) != 1:
|
|
398
|
+
raise Exception('need exactly one FileFinder import hook (found {0})'.format(len(_file_finder_hook)))
|
|
399
|
+
_file_finder_hook = _file_finder_hook[0]
|
|
428
400
|
|
|
429
401
|
return _file_finder_hook
|
|
430
402
|
|
|
@@ -445,20 +417,16 @@ class _AnsiblePathHookFinder:
|
|
|
445
417
|
# out what we *shouldn't* be loading with the limited info it has. So we'll just delegate to the
|
|
446
418
|
# normal path-based loader as best we can to service it. This also allows us to take advantage of Python's
|
|
447
419
|
# built-in FS caching and byte-compilation for most things.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return None
|
|
457
|
-
|
|
458
|
-
return self._file_finder
|
|
420
|
+
# create or consult our cached file finder for this path
|
|
421
|
+
if not self._file_finder:
|
|
422
|
+
try:
|
|
423
|
+
self._file_finder = _AnsiblePathHookFinder._filefinder_path_hook(self._pathctx)
|
|
424
|
+
except ImportError:
|
|
425
|
+
# FUTURE: log at a high logging level? This is normal for things like python36.zip on the path, but
|
|
426
|
+
# might not be in some other situation...
|
|
427
|
+
return None
|
|
459
428
|
|
|
460
|
-
|
|
461
|
-
return pkgutil.ImpImporter(self._pathctx)
|
|
429
|
+
return self._file_finder
|
|
462
430
|
|
|
463
431
|
def find_module(self, fullname, path=None):
|
|
464
432
|
# we ignore the passed in path here- use what we got from the path hook init
|
|
@@ -1124,7 +1092,7 @@ class AnsibleCollectionRef:
|
|
|
1124
1092
|
|
|
1125
1093
|
def _get_collection_path(collection_name):
|
|
1126
1094
|
collection_name = to_native(collection_name)
|
|
1127
|
-
if not collection_name or not isinstance(collection_name,
|
|
1095
|
+
if not collection_name or not isinstance(collection_name, str) or len(collection_name.split('.')) != 2:
|
|
1128
1096
|
raise ValueError('collection_name must be a non-empty string of the form namespace.collection')
|
|
1129
1097
|
try:
|
|
1130
1098
|
collection_pkg = import_module('ansible_collections.' + collection_name)
|
|
@@ -1307,7 +1275,7 @@ def _iter_modules_impl(paths, prefix=''):
|
|
|
1307
1275
|
|
|
1308
1276
|
def _get_collection_metadata(collection_name):
|
|
1309
1277
|
collection_name = to_native(collection_name)
|
|
1310
|
-
if not collection_name or not isinstance(collection_name,
|
|
1278
|
+
if not collection_name or not isinstance(collection_name, str) or len(collection_name.split('.')) != 2:
|
|
1311
1279
|
raise ValueError('collection_name must be a non-empty string of the form namespace.collection')
|
|
1312
1280
|
|
|
1313
1281
|
try:
|
ansible/utils/display.py
CHANGED
|
@@ -33,7 +33,7 @@ import getpass
|
|
|
33
33
|
import io
|
|
34
34
|
import logging
|
|
35
35
|
import os
|
|
36
|
-
import
|
|
36
|
+
import secrets
|
|
37
37
|
import subprocess
|
|
38
38
|
import sys
|
|
39
39
|
import termios
|
|
@@ -154,27 +154,31 @@ logger = None
|
|
|
154
154
|
if getattr(C, 'DEFAULT_LOG_PATH'):
|
|
155
155
|
path = C.DEFAULT_LOG_PATH
|
|
156
156
|
if path and (os.path.exists(path) and os.access(path, os.W_OK)) or os.access(os.path.dirname(path), os.W_OK):
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
handler
|
|
164
|
-
|
|
157
|
+
if not os.path.isdir(path):
|
|
158
|
+
# NOTE: level is kept at INFO to avoid security disclosures caused by certain libraries when using DEBUG
|
|
159
|
+
logging.basicConfig(filename=path, level=logging.INFO, # DO NOT set to logging.DEBUG
|
|
160
|
+
format='%(asctime)s p=%(process)d u=%(user)s n=%(name)s %(levelname)s| %(message)s')
|
|
161
|
+
|
|
162
|
+
logger = logging.getLogger('ansible')
|
|
163
|
+
for handler in logging.root.handlers:
|
|
164
|
+
handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', [])))
|
|
165
|
+
handler.addFilter(FilterUserInjector())
|
|
166
|
+
else:
|
|
167
|
+
print(f"[WARNING]: DEFAULT_LOG_PATH can not be a directory '{path}', aborting", file=sys.stderr)
|
|
165
168
|
else:
|
|
166
|
-
print("[WARNING]: log file at
|
|
169
|
+
print(f"[WARNING]: log file at '{path}' is not writeable and we cannot create it, aborting\n", file=sys.stderr)
|
|
167
170
|
|
|
168
|
-
# map color to log levels
|
|
169
|
-
color_to_log_level = {C.
|
|
170
|
-
C.
|
|
171
|
+
# map color to log levels, in order of priority (low to high)
|
|
172
|
+
color_to_log_level = {C.COLOR_DEBUG: logging.DEBUG,
|
|
173
|
+
C.COLOR_VERBOSE: logging.INFO,
|
|
171
174
|
C.COLOR_OK: logging.INFO,
|
|
172
|
-
C.
|
|
173
|
-
C.COLOR_UNREACHABLE: logging.ERROR,
|
|
174
|
-
C.COLOR_DEBUG: logging.DEBUG,
|
|
175
|
+
C.COLOR_INCLUDED: logging.INFO,
|
|
175
176
|
C.COLOR_CHANGED: logging.INFO,
|
|
177
|
+
C.COLOR_SKIP: logging.WARNING,
|
|
176
178
|
C.COLOR_DEPRECATE: logging.WARNING,
|
|
177
|
-
C.
|
|
179
|
+
C.COLOR_WARN: logging.WARNING,
|
|
180
|
+
C.COLOR_UNREACHABLE: logging.ERROR,
|
|
181
|
+
C.COLOR_ERROR: logging.ERROR}
|
|
178
182
|
|
|
179
183
|
b_COW_PATHS = (
|
|
180
184
|
b"/usr/bin/cowsay",
|
|
@@ -310,8 +314,8 @@ class Display(metaclass=Singleton):
|
|
|
310
314
|
|
|
311
315
|
codecs.register_error('_replacing_warning_handler', self._replacing_warning_handler)
|
|
312
316
|
try:
|
|
313
|
-
sys.stdout.reconfigure(errors='_replacing_warning_handler')
|
|
314
|
-
sys.stderr.reconfigure(errors='_replacing_warning_handler')
|
|
317
|
+
sys.stdout.reconfigure(errors='_replacing_warning_handler') # type: ignore[union-attr]
|
|
318
|
+
sys.stderr.reconfigure(errors='_replacing_warning_handler') # type: ignore[union-attr]
|
|
315
319
|
except Exception as ex:
|
|
316
320
|
self.warning(f"failed to reconfigure stdout/stderr with custom encoding error handler: {ex}")
|
|
317
321
|
|
|
@@ -398,6 +402,7 @@ class Display(metaclass=Singleton):
|
|
|
398
402
|
screen_only: bool = False,
|
|
399
403
|
log_only: bool = False,
|
|
400
404
|
newline: bool = True,
|
|
405
|
+
caplevel: int | None = None,
|
|
401
406
|
) -> None:
|
|
402
407
|
""" Display a message to the user
|
|
403
408
|
|
|
@@ -424,7 +429,7 @@ class Display(metaclass=Singleton):
|
|
|
424
429
|
msg2 = msg2 + u'\n'
|
|
425
430
|
|
|
426
431
|
# Note: After Display() class is refactored need to update the log capture
|
|
427
|
-
# code in '
|
|
432
|
+
# code in 'cli/scripts/ansible_connection_cli_stub.py' (and other relevant places).
|
|
428
433
|
if not stderr:
|
|
429
434
|
fileobj = sys.stdout
|
|
430
435
|
else:
|
|
@@ -447,20 +452,28 @@ class Display(metaclass=Singleton):
|
|
|
447
452
|
# raise
|
|
448
453
|
|
|
449
454
|
if logger and not screen_only:
|
|
450
|
-
self._log(nocolor, color)
|
|
455
|
+
self._log(nocolor, color, caplevel)
|
|
451
456
|
|
|
452
457
|
def _log(self, msg: str, color: str | None = None, caplevel: int | None = None):
|
|
453
458
|
|
|
454
459
|
if logger and (caplevel is None or self.log_verbosity > caplevel):
|
|
455
460
|
msg2 = msg.lstrip('\n')
|
|
456
461
|
|
|
457
|
-
|
|
458
|
-
|
|
462
|
+
if caplevel is None or caplevel > 0:
|
|
463
|
+
lvl = logging.INFO
|
|
464
|
+
elif caplevel == -1:
|
|
465
|
+
lvl = logging.ERROR
|
|
466
|
+
elif caplevel == -2:
|
|
467
|
+
lvl = logging.WARNING
|
|
468
|
+
elif caplevel == -3:
|
|
469
|
+
lvl = logging.DEBUG
|
|
470
|
+
elif color:
|
|
459
471
|
# set logger level based on color (not great)
|
|
472
|
+
# but last resort and backwards compatible
|
|
460
473
|
try:
|
|
461
474
|
lvl = color_to_log_level[color]
|
|
462
475
|
except KeyError:
|
|
463
|
-
# this should not happen, but JIC
|
|
476
|
+
# this should not happen if mapping is updated with new color configs, but JIC
|
|
464
477
|
raise AnsibleAssertionError('Invalid color supplied to display: %s' % color)
|
|
465
478
|
|
|
466
479
|
# actually log
|
|
@@ -509,10 +522,10 @@ class Display(metaclass=Singleton):
|
|
|
509
522
|
@_meets_debug
|
|
510
523
|
@_proxy
|
|
511
524
|
def debug(self, msg: str, host: str | None = None) -> None:
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
525
|
+
prefix = "%6d %0.5f" % (os.getpid(), time.time())
|
|
526
|
+
if host is not None:
|
|
527
|
+
prefix += f" [{host}]"
|
|
528
|
+
self.display(f"{prefix}: {msg}", color=C.COLOR_DEBUG, caplevel=-3)
|
|
516
529
|
|
|
517
530
|
def get_deprecation_message(
|
|
518
531
|
self,
|
|
@@ -591,7 +604,7 @@ class Display(metaclass=Singleton):
|
|
|
591
604
|
new_msg = "\n[WARNING]: \n%s" % msg
|
|
592
605
|
|
|
593
606
|
if new_msg not in self._warns:
|
|
594
|
-
self.display(new_msg, color=C.COLOR_WARN, stderr=True)
|
|
607
|
+
self.display(new_msg, color=C.COLOR_WARN, stderr=True, caplevel=-2)
|
|
595
608
|
self._warns[new_msg] = 1
|
|
596
609
|
|
|
597
610
|
@_proxy
|
|
@@ -633,7 +646,7 @@ class Display(metaclass=Singleton):
|
|
|
633
646
|
if self.noncow:
|
|
634
647
|
thecow = self.noncow
|
|
635
648
|
if thecow == 'random':
|
|
636
|
-
thecow =
|
|
649
|
+
thecow = secrets.choice(list(self.cows_available))
|
|
637
650
|
runcmd.append(b'-f')
|
|
638
651
|
runcmd.append(to_bytes(thecow))
|
|
639
652
|
runcmd.append(to_bytes(msg))
|
|
@@ -650,7 +663,7 @@ class Display(metaclass=Singleton):
|
|
|
650
663
|
else:
|
|
651
664
|
new_msg = u"ERROR! %s" % msg
|
|
652
665
|
if new_msg not in self._errors:
|
|
653
|
-
self.display(new_msg, color=C.COLOR_ERROR, stderr=True)
|
|
666
|
+
self.display(new_msg, color=C.COLOR_ERROR, stderr=True, caplevel=-1)
|
|
654
667
|
self._errors[new_msg] = 1
|
|
655
668
|
|
|
656
669
|
@staticmethod
|
ansible/utils/jsonrpc.py
CHANGED
|
@@ -83,7 +83,7 @@ class JsonRpcServer(object):
|
|
|
83
83
|
result = to_text(result)
|
|
84
84
|
if not isinstance(result, text_type):
|
|
85
85
|
response["result_type"] = "pickle"
|
|
86
|
-
result = to_text(pickle.dumps(result,
|
|
86
|
+
result = to_text(pickle.dumps(result), errors='surrogateescape')
|
|
87
87
|
response['result'] = result
|
|
88
88
|
return response
|
|
89
89
|
|
ansible/utils/listify.py
CHANGED
|
@@ -27,11 +27,7 @@ display = Display()
|
|
|
27
27
|
__all__ = ['listify_lookup_plugin_terms']
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def listify_lookup_plugin_terms(terms, templar,
|
|
31
|
-
|
|
32
|
-
if loader is not None:
|
|
33
|
-
display.deprecated('"listify_lookup_plugin_terms" does not use "dataloader" anymore, the ability to pass it in will be removed in future versions.',
|
|
34
|
-
version='2.18')
|
|
30
|
+
def listify_lookup_plugin_terms(terms, templar, fail_on_undefined=True, convert_bare=False):
|
|
35
31
|
|
|
36
32
|
if isinstance(terms, string_types):
|
|
37
33
|
terms = templar.template(terms.strip(), convert_bare=convert_bare, fail_on_undefined=fail_on_undefined)
|
ansible/utils/path.py
CHANGED
|
@@ -33,6 +33,9 @@ def unfrackpath(path, follow=True, basedir=None):
|
|
|
33
33
|
|
|
34
34
|
:arg path: A byte or text string representing a path to be canonicalized
|
|
35
35
|
:arg follow: A boolean to indicate of symlinks should be resolved or not
|
|
36
|
+
:arg basedir: A byte string, text string, PathLike object, or `None`
|
|
37
|
+
representing where a relative path should be resolved from.
|
|
38
|
+
`None` will be substituted for the current working directory.
|
|
36
39
|
:raises UnicodeDecodeError: If the canonicalized version of the path
|
|
37
40
|
contains non-utf8 byte sequences.
|
|
38
41
|
:rtype: A text string (unicode on pyyhon2, str on python3).
|
ansible/utils/vars.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import keyword
|
|
21
|
-
import
|
|
21
|
+
import secrets
|
|
22
22
|
import uuid
|
|
23
23
|
|
|
24
24
|
from collections.abc import MutableMapping, MutableSequence
|
|
@@ -32,12 +32,10 @@ from ansible.module_utils.common.text.converters import to_native, to_text
|
|
|
32
32
|
from ansible.parsing.splitter import parse_kv
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
ADDITIONAL_PY2_KEYWORDS = frozenset(("True", "False", "None"))
|
|
36
|
-
|
|
37
35
|
_MAXSIZE = 2 ** 32
|
|
38
36
|
cur_id = 0
|
|
39
37
|
node_mac = ("%012x" % uuid.getnode())[:12]
|
|
40
|
-
random_int = ("%08x" %
|
|
38
|
+
random_int = ("%08x" % secrets.randbelow(_MAXSIZE))[:8]
|
|
41
39
|
|
|
42
40
|
|
|
43
41
|
def get_unique_id():
|
|
@@ -237,7 +235,22 @@ def load_options_vars(version):
|
|
|
237
235
|
return load_options_vars.options_vars
|
|
238
236
|
|
|
239
237
|
|
|
240
|
-
def
|
|
238
|
+
def isidentifier(ident):
|
|
239
|
+
"""Determine if string is valid identifier.
|
|
240
|
+
|
|
241
|
+
The purpose of this function is to be used to validate any variables created in
|
|
242
|
+
a play to be valid Python identifiers and to not conflict with Python keywords
|
|
243
|
+
to prevent unexpected behavior. Since Python 2 and Python 3 differ in what
|
|
244
|
+
a valid identifier is, this function unifies the validation so playbooks are
|
|
245
|
+
portable between the two. The following changes were made:
|
|
246
|
+
|
|
247
|
+
* disallow non-ascii characters (Python 3 allows for them as opposed to Python 2)
|
|
248
|
+
|
|
249
|
+
:arg ident: A text string of identifier to check. Note: It is callers
|
|
250
|
+
responsibility to convert ident to text if it is not already.
|
|
251
|
+
|
|
252
|
+
Originally posted at https://stackoverflow.com/a/29586366
|
|
253
|
+
"""
|
|
241
254
|
if not isinstance(ident, string_types):
|
|
242
255
|
return False
|
|
243
256
|
|
|
@@ -251,25 +264,3 @@ def _isidentifier_PY3(ident):
|
|
|
251
264
|
return False
|
|
252
265
|
|
|
253
266
|
return True
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
isidentifier = _isidentifier_PY3
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
isidentifier.__doc__ = """Determine if string is valid identifier.
|
|
260
|
-
|
|
261
|
-
The purpose of this function is to be used to validate any variables created in
|
|
262
|
-
a play to be valid Python identifiers and to not conflict with Python keywords
|
|
263
|
-
to prevent unexpected behavior. Since Python 2 and Python 3 differ in what
|
|
264
|
-
a valid identifier is, this function unifies the validation so playbooks are
|
|
265
|
-
portable between the two. The following changes were made:
|
|
266
|
-
|
|
267
|
-
* disallow non-ascii characters (Python 3 allows for them as opposed to Python 2)
|
|
268
|
-
* True, False and None are reserved keywords (these are reserved keywords
|
|
269
|
-
on Python 3 as opposed to Python 2)
|
|
270
|
-
|
|
271
|
-
:arg ident: A text string of identifier to check. Note: It is callers
|
|
272
|
-
responsibility to convert ident to text if it is not already.
|
|
273
|
-
|
|
274
|
-
Originally posted at http://stackoverflow.com/a/29586366
|
|
275
|
-
"""
|
ansible/vars/manager.py
CHANGED
|
@@ -27,16 +27,14 @@ from hashlib import sha1
|
|
|
27
27
|
from jinja2.exceptions import UndefinedError
|
|
28
28
|
|
|
29
29
|
from ansible import constants as C
|
|
30
|
-
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleFileNotFound, AnsibleAssertionError
|
|
30
|
+
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleFileNotFound, AnsibleAssertionError
|
|
31
31
|
from ansible.inventory.host import Host
|
|
32
32
|
from ansible.inventory.helpers import sort_groups, get_group_vars
|
|
33
33
|
from ansible.module_utils.common.text.converters import to_text
|
|
34
|
-
from ansible.module_utils.six import text_type
|
|
35
|
-
from ansible.plugins.loader import lookup_loader
|
|
34
|
+
from ansible.module_utils.six import text_type
|
|
36
35
|
from ansible.vars.fact_cache import FactCache
|
|
37
36
|
from ansible.template import Templar
|
|
38
37
|
from ansible.utils.display import Display
|
|
39
|
-
from ansible.utils.listify import listify_lookup_plugin_terms
|
|
40
38
|
from ansible.utils.vars import combine_vars, load_extra_vars, load_options_vars
|
|
41
39
|
from ansible.utils.unsafe_proxy import wrap_var
|
|
42
40
|
from ansible.vars.clean import namespace_facts, clean_facts
|
|
@@ -161,6 +159,11 @@ class VariableManager:
|
|
|
161
159
|
on the functionality they provide. These arguments may be removed at a later date without a deprecation
|
|
162
160
|
period and without warning.
|
|
163
161
|
'''
|
|
162
|
+
if include_delegate_to:
|
|
163
|
+
display.deprecated(
|
|
164
|
+
"`VariableManager.get_vars`'s argument `include_delegate_to` has no longer any effect.",
|
|
165
|
+
version="2.19",
|
|
166
|
+
)
|
|
164
167
|
|
|
165
168
|
display.debug("in VariableManager get_vars()")
|
|
166
169
|
|
|
@@ -363,11 +366,6 @@ class VariableManager:
|
|
|
363
366
|
continue
|
|
364
367
|
except AnsibleParserError:
|
|
365
368
|
raise
|
|
366
|
-
else:
|
|
367
|
-
# if include_delegate_to is set to False or we don't have a host, we ignore the missing
|
|
368
|
-
# vars file here because we're working on a delegated host or require host vars, see NOTE above
|
|
369
|
-
if include_delegate_to and host:
|
|
370
|
-
raise AnsibleFileNotFound("vars file %s was not found" % vars_file_item)
|
|
371
369
|
except (UndefinedError, AnsibleUndefinedVariable):
|
|
372
370
|
if host is not None and self._fact_cache.get(host.name, dict()).get('module_setup') and task is not None:
|
|
373
371
|
raise AnsibleUndefinedVariable("an undefined variable was found when attempting to template the vars_files item '%s'"
|
|
@@ -430,11 +428,6 @@ class VariableManager:
|
|
|
430
428
|
# has to be copy, otherwise recursive ref
|
|
431
429
|
all_vars['vars'] = all_vars.copy()
|
|
432
430
|
|
|
433
|
-
# if we have a host and task and we're delegating to another host,
|
|
434
|
-
# figure out the variables for that host now so we don't have to rely on host vars later
|
|
435
|
-
if task and host and task.delegate_to is not None and include_delegate_to:
|
|
436
|
-
all_vars['ansible_delegated_vars'], all_vars['_ansible_loop_cache'] = self._get_delegated_vars(play, task, all_vars)
|
|
437
|
-
|
|
438
431
|
display.debug("done with get_vars()")
|
|
439
432
|
if C.DEFAULT_DEBUG:
|
|
440
433
|
# Use VarsWithSources wrapper class to display var sources
|
|
@@ -546,7 +539,6 @@ class VariableManager:
|
|
|
546
539
|
play=task.get_play(),
|
|
547
540
|
host=delegated_host,
|
|
548
541
|
task=task,
|
|
549
|
-
include_delegate_to=False,
|
|
550
542
|
include_hostvars=True,
|
|
551
543
|
)
|
|
552
544
|
}
|
|
@@ -554,141 +546,6 @@ class VariableManager:
|
|
|
554
546
|
|
|
555
547
|
return delegated_vars, delegated_host_name
|
|
556
548
|
|
|
557
|
-
def _get_delegated_vars(self, play, task, existing_variables):
|
|
558
|
-
# This method has a lot of code copied from ``TaskExecutor._get_loop_items``
|
|
559
|
-
# if this is failing, and ``TaskExecutor._get_loop_items`` is not
|
|
560
|
-
# then more will have to be copied here.
|
|
561
|
-
# TODO: dedupe code here and with ``TaskExecutor._get_loop_items``
|
|
562
|
-
# this may be possible once we move pre-processing pre fork
|
|
563
|
-
|
|
564
|
-
if not hasattr(task, 'loop'):
|
|
565
|
-
# This "task" is not a Task, so we need to skip it
|
|
566
|
-
return {}, None
|
|
567
|
-
|
|
568
|
-
display.deprecated(
|
|
569
|
-
'Getting delegated variables via get_vars is no longer used, and is handled within the TaskExecutor.',
|
|
570
|
-
version='2.18',
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
# we unfortunately need to template the delegate_to field here,
|
|
574
|
-
# as we're fetching vars before post_validate has been called on
|
|
575
|
-
# the task that has been passed in
|
|
576
|
-
vars_copy = existing_variables.copy()
|
|
577
|
-
|
|
578
|
-
# get search path for this task to pass to lookup plugins
|
|
579
|
-
vars_copy['ansible_search_path'] = task.get_search_path()
|
|
580
|
-
|
|
581
|
-
# ensure basedir is always in (dwim already searches here but we need to display it)
|
|
582
|
-
if self._loader.get_basedir() not in vars_copy['ansible_search_path']:
|
|
583
|
-
vars_copy['ansible_search_path'].append(self._loader.get_basedir())
|
|
584
|
-
|
|
585
|
-
templar = Templar(loader=self._loader, variables=vars_copy)
|
|
586
|
-
|
|
587
|
-
items = []
|
|
588
|
-
has_loop = True
|
|
589
|
-
if task.loop_with is not None:
|
|
590
|
-
if task.loop_with in lookup_loader:
|
|
591
|
-
fail = True
|
|
592
|
-
if task.loop_with == 'first_found':
|
|
593
|
-
# first_found loops are special. If the item is undefined then we want to fall through to the next
|
|
594
|
-
fail = False
|
|
595
|
-
try:
|
|
596
|
-
loop_terms = listify_lookup_plugin_terms(terms=task.loop, templar=templar, fail_on_undefined=fail, convert_bare=False)
|
|
597
|
-
|
|
598
|
-
if not fail:
|
|
599
|
-
loop_terms = [t for t in loop_terms if not templar.is_template(t)]
|
|
600
|
-
|
|
601
|
-
mylookup = lookup_loader.get(task.loop_with, loader=self._loader, templar=templar)
|
|
602
|
-
|
|
603
|
-
# give lookup task 'context' for subdir (mostly needed for first_found)
|
|
604
|
-
for subdir in ['template', 'var', 'file']: # TODO: move this to constants?
|
|
605
|
-
if subdir in task.action:
|
|
606
|
-
break
|
|
607
|
-
setattr(mylookup, '_subdir', subdir + 's')
|
|
608
|
-
|
|
609
|
-
items = wrap_var(mylookup.run(terms=loop_terms, variables=vars_copy))
|
|
610
|
-
|
|
611
|
-
except AnsibleTemplateError:
|
|
612
|
-
# This task will be skipped later due to this, so we just setup
|
|
613
|
-
# a dummy array for the later code so it doesn't fail
|
|
614
|
-
items = [None]
|
|
615
|
-
else:
|
|
616
|
-
raise AnsibleError("Failed to find the lookup named '%s' in the available lookup plugins" % task.loop_with)
|
|
617
|
-
elif task.loop is not None:
|
|
618
|
-
try:
|
|
619
|
-
items = templar.template(task.loop)
|
|
620
|
-
except AnsibleTemplateError:
|
|
621
|
-
# This task will be skipped later due to this, so we just setup
|
|
622
|
-
# a dummy array for the later code so it doesn't fail
|
|
623
|
-
items = [None]
|
|
624
|
-
else:
|
|
625
|
-
has_loop = False
|
|
626
|
-
items = [None]
|
|
627
|
-
|
|
628
|
-
# since host can change per loop, we keep dict per host name resolved
|
|
629
|
-
delegated_host_vars = dict()
|
|
630
|
-
item_var = getattr(task.loop_control, 'loop_var', 'item')
|
|
631
|
-
cache_items = False
|
|
632
|
-
for item in items:
|
|
633
|
-
# update the variables with the item value for templating, in case we need it
|
|
634
|
-
if item is not None:
|
|
635
|
-
vars_copy[item_var] = item
|
|
636
|
-
|
|
637
|
-
templar.available_variables = vars_copy
|
|
638
|
-
delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
|
|
639
|
-
if delegated_host_name != task.delegate_to:
|
|
640
|
-
cache_items = True
|
|
641
|
-
if delegated_host_name is None:
|
|
642
|
-
raise AnsibleError(message="Undefined delegate_to host for task:", obj=task._ds)
|
|
643
|
-
if not isinstance(delegated_host_name, string_types):
|
|
644
|
-
raise AnsibleError(message="the field 'delegate_to' has an invalid type (%s), and could not be"
|
|
645
|
-
" converted to a string type." % type(delegated_host_name), obj=task._ds)
|
|
646
|
-
|
|
647
|
-
if delegated_host_name in delegated_host_vars:
|
|
648
|
-
# no need to repeat ourselves, as the delegate_to value
|
|
649
|
-
# does not appear to be tied to the loop item variable
|
|
650
|
-
continue
|
|
651
|
-
|
|
652
|
-
# now try to find the delegated-to host in inventory, or failing that,
|
|
653
|
-
# create a new host on the fly so we can fetch variables for it
|
|
654
|
-
delegated_host = None
|
|
655
|
-
if self._inventory is not None:
|
|
656
|
-
delegated_host = self._inventory.get_host(delegated_host_name)
|
|
657
|
-
# try looking it up based on the address field, and finally
|
|
658
|
-
# fall back to creating a host on the fly to use for the var lookup
|
|
659
|
-
if delegated_host is None:
|
|
660
|
-
for h in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
|
|
661
|
-
# check if the address matches, or if both the delegated_to host
|
|
662
|
-
# and the current host are in the list of localhost aliases
|
|
663
|
-
if h.address == delegated_host_name:
|
|
664
|
-
delegated_host = h
|
|
665
|
-
break
|
|
666
|
-
else:
|
|
667
|
-
delegated_host = Host(name=delegated_host_name)
|
|
668
|
-
else:
|
|
669
|
-
delegated_host = Host(name=delegated_host_name)
|
|
670
|
-
|
|
671
|
-
# now we go fetch the vars for the delegated-to host and save them in our
|
|
672
|
-
# master dictionary of variables to be used later in the TaskExecutor/PlayContext
|
|
673
|
-
delegated_host_vars[delegated_host_name] = self.get_vars(
|
|
674
|
-
play=play,
|
|
675
|
-
host=delegated_host,
|
|
676
|
-
task=task,
|
|
677
|
-
include_delegate_to=False,
|
|
678
|
-
include_hostvars=True,
|
|
679
|
-
)
|
|
680
|
-
delegated_host_vars[delegated_host_name]['inventory_hostname'] = vars_copy.get('inventory_hostname')
|
|
681
|
-
|
|
682
|
-
_ansible_loop_cache = None
|
|
683
|
-
if has_loop and cache_items:
|
|
684
|
-
# delegate_to templating produced a change, so we will cache the templated items
|
|
685
|
-
# in a special private hostvar
|
|
686
|
-
# this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
|
|
687
|
-
# which may reprocess the loop
|
|
688
|
-
_ansible_loop_cache = items
|
|
689
|
-
|
|
690
|
-
return delegated_host_vars, _ansible_loop_cache
|
|
691
|
-
|
|
692
549
|
def clear_facts(self, hostname):
|
|
693
550
|
'''
|
|
694
551
|
Clears the facts for a host
|
ansible/vars/plugins.py
CHANGED
|
@@ -90,7 +90,7 @@ def get_vars_from_path(loader, path, entities, stage):
|
|
|
90
90
|
|
|
91
91
|
collection = '.' in plugin.ansible_name and not plugin.ansible_name.startswith('ansible.builtin.')
|
|
92
92
|
# Warn if a collection plugin has REQUIRES_ENABLED because it has no effect.
|
|
93
|
-
if collection and
|
|
93
|
+
if collection and hasattr(plugin, 'REQUIRES_ENABLED'):
|
|
94
94
|
display.warning(
|
|
95
95
|
"Vars plugins in collections must be enabled to be loaded, REQUIRES_ENABLED is not supported. "
|
|
96
96
|
"This should be removed from the plugin %s." % plugin.ansible_name
|