ansible-core 2.17.4rc1__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.

Files changed (320) 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 +30 -53
  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 +14 -11
  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 +48 -31
  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/playbook/task.py +1 -1
  192. ansible/plugins/__init__.py +2 -0
  193. ansible/plugins/action/__init__.py +7 -9
  194. ansible/plugins/action/reboot.py +2 -2
  195. ansible/plugins/become/__init__.py +1 -1
  196. ansible/plugins/callback/__init__.py +44 -3
  197. ansible/plugins/callback/default.py +1 -1
  198. ansible/plugins/cliconf/__init__.py +1 -1
  199. ansible/plugins/connection/paramiko_ssh.py +2 -80
  200. ansible/plugins/connection/psrp.py +33 -82
  201. ansible/plugins/connection/ssh.py +0 -8
  202. ansible/plugins/connection/winrm.py +46 -1
  203. ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
  204. ansible/plugins/doc_fragments/constructed.py +10 -10
  205. ansible/plugins/doc_fragments/default_callback.py +8 -8
  206. ansible/plugins/doc_fragments/files.py +5 -5
  207. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  208. ansible/plugins/doc_fragments/result_format_callback.py +6 -6
  209. ansible/plugins/doc_fragments/return_common.py +1 -1
  210. ansible/plugins/doc_fragments/shell_common.py +2 -10
  211. ansible/plugins/doc_fragments/shell_windows.py +0 -9
  212. ansible/plugins/doc_fragments/url.py +2 -2
  213. ansible/plugins/doc_fragments/url_windows.py +4 -5
  214. ansible/plugins/doc_fragments/validate.py +1 -1
  215. ansible/plugins/filter/core.py +2 -0
  216. ansible/plugins/filter/human_to_bytes.yml +9 -0
  217. ansible/plugins/filter/password_hash.yml +1 -1
  218. ansible/plugins/filter/strftime.yml +1 -1
  219. ansible/plugins/filter/to_nice_json.yml +7 -3
  220. ansible/plugins/filter/to_uuid.yml +1 -1
  221. ansible/plugins/inventory/script.py +1 -1
  222. ansible/plugins/list.py +1 -1
  223. ansible/plugins/loader.py +0 -11
  224. ansible/plugins/lookup/config.py +1 -1
  225. ansible/plugins/lookup/csvfile.py +21 -9
  226. ansible/plugins/lookup/env.py +8 -9
  227. ansible/plugins/lookup/ini.py +10 -1
  228. ansible/plugins/lookup/random_choice.py +2 -2
  229. ansible/plugins/lookup/url.py +7 -2
  230. ansible/plugins/shell/__init__.py +15 -20
  231. ansible/plugins/shell/powershell.py +9 -6
  232. ansible/plugins/strategy/__init__.py +16 -7
  233. ansible/plugins/test/core.py +23 -1
  234. ansible/plugins/test/issubset.yml +1 -1
  235. ansible/plugins/test/subset.yml +1 -1
  236. ansible/plugins/test/timedout.yml +20 -0
  237. ansible/plugins/test/vault_encrypted.yml +6 -6
  238. ansible/plugins/test/vaulted_file.yml +19 -0
  239. ansible/release.py +2 -2
  240. ansible/template/__init__.py +3 -8
  241. ansible/utils/collection_loader/_collection_finder.py +23 -55
  242. ansible/utils/display.py +44 -31
  243. ansible/utils/jsonrpc.py +1 -1
  244. ansible/utils/listify.py +1 -5
  245. ansible/utils/path.py +3 -0
  246. ansible/utils/vars.py +18 -27
  247. ansible/vars/manager.py +7 -150
  248. ansible/vars/plugins.py +1 -1
  249. ansible_core-2.18.0b1.dist-info/Apache-License.txt +202 -0
  250. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/METADATA +36 -23
  251. ansible_core-2.18.0b1.dist-info/MIT-license.txt +14 -0
  252. ansible_core-2.18.0b1.dist-info/PSF-license.txt +48 -0
  253. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/RECORD +311 -306
  254. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/WHEEL +1 -1
  255. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/entry_points.txt +1 -1
  256. ansible_core-2.18.0b1.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 +5 -7
  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/host_configs.py +10 -0
  288. ansible_test/_internal/host_profiles.py +9 -13
  289. ansible_test/_internal/pypi_proxy.py +1 -1
  290. ansible_test/_internal/python_requirements.py +5 -14
  291. ansible_test/_internal/timeout.py +1 -1
  292. ansible_test/_internal/util.py +40 -0
  293. ansible_test/_internal/util_common.py +5 -1
  294. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
  295. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
  296. ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
  297. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  298. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  299. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
  300. ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
  301. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
  302. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
  303. ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
  304. ansible_test/_util/target/common/constants.py +2 -2
  305. ansible_test/_util/target/injector/python.py +5 -0
  306. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
  307. ansible_test/_util/target/sanity/import/importer.py +1 -1
  308. ansible_test/_util/target/setup/bootstrap.sh +6 -17
  309. ansible_test/_util/target/setup/requirements.py +14 -20
  310. ansible_test/config/config.yml +1 -1
  311. ansible_core-2.17.4rc1.data/scripts/ansible-test +0 -44
  312. ansible_test/_data/requirements/sanity.mypy.in +0 -10
  313. ansible_test/_data/requirements/sanity.mypy.txt +0 -18
  314. ansible_test/_internal/commands/sanity/mypy.py +0 -274
  315. ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
  316. ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
  317. ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
  318. ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
  319. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/COPYING +0 -0
  320. {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/top_level.txt +0 -0
ansible/modules/dnf5.py CHANGED
@@ -17,7 +17,7 @@ options:
17
17
  name:
18
18
  description:
19
19
  - "A package name or package specifier with version, like C(name-1.0).
20
- When using state=latest, this can be '*' which means run: dnf -y update.
20
+ When using O(state=latest), this can be C(*) which means run: C(dnf -y update).
21
21
  You can also pass a url or a local path to an rpm file.
22
22
  To operate on several packages this can accept a comma separated string of packages or a list of packages."
23
23
  - Comparison operators for package version are valid here C(>), C(<), C(>=), C(<=). Example - C(name >= 1.0).
@@ -37,15 +37,15 @@ options:
37
37
  state:
38
38
  description:
39
39
  - Whether to install (V(present), V(latest)), or remove (V(absent)) a package.
40
- - Default is V(None), however in effect the default action is V(present) unless the V(autoremove) option is
41
- enabled for this module, then V(absent) is inferred.
40
+ - Default is V(None), however in effect the default action is V(present) unless the O(autoremove=true),
41
+ then V(absent) is inferred.
42
42
  choices: ['absent', 'present', 'installed', 'removed', 'latest']
43
43
  type: str
44
44
  enablerepo:
45
45
  description:
46
46
  - I(Repoid) of repositories to enable for the install/update operation.
47
47
  These repos will not persist beyond the transaction.
48
- When specifying multiple repos, separate them with a ",".
48
+ When specifying multiple repos, separate them with a C(,).
49
49
  type: list
50
50
  elements: str
51
51
  default: []
@@ -53,7 +53,7 @@ options:
53
53
  description:
54
54
  - I(Repoid) of repositories to disable for the install/update operation.
55
55
  These repos will not persist beyond the transaction.
56
- When specifying multiple repos, separate them with a ",".
56
+ When specifying multiple repos, separate them with a C(,).
57
57
  type: list
58
58
  elements: str
59
59
  default: []
@@ -84,12 +84,12 @@ options:
84
84
  description:
85
85
  - If V(true), removes all "leaf" packages from the system that were originally
86
86
  installed as dependencies of user-installed packages but which are no longer
87
- required by any such package. Should be used alone or when O(state) is V(absent)
87
+ required by any such package. Should be used alone or when O(state=absent).
88
88
  type: bool
89
89
  default: "no"
90
90
  exclude:
91
91
  description:
92
- - Package name(s) to exclude when state=present, or latest. This can be a
92
+ - Package name(s) to exclude when O(state=present) or O(state=latest). This can be a
93
93
  list or a comma separated string.
94
94
  type: list
95
95
  elements: str
@@ -97,20 +97,20 @@ options:
97
97
  skip_broken:
98
98
  description:
99
99
  - Skip all unavailable packages or packages with broken dependencies
100
- without raising an error. Equivalent to passing the --skip-broken option.
100
+ without raising an error. Equivalent to passing the C(--skip-broken) option.
101
101
  type: bool
102
102
  default: "no"
103
103
  update_cache:
104
104
  description:
105
105
  - Force dnf to check if cache is out of date and redownload if needed.
106
- Has an effect only if O(state) is V(present) or V(latest).
106
+ Has an effect only if O(state=present) or O(state=latest).
107
107
  type: bool
108
108
  default: "no"
109
109
  aliases: [ expire-cache ]
110
110
  update_only:
111
111
  description:
112
112
  - When using latest, only update installed packages. Do not install packages.
113
- - Has an effect only if O(state) is V(latest)
113
+ - Has an effect only if O(state=present) or O(state=latest).
114
114
  default: "no"
115
115
  type: bool
116
116
  security:
@@ -127,17 +127,19 @@ options:
127
127
  type: bool
128
128
  enable_plugin:
129
129
  description:
130
- - This is currently a no-op as dnf5 itself does not implement this feature.
131
130
  - I(Plugin) name to enable for the install/update operation.
132
131
  The enabled plugin will not persist beyond the transaction.
132
+ - O(disable_plugin) takes precedence in case a plugin is listed in both O(enable_plugin) and O(disable_plugin).
133
+ - Requires python3-libdnf5 5.2.0.0+.
133
134
  type: list
134
135
  elements: str
135
136
  default: []
136
137
  disable_plugin:
137
138
  description:
138
- - This is currently a no-op as dnf5 itself does not implement this feature.
139
139
  - I(Plugin) name to disable for the install/update operation.
140
140
  The disabled plugins will not persist beyond the transaction.
141
+ - O(disable_plugin) takes precedence in case a plugin is listed in both O(enable_plugin) and O(disable_plugin).
142
+ - Requires python3-libdnf5 5.2.0.0+.
141
143
  type: list
142
144
  default: []
143
145
  elements: str
@@ -145,7 +147,7 @@ options:
145
147
  description:
146
148
  - Disable the excludes defined in DNF config files.
147
149
  - If set to V(all), disables all excludes.
148
- - If set to V(main), disable excludes defined in [main] in dnf.conf.
150
+ - If set to V(main), disable excludes defined in C([main]) in C(dnf.conf).
149
151
  - If set to V(repoid), disable excludes defined for given repo id.
150
152
  type: str
151
153
  validate_certs:
@@ -164,7 +166,7 @@ options:
164
166
  description:
165
167
  - Specify if the named package and version is allowed to downgrade
166
168
  a maybe already installed higher version of that package.
167
- Note that setting allow_downgrade=True can make this module
169
+ Note that setting O(allow_downgrade=true) can make this module
168
170
  behave in a non-idempotent way. The task could end up with a set
169
171
  of packages that does not match the complete list of specified
170
172
  packages to install (because dependencies between the downgraded
@@ -243,7 +245,6 @@ attributes:
243
245
  platform:
244
246
  platforms: rhel
245
247
  requirements:
246
- - "python3"
247
248
  - "python3-libdnf5"
248
249
  version_added: 2.15
249
250
  """
@@ -448,6 +449,21 @@ class Dnf5Module(YumDnf):
448
449
 
449
450
  self.pkg_mgr_name = "dnf5"
450
451
 
452
+ def fail_on_non_existing_plugins(self, base):
453
+ # https://github.com/rpm-software-management/dnf5/issues/1460
454
+ plugin_names = [p.get_name() for p in base.get_plugins_info()]
455
+ msg = []
456
+ if enable_unmatched := set(self.enable_plugin).difference(plugin_names):
457
+ msg.append(
458
+ f"No matches were found for the following plugin name patterns while enabling libdnf5 plugins: {', '.join(enable_unmatched)}."
459
+ )
460
+ if disable_unmatched := set(self.disable_plugin).difference(plugin_names):
461
+ msg.append(
462
+ f"No matches were found for the following plugin name patterns while disabling libdnf5 plugins: {', '.join(disable_unmatched)}."
463
+ )
464
+ if msg:
465
+ self.module.fail_json(msg=" ".join(msg))
466
+
451
467
  def _ensure_dnf(self):
452
468
  locale = get_best_parsable_locale(self.module)
453
469
  os.environ["LC_ALL"] = os.environ["LC_MESSAGES"] = locale
@@ -466,7 +482,6 @@ class Dnf5Module(YumDnf):
466
482
  system_interpreters = [
467
483
  "/usr/libexec/platform-python",
468
484
  "/usr/bin/python3",
469
- "/usr/bin/python2",
470
485
  "/usr/bin/python",
471
486
  ]
472
487
 
@@ -490,12 +505,6 @@ class Dnf5Module(YumDnf):
490
505
  )
491
506
 
492
507
  def run(self):
493
- if sys.version_info.major < 3:
494
- self.module.fail_json(
495
- msg="The dnf5 module requires Python 3.",
496
- failures=[],
497
- rc=1,
498
- )
499
508
  if not self.list and not self.download_only and os.geteuid() != 0:
500
509
  self.module.fail_json(
501
510
  msg="This command has to be run under the root user.",
@@ -503,13 +512,6 @@ class Dnf5Module(YumDnf):
503
512
  rc=1,
504
513
  )
505
514
 
506
- if self.enable_plugin or self.disable_plugin:
507
- self.module.fail_json(
508
- msg="enable_plugin and disable_plugin options are not yet implemented in DNF5",
509
- failures=[],
510
- rc=1,
511
- )
512
-
513
515
  base = libdnf5.base.Base()
514
516
  conf = base.get_config()
515
517
 
@@ -552,8 +554,23 @@ class Dnf5Module(YumDnf):
552
554
  if self.download_dir:
553
555
  conf.destdir = self.download_dir
554
556
 
557
+ if self.enable_plugin:
558
+ try:
559
+ base.enable_disable_plugins(self.enable_plugin, True)
560
+ except AttributeError:
561
+ self.module.fail_json(msg="'enable_plugin' requires python3-libdnf5 5.2.0.0+")
562
+
563
+ if self.disable_plugin:
564
+ try:
565
+ base.enable_disable_plugins(self.disable_plugin, False)
566
+ except AttributeError:
567
+ self.module.fail_json(msg="'disable_plugin' requires python3-libdnf5 5.2.0.0+")
568
+
555
569
  base.setup()
556
570
 
571
+ # https://github.com/rpm-software-management/dnf5/issues/1460
572
+ self.fail_on_non_existing_plugins(base)
573
+
557
574
  log_router = base.get_logger()
558
575
  global_logger = libdnf5.logger.GlobalLogger()
559
576
  global_logger.set(log_router.get(), libdnf5.logger.Logger.Level_DEBUG)
@@ -638,7 +655,7 @@ class Dnf5Module(YumDnf):
638
655
  results = []
639
656
  if self.names == ["*"] and self.state == "latest":
640
657
  goal.add_rpm_upgrade(settings)
641
- elif self.state in {"install", "present", "latest"}:
658
+ elif self.state in {"installed", "present", "latest"}:
642
659
  upgrade = self.state == "latest"
643
660
  for spec in self.names:
644
661
  if is_newer_version_installed(base, spec):
@@ -671,7 +688,7 @@ class Dnf5Module(YumDnf):
671
688
  if transaction.get_problems():
672
689
  failures = []
673
690
  for log_event in transaction.get_resolve_logs():
674
- if log_event.get_problem() == libdnf5.base.GoalProblem_NOT_FOUND and self.state in {"install", "present", "latest"}:
691
+ if log_event.get_problem() == libdnf5.base.GoalProblem_NOT_FOUND and self.state in {"installed", "present", "latest"}:
675
692
  # NOTE dnf module compat
676
693
  failures.append("No package {} available.".format(log_event.get_spec()))
677
694
  else:
@@ -11,7 +11,7 @@ DOCUMENTATION = '''
11
11
  module: dpkg_selections
12
12
  short_description: Dpkg package selection selections
13
13
  description:
14
- - Change dpkg package selection state via --get-selections and --set-selections.
14
+ - Change dpkg package selection state via C(--get-selections) and C(--set-selections).
15
15
  version_added: "2.0"
16
16
  author:
17
17
  - Brian Brazil (@brian-brazil) <brian.brazil@boxever.com>
@@ -68,7 +68,7 @@ def main():
68
68
  dpkg = module.get_bin_path('dpkg', True)
69
69
 
70
70
  locale = get_best_parsable_locale(module)
71
- DPKG_ENV = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale, LC_CTYPE=locale)
71
+ DPKG_ENV = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale, LC_CTYPE=locale, LANGUAGE=locale)
72
72
  module.run_command_environ_update = DPKG_ENV
73
73
 
74
74
  name = module.params['name']
ansible/modules/expect.py CHANGED
@@ -70,10 +70,10 @@ notes:
70
70
  - If you want to run a command through the shell (say you are using C(<),
71
71
  C(>), C(|), and so on), you must specify a shell in the command such as
72
72
  C(/bin/bash -c "/path/to/something | grep else").
73
- - Case insensitive searches are indicated with a prefix of C(?i).
73
+ - Case insensitive searches are indicated with a prefix of C((?i)).
74
74
  - The C(pexpect) library used by this module operates with a search window
75
75
  of 2000 bytes, and does not use a multiline regex match. To perform a
76
- start of line bound match, use a pattern like ``(?m)^pattern``
76
+ start of line bound match, use a pattern like C((?m)^pattern).
77
77
  - The M(ansible.builtin.expect) module is designed for simple scenarios.
78
78
  For more complex needs, consider the use of expect code with the M(ansible.builtin.shell)
79
79
  or M(ansible.builtin.script) modules. (An example is part of the M(ansible.builtin.shell) module documentation).
ansible/modules/fetch.py CHANGED
@@ -28,8 +28,8 @@ options:
28
28
  dest:
29
29
  description:
30
30
  - A directory to save the file into.
31
- - For example, if the O(dest) directory is C(/backup) a O(src) file named C(/etc/profile) on host
32
- C(host.example.com), would be saved into C(/backup/host.example.com/etc/profile).
31
+ - For example, if O(dest=/backup), then O(src=/etc/profile) on host
32
+ C(host.example.com), would save the file into C(/backup/host.example.com/etc/profile).
33
33
  The host name is based on the inventory name.
34
34
  required: yes
35
35
  fail_on_missing:
ansible/modules/file.py CHANGED
@@ -63,9 +63,9 @@ options:
63
63
  force:
64
64
  description:
65
65
  - >
66
- Force the creation of the symlinks in two cases: the source file does
66
+ Force the creation of the links in two cases: if the link type is symbolic and the source file does
67
67
  not exist (but will appear later); the destination exists and is a file (so, we need to unlink the
68
- O(path) file and create a symlink to the O(src) file in place of it).
68
+ O(path) file and create a link to the O(src) file in place of it).
69
69
  type: bool
70
70
  default: no
71
71
  follow:
@@ -73,7 +73,7 @@ options:
73
73
  - This flag indicates that filesystem links, if they exist, should be followed.
74
74
  - O(follow=yes) and O(state=link) can modify O(src) when combined with parameters such as O(mode).
75
75
  - Previous to Ansible 2.5, this was V(false) by default.
76
- - While creating a symlink with a non-existent destination, set O(follow) to V(false) to avoid a warning message related to permission issues.
76
+ - While creating a symlink with a non-existent destination, set O(follow=false) to avoid a warning message related to permission issues.
77
77
  The warning message is added to notify the user that we can not set permissions to the non-existent destination.
78
78
  type: bool
79
79
  default: yes
@@ -872,6 +872,8 @@ def ensure_hardlink(path, src, follow, force, timestamps):
872
872
  'path': path})
873
873
  else:
874
874
  try:
875
+ if follow and os.path.islink(b_src):
876
+ b_src = os.readlink(b_src)
875
877
  os.link(b_src, b_path)
876
878
  except OSError as e:
877
879
  raise AnsibleModuleError(results={'msg': 'Error while linking: %s'
ansible/modules/find.py CHANGED
@@ -58,8 +58,8 @@ options:
58
58
  contains:
59
59
  description:
60
60
  - A regular expression or pattern which should be matched against the file content.
61
- - If O(read_whole_file) is V(false) it matches against the beginning of the line (uses
62
- V(re.match(\))). If O(read_whole_file) is V(true), it searches anywhere for that pattern
61
+ - If O(read_whole_file=false) it matches against the beginning of the line (uses
62
+ V(re.match(\))). If O(read_whole_file=true), it searches anywhere for that pattern
63
63
  (uses V(re.search(\))).
64
64
  - Works only when O(file_type) is V(file).
65
65
  type: str
@@ -75,14 +75,15 @@ options:
75
75
  paths:
76
76
  description:
77
77
  - List of paths of directories to search. All paths must be fully qualified.
78
+ - From ansible-core 2.18 and onwards, the data type has changed from C(str) to C(path).
78
79
  type: list
79
80
  required: true
80
81
  aliases: [ name, path ]
81
- elements: str
82
+ elements: path
82
83
  file_type:
83
84
  description:
84
85
  - Type of file to select.
85
- - The 'link' and 'any' choices were added in Ansible 2.3.
86
+ - The V(link) and V(any) choices were added in Ansible 2.3.
86
87
  type: str
87
88
  choices: [ any, directory, file, link ]
88
89
  default: file
@@ -116,7 +117,7 @@ options:
116
117
  restricted to modes that can be applied using the python
117
118
  C(os.chmod) function.
118
119
  - The mode can be provided as an octal such as V("0644") or
119
- as symbolic such as V(u=rw,g=r,o=r)
120
+ as symbolic such as V(u=rw,g=r,o=r).
120
121
  type: raw
121
122
  version_added: '2.16'
122
123
  exact_mode:
@@ -145,15 +146,23 @@ options:
145
146
  depth:
146
147
  description:
147
148
  - Set the maximum number of levels to descend into.
148
- - Setting recurse to V(false) will override this value, which is effectively depth 1.
149
+ - Setting O(recurse=false) will override this value, which is effectively depth 1.
149
150
  - Default is unlimited depth.
150
151
  type: int
151
152
  version_added: "2.6"
152
153
  encoding:
153
154
  description:
154
- - When doing a C(contains) search, determine the encoding of the files to be searched.
155
+ - When doing a O(contains) search, determine the encoding of the files to be searched.
155
156
  type: str
156
157
  version_added: "2.17"
158
+ limit:
159
+ description:
160
+ - Limit the maximum number of matching paths returned. After finding this many, the find action will stop looking.
161
+ - Matches are made from the top, down (i.e. shallowest directory first).
162
+ - If not set, or set to v(null), it will do unlimited matches.
163
+ - Default is unlimited matches.
164
+ type: int
165
+ version_added: "2.18"
157
166
  extends_documentation_fragment: action_common_attributes
158
167
  attributes:
159
168
  check_mode:
@@ -227,6 +236,16 @@ EXAMPLES = r'''
227
236
  - '^_[0-9]{2,4}_.*.log$'
228
237
  - '^[a-z]{1,5}_.*log$'
229
238
 
239
+ - name: Find file containing "wally" without necessarily reading all files
240
+ ansible.builtin.find:
241
+ paths: /var/log
242
+ file_type: file
243
+ contains: wally
244
+ read_whole_file: true
245
+ patterns: "^.*\\.log$"
246
+ use_regex: true
247
+ recurse: true
248
+ limit: 1
230
249
  '''
231
250
 
232
251
  RETURN = r'''
@@ -450,7 +469,7 @@ def statinfo(st):
450
469
  def main():
451
470
  module = AnsibleModule(
452
471
  argument_spec=dict(
453
- paths=dict(type='list', required=True, aliases=['name', 'path'], elements='str'),
472
+ paths=dict(type='list', required=True, aliases=['name', 'path'], elements='path'),
454
473
  patterns=dict(type='list', default=[], aliases=['pattern'], elements='str'),
455
474
  excludes=dict(type='list', aliases=['exclude'], elements='str'),
456
475
  contains=dict(type='str'),
@@ -467,7 +486,8 @@ def main():
467
486
  depth=dict(type='int'),
468
487
  mode=dict(type='raw'),
469
488
  exact_mode=dict(type='bool', default=True),
470
- encoding=dict(type='str')
489
+ encoding=dict(type='str'),
490
+ limit=dict(type='int')
471
491
  ),
472
492
  supports_check_mode=True,
473
493
  )
@@ -520,17 +540,20 @@ def main():
520
540
  else:
521
541
  module.fail_json(size=params['size'], msg="failed to process size")
522
542
 
543
+ if params['limit'] is not None and params['limit'] <= 0:
544
+ module.fail_json(msg="limit cannot be %d (use None for unlimited)" % params['limit'])
545
+
523
546
  now = time.time()
524
547
  msg = 'All paths examined'
525
548
  looked = 0
526
549
  has_warnings = False
527
550
  for npath in params['paths']:
528
- npath = os.path.expanduser(os.path.expandvars(npath))
529
551
  try:
530
552
  if not os.path.isdir(npath):
531
553
  raise Exception("'%s' is not a directory" % to_native(npath))
532
554
 
533
- for root, dirs, files in os.walk(npath, onerror=handle_walk_errors, followlinks=params['follow']):
555
+ # Setting `topdown=True` to explicitly guarantee matches are made from the shallowest directory first
556
+ for root, dirs, files in os.walk(npath, onerror=handle_walk_errors, followlinks=params['follow'], topdown=True):
534
557
  looked = looked + len(files) + len(dirs)
535
558
  for fsobj in (files + dirs):
536
559
  fsname = os.path.normpath(os.path.join(root, fsobj))
@@ -596,7 +619,12 @@ def main():
596
619
  r.update(statinfo(st))
597
620
  filelist.append(r)
598
621
 
599
- if not params['recurse']:
622
+ if len(filelist) == params["limit"]:
623
+ # Breaks out of directory files loop only
624
+ msg = "Limit of matches reached"
625
+ break
626
+
627
+ if not params['recurse'] or len(filelist) == params["limit"]:
600
628
  break
601
629
  except Exception as e:
602
630
  skipped[npath] = to_text(e)
@@ -27,6 +27,8 @@ options:
27
27
  - By default it will be true if more than one fact module is used.
28
28
  - For low cost/delay fact modules parallelism overhead might end up meaning the whole process takes longer.
29
29
  Test your specific case to see if it is a speed improvement or not.
30
+ - The C(ansible_facts_parallel) variable can be used to set this option,
31
+ overriding the default, but not the direct assignment of the option in the task.
30
32
  type: bool
31
33
  attributes:
32
34
  action:
@@ -49,8 +51,8 @@ attributes:
49
51
  notes:
50
52
  - This is mostly a wrapper around other fact gathering modules.
51
53
  - Options passed into this action must be supported by all the underlying fact modules configured.
52
- - If using C(gather_timeout) and parallel execution, it will limit the total execution time of
53
- modules that do not accept C(gather_timeout) themselves.
54
+ - If using O(ignore:gather_timeout) and parallel execution, it will limit the total execution time of
55
+ modules that do not accept O(ignore:gather_timeout) themselves.
54
56
  - Facts returned by each module will be merged, conflicts will favor 'last merged'.
55
57
  Order is not guaranteed, when doing parallel gathering on multiple modules.
56
58
  author:
@@ -13,7 +13,7 @@ short_description: Downloads files from HTTP, HTTPS, or FTP to node
13
13
  description:
14
14
  - Downloads files from HTTP, HTTPS, or FTP to the remote server. The remote
15
15
  server I(must) have direct access to the remote resource.
16
- - By default, if an environment variable C(<protocol>_proxy) is set on
16
+ - By default, if an environment variable E(<protocol>_proxy) is set on
17
17
  the target host, requests will be sent through that proxy. This
18
18
  behaviour can be overridden by setting a variable for this task
19
19
  (see R(setting the environment,playbooks_environment)),
@@ -27,23 +27,23 @@ version_added: '0.6'
27
27
  options:
28
28
  ciphers:
29
29
  description:
30
- - SSL/TLS Ciphers to use for the request
31
- - 'When a list is provided, all ciphers are joined in order with V(:)'
30
+ - SSL/TLS Ciphers to use for the request.
31
+ - 'When a list is provided, all ciphers are joined in order with C(:).'
32
32
  - See the L(OpenSSL Cipher List Format,https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html#CIPHER-LIST-FORMAT)
33
33
  for more details.
34
- - The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions
34
+ - The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions.
35
35
  type: list
36
36
  elements: str
37
37
  version_added: '2.14'
38
38
  decompress:
39
39
  description:
40
- - Whether to attempt to decompress gzip content-encoded responses
40
+ - Whether to attempt to decompress gzip content-encoded responses.
41
41
  type: bool
42
42
  default: true
43
43
  version_added: '2.14'
44
44
  url:
45
45
  description:
46
- - HTTP, HTTPS, or FTP URL in the form (http|https|ftp)://[user[:pass]]@host.domain[:port]/path
46
+ - HTTP, HTTPS, or FTP URL in the form C((http|https|ftp)://[user[:pass]]@host.domain[:port]/path).
47
47
  type: str
48
48
  required: true
49
49
  dest:
@@ -60,9 +60,9 @@ options:
60
60
  tmp_dest:
61
61
  description:
62
62
  - Absolute path of where temporary file is downloaded to.
63
- - When run on Ansible 2.5 or greater, path defaults to ansible's remote_tmp setting
63
+ - When run on Ansible 2.5 or greater, path defaults to ansible's C(remote_tmp) setting.
64
64
  - When run on Ansible prior to 2.5, it defaults to E(TMPDIR), E(TEMP) or E(TMP) env variables or a platform specific value.
65
- - U(https://docs.python.org/3/library/tempfile.html#tempfile.tempdir)
65
+ - U(https://docs.python.org/3/library/tempfile.html#tempfile.tempdir).
66
66
  type: path
67
67
  version_added: '2.1'
68
68
  force:
@@ -87,18 +87,20 @@ options:
87
87
  - 'If a checksum is passed to this parameter, the digest of the
88
88
  destination file will be calculated after it is downloaded to ensure
89
89
  its integrity and verify that the transfer completed successfully.
90
- Format: <algorithm>:<checksum|url>, e.g. checksum="sha256:D98291AC[...]B6DC7B97",
91
- checksum="sha256:http://example.com/path/sha256sum.txt"'
90
+ Format: <algorithm>:<checksum|url>, for example C(checksum="sha256:D98291AC[...]B6DC7B97",
91
+ C(checksum="sha256:http://example.com/path/sha256sum.txt").'
92
92
  - If you worry about portability, only the sha1 algorithm is available
93
93
  on all platforms and python versions.
94
- - The Python ``hashlib`` module is responsible for providing the available algorithms.
94
+ - The Python C(hashlib) module is responsible for providing the available algorithms.
95
95
  The choices vary based on Python version and OpenSSL version.
96
- - On systems running in FIPS compliant mode, the ``md5`` algorithm may be unavailable.
96
+ - On systems running in FIPS compliant mode, the C(md5) algorithm may be unavailable.
97
97
  - Additionally, if a checksum is passed to this parameter, and the file exist under
98
98
  the O(dest) location, the C(destination_checksum) would be calculated, and if
99
99
  checksum equals C(destination_checksum), the file download would be skipped
100
- (unless O(force) is V(true)). If the checksum does not equal C(destination_checksum),
100
+ (unless O(force=true)). If the checksum does not equal C(destination_checksum),
101
101
  the destination file is deleted.
102
+ - If the checksum URL requires username and password, O(url_username) and O(url_password) are used
103
+ to download the checksum file.
102
104
  type: str
103
105
  default: ''
104
106
  version_added: "2.0"
@@ -185,16 +187,16 @@ options:
185
187
  authentication.
186
188
  - Requires the Python library L(gssapi,https://github.com/pythongssapi/python-gssapi) to be installed.
187
189
  - Credentials for GSSAPI can be specified with O(url_username)/O(url_password) or with the GSSAPI env var
188
- C(KRB5CCNAME) that specified a custom Kerberos credential cache.
190
+ E(KRB5CCNAME) that specified a custom Kerberos credential cache.
189
191
  - NTLM authentication is I(not) supported even if the GSSAPI mech for NTLM has been installed.
190
192
  type: bool
191
193
  default: no
192
194
  version_added: '2.11'
193
195
  use_netrc:
194
196
  description:
195
- - Determining whether to use credentials from ``~/.netrc`` file
196
- - By default .netrc is used with Basic authentication headers
197
- - When set to False, .netrc credentials are ignored
197
+ - Determining whether to use credentials from C(~/.netrc) file.
198
+ - By default C(.netrc) is used with Basic authentication headers.
199
+ - When V(false), C(.netrc) credentials are ignored.
198
200
  type: bool
199
201
  default: true
200
202
  version_added: '2.14'
@@ -661,6 +663,16 @@ def main():
661
663
  result['checksum_src'] != result['checksum_dest'])
662
664
  module.exit_json(msg=info.get('msg', ''), **result)
663
665
 
666
+ # If a checksum was provided, ensure that the temporary file matches this checksum
667
+ # before moving it to the destination.
668
+ if checksum != '':
669
+ tmpsrc_checksum = module.digest_from_file(tmpsrc, algorithm)
670
+
671
+ if checksum != tmpsrc_checksum:
672
+ os.remove(tmpsrc)
673
+ module.fail_json(msg=f"The checksum for {tmpsrc} did not match {checksum}; it was {tmpsrc_checksum}.", **result)
674
+
675
+ # Copy temporary file to destination if necessary
664
676
  backup_file = None
665
677
  if result['checksum_src'] != result['checksum_dest']:
666
678
  try:
@@ -679,13 +691,6 @@ def main():
679
691
  if os.path.exists(tmpsrc):
680
692
  os.remove(tmpsrc)
681
693
 
682
- if checksum != '':
683
- destination_checksum = module.digest_from_file(dest, algorithm)
684
-
685
- if checksum != destination_checksum:
686
- os.remove(dest)
687
- module.fail_json(msg="The checksum for %s did not match %s; it was %s." % (dest, checksum, destination_checksum), **result)
688
-
689
694
  # allow file attribute changes
690
695
  file_args = module.load_file_common_arguments(module.params, path=dest)
691
696
  result['changed'] = module.set_fs_attributes_if_different(file_args, result['changed'])