ansible-core 2.15.4rc1__py3-none-any.whl → 2.16.0b2__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 (427) hide show
  1. ansible/cli/__init__.py +3 -3
  2. ansible/cli/adhoc.py +1 -1
  3. ansible/cli/arguments/option_helpers.py +15 -5
  4. ansible/cli/config.py +2 -2
  5. ansible/cli/console.py +21 -17
  6. ansible/cli/doc.py +8 -9
  7. ansible/cli/galaxy.py +60 -27
  8. ansible/cli/inventory.py +1 -1
  9. ansible/cli/playbook.py +1 -1
  10. ansible/cli/pull.py +2 -2
  11. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  12. ansible/cli/vault.py +11 -6
  13. ansible/collections/__init__.py +0 -29
  14. ansible/collections/list.py +23 -44
  15. ansible/config/ansible_builtin_runtime.yml +8 -4
  16. ansible/config/base.yml +34 -22
  17. ansible/config/manager.py +1 -1
  18. ansible/constants.py +3 -5
  19. ansible/errors/__init__.py +1 -1
  20. ansible/executor/interpreter_discovery.py +1 -1
  21. ansible/executor/module_common.py +39 -32
  22. ansible/executor/play_iterator.py +0 -15
  23. ansible/executor/playbook_executor.py +3 -3
  24. ansible/executor/powershell/module_manifest.py +1 -1
  25. ansible/executor/powershell/module_wrapper.ps1 +4 -1
  26. ansible/executor/process/worker.py +22 -7
  27. ansible/executor/task_executor.py +39 -40
  28. ansible/executor/task_queue_manager.py +8 -11
  29. ansible/galaxy/__init__.py +1 -1
  30. ansible/galaxy/api.py +8 -11
  31. ansible/galaxy/collection/__init__.py +17 -4
  32. ansible/galaxy/collection/concrete_artifact_manager.py +7 -2
  33. ansible/galaxy/collection/galaxy_api_proxy.py +1 -1
  34. ansible/galaxy/data/container/README.md +3 -5
  35. ansible/galaxy/dependency_resolution/__init__.py +1 -6
  36. ansible/galaxy/dependency_resolution/dataclasses.py +22 -1
  37. ansible/galaxy/dependency_resolution/providers.py +61 -69
  38. ansible/galaxy/role.py +31 -13
  39. ansible/galaxy/token.py +2 -2
  40. ansible/inventory/group.py +1 -1
  41. ansible/inventory/manager.py +1 -1
  42. ansible/module_utils/ansible_release.py +2 -2
  43. ansible/module_utils/basic.py +11 -41
  44. ansible/module_utils/common/file.py +0 -100
  45. ansible/module_utils/common/json.py +1 -1
  46. ansible/module_utils/common/locale.py +1 -1
  47. ansible/module_utils/common/text/converters.py +2 -2
  48. ansible/module_utils/common/validation.py +1 -1
  49. ansible/module_utils/compat/_selectors2.py +4 -4
  50. ansible/module_utils/compat/datetime.py +40 -0
  51. ansible/module_utils/compat/selinux.py +1 -1
  52. ansible/module_utils/compat/typing.py +1 -1
  53. ansible/module_utils/connection.py +1 -1
  54. ansible/module_utils/facts/hardware/linux.py +2 -2
  55. ansible/module_utils/facts/hardware/openbsd.py +1 -1
  56. ansible/module_utils/facts/network/linux.py +3 -3
  57. ansible/module_utils/facts/other/facter.py +8 -15
  58. ansible/module_utils/facts/sysctl.py +1 -1
  59. ansible/module_utils/facts/system/date_time.py +2 -2
  60. ansible/module_utils/facts/system/distribution.py +1 -1
  61. ansible/module_utils/facts/system/local.py +6 -2
  62. ansible/module_utils/facts/system/pkg_mgr.py +6 -1
  63. ansible/module_utils/facts/system/service_mgr.py +4 -2
  64. ansible/module_utils/parsing/convert_bool.py +1 -1
  65. ansible/module_utils/service.py +9 -6
  66. ansible/module_utils/urls.py +40 -22
  67. ansible/modules/add_host.py +2 -2
  68. ansible/modules/apt.py +48 -31
  69. ansible/modules/apt_key.py +4 -4
  70. ansible/modules/apt_repository.py +5 -5
  71. ansible/modules/assemble.py +7 -7
  72. ansible/modules/assert.py +1 -1
  73. ansible/modules/async_status.py +11 -7
  74. ansible/modules/async_wrapper.py +1 -1
  75. ansible/modules/blockinfile.py +60 -17
  76. ansible/modules/command.py +37 -15
  77. ansible/modules/copy.py +35 -30
  78. ansible/modules/cron.py +14 -14
  79. ansible/modules/deb822_repository.py +4 -3
  80. ansible/modules/debconf.py +35 -14
  81. ansible/modules/debug.py +1 -1
  82. ansible/modules/dnf.py +29 -27
  83. ansible/modules/dnf5.py +22 -22
  84. ansible/modules/dpkg_selections.py +9 -2
  85. ansible/modules/expect.py +4 -4
  86. ansible/modules/fetch.py +7 -7
  87. ansible/modules/file.py +30 -30
  88. ansible/modules/find.py +82 -22
  89. ansible/modules/gather_facts.py +6 -2
  90. ansible/modules/get_url.py +29 -29
  91. ansible/modules/getent.py +4 -4
  92. ansible/modules/git.py +27 -27
  93. ansible/modules/group.py +5 -12
  94. ansible/modules/hostname.py +21 -2
  95. ansible/modules/include_role.py +5 -5
  96. ansible/modules/include_tasks.py +2 -2
  97. ansible/modules/include_vars.py +5 -5
  98. ansible/modules/iptables.py +70 -65
  99. ansible/modules/known_hosts.py +7 -7
  100. ansible/modules/lineinfile.py +33 -33
  101. ansible/modules/meta.py +13 -13
  102. ansible/modules/package.py +8 -8
  103. ansible/modules/package_facts.py +3 -3
  104. ansible/modules/pause.py +2 -2
  105. ansible/modules/ping.py +5 -5
  106. ansible/modules/pip.py +80 -46
  107. ansible/modules/reboot.py +8 -4
  108. ansible/modules/replace.py +20 -15
  109. ansible/modules/rpm_key.py +2 -2
  110. ansible/modules/script.py +16 -10
  111. ansible/modules/service.py +26 -98
  112. ansible/modules/service_facts.py +36 -12
  113. ansible/modules/set_fact.py +2 -2
  114. ansible/modules/set_stats.py +2 -2
  115. ansible/modules/setup.py +18 -18
  116. ansible/modules/shell.py +3 -3
  117. ansible/modules/stat.py +9 -30
  118. ansible/modules/subversion.py +9 -9
  119. ansible/modules/systemd.py +20 -19
  120. ansible/modules/systemd_service.py +20 -19
  121. ansible/modules/sysvinit.py +26 -21
  122. ansible/modules/tempfile.py +5 -4
  123. ansible/modules/template.py +60 -6
  124. ansible/modules/unarchive.py +21 -18
  125. ansible/modules/uri.py +39 -39
  126. ansible/modules/user.py +81 -53
  127. ansible/modules/wait_for.py +22 -21
  128. ansible/modules/wait_for_connection.py +4 -4
  129. ansible/modules/yum.py +38 -38
  130. ansible/modules/yum_repository.py +58 -80
  131. ansible/parsing/dataloader.py +27 -27
  132. ansible/parsing/mod_args.py +1 -1
  133. ansible/parsing/plugin_docs.py +3 -3
  134. ansible/parsing/splitter.py +14 -16
  135. ansible/parsing/utils/yaml.py +1 -1
  136. ansible/parsing/vault/__init__.py +8 -6
  137. ansible/parsing/yaml/constructor.py +1 -1
  138. ansible/parsing/yaml/objects.py +1 -1
  139. ansible/playbook/__init__.py +1 -1
  140. ansible/playbook/base.py +2 -2
  141. ansible/playbook/block.py +0 -1
  142. ansible/playbook/conditional.py +40 -114
  143. ansible/playbook/helpers.py +5 -28
  144. ansible/playbook/included_file.py +8 -7
  145. ansible/playbook/play.py +1 -1
  146. ansible/playbook/play_context.py +2 -2
  147. ansible/playbook/playbook_include.py +2 -2
  148. ansible/playbook/role/__init__.py +1 -1
  149. ansible/playbook/role/include.py +1 -1
  150. ansible/playbook/role/metadata.py +1 -1
  151. ansible/playbook/role_include.py +1 -1
  152. ansible/playbook/task.py +2 -2
  153. ansible/playbook/task_include.py +1 -24
  154. ansible/plugins/__init__.py +13 -5
  155. ansible/plugins/action/__init__.py +17 -43
  156. ansible/plugins/action/add_host.py +2 -3
  157. ansible/plugins/action/assemble.py +1 -1
  158. ansible/plugins/action/assert.py +2 -1
  159. ansible/plugins/action/copy.py +2 -2
  160. ansible/plugins/action/debug.py +2 -1
  161. ansible/plugins/action/fail.py +1 -0
  162. ansible/plugins/action/fetch.py +3 -1
  163. ansible/plugins/action/gather_facts.py +37 -13
  164. ansible/plugins/action/group_by.py +1 -0
  165. ansible/plugins/action/include_vars.py +3 -2
  166. ansible/plugins/action/normal.py +3 -3
  167. ansible/plugins/action/pause.py +1 -1
  168. ansible/plugins/action/reboot.py +21 -16
  169. ansible/plugins/action/script.py +23 -8
  170. ansible/plugins/action/set_fact.py +1 -0
  171. ansible/plugins/action/set_stats.py +1 -0
  172. ansible/plugins/action/shell.py +6 -0
  173. ansible/plugins/action/template.py +1 -1
  174. ansible/plugins/action/unarchive.py +1 -1
  175. ansible/plugins/action/uri.py +1 -1
  176. ansible/plugins/action/validate_argument_spec.py +1 -0
  177. ansible/plugins/action/wait_for_connection.py +4 -4
  178. ansible/plugins/become/__init__.py +1 -1
  179. ansible/plugins/become/su.py +1 -1
  180. ansible/plugins/cache/__init__.py +1 -1
  181. ansible/plugins/callback/junit.py +1 -1
  182. ansible/plugins/callback/oneline.py +1 -1
  183. ansible/plugins/callback/tree.py +1 -1
  184. ansible/plugins/cliconf/__init__.py +2 -2
  185. ansible/plugins/connection/__init__.py +65 -37
  186. ansible/plugins/connection/local.py +9 -8
  187. ansible/plugins/connection/paramiko_ssh.py +34 -28
  188. ansible/plugins/connection/psrp.py +56 -43
  189. ansible/plugins/connection/ssh.py +67 -43
  190. ansible/plugins/connection/winrm.py +77 -30
  191. ansible/plugins/doc_fragments/constructed.py +4 -4
  192. ansible/plugins/doc_fragments/files.py +12 -12
  193. ansible/plugins/doc_fragments/inventory_cache.py +0 -6
  194. ansible/plugins/doc_fragments/result_format_callback.py +5 -5
  195. ansible/plugins/doc_fragments/shell_common.py +2 -2
  196. ansible/plugins/doc_fragments/shell_windows.py +1 -1
  197. ansible/plugins/doc_fragments/template_common.py +6 -6
  198. ansible/plugins/doc_fragments/url.py +10 -10
  199. ansible/plugins/doc_fragments/url_windows.py +15 -15
  200. ansible/plugins/doc_fragments/vars_plugin_staging.py +4 -4
  201. ansible/plugins/filter/b64decode.yml +1 -1
  202. ansible/plugins/filter/b64encode.yml +2 -2
  203. ansible/plugins/filter/bool.yml +5 -5
  204. ansible/plugins/filter/combine.yml +1 -1
  205. ansible/plugins/filter/commonpath.yml +2 -1
  206. ansible/plugins/filter/core.py +6 -8
  207. ansible/plugins/filter/dict2items.yml +11 -1
  208. ansible/plugins/filter/difference.yml +1 -0
  209. ansible/plugins/filter/encryption.py +1 -1
  210. ansible/plugins/filter/extract.yml +1 -1
  211. ansible/plugins/filter/flatten.yml +1 -1
  212. ansible/plugins/filter/from_yaml.yml +1 -1
  213. ansible/plugins/filter/from_yaml_all.yml +2 -2
  214. ansible/plugins/filter/hash.yml +1 -1
  215. ansible/plugins/filter/human_readable.yml +1 -1
  216. ansible/plugins/filter/human_to_bytes.yml +2 -2
  217. ansible/plugins/filter/intersect.yml +1 -0
  218. ansible/plugins/filter/mandatory.yml +7 -0
  219. ansible/plugins/filter/mathstuff.py +15 -17
  220. ansible/plugins/filter/normpath.yml +1 -1
  221. ansible/plugins/filter/path_join.yml +8 -1
  222. ansible/plugins/filter/realpath.yml +3 -2
  223. ansible/plugins/filter/regex_findall.yml +8 -2
  224. ansible/plugins/filter/regex_replace.yml +9 -3
  225. ansible/plugins/filter/regex_search.yml +8 -2
  226. ansible/plugins/filter/relpath.yml +2 -2
  227. ansible/plugins/filter/root.yml +1 -1
  228. ansible/plugins/filter/splitext.yml +1 -1
  229. ansible/plugins/filter/subelements.yml +2 -2
  230. ansible/plugins/filter/symmetric_difference.yml +1 -0
  231. ansible/plugins/filter/ternary.yml +5 -5
  232. ansible/plugins/filter/to_json.yml +7 -7
  233. ansible/plugins/filter/to_nice_json.yml +5 -5
  234. ansible/plugins/filter/to_yaml.yml +2 -2
  235. ansible/plugins/filter/type_debug.yml +1 -1
  236. ansible/plugins/filter/union.yml +1 -0
  237. ansible/plugins/filter/unvault.yml +2 -2
  238. ansible/plugins/filter/urldecode.yml +13 -32
  239. ansible/plugins/filter/urlsplit.py +1 -1
  240. ansible/plugins/filter/vault.yml +1 -1
  241. ansible/plugins/filter/zip.yml +1 -1
  242. ansible/plugins/filter/zip_longest.yml +1 -1
  243. ansible/plugins/inventory/__init__.py +1 -1
  244. ansible/plugins/inventory/advanced_host_list.py +1 -1
  245. ansible/plugins/inventory/constructed.py +2 -2
  246. ansible/plugins/inventory/host_list.py +1 -1
  247. ansible/plugins/inventory/ini.py +6 -3
  248. ansible/plugins/inventory/script.py +8 -2
  249. ansible/plugins/inventory/toml.py +1 -1
  250. ansible/plugins/inventory/yaml.py +1 -1
  251. ansible/plugins/list.py +21 -17
  252. ansible/plugins/loader.py +66 -88
  253. ansible/plugins/lookup/__init__.py +1 -1
  254. ansible/plugins/lookup/config.py +16 -6
  255. ansible/plugins/lookup/csvfile.py +7 -4
  256. ansible/plugins/lookup/env.py +1 -1
  257. ansible/plugins/lookup/file.py +5 -2
  258. ansible/plugins/lookup/fileglob.py +5 -2
  259. ansible/plugins/lookup/first_found.py +20 -14
  260. ansible/plugins/lookup/ini.py +6 -3
  261. ansible/plugins/lookup/lines.py +2 -1
  262. ansible/plugins/lookup/password.py +7 -7
  263. ansible/plugins/lookup/pipe.py +1 -0
  264. ansible/plugins/lookup/random_choice.py +2 -2
  265. ansible/plugins/lookup/sequence.py +1 -1
  266. ansible/plugins/lookup/subelements.py +2 -2
  267. ansible/plugins/lookup/template.py +4 -1
  268. ansible/plugins/lookup/unvault.py +4 -1
  269. ansible/plugins/lookup/url.py +6 -6
  270. ansible/plugins/lookup/varnames.py +1 -1
  271. ansible/plugins/netconf/__init__.py +3 -3
  272. ansible/plugins/shell/__init__.py +1 -1
  273. ansible/plugins/shell/cmd.py +7 -7
  274. ansible/plugins/shell/powershell.py +1 -1
  275. ansible/plugins/strategy/__init__.py +8 -10
  276. ansible/plugins/strategy/free.py +1 -1
  277. ansible/plugins/strategy/linear.py +3 -3
  278. ansible/plugins/terminal/__init__.py +2 -2
  279. ansible/plugins/test/abs.yml +1 -1
  280. ansible/plugins/test/all.yml +1 -1
  281. ansible/plugins/test/any.yml +1 -1
  282. ansible/plugins/test/change.yml +2 -2
  283. ansible/plugins/test/changed.yml +2 -2
  284. ansible/plugins/test/contains.yml +1 -1
  285. ansible/plugins/test/core.py +1 -1
  286. ansible/plugins/test/directory.yml +1 -1
  287. ansible/plugins/test/exists.yml +3 -2
  288. ansible/plugins/test/failed.yml +2 -2
  289. ansible/plugins/test/failure.yml +2 -2
  290. ansible/plugins/test/falsy.yml +2 -2
  291. ansible/plugins/test/file.yml +1 -1
  292. ansible/plugins/test/finished.yml +2 -2
  293. ansible/plugins/test/is_abs.yml +1 -1
  294. ansible/plugins/test/is_dir.yml +1 -1
  295. ansible/plugins/test/is_file.yml +1 -1
  296. ansible/plugins/test/is_link.yml +1 -1
  297. ansible/plugins/test/is_mount.yml +1 -1
  298. ansible/plugins/test/is_same_file.yml +1 -1
  299. ansible/plugins/test/isnan.yml +1 -1
  300. ansible/plugins/test/issubset.yml +1 -2
  301. ansible/plugins/test/issuperset.yml +1 -2
  302. ansible/plugins/test/link.yml +1 -1
  303. ansible/plugins/test/link_exists.yml +1 -1
  304. ansible/plugins/test/match.yml +2 -2
  305. ansible/plugins/test/mount.yml +1 -1
  306. ansible/plugins/test/nan.yml +1 -1
  307. ansible/plugins/test/reachable.yml +2 -2
  308. ansible/plugins/test/regex.yml +1 -1
  309. ansible/plugins/test/same_file.yml +1 -1
  310. ansible/plugins/test/search.yml +2 -2
  311. ansible/plugins/test/skip.yml +3 -3
  312. ansible/plugins/test/skipped.yml +3 -3
  313. ansible/plugins/test/started.yml +2 -2
  314. ansible/plugins/test/subset.yml +1 -2
  315. ansible/plugins/test/succeeded.yml +2 -2
  316. ansible/plugins/test/success.yml +2 -2
  317. ansible/plugins/test/successful.yml +2 -2
  318. ansible/plugins/test/superset.yml +1 -2
  319. ansible/plugins/test/truthy.yml +3 -3
  320. ansible/plugins/test/unreachable.yml +2 -2
  321. ansible/plugins/test/uri.yml +1 -1
  322. ansible/plugins/test/url.yml +1 -1
  323. ansible/plugins/test/urn.yml +1 -1
  324. ansible/plugins/test/vault_encrypted.yml +1 -1
  325. ansible/plugins/test/version.yml +7 -7
  326. ansible/plugins/test/version_compare.yml +7 -7
  327. ansible/plugins/vars/host_group_vars.py +1 -1
  328. ansible/release.py +2 -2
  329. ansible/template/__init__.py +24 -26
  330. ansible/template/native_helpers.py +1 -1
  331. ansible/template/vars.py +1 -1
  332. ansible/utils/_junit_xml.py +1 -1
  333. ansible/utils/cmd_functions.py +1 -1
  334. ansible/utils/collection_loader/_collection_finder.py +12 -1
  335. ansible/utils/display.py +113 -62
  336. ansible/utils/encrypt.py +11 -14
  337. ansible/utils/hashing.py +1 -1
  338. ansible/utils/jsonrpc.py +1 -1
  339. ansible/utils/path.py +1 -1
  340. ansible/utils/plugin_docs.py +1 -1
  341. ansible/utils/py3compat.py +1 -1
  342. ansible/utils/shlex.py +2 -10
  343. ansible/utils/ssh_functions.py +5 -4
  344. ansible/utils/unicode.py +1 -1
  345. ansible/utils/unsafe_proxy.py +1 -1
  346. ansible/utils/vars.py +4 -29
  347. ansible/vars/hostvars.py +1 -2
  348. ansible/vars/manager.py +13 -9
  349. ansible/vars/plugins.py +2 -2
  350. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/COPYING +4 -5
  351. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/METADATA +2 -4
  352. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/RECORD +424 -425
  353. ansible_test/_data/completion/docker.txt +9 -9
  354. ansible_test/_data/completion/remote.txt +4 -7
  355. ansible_test/_data/completion/windows.txt +0 -2
  356. ansible_test/_data/requirements/ansible-test.txt +2 -1
  357. ansible_test/_data/requirements/ansible.txt +0 -3
  358. ansible_test/_data/requirements/constraints.txt +0 -2
  359. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -5
  360. ansible_test/_data/requirements/sanity.changelog.in +1 -2
  361. ansible_test/_data/requirements/sanity.changelog.txt +4 -6
  362. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -4
  363. ansible_test/_data/requirements/sanity.import.txt +1 -3
  364. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -3
  365. ansible_test/_data/requirements/sanity.mypy.txt +12 -12
  366. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  367. ansible_test/_data/requirements/sanity.pylint.txt +6 -12
  368. ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -3
  369. ansible_test/_data/requirements/sanity.validate-modules.in +1 -1
  370. ansible_test/_data/requirements/sanity.validate-modules.txt +3 -5
  371. ansible_test/_data/requirements/sanity.yamllint.txt +3 -5
  372. ansible_test/_data/requirements/units.txt +0 -1
  373. ansible_test/_internal/ci/azp.py +4 -4
  374. ansible_test/_internal/cli/environments.py +0 -13
  375. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +4 -4
  376. ansible_test/_internal/commands/coverage/combine.py +1 -1
  377. ansible_test/_internal/commands/integration/cloud/acme.py +6 -8
  378. ansible_test/_internal/commands/integration/cloud/cs.py +4 -9
  379. ansible_test/_internal/commands/integration/cloud/galaxy.py +103 -96
  380. ansible_test/_internal/commands/integration/cloud/httptester.py +0 -3
  381. ansible_test/_internal/commands/integration/cloud/nios.py +7 -9
  382. ansible_test/_internal/commands/integration/cloud/openshift.py +2 -7
  383. ansible_test/_internal/commands/integration/cloud/vcenter.py +11 -95
  384. ansible_test/_internal/commands/sanity/__init__.py +10 -0
  385. ansible_test/_internal/commands/sanity/import.py +8 -2
  386. ansible_test/_internal/commands/sanity/pylint.py +27 -1
  387. ansible_test/_internal/commands/units/__init__.py +2 -1
  388. ansible_test/_internal/config.py +0 -7
  389. ansible_test/_internal/containers.py +11 -56
  390. ansible_test/_internal/core_ci.py +0 -7
  391. ansible_test/_internal/coverage_util.py +8 -3
  392. ansible_test/_internal/delegation.py +0 -1
  393. ansible_test/_internal/diff.py +1 -1
  394. ansible_test/_internal/docker_util.py +9 -2
  395. ansible_test/_internal/host_profiles.py +6 -6
  396. ansible_test/_internal/http.py +1 -1
  397. ansible_test/_internal/junit_xml.py +1 -1
  398. ansible_test/_internal/pypi_proxy.py +1 -1
  399. ansible_test/_internal/python_requirements.py +3 -8
  400. ansible_test/_internal/util.py +1 -6
  401. ansible_test/_util/controller/sanity/code-smell/no-get-exception.json +4 -0
  402. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.json +4 -0
  403. ansible_test/_util/controller/sanity/code-smell/use-compat-six.json +4 -0
  404. ansible_test/_util/controller/sanity/mypy/ansible-core.ini +3 -0
  405. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +2 -0
  406. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +0 -1
  407. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  408. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +172 -10
  409. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +13 -2
  410. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +7 -1
  411. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +6 -6
  412. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -1
  413. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -1
  414. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +3 -3
  415. ansible_test/_util/controller/tools/collection_detail.py +2 -2
  416. ansible_test/_util/target/common/constants.py +2 -2
  417. ansible_test/_util/target/pytest/plugins/ansible_forked.py +103 -0
  418. ansible_test/_util/target/sanity/import/importer.py +0 -8
  419. ansible_test/_util/target/setup/bootstrap.sh +36 -16
  420. ansible_test/_util/target/setup/quiet_pip.py +0 -4
  421. ansible/modules/_include.py +0 -80
  422. ansible_test/_internal/commands/integration/cloud/foreman.py +0 -102
  423. ansible_test/_util/target/setup/ConfigureRemotingForAnsible.ps1 +0 -435
  424. {ansible_core-2.15.4rc1.data → ansible_core-2.16.0b2.data}/scripts/ansible-test +0 -0
  425. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/WHEEL +0 -0
  426. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/entry_points.txt +0 -0
  427. {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/top_level.txt +0 -0
@@ -28,6 +28,8 @@ DOCUMENTATION = '''
28
28
  notes:
29
29
  - Enabled in configuration by default.
30
30
  - The plugin does not cache results because external inventory scripts are responsible for their own caching.
31
+ - To write your own inventory script see (R(Developing dynamic inventory,developing_inventory) from the documentation site.
32
+ - To find the scripts that used to be part of the code release, go to U(https://github.com/ansible-community/contrib-scripts/).
31
33
  '''
32
34
 
33
35
  import os
@@ -37,7 +39,7 @@ from collections.abc import Mapping
37
39
 
38
40
  from ansible.errors import AnsibleError, AnsibleParserError
39
41
  from ansible.module_utils.basic import json_dict_bytes_to_unicode
40
- from ansible.module_utils._text import to_native, to_text
42
+ from ansible.module_utils.common.text.converters import to_native, to_text
41
43
  from ansible.plugins.inventory import BaseInventoryPlugin
42
44
  from ansible.utils.display import Display
43
45
 
@@ -187,7 +189,11 @@ class InventoryModule(BaseInventoryPlugin):
187
189
  sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
188
190
  except OSError as e:
189
191
  raise AnsibleError("problem running %s (%s)" % (' '.join(cmd), e))
190
- (out, err) = sp.communicate()
192
+ (out, stderr) = sp.communicate()
193
+
194
+ if sp.returncode != 0:
195
+ raise AnsibleError("Inventory script (%s) had an execution error: %s" % (path, to_native(stderr)))
196
+
191
197
  if out.strip() == '':
192
198
  return {}
193
199
  try:
@@ -94,7 +94,7 @@ from collections.abc import MutableMapping, MutableSequence
94
94
  from functools import partial
95
95
 
96
96
  from ansible.errors import AnsibleFileNotFound, AnsibleParserError, AnsibleRuntimeError
97
- from ansible.module_utils._text import to_bytes, to_native, to_text
97
+ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
98
98
  from ansible.module_utils.six import string_types, text_type
99
99
  from ansible.parsing.yaml.objects import AnsibleSequence, AnsibleUnicode
100
100
  from ansible.plugins.inventory import BaseFileInventoryPlugin
@@ -72,7 +72,7 @@ from collections.abc import MutableMapping
72
72
 
73
73
  from ansible.errors import AnsibleError, AnsibleParserError
74
74
  from ansible.module_utils.six import string_types
75
- from ansible.module_utils._text import to_native, to_text
75
+ from ansible.module_utils.common.text.converters import to_native, to_text
76
76
  from ansible.plugins.inventory import BaseFileInventoryPlugin
77
77
 
78
78
  NoneType = type(None)
ansible/plugins/list.py CHANGED
@@ -11,7 +11,7 @@ from ansible import context
11
11
  from ansible import constants as C
12
12
  from ansible.collections.list import list_collections
13
13
  from ansible.errors import AnsibleError
14
- from ansible.module_utils._text import to_native, to_bytes
14
+ from ansible.module_utils.common.text.converters import to_native, to_bytes
15
15
  from ansible.plugins import loader
16
16
  from ansible.utils.display import Display
17
17
  from ansible.utils.collection_loader._collection_finder import _get_collection_path
@@ -171,28 +171,32 @@ def list_collection_plugins(ptype, collections, search_paths=None):
171
171
  return plugins
172
172
 
173
173
 
174
- def list_plugins(ptype, collection=None, search_paths=None):
174
+ def list_plugins(ptype, collections=None, search_paths=None):
175
+ if isinstance(collections, str):
176
+ collections = [collections]
175
177
 
176
178
  # {plugin_name: (filepath, class), ...}
177
179
  plugins = {}
178
- collections = {}
179
- if collection is None:
180
+ plugin_collections = {}
181
+ if collections is None:
180
182
  # list all collections, add synthetic ones
181
- collections['ansible.builtin'] = b''
182
- collections['ansible.legacy'] = b''
183
- collections.update(list_collections(search_paths=search_paths, dedupe=True))
184
- elif collection == 'ansible.legacy':
185
- # add builtin, since legacy also resolves to these
186
- collections[collection] = b''
187
- collections['ansible.builtin'] = b''
183
+ plugin_collections['ansible.builtin'] = b''
184
+ plugin_collections['ansible.legacy'] = b''
185
+ plugin_collections.update(list_collections(search_paths=search_paths, dedupe=True))
188
186
  else:
189
- try:
190
- collections[collection] = to_bytes(_get_collection_path(collection))
191
- except ValueError as e:
192
- raise AnsibleError("Cannot use supplied collection {0}: {1}".format(collection, to_native(e)), orig_exc=e)
187
+ for collection in collections:
188
+ if collection == 'ansible.legacy':
189
+ # add builtin, since legacy also resolves to these
190
+ plugin_collections[collection] = b''
191
+ plugin_collections['ansible.builtin'] = b''
192
+ else:
193
+ try:
194
+ plugin_collections[collection] = to_bytes(_get_collection_path(collection))
195
+ except ValueError as e:
196
+ raise AnsibleError("Cannot use supplied collection {0}: {1}".format(collection, to_native(e)), orig_exc=e)
193
197
 
194
- if collections:
195
- plugins.update(list_collection_plugins(ptype, collections))
198
+ if plugin_collections:
199
+ plugins.update(list_collection_plugins(ptype, plugin_collections))
196
200
 
197
201
  return plugins
198
202
 
ansible/plugins/loader.py CHANGED
@@ -18,10 +18,14 @@ from collections import defaultdict, namedtuple
18
18
  from traceback import format_exc
19
19
 
20
20
  import ansible.module_utils.compat.typing as t
21
+
22
+ from .filter import AnsibleJinja2Filter
23
+ from .test import AnsibleJinja2Test
24
+
21
25
  from ansible import __version__ as ansible_version
22
26
  from ansible import constants as C
23
27
  from ansible.errors import AnsibleError, AnsiblePluginCircularRedirect, AnsiblePluginRemovedError, AnsibleCollectionUnsupportedVersionError
24
- from ansible.module_utils._text import to_bytes, to_text, to_native
28
+ from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
25
29
  from ansible.module_utils.compat.importlib import import_module
26
30
  from ansible.module_utils.six import string_types
27
31
  from ansible.parsing.utils.yaml import from_yaml
@@ -1067,28 +1071,17 @@ class Jinja2Loader(PluginLoader):
1067
1071
  We need to do a few things differently in the base class because of file == plugin
1068
1072
  assumptions and dedupe logic.
1069
1073
  """
1070
- def __init__(self, class_name, package, config, subdir, aliases=None, required_base_class=None):
1071
-
1074
+ def __init__(self, class_name, package, config, subdir, plugin_wrapper_type, aliases=None, required_base_class=None):
1072
1075
  super(Jinja2Loader, self).__init__(class_name, package, config, subdir, aliases=aliases, required_base_class=required_base_class)
1073
- self._loaded_j2_file_maps = []
1076
+ self._plugin_wrapper_type = plugin_wrapper_type
1077
+ self._cached_non_collection_wrappers = {}
1074
1078
 
1075
1079
  def _clear_caches(self):
1076
1080
  super(Jinja2Loader, self)._clear_caches()
1077
- self._loaded_j2_file_maps = []
1081
+ self._cached_non_collection_wrappers = {}
1078
1082
 
1079
1083
  def find_plugin(self, name, mod_type='', ignore_deprecated=False, check_aliases=False, collection_list=None):
1080
-
1081
- # TODO: handle collection plugin find, see 'get_with_context'
1082
- # this can really 'find plugin file'
1083
- plugin = super(Jinja2Loader, self).find_plugin(name, mod_type=mod_type, ignore_deprecated=ignore_deprecated, check_aliases=check_aliases,
1084
- collection_list=collection_list)
1085
-
1086
- # if not found, try loading all non collection plugins and see if this in there
1087
- if not plugin:
1088
- all_plugins = self.all()
1089
- plugin = all_plugins.get(name, None)
1090
-
1091
- return plugin
1084
+ raise NotImplementedError('find_plugin is not supported on Jinja2Loader')
1092
1085
 
1093
1086
  @property
1094
1087
  def method_map_name(self):
@@ -1122,8 +1115,7 @@ class Jinja2Loader(PluginLoader):
1122
1115
  for func_name, func in plugin_map:
1123
1116
  fq_name = '.'.join((collection, func_name))
1124
1117
  full = '.'.join((full_name, func_name))
1125
- pclass = self._load_jinja2_class()
1126
- plugin = pclass(func)
1118
+ plugin = self._plugin_wrapper_type(func)
1127
1119
  if plugin in plugins:
1128
1120
  continue
1129
1121
  self._update_object(plugin, full, plugin_path, resolved=fq_name)
@@ -1131,27 +1123,28 @@ class Jinja2Loader(PluginLoader):
1131
1123
 
1132
1124
  return plugins
1133
1125
 
1126
+ # FUTURE: now that the resulting plugins are closer, refactor base class method with some extra
1127
+ # hooks so we can avoid all the duplicated plugin metadata logic, and also cache the collection results properly here
1134
1128
  def get_with_context(self, name, *args, **kwargs):
1135
-
1136
- # found_in_cache = True
1137
- class_only = kwargs.pop('class_only', False) # just pop it, dont want to pass through
1138
- collection_list = kwargs.pop('collection_list', None)
1129
+ # pop N/A kwargs to avoid passthrough to parent methods
1130
+ kwargs.pop('class_only', False)
1131
+ kwargs.pop('collection_list', None)
1139
1132
 
1140
1133
  context = PluginLoadContext()
1141
1134
 
1142
1135
  # avoid collection path for legacy
1143
1136
  name = name.removeprefix('ansible.legacy.')
1144
1137
 
1145
- if '.' not in name:
1146
- # Filter/tests must always be FQCN except builtin and legacy
1147
- for known_plugin in self.all(*args, **kwargs):
1148
- if known_plugin.matches_name([name]):
1149
- context.resolved = True
1150
- context.plugin_resolved_name = name
1151
- context.plugin_resolved_path = known_plugin._original_path
1152
- context.plugin_resolved_collection = 'ansible.builtin' if known_plugin.ansible_name.startswith('ansible.builtin.') else ''
1153
- context._resolved_fqcn = known_plugin.ansible_name
1154
- return get_with_context_result(known_plugin, context)
1138
+ self._ensure_non_collection_wrappers(*args, **kwargs)
1139
+
1140
+ # check for stuff loaded via legacy/builtin paths first
1141
+ if known_plugin := self._cached_non_collection_wrappers.get(name):
1142
+ context.resolved = True
1143
+ context.plugin_resolved_name = name
1144
+ context.plugin_resolved_path = known_plugin._original_path
1145
+ context.plugin_resolved_collection = 'ansible.builtin' if known_plugin.ansible_name.startswith('ansible.builtin.') else ''
1146
+ context._resolved_fqcn = known_plugin.ansible_name
1147
+ return get_with_context_result(known_plugin, context)
1155
1148
 
1156
1149
  plugin = None
1157
1150
  key, leaf_key = get_fqcr_and_name(name)
@@ -1237,14 +1230,10 @@ class Jinja2Loader(PluginLoader):
1237
1230
  # use 'parent' loader class to find files, but cannot return this as it can contain
1238
1231
  # multiple plugins per file
1239
1232
  plugin_impl = super(Jinja2Loader, self).get_with_context(module_name, *args, **kwargs)
1240
- except Exception as e:
1241
- raise KeyError(to_native(e))
1242
-
1243
- try:
1244
1233
  method_map = getattr(plugin_impl.object, self.method_map_name)
1245
1234
  plugin_map = method_map().items()
1246
1235
  except Exception as e:
1247
- display.warning("Skipping %s plugins in '%s' as it seems to be invalid: %r" % (self.type, to_text(plugin_impl.object._original_path), e))
1236
+ display.warning(f"Skipping {self.type} plugins in {module_name}'; an error occurred while loading: {e}")
1248
1237
  continue
1249
1238
 
1250
1239
  for func_name, func in plugin_map:
@@ -1253,11 +1242,11 @@ class Jinja2Loader(PluginLoader):
1253
1242
  # TODO: load anyways into CACHE so we only match each at end of loop
1254
1243
  # the files themseves should already be cached by base class caching of modules(python)
1255
1244
  if key in (func_name, fq_name):
1256
- pclass = self._load_jinja2_class()
1257
- plugin = pclass(func)
1245
+ plugin = self._plugin_wrapper_type(func)
1258
1246
  if plugin:
1259
1247
  context = plugin_impl.plugin_load_context
1260
1248
  self._update_object(plugin, src_name, plugin_impl.object._original_path, resolved=fq_name)
1249
+ # FIXME: once we start caching these results, we'll be missing functions that would have loaded later
1261
1250
  break # go to next file as it can override if dupe (dont break both loops)
1262
1251
 
1263
1252
  except AnsiblePluginRemovedError as apre:
@@ -1272,8 +1261,7 @@ class Jinja2Loader(PluginLoader):
1272
1261
  return get_with_context_result(plugin, context)
1273
1262
 
1274
1263
  def all(self, *args, **kwargs):
1275
-
1276
- # inputs, we ignore 'dedupe' we always do, used in base class to find files for this one
1264
+ kwargs.pop('_dedupe', None)
1277
1265
  path_only = kwargs.pop('path_only', False)
1278
1266
  class_only = kwargs.pop('class_only', False) # basically ignored for test/filters since they are functions
1279
1267
 
@@ -1281,9 +1269,19 @@ class Jinja2Loader(PluginLoader):
1281
1269
  if path_only and class_only:
1282
1270
  raise AnsibleError('Do not set both path_only and class_only when calling PluginLoader.all()')
1283
1271
 
1284
- found = set()
1272
+ self._ensure_non_collection_wrappers(*args, **kwargs)
1273
+ if path_only:
1274
+ yield from (w._original_path for w in self._cached_non_collection_wrappers.values())
1275
+ else:
1276
+ yield from (w for w in self._cached_non_collection_wrappers.values())
1277
+
1278
+ def _ensure_non_collection_wrappers(self, *args, **kwargs):
1279
+ if self._cached_non_collection_wrappers:
1280
+ return
1281
+
1285
1282
  # get plugins from files in configured paths (multiple in each)
1286
- for p_map in self._j2_all_file_maps(*args, **kwargs):
1283
+ for p_map in super(Jinja2Loader, self).all(*args, **kwargs):
1284
+ is_builtin = p_map.ansible_name.startswith('ansible.builtin.')
1287
1285
 
1288
1286
  # p_map is really object from file with class that holds multiple plugins
1289
1287
  plugins_list = getattr(p_map, self.method_map_name)
@@ -1294,57 +1292,35 @@ class Jinja2Loader(PluginLoader):
1294
1292
  continue
1295
1293
 
1296
1294
  for plugin_name in plugins.keys():
1297
- if plugin_name in _PLUGIN_FILTERS[self.package]:
1298
- display.debug("%s skipped due to a defined plugin filter" % plugin_name)
1295
+ if '.' in plugin_name:
1296
+ display.debug(f'{plugin_name} skipped in {p_map._original_path}; Jinja plugin short names may not contain "."')
1299
1297
  continue
1300
1298
 
1301
- if plugin_name in found:
1302
- display.debug("%s skipped as duplicate" % plugin_name)
1299
+ if plugin_name in _PLUGIN_FILTERS[self.package]:
1300
+ display.debug("%s skipped due to a defined plugin filter" % plugin_name)
1303
1301
  continue
1304
1302
 
1305
- if path_only:
1306
- result = p_map._original_path
1307
- else:
1308
- # loader class is for the file with multiple plugins, but each plugin now has it's own class
1309
- pclass = self._load_jinja2_class()
1310
- result = pclass(plugins[plugin_name]) # if bad plugin, let exception rise
1311
- found.add(plugin_name)
1312
- fqcn = plugin_name
1313
- collection = '.'.join(p_map.ansible_name.split('.')[:2]) if p_map.ansible_name.count('.') >= 2 else ''
1314
- if not plugin_name.startswith(collection):
1315
- fqcn = f"{collection}.{plugin_name}"
1316
-
1317
- self._update_object(result, plugin_name, p_map._original_path, resolved=fqcn)
1318
- yield result
1319
-
1320
- def _load_jinja2_class(self):
1321
- """ override the normal method of plugin classname as these are used in the generic funciton
1322
- to access the 'multimap' of filter/tests to function, this is a 'singular' plugin for
1323
- each entry.
1324
- """
1325
- class_name = 'AnsibleJinja2%s' % get_plugin_class(self.class_name).capitalize()
1326
- module = __import__(self.package, fromlist=[class_name])
1327
-
1328
- return getattr(module, class_name)
1303
+ # the plugin class returned by the loader may host multiple Jinja plugins, but we wrap each plugin in
1304
+ # its own surrogate wrapper instance here to ease the bookkeeping...
1305
+ wrapper = self._plugin_wrapper_type(plugins[plugin_name])
1306
+ fqcn = plugin_name
1307
+ collection = '.'.join(p_map.ansible_name.split('.')[:2]) if p_map.ansible_name.count('.') >= 2 else ''
1308
+ if not plugin_name.startswith(collection):
1309
+ fqcn = f"{collection}.{plugin_name}"
1329
1310
 
1330
- def _j2_all_file_maps(self, *args, **kwargs):
1331
- """
1332
- * Unlike other plugin types, file != plugin, a file can contain multiple plugins (of same type).
1333
- This is why we do not deduplicate ansible file names at this point, we mostly care about
1334
- the names of the actual jinja2 plugins which are inside of our files.
1335
- * This method will NOT fetch collection plugin files, only those that would be expected under 'ansible.builtin/legacy'.
1336
- """
1337
- # populate cache if needed
1338
- if not self._loaded_j2_file_maps:
1311
+ self._update_object(wrapper, plugin_name, p_map._original_path, resolved=fqcn)
1339
1312
 
1340
- # We don't deduplicate ansible file names.
1341
- # Instead, calling code deduplicates jinja2 plugin names when loading each file.
1342
- kwargs['_dedupe'] = False
1313
+ target_names = {plugin_name, fqcn}
1314
+ if is_builtin:
1315
+ target_names.add(f'ansible.builtin.{plugin_name}')
1343
1316
 
1344
- # To match correct precedence, call base class' all() to get a list of files,
1345
- self._loaded_j2_file_maps = list(super(Jinja2Loader, self).all(*args, **kwargs))
1317
+ for target_name in target_names:
1318
+ if existing_plugin := self._cached_non_collection_wrappers.get(target_name):
1319
+ display.debug(f'Jinja plugin {target_name} from {p_map._original_path} skipped; '
1320
+ f'shadowed by plugin from {existing_plugin._original_path})')
1321
+ continue
1346
1322
 
1347
- return self._loaded_j2_file_maps
1323
+ self._cached_non_collection_wrappers[target_name] = wrapper
1348
1324
 
1349
1325
 
1350
1326
  def get_fqcr_and_name(resource, collection='ansible.builtin'):
@@ -1572,13 +1548,15 @@ filter_loader = Jinja2Loader(
1572
1548
  'ansible.plugins.filter',
1573
1549
  C.DEFAULT_FILTER_PLUGIN_PATH,
1574
1550
  'filter_plugins',
1551
+ AnsibleJinja2Filter
1575
1552
  )
1576
1553
 
1577
1554
  test_loader = Jinja2Loader(
1578
1555
  'TestModule',
1579
1556
  'ansible.plugins.test',
1580
1557
  C.DEFAULT_TEST_PLUGIN_PATH,
1581
- 'test_plugins'
1558
+ 'test_plugins',
1559
+ AnsibleJinja2Test
1582
1560
  )
1583
1561
 
1584
1562
  strategy_loader = PluginLoader(
@@ -100,7 +100,7 @@ class LookupBase(AnsiblePlugin):
100
100
  must be converted into python's unicode type as the strings will be run
101
101
  through jinja2 which has this requirement. You can use::
102
102
 
103
- from ansible.module_utils._text import to_text
103
+ from ansible.module_utils.common.text.converters import to_text
104
104
  result_string = to_text(result_string)
105
105
  """
106
106
  pass
@@ -33,6 +33,10 @@ DOCUMENTATION = """
33
33
  description: name of the plugin for which you want to retrieve configuration settings.
34
34
  type: string
35
35
  version_added: '2.12'
36
+ show_origin:
37
+ description: toggle the display of what configuration subsystem the value came from
38
+ type: bool
39
+ version_added: '2.16'
36
40
  """
37
41
 
38
42
  EXAMPLES = """
@@ -67,7 +71,8 @@ EXAMPLES = """
67
71
  RETURN = """
68
72
  _raw:
69
73
  description:
70
- - value(s) of the key(s) in the config
74
+ - A list of value(s) of the key(s) in the config if show_origin is false (default)
75
+ - Optionally, a list of 2 element lists (value, origin) if show_origin is true
71
76
  type: raw
72
77
  """
73
78
 
@@ -75,7 +80,7 @@ import ansible.plugins.loader as plugin_loader
75
80
 
76
81
  from ansible import constants as C
77
82
  from ansible.errors import AnsibleError, AnsibleLookupError, AnsibleOptionsError
78
- from ansible.module_utils._text import to_native
83
+ from ansible.module_utils.common.text.converters import to_native
79
84
  from ansible.module_utils.six import string_types
80
85
  from ansible.plugins.lookup import LookupBase
81
86
  from ansible.utils.sentinel import Sentinel
@@ -92,7 +97,7 @@ def _get_plugin_config(pname, ptype, config, variables):
92
97
  p = loader.get(pname, class_only=True)
93
98
  if p is None:
94
99
  raise AnsibleLookupError('Unable to load %s plugin "%s"' % (ptype, pname))
95
- result = C.config.get_config_value(config, plugin_type=ptype, plugin_name=p._load_name, variables=variables)
100
+ result, origin = C.config.get_config_value_and_origin(config, plugin_type=ptype, plugin_name=p._load_name, variables=variables)
96
101
  except AnsibleLookupError:
97
102
  raise
98
103
  except AnsibleError as e:
@@ -101,7 +106,7 @@ def _get_plugin_config(pname, ptype, config, variables):
101
106
  raise MissingSetting(msg, orig_exc=e)
102
107
  raise e
103
108
 
104
- return result
109
+ return result, origin
105
110
 
106
111
 
107
112
  def _get_global_config(config):
@@ -124,6 +129,7 @@ class LookupModule(LookupBase):
124
129
  missing = self.get_option('on_missing')
125
130
  ptype = self.get_option('plugin_type')
126
131
  pname = self.get_option('plugin_name')
132
+ show_origin = self.get_option('show_origin')
127
133
 
128
134
  if (ptype or pname) and not (ptype and pname):
129
135
  raise AnsibleOptionsError('Both plugin_type and plugin_name are required, cannot use one without the other')
@@ -138,9 +144,10 @@ class LookupModule(LookupBase):
138
144
  raise AnsibleOptionsError('Invalid setting identifier, "%s" is not a string, its a %s' % (term, type(term)))
139
145
 
140
146
  result = Sentinel
147
+ origin = None
141
148
  try:
142
149
  if pname:
143
- result = _get_plugin_config(pname, ptype, term, variables)
150
+ result, origin = _get_plugin_config(pname, ptype, term, variables)
144
151
  else:
145
152
  result = _get_global_config(term)
146
153
  except MissingSetting as e:
@@ -152,5 +159,8 @@ class LookupModule(LookupBase):
152
159
  pass # this is not needed, but added to have all 3 options stated
153
160
 
154
161
  if result is not Sentinel:
155
- ret.append(result)
162
+ if show_origin:
163
+ ret.append((result, origin))
164
+ else:
165
+ ret.append(result)
156
166
  return ret
@@ -12,7 +12,7 @@ DOCUMENTATION = r"""
12
12
  description:
13
13
  - The csvfile lookup reads the contents of a file in CSV (comma-separated value) format.
14
14
  The lookup looks for the row where the first column matches keyname (which can be multiple words)
15
- and returns the value in the C(col) column (default 1, which indexed from 0 means the second column in the file).
15
+ and returns the value in the O(col) column (default 1, which indexed from 0 means the second column in the file).
16
16
  options:
17
17
  col:
18
18
  description: column to return (0 indexed).
@@ -20,7 +20,7 @@ DOCUMENTATION = r"""
20
20
  default:
21
21
  description: what to return if the value is not found in the file.
22
22
  delimiter:
23
- description: field separator in the file, for a tab you can specify C(TAB) or C(\t).
23
+ description: field separator in the file, for a tab you can specify V(TAB) or V(\\t).
24
24
  default: TAB
25
25
  file:
26
26
  description: name of the CSV/TSV file to open.
@@ -35,6 +35,9 @@ DOCUMENTATION = r"""
35
35
  - For historical reasons, in the search keyname, quotes are treated
36
36
  literally and cannot be used around the string unless they appear
37
37
  (escaped as required) in the first column of the file you are parsing.
38
+ seealso:
39
+ - ref: playbook_task_paths
40
+ description: Search paths used for relative files.
38
41
  """
39
42
 
40
43
  EXAMPLES = """
@@ -54,7 +57,7 @@ EXAMPLES = """
54
57
  neighbor_as: "{{ csvline[5] }}"
55
58
  neigh_int_ip: "{{ csvline[6] }}"
56
59
  vars:
57
- csvline = "{{ lookup('ansible.builtin.csvfile', bgp_neighbor_ip, file='bgp_neighbors.csv', delimiter=',') }}"
60
+ csvline: "{{ lookup('ansible.builtin.csvfile', bgp_neighbor_ip, file='bgp_neighbors.csv', delimiter=',') }}"
58
61
  delegate_to: localhost
59
62
  """
60
63
 
@@ -75,7 +78,7 @@ from ansible.errors import AnsibleError, AnsibleAssertionError
75
78
  from ansible.parsing.splitter import parse_kv
76
79
  from ansible.plugins.lookup import LookupBase
77
80
  from ansible.module_utils.six import PY2
78
- from ansible.module_utils._text import to_bytes, to_native, to_text
81
+ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
79
82
 
80
83
 
81
84
  class CSVRecoder:
@@ -23,7 +23,7 @@ DOCUMENTATION = """
23
23
  default: ''
24
24
  version_added: '2.13'
25
25
  notes:
26
- - You can pass the C(Undefined) object as C(default) to force an undefined error
26
+ - You can pass the C(Undefined) object as O(default) to force an undefined error
27
27
  """
28
28
 
29
29
  EXAMPLES = """
@@ -28,11 +28,14 @@ DOCUMENTATION = """
28
28
  notes:
29
29
  - if read in variable context, the file can be interpreted as YAML if the content is valid to the parser.
30
30
  - this lookup does not understand 'globbing', use the fileglob lookup instead.
31
+ seealso:
32
+ - ref: playbook_task_paths
33
+ description: Search paths used for relative files.
31
34
  """
32
35
 
33
36
  EXAMPLES = """
34
37
  - ansible.builtin.debug:
35
- msg: "the value of foo.txt is {{lookup('ansible.builtin.file', '/etc/foo.txt') }}"
38
+ msg: "the value of foo.txt is {{ lookup('ansible.builtin.file', '/etc/foo.txt') }}"
36
39
 
37
40
  - name: display multiple file contents
38
41
  ansible.builtin.debug: var=item
@@ -52,7 +55,7 @@ RETURN = """
52
55
 
53
56
  from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleLookupError
54
57
  from ansible.plugins.lookup import LookupBase
55
- from ansible.module_utils._text import to_text
58
+ from ansible.module_utils.common.text.converters import to_text
56
59
  from ansible.utils.display import Display
57
60
 
58
61
  display = Display()
@@ -21,7 +21,10 @@ DOCUMENTATION = """
21
21
  - See R(Ansible task paths,playbook_task_paths) to understand how file lookup occurs with paths.
22
22
  - Matching is against local system files on the Ansible controller.
23
23
  To iterate a list of files on a remote node, use the M(ansible.builtin.find) module.
24
- - Returns a string list of paths joined by commas, or an empty list if no files match. For a 'true list' pass C(wantlist=True) to the lookup.
24
+ - Returns a string list of paths joined by commas, or an empty list if no files match. For a 'true list' pass O(ignore:wantlist=True) to the lookup.
25
+ seealso:
26
+ - ref: playbook_task_paths
27
+ description: Search paths used for relative files.
25
28
  """
26
29
 
27
30
  EXAMPLES = """
@@ -50,7 +53,7 @@ import os
50
53
  import glob
51
54
 
52
55
  from ansible.plugins.lookup import LookupBase
53
- from ansible.module_utils._text import to_bytes, to_text
56
+ from ansible.module_utils.common.text.converters import to_bytes, to_text
54
57
 
55
58
 
56
59
  class LookupModule(LookupBase):
@@ -15,9 +15,9 @@ DOCUMENTATION = """
15
15
  to the containing locations of role / play / include and so on.
16
16
  - The list of files has precedence over the paths searched.
17
17
  For example, A task in a role has a 'file1' in the play's relative path, this will be used, 'file2' in role's relative path will not.
18
- - Either a list of files C(_terms) or a key C(files) with a list of files is required for this plugin to operate.
18
+ - Either a list of files O(_terms) or a key O(files) with a list of files is required for this plugin to operate.
19
19
  notes:
20
- - This lookup can be used in 'dual mode', either passing a list of file names or a dictionary that has C(files) and C(paths).
20
+ - This lookup can be used in 'dual mode', either passing a list of file names or a dictionary that has O(files) and O(paths).
21
21
  options:
22
22
  _terms:
23
23
  description: A list of file names.
@@ -35,16 +35,19 @@ DOCUMENTATION = """
35
35
  type: boolean
36
36
  default: False
37
37
  description:
38
- - When C(True), return an empty list when no files are matched.
38
+ - When V(True), return an empty list when no files are matched.
39
39
  - This is useful when used with C(with_first_found), as an empty list return to C(with_) calls
40
40
  causes the calling task to be skipped.
41
- - When used as a template via C(lookup) or C(query), setting I(skip=True) will *not* cause the task to skip.
41
+ - When used as a template via C(lookup) or C(query), setting O(skip=True) will *not* cause the task to skip.
42
42
  Tasks must handle the empty list return from the template.
43
- - When C(False) and C(lookup) or C(query) specifies I(errors='ignore') all errors (including no file found,
43
+ - When V(False) and C(lookup) or C(query) specifies O(ignore:errors='ignore') all errors (including no file found,
44
44
  but potentially others) return an empty string or an empty list respectively.
45
- - When C(True) and C(lookup) or C(query) specifies I(errors='ignore'), no file found will return an empty
45
+ - When V(True) and C(lookup) or C(query) specifies O(ignore:errors='ignore'), no file found will return an empty
46
46
  list and other potential errors return an empty string or empty list depending on the template call
47
- (in other words return values of C(lookup) v C(query)).
47
+ (in other words return values of C(lookup) vs C(query)).
48
+ seealso:
49
+ - ref: playbook_task_paths
50
+ description: Search paths used for relative paths/files.
48
51
  """
49
52
 
50
53
  EXAMPLES = """
@@ -165,15 +168,13 @@ class LookupModule(LookupBase):
165
168
  total_search = []
166
169
  skip = False
167
170
 
168
- if not terms and kwargs:
169
- terms = ['']
170
-
171
171
  # can use a dict instead of list item to pass inline config
172
172
  for term in terms:
173
173
  if isinstance(term, Mapping):
174
174
  self.set_options(var_options=variables, direct=term)
175
+ files = self.get_option('files')
175
176
  elif isinstance(term, string_types):
176
- self.set_options(var_options=variables, direct=kwargs)
177
+ files = [term]
177
178
  elif isinstance(term, Sequence):
178
179
  partial, skip = self._process_terms(term, variables, kwargs)
179
180
  total_search.extend(partial)
@@ -181,7 +182,6 @@ class LookupModule(LookupBase):
181
182
  else:
182
183
  raise AnsibleLookupError("Invalid term supplied, can handle string, mapping or list of strings but got: %s for %s" % (type(term), term))
183
184
 
184
- files = self.get_option('files')
185
185
  paths = self.get_option('paths')
186
186
 
187
187
  # NOTE: this is used as 'global' but can be set many times?!?!?
@@ -198,8 +198,8 @@ class LookupModule(LookupBase):
198
198
  f = os.path.join(path, fn)
199
199
  total_search.append(f)
200
200
  elif filelist:
201
- # NOTE: this seems wrong, should be 'extend' as any option/entry can clobber all
202
- total_search = filelist
201
+ # NOTE: this is now 'extend', previouslly it would clobber all options, but we deemed that a bug
202
+ total_search.extend(filelist)
203
203
  else:
204
204
  total_search.append(term)
205
205
 
@@ -207,6 +207,10 @@ class LookupModule(LookupBase):
207
207
 
208
208
  def run(self, terms, variables, **kwargs):
209
209
 
210
+ if not terms:
211
+ self.set_options(var_options=variables, direct=kwargs)
212
+ terms = self.get_option('files')
213
+
210
214
  total_search, skip = self._process_terms(terms, variables, kwargs)
211
215
 
212
216
  # NOTE: during refactor noticed that the 'using a dict' as term
@@ -222,6 +226,8 @@ class LookupModule(LookupBase):
222
226
  try:
223
227
  fn = self._templar.template(fn)
224
228
  except (AnsibleUndefinedVariable, UndefinedError):
229
+ # NOTE: backwards compat ff behaviour is to ignore errors when vars are undefined.
230
+ # moved here from task_executor.
225
231
  continue
226
232
 
227
233
  # get subdir if set by task executor, default to files otherwise