ansible-core 2.17.6rc1__py3-none-any.whl → 2.18.0__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.

Files changed (325) hide show
  1. ansible/__main__.py +2 -17
  2. ansible/cli/__init__.py +3 -15
  3. ansible/cli/config.py +187 -24
  4. ansible/cli/console.py +1 -1
  5. ansible/cli/doc.py +38 -16
  6. ansible/cli/galaxy.py +3 -49
  7. ansible/cli/inventory.py +2 -2
  8. ansible/cli/pull.py +2 -2
  9. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -10
  10. ansible/config/base.yml +127 -57
  11. ansible/config/manager.py +89 -11
  12. ansible/constants.py +32 -9
  13. ansible/errors/__init__.py +5 -0
  14. ansible/executor/interpreter_discovery.py +1 -1
  15. ansible/executor/play_iterator.py +16 -0
  16. ansible/executor/playbook_executor.py +1 -4
  17. ansible/executor/powershell/become_wrapper.ps1 +4 -5
  18. ansible/executor/powershell/bootstrap_wrapper.ps1 +2 -3
  19. ansible/executor/powershell/exec_wrapper.ps1 +1 -1
  20. ansible/executor/powershell/module_manifest.py +2 -2
  21. ansible/executor/task_executor.py +50 -39
  22. ansible/executor/task_queue_manager.py +1 -1
  23. ansible/executor/task_result.py +1 -1
  24. ansible/galaxy/api.py +3 -4
  25. ansible/galaxy/collection/__init__.py +21 -10
  26. ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
  27. ansible/galaxy/collection/galaxy_api_proxy.py +10 -16
  28. ansible/galaxy/collection/gpg.py +17 -23
  29. ansible/galaxy/data/COPYING +7 -0
  30. ansible/galaxy/data/apb/Dockerfile.j2 +1 -0
  31. ansible/galaxy/data/apb/Makefile.j2 +1 -0
  32. ansible/galaxy/data/apb/README.md +7 -3
  33. ansible/galaxy/data/apb/apb.yml.j2 +1 -0
  34. ansible/galaxy/data/apb/defaults/main.yml.j2 +1 -0
  35. ansible/galaxy/data/apb/handlers/main.yml.j2 +1 -0
  36. ansible/galaxy/data/apb/meta/main.yml.j2 +1 -0
  37. ansible/galaxy/data/apb/playbooks/deprovision.yml.j2 +1 -0
  38. ansible/galaxy/data/apb/playbooks/provision.yml.j2 +1 -0
  39. ansible/galaxy/data/apb/tasks/main.yml.j2 +1 -0
  40. ansible/galaxy/data/apb/tests/ansible.cfg +1 -0
  41. ansible/galaxy/data/apb/tests/inventory +1 -0
  42. ansible/galaxy/data/apb/tests/test.yml.j2 +1 -0
  43. ansible/galaxy/data/apb/vars/main.yml.j2 +1 -0
  44. ansible/galaxy/data/collections_galaxy_meta.yml +1 -0
  45. ansible/galaxy/data/container/defaults/main.yml.j2 +1 -0
  46. ansible/galaxy/data/container/handlers/main.yml.j2 +1 -0
  47. ansible/galaxy/data/container/meta/container.yml.j2 +1 -0
  48. ansible/galaxy/data/container/meta/main.yml.j2 +1 -0
  49. ansible/galaxy/data/container/tasks/main.yml.j2 +1 -0
  50. ansible/galaxy/data/container/tests/ansible.cfg +1 -0
  51. ansible/galaxy/data/container/tests/inventory +1 -0
  52. ansible/galaxy/data/container/tests/test.yml.j2 +1 -0
  53. ansible/galaxy/data/container/vars/main.yml.j2 +1 -0
  54. ansible/galaxy/data/default/collection/README.md.j2 +1 -0
  55. ansible/galaxy/data/default/collection/galaxy.yml.j2 +1 -0
  56. ansible/galaxy/data/default/collection/meta/runtime.yml +1 -0
  57. ansible/galaxy/data/default/collection/plugins/README.md.j2 +1 -0
  58. ansible/galaxy/data/default/role/defaults/main.yml.j2 +1 -0
  59. ansible/galaxy/data/default/role/handlers/main.yml.j2 +1 -0
  60. ansible/galaxy/data/default/role/meta/main.yml.j2 +1 -0
  61. ansible/galaxy/data/default/role/tasks/main.yml.j2 +1 -0
  62. ansible/galaxy/data/default/role/tests/inventory +1 -0
  63. ansible/galaxy/data/default/role/tests/test.yml.j2 +1 -0
  64. ansible/galaxy/data/default/role/vars/main.yml.j2 +1 -0
  65. ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -0
  66. ansible/galaxy/data/network/defaults/main.yml.j2 +1 -0
  67. ansible/galaxy/data/network/library/example_command.py.j2 +1 -0
  68. ansible/galaxy/data/network/library/example_config.py.j2 +1 -0
  69. ansible/galaxy/data/network/library/example_facts.py.j2 +1 -0
  70. ansible/galaxy/data/network/meta/main.yml.j2 +1 -0
  71. ansible/galaxy/data/network/module_utils/example.py.j2 +1 -0
  72. ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -0
  73. ansible/galaxy/data/network/tasks/main.yml.j2 +1 -0
  74. ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -0
  75. ansible/galaxy/data/network/tests/inventory +1 -0
  76. ansible/galaxy/data/network/tests/test.yml.j2 +1 -0
  77. ansible/galaxy/data/network/vars/main.yml.j2 +1 -0
  78. ansible/galaxy/dependency_resolution/providers.py +3 -3
  79. ansible/galaxy/role.py +1 -1
  80. ansible/galaxy/token.py +20 -8
  81. ansible/keyword_desc.yml +1 -1
  82. ansible/module_utils/_internal/__init__.py +0 -0
  83. ansible/module_utils/_internal/_concurrent/__init__.py +0 -0
  84. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +28 -0
  85. ansible/module_utils/_internal/_concurrent/_futures.py +21 -0
  86. ansible/module_utils/ansible_release.py +2 -2
  87. ansible/module_utils/api.py +2 -2
  88. ansible/module_utils/basic.py +8 -8
  89. ansible/module_utils/common/collections.py +1 -1
  90. ansible/module_utils/common/file.py +0 -6
  91. ansible/module_utils/common/process.py +22 -9
  92. ansible/module_utils/common/text/converters.py +5 -8
  93. ansible/module_utils/common/text/formatters.py +20 -4
  94. ansible/module_utils/common/validation.py +33 -25
  95. ansible/module_utils/compat/paramiko.py +6 -1
  96. ansible/module_utils/compat/selinux.py +2 -2
  97. ansible/module_utils/connection.py +8 -24
  98. ansible/module_utils/csharp/Ansible.Become.cs +14 -25
  99. ansible/module_utils/csharp/Ansible.Process.cs +1 -1
  100. ansible/module_utils/distro/__init__.py +1 -1
  101. ansible/module_utils/distro/_distro.py +8 -4
  102. ansible/module_utils/facts/collector.py +2 -0
  103. ansible/module_utils/facts/default_collectors.py +3 -1
  104. ansible/module_utils/facts/hardware/aix.py +54 -52
  105. ansible/module_utils/facts/hardware/darwin.py +37 -34
  106. ansible/module_utils/facts/hardware/freebsd.py +55 -15
  107. ansible/module_utils/facts/hardware/hpux.py +3 -0
  108. ansible/module_utils/facts/hardware/linux.py +101 -57
  109. ansible/module_utils/facts/hardware/netbsd.py +3 -0
  110. ansible/module_utils/facts/hardware/openbsd.py +4 -1
  111. ansible/module_utils/facts/hardware/sunos.py +7 -1
  112. ansible/module_utils/facts/network/aix.py +16 -17
  113. ansible/module_utils/facts/network/fc_wwn.py +4 -1
  114. ansible/module_utils/facts/network/hpux.py +21 -4
  115. ansible/module_utils/facts/network/iscsi.py +7 -8
  116. ansible/module_utils/facts/network/linux.py +0 -2
  117. ansible/module_utils/facts/other/facter.py +9 -4
  118. ansible/module_utils/facts/other/ohai.py +5 -5
  119. ansible/module_utils/facts/packages.py +49 -7
  120. ansible/module_utils/facts/sysctl.py +33 -31
  121. ansible/module_utils/facts/system/distribution.py +1 -1
  122. ansible/module_utils/facts/system/local.py +12 -22
  123. ansible/module_utils/facts/system/service_mgr.py +3 -1
  124. ansible/module_utils/facts/system/systemd.py +47 -0
  125. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  126. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  127. ansible/module_utils/splitter.py +1 -1
  128. ansible/modules/add_host.py +1 -1
  129. ansible/modules/apt.py +43 -32
  130. ansible/modules/apt_key.py +6 -6
  131. ansible/modules/apt_repository.py +23 -14
  132. ansible/modules/assemble.py +7 -2
  133. ansible/modules/assert.py +4 -4
  134. ansible/modules/blockinfile.py +3 -6
  135. ansible/modules/command.py +1 -1
  136. ansible/modules/copy.py +4 -4
  137. ansible/modules/cron.py +13 -10
  138. ansible/modules/deb822_repository.py +16 -17
  139. ansible/modules/debconf.py +9 -9
  140. ansible/modules/debug.py +1 -1
  141. ansible/modules/dnf.py +79 -164
  142. ansible/modules/dnf5.py +54 -29
  143. ansible/modules/dpkg_selections.py +2 -2
  144. ansible/modules/expect.py +2 -2
  145. ansible/modules/fetch.py +2 -2
  146. ansible/modules/file.py +5 -3
  147. ansible/modules/find.py +40 -12
  148. ansible/modules/gather_facts.py +4 -2
  149. ansible/modules/get_url.py +29 -24
  150. ansible/modules/git.py +35 -35
  151. ansible/modules/group.py +71 -1
  152. ansible/modules/hostname.py +2 -4
  153. ansible/modules/include_vars.py +5 -5
  154. ansible/modules/iptables.py +13 -16
  155. ansible/modules/known_hosts.py +16 -13
  156. ansible/modules/lineinfile.py +1 -4
  157. ansible/modules/meta.py +6 -1
  158. ansible/modules/mount_facts.py +651 -0
  159. ansible/modules/package_facts.py +63 -80
  160. ansible/modules/pause.py +4 -3
  161. ansible/modules/pip.py +14 -14
  162. ansible/modules/replace.py +1 -4
  163. ansible/modules/rpm_key.py +31 -11
  164. ansible/modules/service.py +8 -8
  165. ansible/modules/service_facts.py +20 -5
  166. ansible/modules/set_stats.py +1 -1
  167. ansible/modules/setup.py +3 -3
  168. ansible/modules/stat.py +3 -3
  169. ansible/modules/subversion.py +1 -1
  170. ansible/modules/systemd.py +16 -10
  171. ansible/modules/systemd_service.py +16 -10
  172. ansible/modules/sysvinit.py +4 -4
  173. ansible/modules/unarchive.py +35 -22
  174. ansible/modules/uri.py +24 -18
  175. ansible/modules/user.py +145 -12
  176. ansible/modules/validate_argument_spec.py +3 -3
  177. ansible/modules/wait_for_connection.py +2 -1
  178. ansible/modules/yum_repository.py +136 -179
  179. ansible/parsing/dataloader.py +2 -2
  180. ansible/parsing/mod_args.py +11 -10
  181. ansible/parsing/vault/__init__.py +8 -3
  182. ansible/parsing/yaml/constructor.py +10 -8
  183. ansible/parsing/yaml/objects.py +1 -1
  184. ansible/playbook/base.py +12 -23
  185. ansible/playbook/helpers.py +4 -0
  186. ansible/playbook/loop_control.py +8 -0
  187. ansible/playbook/play.py +4 -22
  188. ansible/playbook/play_context.py +0 -16
  189. ansible/playbook/playbook_include.py +2 -2
  190. ansible/playbook/role/__init__.py +2 -2
  191. ansible/plugins/__init__.py +2 -0
  192. ansible/plugins/action/__init__.py +7 -9
  193. ansible/plugins/action/dnf.py +7 -5
  194. ansible/plugins/action/package.py +5 -4
  195. ansible/plugins/action/reboot.py +2 -2
  196. ansible/plugins/become/__init__.py +1 -1
  197. ansible/plugins/callback/__init__.py +44 -3
  198. ansible/plugins/callback/default.py +1 -1
  199. ansible/plugins/cliconf/__init__.py +1 -1
  200. ansible/plugins/connection/paramiko_ssh.py +2 -80
  201. ansible/plugins/connection/psrp.py +33 -82
  202. ansible/plugins/connection/ssh.py +0 -8
  203. ansible/plugins/connection/winrm.py +46 -1
  204. ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
  205. ansible/plugins/doc_fragments/constructed.py +10 -10
  206. ansible/plugins/doc_fragments/default_callback.py +8 -8
  207. ansible/plugins/doc_fragments/files.py +5 -5
  208. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  209. ansible/plugins/doc_fragments/result_format_callback.py +6 -6
  210. ansible/plugins/doc_fragments/return_common.py +1 -1
  211. ansible/plugins/doc_fragments/shell_common.py +2 -10
  212. ansible/plugins/doc_fragments/shell_windows.py +0 -9
  213. ansible/plugins/doc_fragments/url.py +2 -2
  214. ansible/plugins/doc_fragments/url_windows.py +4 -5
  215. ansible/plugins/doc_fragments/validate.py +1 -1
  216. ansible/plugins/filter/core.py +2 -0
  217. ansible/plugins/filter/human_to_bytes.yml +9 -0
  218. ansible/plugins/filter/password_hash.yml +1 -1
  219. ansible/plugins/filter/strftime.yml +1 -1
  220. ansible/plugins/filter/to_nice_json.yml +7 -3
  221. ansible/plugins/filter/to_uuid.yml +1 -1
  222. ansible/plugins/inventory/script.py +1 -1
  223. ansible/plugins/list.py +1 -1
  224. ansible/plugins/loader.py +0 -11
  225. ansible/plugins/lookup/config.py +1 -1
  226. ansible/plugins/lookup/csvfile.py +21 -9
  227. ansible/plugins/lookup/env.py +8 -9
  228. ansible/plugins/lookup/ini.py +10 -1
  229. ansible/plugins/lookup/random_choice.py +2 -2
  230. ansible/plugins/lookup/url.py +7 -2
  231. ansible/plugins/shell/__init__.py +15 -20
  232. ansible/plugins/shell/powershell.py +9 -6
  233. ansible/plugins/strategy/__init__.py +16 -7
  234. ansible/plugins/test/core.py +23 -1
  235. ansible/plugins/test/issubset.yml +1 -1
  236. ansible/plugins/test/subset.yml +1 -1
  237. ansible/plugins/test/timedout.yml +20 -0
  238. ansible/plugins/test/vault_encrypted.yml +6 -6
  239. ansible/plugins/test/vaulted_file.yml +19 -0
  240. ansible/release.py +2 -2
  241. ansible/template/__init__.py +3 -8
  242. ansible/utils/collection_loader/_collection_finder.py +23 -55
  243. ansible/utils/display.py +44 -31
  244. ansible/utils/jsonrpc.py +1 -1
  245. ansible/utils/listify.py +1 -5
  246. ansible/utils/path.py +3 -0
  247. ansible/utils/vars.py +18 -27
  248. ansible/vars/manager.py +7 -150
  249. ansible/vars/plugins.py +1 -1
  250. ansible_core-2.18.0.dist-info/Apache-License.txt +202 -0
  251. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.dist-info}/METADATA +36 -23
  252. ansible_core-2.18.0.dist-info/MIT-license.txt +14 -0
  253. ansible_core-2.18.0.dist-info/PSF-license.txt +48 -0
  254. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.dist-info}/RECORD +316 -311
  255. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.dist-info}/entry_points.txt +1 -1
  256. ansible_core-2.18.0.dist-info/simplified_bsd.txt +8 -0
  257. ansible_test/_data/completion/docker.txt +7 -7
  258. ansible_test/_data/completion/remote.txt +5 -4
  259. ansible_test/_data/completion/windows.txt +4 -4
  260. ansible_test/_data/requirements/ansible-test.txt +1 -2
  261. ansible_test/_data/requirements/constraints.txt +1 -2
  262. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  263. ansible_test/_data/requirements/sanity.changelog.in +1 -1
  264. ansible_test/_data/requirements/sanity.changelog.txt +4 -4
  265. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  266. ansible_test/_data/requirements/sanity.import.txt +1 -1
  267. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
  268. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  269. ansible_test/_data/requirements/sanity.pylint.txt +6 -8
  270. ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -2
  271. ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
  272. ansible_test/_data/requirements/sanity.yamllint.in +1 -0
  273. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  274. ansible_test/_internal/ansible_util.py +8 -35
  275. ansible_test/_internal/ci/azp.py +1 -1
  276. ansible_test/_internal/classification/__init__.py +0 -2
  277. ansible_test/_internal/cli/parsers/key_value_parsers.py +3 -0
  278. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -1
  279. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  280. ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
  281. ansible_test/_internal/commands/sanity/__init__.py +96 -19
  282. ansible_test/_internal/commands/sanity/pylint.py +20 -24
  283. ansible_test/_internal/completion.py +2 -0
  284. ansible_test/_internal/constants.py +0 -1
  285. ansible_test/_internal/coverage_util.py +1 -2
  286. ansible_test/_internal/docker_util.py +1 -1
  287. ansible_test/_internal/encoding.py +4 -4
  288. ansible_test/_internal/host_configs.py +10 -0
  289. ansible_test/_internal/host_profiles.py +9 -13
  290. ansible_test/_internal/pypi_proxy.py +1 -1
  291. ansible_test/_internal/python_requirements.py +5 -14
  292. ansible_test/_internal/timeout.py +1 -1
  293. ansible_test/_internal/util.py +40 -0
  294. ansible_test/_internal/util_common.py +5 -1
  295. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
  296. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
  297. ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
  298. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +5 -0
  299. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +5 -0
  300. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +5 -0
  301. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +6 -0
  302. ansible_test/_util/controller/sanity/pylint/config/default.cfg +6 -0
  303. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
  304. ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
  305. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
  306. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
  307. ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
  308. ansible_test/_util/target/common/constants.py +2 -2
  309. ansible_test/_util/target/injector/python.py +5 -0
  310. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
  311. ansible_test/_util/target/sanity/import/importer.py +1 -1
  312. ansible_test/_util/target/setup/bootstrap.sh +6 -17
  313. ansible_test/_util/target/setup/requirements.py +18 -24
  314. ansible_test/config/config.yml +1 -1
  315. ansible_core-2.17.6rc1.data/scripts/ansible-test +0 -44
  316. ansible_test/_data/requirements/sanity.mypy.in +0 -10
  317. ansible_test/_data/requirements/sanity.mypy.txt +0 -18
  318. ansible_test/_internal/commands/sanity/mypy.py +0 -274
  319. ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
  320. ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
  321. ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
  322. ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
  323. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.dist-info}/COPYING +0 -0
  324. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.dist-info}/WHEEL +0 -0
  325. {ansible_core-2.17.6rc1.dist-info → ansible_core-2.18.0.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
- try:
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, string_types):
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, string_types):
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, string_types):
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
- if PY3:
416
- # cache the native FileFinder (take advantage of its filesystem cache for future find/load requests)
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
- if PY3:
423
- # try to find the FileFinder hook to call for fallback path-based imports in Py3
424
- _file_finder_hook = [ph for ph in sys.path_hooks if 'FileFinder' in repr(ph)]
425
- if len(_file_finder_hook) != 1:
426
- raise Exception('need exactly one FileFinder import hook (found {0})'.format(len(_file_finder_hook)))
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
- if PY3:
449
- # create or consult our cached file finder for this path
450
- if not self._file_finder:
451
- try:
452
- self._file_finder = _AnsiblePathHookFinder._filefinder_path_hook(self._pathctx)
453
- except ImportError:
454
- # FUTURE: log at a high logging level? This is normal for things like python36.zip on the path, but
455
- # might not be in some other situation...
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
- # call py2's internal loader
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, string_types) or len(collection_name.split('.')) != 2:
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, string_types) or len(collection_name.split('.')) != 2:
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 random
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
- # NOTE: level is kept at INFO to avoid security disclosures caused by certain libraries when using DEBUG
158
- logging.basicConfig(filename=path, level=logging.INFO, # DO NOT set to logging.DEBUG
159
- format='%(asctime)s p=%(process)d u=%(user)s n=%(name)s | %(message)s')
160
-
161
- logger = logging.getLogger('ansible')
162
- for handler in logging.root.handlers:
163
- handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', [])))
164
- handler.addFilter(FilterUserInjector())
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 %s is not writeable and we cannot create it, aborting\n" % path, file=sys.stderr)
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.COLOR_ERROR: logging.ERROR,
170
- C.COLOR_WARN: logging.WARNING,
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.COLOR_SKIP: logging.WARNING,
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.COLOR_VERBOSE: logging.INFO}
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 'bin/ansible-connection' (and other relevant places).
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
- lvl = logging.INFO
458
- if color:
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
- if host is None:
513
- self.display("%6d %0.5f: %s" % (os.getpid(), time.time(), msg), color=C.COLOR_DEBUG)
514
- else:
515
- self.display("%6d %0.5f [%s]: %s" % (os.getpid(), time.time(), host, msg), color=C.COLOR_DEBUG)
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 = random.choice(list(self.cows_available))
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, protocol=0))
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, loader=None, fail_on_undefined=True, convert_bare=False):
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 random
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" % random.randint(0, _MAXSIZE))[:8]
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 _isidentifier_PY3(ident):
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, AnsibleTemplateError
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, string_types
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 (hasattr(plugin, 'REQUIRES_ENABLED') or hasattr(plugin, 'REQUIRES_WHITELIST')):
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