ansible-core 2.17.6__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.6.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.6.dist-info → ansible_core-2.18.0.dist-info}/RECORD +316 -311
  255. {ansible_core-2.17.6.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.6.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.6.dist-info → ansible_core-2.18.0.dist-info}/COPYING +0 -0
  324. {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/WHEEL +0 -0
  325. {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/top_level.txt +0 -0
ansible/playbook/base.py CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import decimal
7
8
  import itertools
8
9
  import operator
9
10
  import os
@@ -386,13 +387,13 @@ class FieldAttributeBase:
386
387
  return fq_group_name, resolved_actions
387
388
 
388
389
  def _resolve_action(self, action_name, mandatory=True):
389
- context = module_loader.find_plugin_with_context(action_name)
390
+ context = module_loader.find_plugin_with_context(action_name, ignore_deprecated=(not mandatory))
390
391
  if context.resolved and not context.action_plugin:
391
- prefer = action_loader.find_plugin_with_context(action_name)
392
+ prefer = action_loader.find_plugin_with_context(action_name, ignore_deprecated=(not mandatory))
392
393
  if prefer.resolved:
393
394
  context = prefer
394
395
  elif not context.resolved:
395
- context = action_loader.find_plugin_with_context(action_name)
396
+ context = action_loader.find_plugin_with_context(action_name, ignore_deprecated=(not mandatory))
396
397
 
397
398
  if context.resolved:
398
399
  return context.resolved_fqcn
@@ -440,7 +441,13 @@ class FieldAttributeBase:
440
441
  if attribute.isa == 'string':
441
442
  value = to_text(value)
442
443
  elif attribute.isa == 'int':
443
- value = int(value)
444
+ if not isinstance(value, int):
445
+ try:
446
+ if (decimal_value := decimal.Decimal(value)) != (int_value := int(decimal_value)):
447
+ raise decimal.DecimalException(f'Floating-point value {value!r} would be truncated.')
448
+ value = int_value
449
+ except decimal.DecimalException as e:
450
+ raise ValueError from e
444
451
  elif attribute.isa == 'float':
445
452
  value = float(value)
446
453
  elif attribute.isa == 'bool':
@@ -574,9 +581,7 @@ class FieldAttributeBase:
574
581
 
575
582
  def _load_vars(self, attr, ds):
576
583
  '''
577
- Vars in a play can be specified either as a dictionary directly, or
578
- as a list of dictionaries. If the later, this method will turn the
579
- list into a single dictionary.
584
+ Vars in a play must be specified as a dictionary.
580
585
  '''
581
586
 
582
587
  def _validate_variable_keys(ds):
@@ -588,22 +593,6 @@ class FieldAttributeBase:
588
593
  if isinstance(ds, dict):
589
594
  _validate_variable_keys(ds)
590
595
  return combine_vars(self.vars, ds)
591
- elif isinstance(ds, list):
592
- line_file = getattr(ds, 'ansible_pos', ("unknown", 0))
593
- display.deprecated(
594
- (
595
- 'Specifying a list of dictionaries for vars is deprecated in favor of '
596
- 'specifying a dictionary. Error occurred in the file: %s, line: %d' % (line_file[0], line_file[1])
597
- ),
598
- version='2.18'
599
- )
600
- all_vars = self.vars
601
- for item in ds:
602
- if not isinstance(item, dict):
603
- raise ValueError
604
- _validate_variable_keys(item)
605
- all_vars = combine_vars(all_vars, item)
606
- return all_vars
607
596
  elif ds is None:
608
597
  return {}
609
598
  else:
@@ -293,8 +293,12 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
293
293
  else:
294
294
  if use_handlers:
295
295
  t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader)
296
+ if t.action in C._ACTION_META and t.args.get('_raw_params') == "end_role":
297
+ raise AnsibleParserError("Cannot execute 'end_role' from a handler")
296
298
  else:
297
299
  t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader)
300
+ if t.action in C._ACTION_META and t.args.get('_raw_params') == "end_role" and role is None:
301
+ raise AnsibleParserError("Cannot execute 'end_role' from outside of a role")
298
302
 
299
303
  task_list.append(t)
300
304
 
@@ -29,6 +29,7 @@ class LoopControl(FieldAttributeBase):
29
29
  pause = NonInheritableFieldAttribute(isa='float', default=0, always_post_validate=True)
30
30
  extended = NonInheritableFieldAttribute(isa='bool', always_post_validate=True)
31
31
  extended_allitems = NonInheritableFieldAttribute(isa='bool', default=True, always_post_validate=True)
32
+ break_when = NonInheritableFieldAttribute(isa='list', default=list)
32
33
 
33
34
  def __init__(self):
34
35
  super(LoopControl, self).__init__()
@@ -37,3 +38,10 @@ class LoopControl(FieldAttributeBase):
37
38
  def load(data, variable_manager=None, loader=None):
38
39
  t = LoopControl()
39
40
  return t.load_data(data, variable_manager=variable_manager, loader=loader)
41
+
42
+ def _post_validate_break_when(self, attr, value, templar):
43
+ '''
44
+ break_when is evaluated after the execution of the loop is complete,
45
+ and should not be templated during the regular post_validate step.
46
+ '''
47
+ return value
ansible/playbook/play.py CHANGED
@@ -28,7 +28,7 @@ from ansible.playbook.base import Base
28
28
  from ansible.playbook.block import Block
29
29
  from ansible.playbook.collectionsearch import CollectionSearch
30
30
  from ansible.playbook.helpers import load_list_of_blocks, load_list_of_roles
31
- from ansible.playbook.role import Role, hash_params
31
+ from ansible.playbook.role import Role
32
32
  from ansible.playbook.task import Task
33
33
  from ansible.playbook.taggable import Taggable
34
34
  from ansible.vars.manager import preprocess_vars
@@ -57,11 +57,9 @@ class Play(Base, Taggable, CollectionSearch):
57
57
 
58
58
  # Facts
59
59
  gather_facts = NonInheritableFieldAttribute(isa='bool', default=None, always_post_validate=True)
60
-
61
- # defaults to be deprecated, should be 'None' in future
62
- gather_subset = NonInheritableFieldAttribute(isa='list', default=(lambda: C.DEFAULT_GATHER_SUBSET), listof=string_types, always_post_validate=True)
63
- gather_timeout = NonInheritableFieldAttribute(isa='int', default=C.DEFAULT_GATHER_TIMEOUT, always_post_validate=True)
64
- fact_path = NonInheritableFieldAttribute(isa='string', default=C.DEFAULT_FACT_PATH)
60
+ gather_subset = NonInheritableFieldAttribute(isa='list', default=None, listof=string_types, always_post_validate=True)
61
+ gather_timeout = NonInheritableFieldAttribute(isa='int', default=None, always_post_validate=True)
62
+ fact_path = NonInheritableFieldAttribute(isa='string', default=None)
65
63
 
66
64
  # Variable Attributes
67
65
  vars_files = NonInheritableFieldAttribute(isa='list', default=list, priority=99)
@@ -102,22 +100,6 @@ class Play(Base, Taggable, CollectionSearch):
102
100
  def __repr__(self):
103
101
  return self.get_name()
104
102
 
105
- @property
106
- def ROLE_CACHE(self):
107
- """Backwards compat for custom strategies using ``play.ROLE_CACHE``
108
- """
109
- display.deprecated(
110
- 'Play.ROLE_CACHE is deprecated in favor of Play.role_cache, or StrategyBase._get_cached_role',
111
- version='2.18',
112
- )
113
- cache = {}
114
- for path, roles in self.role_cache.items():
115
- for role in roles:
116
- name = role.get_name()
117
- hashed_params = hash_params(role._get_hash_dict())
118
- cache.setdefault(name, {})[hashed_params] = role
119
- return cache
120
-
121
103
  def _validate_hosts(self, attribute, name, value):
122
104
  # Only validate 'hosts' if a value was passed in to original data set.
123
105
  if 'hosts' in self._ds:
@@ -113,22 +113,6 @@ class PlayContext(Base):
113
113
  # "PlayContext.force_handlers should not be used, the calling code should be using play itself instead"
114
114
  force_handlers = FieldAttribute(isa='bool', default=False)
115
115
 
116
- @property
117
- def verbosity(self):
118
- display.deprecated(
119
- "PlayContext.verbosity is deprecated, use ansible.utils.display.Display.verbosity instead.",
120
- version="2.18"
121
- )
122
- return self._internal_verbosity
123
-
124
- @verbosity.setter
125
- def verbosity(self, value):
126
- display.deprecated(
127
- "PlayContext.verbosity is deprecated, use ansible.utils.display.Display.verbosity instead.",
128
- version="2.18"
129
- )
130
- self._internal_verbosity = value
131
-
132
116
  def __init__(self, play=None, passwords=None, connection_lockfd=None):
133
117
  # Note: play is really not optional. The only time it could be omitted is when we create
134
118
  # a PlayContext just so we can invoke its deserialize method to load it from a serialized
@@ -90,7 +90,7 @@ class PlaybookInclude(Base, Conditional, Taggable):
90
90
  # it is a collection playbook, setup default collections
91
91
  AnsibleCollectionConfig.default_collection = playbook_collection
92
92
  else:
93
- # it is NOT a collection playbook, setup adjecent paths
93
+ # it is NOT a collection playbook, setup adjacent paths
94
94
  AnsibleCollectionConfig.playbook_paths.append(os.path.dirname(os.path.abspath(to_bytes(playbook, errors='surrogate_or_strict'))))
95
95
 
96
96
  pb._load_playbook_data(file_name=playbook, variable_manager=variable_manager, vars=self.vars.copy())
@@ -123,7 +123,7 @@ class PlaybookInclude(Base, Conditional, Taggable):
123
123
 
124
124
  def preprocess_data(self, ds):
125
125
  '''
126
- Regorganizes the data for a PlaybookInclude datastructure to line
126
+ Reorganizes the data for a PlaybookInclude datastructure to line
127
127
  up with what we expect the proper attributes to be
128
128
  '''
129
129
 
@@ -107,7 +107,7 @@ class Role(Base, Conditional, Taggable, CollectionSearch, Delegatable):
107
107
  self.static = static
108
108
 
109
109
  # includes (static=false) default to private, while imports (static=true) default to public
110
- # but both can be overriden by global config if set
110
+ # but both can be overridden by global config if set
111
111
  if public is None:
112
112
  global_private, origin = C.config.get_config_value_and_origin('DEFAULT_PRIVATE_ROLE_VARS')
113
113
  if origin == 'default':
@@ -508,7 +508,7 @@ class Role(Base, Conditional, Taggable, CollectionSearch, Delegatable):
508
508
  # get exported variables from meta/dependencies
509
509
  seen = []
510
510
  for dep in self.get_all_dependencies():
511
- # Avoid reruning dupe deps since they can have vars from previous invocations and they accumulate in deps
511
+ # Avoid rerunning dupe deps since they can have vars from previous invocations and they accumulate in deps
512
512
  # TODO: re-examine dep loading to see if we are somehow improperly adding the same dep too many times
513
513
  if dep not in seen:
514
514
  # only take 'exportable' vars from deps
@@ -99,6 +99,7 @@ class AnsiblePlugin(ABC):
99
99
 
100
100
  def set_option(self, option, value):
101
101
  self._options[option] = C.config.get_config_value(option, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={option: value})
102
+ C.handle_config_noise(display)
102
103
 
103
104
  def set_options(self, task_keys=None, var_options=None, direct=None):
104
105
  '''
@@ -115,6 +116,7 @@ class AnsiblePlugin(ABC):
115
116
  if self.allow_extras and var_options and '_extras' in var_options:
116
117
  # these are largely unvalidated passthroughs, either plugin or underlying API will validate
117
118
  self._options['_extras'] = var_options['_extras']
119
+ C.handle_config_noise(display)
118
120
 
119
121
  def has_option(self, option):
120
122
  if not self._options:
@@ -8,8 +8,8 @@ from __future__ import annotations
8
8
  import base64
9
9
  import json
10
10
  import os
11
- import random
12
11
  import re
12
+ import secrets
13
13
  import shlex
14
14
  import stat
15
15
  import tempfile
@@ -146,7 +146,7 @@ class ActionBase(ABC):
146
146
  Be cautious when directly passing ``new_module_args`` directly to a
147
147
  module invocation, as it will contain the defaults, and not only
148
148
  the args supplied from the task. If you do this, the module
149
- should not define ``mututally_exclusive`` or similar.
149
+ should not define ``mutually_exclusive`` or similar.
150
150
 
151
151
  This code is roughly copied from the ``validate_argument_spec``
152
152
  action plugin for use by other action plugins.
@@ -1114,7 +1114,7 @@ class ActionBase(ABC):
1114
1114
  remote_files.append(remote_async_module_path)
1115
1115
 
1116
1116
  async_limit = self._task.async_val
1117
- async_jid = f'j{random.randint(0, 999999999999)}'
1117
+ async_jid = f'j{secrets.randbelow(999999999999)}'
1118
1118
 
1119
1119
  # call the interpreter for async_wrapper directly
1120
1120
  # this permits use of a script for an interpreter on non-Linux platforms
@@ -1226,15 +1226,13 @@ class ActionBase(ABC):
1226
1226
  try:
1227
1227
  _validate_utf8_json(data)
1228
1228
  except UnicodeEncodeError:
1229
- # When removing this, also remove the loop and latin-1 from ansible.module_utils.common.text.converters.jsonify
1230
- display.deprecated(
1229
+ raise ValueError(
1231
1230
  f'Module "{self._task.resolved_action or self._task.action}" returned non UTF-8 data in '
1232
- 'the JSON response. This will become an error in the future',
1233
- version='2.18',
1231
+ 'the JSON response.',
1234
1232
  )
1235
1233
 
1236
1234
  data['_ansible_parsed'] = True
1237
- except ValueError:
1235
+ except ValueError as e:
1238
1236
  # not valid json, lets try to capture error
1239
1237
  data = dict(failed=True, _ansible_parsed=False)
1240
1238
  data['module_stdout'] = res.get('stdout', u'')
@@ -1248,7 +1246,7 @@ class ActionBase(ABC):
1248
1246
  data['exception'] = data['module_stdout']
1249
1247
 
1250
1248
  # The default
1251
- data['msg'] = "MODULE FAILURE"
1249
+ data['msg'] = f"MODULE FAILURE: {e}"
1252
1250
 
1253
1251
  # try to figure out if we are missing interpreter
1254
1252
  if self._used_interpreter is not None:
@@ -41,6 +41,13 @@ class ActionModule(ActionBase):
41
41
  facts = self._execute_module(
42
42
  module_name="ansible.legacy.setup", module_args=dict(filter="ansible_pkg_mgr", gather_subset="!all"),
43
43
  task_vars=task_vars)
44
+
45
+ if facts.get("failed", False):
46
+ raise AnsibleActionFail(
47
+ f"Failed to fetch ansible_pkg_mgr to determine the dnf action backend: {facts.get('msg')}",
48
+ result=facts,
49
+ )
50
+
44
51
  display.debug("Facts %s" % facts)
45
52
  module = facts.get("ansible_facts", {}).get("ansible_pkg_mgr", "auto")
46
53
  if (not self._task.delegate_to or self._task.delegate_facts) and module != 'auto':
@@ -75,9 +82,4 @@ class ActionModule(ActionBase):
75
82
  result.update(self._execute_module(
76
83
  module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
77
84
 
78
- # Cleanup
79
- if not self._task.async_val:
80
- # remove a temporary path we created
81
- self._remove_tmp_path(self._connection._shell.tmpdir)
82
-
83
85
  return result
@@ -68,6 +68,11 @@ class ActionModule(ActionBase):
68
68
  module_args=dict(filter='ansible_pkg_mgr', gather_subset='!all'),
69
69
  task_vars=task_vars,
70
70
  )
71
+ if facts.get("failed", False):
72
+ raise AnsibleActionFail(
73
+ f"Failed to fetch ansible_pkg_mgr to determine the package action backend: {facts.get('msg')}",
74
+ result=facts,
75
+ )
71
76
  pmgr = 'ansible_pkg_mgr'
72
77
 
73
78
  try:
@@ -103,9 +108,5 @@ class ActionModule(ActionBase):
103
108
 
104
109
  except AnsibleAction as e:
105
110
  result.update(e.result)
106
- finally:
107
- if not self._task.async_val:
108
- # remove a temporary path we created
109
- self._remove_tmp_path(self._connection._shell.tmpdir)
110
111
 
111
112
  return result
@@ -4,7 +4,7 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import random
7
+ import secrets
8
8
  import time
9
9
 
10
10
  from datetime import datetime, timedelta, timezone
@@ -304,7 +304,7 @@ class ActionModule(ActionBase):
304
304
  except AnsibleConnectionFailure:
305
305
  pass
306
306
  # Use exponential backoff with a max timeout, plus a little bit of randomness
307
- random_int = random.randint(0, 1000) / 1000
307
+ random_int = secrets.randbelow(1000) / 1000
308
308
  fail_sleep = 2 ** fail_count + random_int
309
309
  if fail_sleep > max_fail_sleep:
310
310
 
@@ -6,7 +6,7 @@ from __future__ import annotations
6
6
  import shlex
7
7
 
8
8
  from abc import abstractmethod
9
- from random import choice
9
+ from secrets import choice
10
10
  from string import ascii_lowercase
11
11
  from gettext import dgettext
12
12
 
@@ -22,6 +22,7 @@ import json
22
22
  import re
23
23
  import sys
24
24
  import textwrap
25
+ from typing import TYPE_CHECKING
25
26
 
26
27
  from collections import OrderedDict
27
28
  from collections.abc import MutableMapping
@@ -41,6 +42,9 @@ from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
41
42
 
42
43
  import yaml
43
44
 
45
+ if TYPE_CHECKING:
46
+ from ansible.executor.task_result import TaskResult
47
+
44
48
  global_display = Display()
45
49
 
46
50
 
@@ -503,15 +507,52 @@ class CallbackBase(AnsiblePlugin):
503
507
  def v2_on_any(self, *args, **kwargs):
504
508
  self.on_any(args, kwargs)
505
509
 
506
- def v2_runner_on_failed(self, result, ignore_errors=False):
510
+ def v2_runner_on_failed(self, result: TaskResult, ignore_errors: bool = False) -> None:
511
+ """Get details about a failed task and whether or not Ansible should continue
512
+ running tasks on the host where the failure occurred, then process the details
513
+ as required by the callback (output, profiling, logging, notifications, etc.)
514
+
515
+ Note: The 'ignore_errors' directive only works when the task can run and returns
516
+ a value of 'failed'. It does not make Ansible ignore undefined variable errors,
517
+ connection failures, execution issues (for example, missing packages), or syntax errors.
518
+
519
+ Customization note: For more information about the attributes and methods of the
520
+ TaskResult class, see lib/ansible/executor/task_result.py.
521
+
522
+ :param TaskResult result: An object that contains details about the task
523
+ :param bool ignore_errors: Whether or not Ansible should continue running tasks on the host
524
+ where the failure occurred
525
+
526
+ :return: None
527
+ """
507
528
  host = result._host.get_name()
508
529
  self.runner_on_failed(host, result._result, ignore_errors)
509
530
 
510
- def v2_runner_on_ok(self, result):
531
+ def v2_runner_on_ok(self, result: TaskResult) -> None:
532
+ """Get details about a successful task and process them as required by the callback
533
+ (output, profiling, logging, notifications, etc.)
534
+
535
+ Customization note: For more information about the attributes and methods of the
536
+ TaskResult class, see lib/ansible/executor/task_result.py.
537
+
538
+ :param TaskResult result: An object that contains details about the task
539
+
540
+ :return: None
541
+ """
511
542
  host = result._host.get_name()
512
543
  self.runner_on_ok(host, result._result)
513
544
 
514
- def v2_runner_on_skipped(self, result):
545
+ def v2_runner_on_skipped(self, result: TaskResult) -> None:
546
+ """Get details about a skipped task and process them as required by the callback
547
+ (output, profiling, logging, notifications, etc.)
548
+
549
+ Customization note: For more information about the attributes and methods of the
550
+ TaskResult class, see lib/ansible/executor/task_result.py.
551
+
552
+ :param TaskResult result: An object that contains details about the task
553
+
554
+ :return: None
555
+ """
515
556
  if C.DISPLAY_SKIPPED_HOSTS:
516
557
  host = result._host.get_name()
517
558
  self.runner_on_skipped(host, self._get_item_label(getattr(result._result, 'results', {})))
@@ -293,7 +293,7 @@ class CallbackModule(CallbackBase):
293
293
  label = self._get_item_label(included_file._vars)
294
294
  if label:
295
295
  msg += " => (item=%s)" % label
296
- self._display.display(msg, color=C.COLOR_SKIP)
296
+ self._display.display(msg, color=C.COLOR_INCLUDED)
297
297
 
298
298
  def v2_playbook_on_stats(self, stats):
299
299
  self._display.banner("PLAY RECAP")
@@ -263,7 +263,7 @@ class CliconfBase(AnsiblePlugin):
263
263
  'supports_commit_comment': <bool>, # identify if adding comment to commit is supported of not
264
264
  'supports_onbox_diff': <bool>, # identify if on box diff capability is supported or not
265
265
  'supports_generate_diff': <bool>, # identify if diff capability is supported within plugin
266
- 'supports_multiline_delimiter': <bool>, # identify if multiline demiliter is supported within config
266
+ 'supports_multiline_delimiter': <bool>, # identify if multiline delimiter is supported within config
267
267
  'supports_diff_match': <bool>, # identify if match is supported
268
268
  'supports_diff_ignore_lines': <bool>, # identify if ignore line in diff is supported
269
269
  'supports_config_replace': <bool>, # identify if running config replace with candidate config is supported
@@ -111,8 +111,7 @@ DOCUMENTATION = """
111
111
  proxy_command:
112
112
  default: ''
113
113
  description:
114
- - Proxy information for running the connection via a jumphost
115
- - Also this plugin will scan 'ssh_args', 'ssh_extra_args' and 'ssh_common_args' from the 'ssh' plugin settings for proxy information if set.
114
+ - Proxy information for running the connection via a jumphost.
116
115
  type: string
117
116
  env: [{name: ANSIBLE_PARAMIKO_PROXY_COMMAND}]
118
117
  ini:
@@ -120,60 +119,6 @@ DOCUMENTATION = """
120
119
  vars:
121
120
  - name: ansible_paramiko_proxy_command
122
121
  version_added: '2.15'
123
- ssh_args:
124
- description: Only used in parsing ProxyCommand for use in this plugin.
125
- default: ''
126
- type: string
127
- ini:
128
- - section: 'ssh_connection'
129
- key: 'ssh_args'
130
- env:
131
- - name: ANSIBLE_SSH_ARGS
132
- vars:
133
- - name: ansible_ssh_args
134
- version_added: '2.7'
135
- deprecated:
136
- why: In favor of the "proxy_command" option.
137
- version: "2.18"
138
- alternatives: proxy_command
139
- ssh_common_args:
140
- description: Only used in parsing ProxyCommand for use in this plugin.
141
- type: string
142
- ini:
143
- - section: 'ssh_connection'
144
- key: 'ssh_common_args'
145
- version_added: '2.7'
146
- env:
147
- - name: ANSIBLE_SSH_COMMON_ARGS
148
- version_added: '2.7'
149
- vars:
150
- - name: ansible_ssh_common_args
151
- cli:
152
- - name: ssh_common_args
153
- default: ''
154
- deprecated:
155
- why: In favor of the "proxy_command" option.
156
- version: "2.18"
157
- alternatives: proxy_command
158
- ssh_extra_args:
159
- description: Only used in parsing ProxyCommand for use in this plugin.
160
- type: string
161
- vars:
162
- - name: ansible_ssh_extra_args
163
- env:
164
- - name: ANSIBLE_SSH_EXTRA_ARGS
165
- version_added: '2.7'
166
- ini:
167
- - key: ssh_extra_args
168
- section: ssh_connection
169
- version_added: '2.7'
170
- cli:
171
- - name: ssh_extra_args
172
- default: ''
173
- deprecated:
174
- why: In favor of the "proxy_command" option.
175
- version: "2.18"
176
- alternatives: proxy_command
177
122
  pty:
178
123
  default: True
179
124
  description: 'SUDO usually requires a PTY, True to give a PTY and False to not give a PTY.'
@@ -399,30 +344,7 @@ class Connection(ConnectionBase):
399
344
  self._log_channel = name
400
345
 
401
346
  def _parse_proxy_command(self, port: int = 22) -> dict[str, t.Any]:
402
- proxy_command = None
403
- # Parse ansible_ssh_common_args, specifically looking for ProxyCommand
404
- ssh_args = [
405
- self.get_option('ssh_extra_args'),
406
- self.get_option('ssh_common_args'),
407
- self.get_option('ssh_args', ''),
408
- ]
409
-
410
- args = self._split_ssh_args(' '.join(ssh_args))
411
- for i, arg in enumerate(args):
412
- if arg.lower() == 'proxycommand':
413
- # _split_ssh_args split ProxyCommand from the command itself
414
- proxy_command = args[i + 1]
415
- else:
416
- # ProxyCommand and the command itself are a single string
417
- match = SETTINGS_REGEX.match(arg)
418
- if match:
419
- if match.group(1).lower() == 'proxycommand':
420
- proxy_command = match.group(2)
421
-
422
- if proxy_command:
423
- break
424
-
425
- proxy_command = self.get_option('proxy_command') or proxy_command
347
+ proxy_command = self.get_option('proxy_command') or None
426
348
 
427
349
  sock_kwarg = {}
428
350
  if proxy_command: