ansible-core 2.18.7__py3-none-any.whl → 2.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (757) hide show
  1. ansible/_internal/__init__.py +53 -0
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/_ansiballz/_wrapper.py +262 -0
  5. ansible/_internal/_collection_proxy.py +47 -0
  6. ansible/_internal/_datatag/__init__.py +0 -0
  7. ansible/_internal/_datatag/_tags.py +130 -0
  8. ansible/_internal/_datatag/_utils.py +19 -0
  9. ansible/_internal/_datatag/_wrappers.py +33 -0
  10. ansible/_internal/_errors/__init__.py +0 -0
  11. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  12. ansible/_internal/_errors/_captured.py +123 -0
  13. ansible/_internal/_errors/_error_factory.py +89 -0
  14. ansible/_internal/_errors/_error_utils.py +240 -0
  15. ansible/_internal/_errors/_handler.py +91 -0
  16. ansible/_internal/_errors/_task_timeout.py +28 -0
  17. ansible/_internal/_event_formatting.py +127 -0
  18. ansible/_internal/_json/__init__.py +214 -0
  19. ansible/_internal/_json/_legacy_encoder.py +34 -0
  20. ansible/_internal/_json/_profiles/__init__.py +0 -0
  21. ansible/_internal/_json/_profiles/_cache_persistence.py +57 -0
  22. ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
  23. ansible/_internal/_json/_profiles/_legacy.py +189 -0
  24. ansible/_internal/_locking.py +21 -0
  25. ansible/_internal/_plugins/__init__.py +0 -0
  26. ansible/_internal/_plugins/_cache.py +57 -0
  27. ansible/_internal/_ssh/__init__.py +0 -0
  28. ansible/_internal/_ssh/_agent_launch.py +91 -0
  29. ansible/_internal/_ssh/_ssh_agent.py +619 -0
  30. ansible/_internal/_task.py +78 -0
  31. ansible/_internal/_templating/__init__.py +12 -0
  32. ansible/_internal/_templating/_access.py +86 -0
  33. ansible/_internal/_templating/_chain_templar.py +63 -0
  34. ansible/_internal/_templating/_datatag.py +95 -0
  35. ansible/_internal/_templating/_engine.py +592 -0
  36. ansible/_internal/_templating/_errors.py +28 -0
  37. ansible/_internal/_templating/_jinja_bits.py +1106 -0
  38. ansible/_internal/_templating/_jinja_common.py +323 -0
  39. ansible/_internal/_templating/_jinja_patches.py +44 -0
  40. ansible/_internal/_templating/_jinja_plugins.py +375 -0
  41. ansible/_internal/_templating/_lazy_containers.py +633 -0
  42. ansible/_internal/_templating/_marker_behaviors.py +103 -0
  43. ansible/_internal/_templating/_template_vars.py +72 -0
  44. ansible/_internal/_templating/_transform.py +70 -0
  45. ansible/_internal/_templating/_utils.py +108 -0
  46. ansible/_internal/_testing.py +26 -0
  47. ansible/_internal/_wrapt.py +1052 -0
  48. ansible/_internal/_yaml/__init__.py +0 -0
  49. ansible/_internal/_yaml/_constructor.py +240 -0
  50. ansible/_internal/_yaml/_dumper.py +70 -0
  51. ansible/_internal/_yaml/_errors.py +166 -0
  52. ansible/_internal/_yaml/_loader.py +66 -0
  53. ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
  54. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
  55. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
  56. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +27 -0
  57. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
  58. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
  59. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
  60. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
  61. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
  62. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
  63. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
  64. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
  65. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
  66. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
  67. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
  68. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
  69. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
  70. ansible/cli/__init__.py +93 -104
  71. ansible/cli/_ssh_askpass.py +54 -0
  72. ansible/cli/adhoc.py +20 -10
  73. ansible/cli/arguments/option_helpers.py +163 -10
  74. ansible/cli/config.py +43 -68
  75. ansible/cli/console.py +13 -11
  76. ansible/cli/doc.py +134 -77
  77. ansible/cli/galaxy.py +27 -20
  78. ansible/cli/inventory.py +28 -28
  79. ansible/cli/playbook.py +4 -12
  80. ansible/cli/pull.py +6 -3
  81. ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
  82. ansible/cli/vault.py +12 -11
  83. ansible/compat/__init__.py +2 -2
  84. ansible/compat/importlib_resources.py +9 -12
  85. ansible/config/base.yml +218 -133
  86. ansible/config/manager.py +220 -159
  87. ansible/constants.py +2 -65
  88. ansible/errors/__init__.py +350 -256
  89. ansible/executor/interpreter_discovery.py +28 -149
  90. ansible/executor/module_common.py +480 -514
  91. ansible/executor/play_iterator.py +22 -27
  92. ansible/executor/playbook_executor.py +11 -11
  93. ansible/executor/powershell/async_watchdog.ps1 +97 -102
  94. ansible/executor/powershell/async_wrapper.ps1 +204 -153
  95. ansible/executor/powershell/become_wrapper.ps1 +107 -144
  96. ansible/executor/powershell/bootstrap_wrapper.ps1 +46 -9
  97. ansible/executor/powershell/coverage_wrapper.ps1 +91 -135
  98. ansible/executor/powershell/exec_wrapper.ps1 +675 -196
  99. ansible/executor/powershell/module_manifest.py +469 -265
  100. ansible/executor/powershell/module_wrapper.ps1 +195 -186
  101. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  102. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  103. ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
  104. ansible/executor/powershell/psrp_put_file.ps1 +122 -0
  105. ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
  106. ansible/executor/powershell/winrm_put_file.ps1 +36 -0
  107. ansible/executor/process/worker.py +139 -149
  108. ansible/executor/stats.py +5 -5
  109. ansible/executor/task_executor.py +270 -297
  110. ansible/executor/task_queue_manager.py +135 -137
  111. ansible/executor/task_result.py +182 -79
  112. ansible/galaxy/__init__.py +2 -2
  113. ansible/galaxy/api.py +26 -25
  114. ansible/galaxy/collection/__init__.py +6 -14
  115. ansible/galaxy/collection/concrete_artifact_manager.py +12 -21
  116. ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
  117. ansible/galaxy/dependency_resolution/providers.py +4 -4
  118. ansible/galaxy/dependency_resolution/reporters.py +81 -0
  119. ansible/galaxy/role.py +6 -10
  120. ansible/galaxy/token.py +28 -21
  121. ansible/inventory/data.py +47 -57
  122. ansible/inventory/group.py +50 -73
  123. ansible/inventory/helpers.py +9 -0
  124. ansible/inventory/host.py +37 -54
  125. ansible/inventory/manager.py +79 -34
  126. ansible/keyword_desc.yml +1 -1
  127. ansible/module_utils/_internal/__init__.py +55 -0
  128. ansible/module_utils/_internal/_ambient_context.py +58 -0
  129. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  130. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  131. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  132. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  133. ansible/module_utils/_internal/_ansiballz/_loader.py +81 -0
  134. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  135. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  136. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
  137. ansible/module_utils/_internal/_dataclass_validation.py +217 -0
  138. ansible/module_utils/_internal/_datatag/__init__.py +961 -0
  139. ansible/module_utils/_internal/_datatag/_tags.py +16 -0
  140. ansible/module_utils/_internal/_debugging.py +31 -0
  141. ansible/module_utils/_internal/_deprecator.py +157 -0
  142. ansible/module_utils/_internal/_errors.py +101 -0
  143. ansible/module_utils/_internal/_event_utils.py +61 -0
  144. ansible/module_utils/_internal/_json/__init__.py +63 -0
  145. ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
  146. ansible/module_utils/_internal/_json/_profiles/__init__.py +428 -0
  147. ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
  148. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +33 -0
  149. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +37 -0
  150. ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
  151. ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
  152. ansible/module_utils/_internal/_json/_profiles/_tagless.py +52 -0
  153. ansible/module_utils/_internal/_messages.py +130 -0
  154. ansible/module_utils/_internal/_patches/__init__.py +66 -0
  155. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +53 -0
  156. ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
  157. ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
  158. ansible/module_utils/_internal/_plugin_info.py +38 -0
  159. ansible/module_utils/_internal/_stack.py +22 -0
  160. ansible/module_utils/_internal/_testing.py +0 -0
  161. ansible/module_utils/_internal/_text_utils.py +6 -0
  162. ansible/module_utils/_internal/_traceback.py +92 -0
  163. ansible/module_utils/_internal/_validation.py +14 -0
  164. ansible/module_utils/ansible_release.py +2 -2
  165. ansible/module_utils/api.py +1 -2
  166. ansible/module_utils/basic.py +303 -202
  167. ansible/module_utils/common/_utils.py +24 -28
  168. ansible/module_utils/common/arg_spec.py +8 -3
  169. ansible/module_utils/common/collections.py +7 -2
  170. ansible/module_utils/common/dict_transformations.py +2 -2
  171. ansible/module_utils/common/file.py +2 -2
  172. ansible/module_utils/common/json.py +90 -84
  173. ansible/module_utils/common/locale.py +2 -2
  174. ansible/module_utils/common/parameters.py +27 -24
  175. ansible/module_utils/common/process.py +2 -3
  176. ansible/module_utils/common/respawn.py +11 -33
  177. ansible/module_utils/common/sentinel.py +66 -0
  178. ansible/module_utils/common/sys_info.py +8 -8
  179. ansible/module_utils/common/text/converters.py +16 -37
  180. ansible/module_utils/common/validation.py +35 -24
  181. ansible/module_utils/common/warnings.py +143 -25
  182. ansible/module_utils/common/yaml.py +29 -3
  183. ansible/module_utils/compat/datetime.py +33 -21
  184. ansible/module_utils/compat/paramiko.py +21 -10
  185. ansible/module_utils/compat/typing.py +6 -5
  186. ansible/module_utils/connection.py +10 -13
  187. ansible/module_utils/csharp/Ansible.Basic.cs +15 -12
  188. ansible/module_utils/csharp/Ansible.Become.cs +1 -0
  189. ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
  190. ansible/module_utils/csharp/Ansible._Async.cs +517 -0
  191. ansible/module_utils/datatag.py +49 -0
  192. ansible/module_utils/distro/__init__.py +2 -2
  193. ansible/module_utils/facts/ansible_collector.py +4 -5
  194. ansible/module_utils/facts/collector.py +13 -14
  195. ansible/module_utils/facts/compat.py +4 -4
  196. ansible/module_utils/facts/default_collectors.py +1 -1
  197. ansible/module_utils/facts/hardware/aix.py +34 -0
  198. ansible/module_utils/facts/hardware/base.py +2 -2
  199. ansible/module_utils/facts/hardware/darwin.py +1 -3
  200. ansible/module_utils/facts/hardware/freebsd.py +2 -2
  201. ansible/module_utils/facts/hardware/linux.py +5 -5
  202. ansible/module_utils/facts/namespace.py +1 -1
  203. ansible/module_utils/facts/network/base.py +1 -1
  204. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  205. ansible/module_utils/facts/network/iscsi.py +1 -2
  206. ansible/module_utils/facts/network/nvme.py +1 -2
  207. ansible/module_utils/facts/other/facter.py +2 -3
  208. ansible/module_utils/facts/other/ohai.py +2 -3
  209. ansible/module_utils/facts/sysctl.py +4 -6
  210. ansible/module_utils/facts/system/apparmor.py +1 -2
  211. ansible/module_utils/facts/system/caps.py +3 -3
  212. ansible/module_utils/facts/system/chroot.py +1 -2
  213. ansible/module_utils/facts/system/cmdline.py +1 -2
  214. ansible/module_utils/facts/system/date_time.py +5 -3
  215. ansible/module_utils/facts/system/distribution.py +27 -13
  216. ansible/module_utils/facts/system/dns.py +1 -1
  217. ansible/module_utils/facts/system/env.py +1 -2
  218. ansible/module_utils/facts/system/fips.py +7 -20
  219. ansible/module_utils/facts/system/loadavg.py +1 -2
  220. ansible/module_utils/facts/system/local.py +2 -3
  221. ansible/module_utils/facts/system/lsb.py +1 -2
  222. ansible/module_utils/facts/system/pkg_mgr.py +1 -2
  223. ansible/module_utils/facts/system/platform.py +1 -2
  224. ansible/module_utils/facts/system/python.py +1 -2
  225. ansible/module_utils/facts/system/selinux.py +1 -1
  226. ansible/module_utils/facts/system/service_mgr.py +1 -2
  227. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
  228. ansible/module_utils/facts/system/systemd.py +1 -1
  229. ansible/module_utils/facts/system/user.py +1 -2
  230. ansible/module_utils/facts/utils.py +3 -3
  231. ansible/module_utils/facts/virtual/base.py +1 -1
  232. ansible/module_utils/facts/virtual/linux.py +3 -3
  233. ansible/module_utils/facts/virtual/sunos.py +3 -15
  234. ansible/module_utils/facts/virtual/sysctl.py +3 -16
  235. ansible/module_utils/json_utils.py +2 -2
  236. ansible/module_utils/parsing/convert_bool.py +7 -1
  237. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  238. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  239. ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
  240. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  241. ansible/module_utils/service.py +21 -31
  242. ansible/module_utils/splitter.py +7 -7
  243. ansible/module_utils/testing.py +31 -0
  244. ansible/module_utils/urls.py +64 -35
  245. ansible/modules/add_host.py +4 -4
  246. ansible/modules/apt.py +69 -49
  247. ansible/modules/apt_key.py +19 -12
  248. ansible/modules/apt_repository.py +32 -51
  249. ansible/modules/assemble.py +16 -14
  250. ansible/modules/assert.py +4 -4
  251. ansible/modules/async_status.py +24 -24
  252. ansible/modules/async_wrapper.py +20 -25
  253. ansible/modules/blockinfile.py +6 -7
  254. ansible/modules/command.py +13 -20
  255. ansible/modules/copy.py +60 -147
  256. ansible/modules/cron.py +24 -21
  257. ansible/modules/deb822_repository.py +8 -9
  258. ansible/modules/debconf.py +5 -5
  259. ansible/modules/debug.py +4 -4
  260. ansible/modules/dnf.py +8 -8
  261. ansible/modules/dnf5.py +39 -13
  262. ansible/modules/dpkg_selections.py +4 -4
  263. ansible/modules/expect.py +13 -15
  264. ansible/modules/fail.py +4 -4
  265. ansible/modules/fetch.py +4 -4
  266. ansible/modules/file.py +184 -144
  267. ansible/modules/find.py +22 -20
  268. ansible/modules/gather_facts.py +3 -3
  269. ansible/modules/get_url.py +77 -54
  270. ansible/modules/getent.py +7 -9
  271. ansible/modules/git.py +38 -38
  272. ansible/modules/group.py +6 -6
  273. ansible/modules/group_by.py +4 -4
  274. ansible/modules/hostname.py +15 -32
  275. ansible/modules/import_playbook.py +6 -6
  276. ansible/modules/import_role.py +6 -6
  277. ansible/modules/import_tasks.py +6 -6
  278. ansible/modules/include_role.py +6 -6
  279. ansible/modules/include_tasks.py +6 -6
  280. ansible/modules/include_vars.py +6 -6
  281. ansible/modules/iptables.py +86 -73
  282. ansible/modules/known_hosts.py +22 -24
  283. ansible/modules/lineinfile.py +5 -5
  284. ansible/modules/meta.py +4 -4
  285. ansible/modules/mount_facts.py +2 -2
  286. ansible/modules/package.py +10 -4
  287. ansible/modules/package_facts.py +22 -10
  288. ansible/modules/pause.py +6 -6
  289. ansible/modules/ping.py +6 -6
  290. ansible/modules/pip.py +21 -26
  291. ansible/modules/raw.py +6 -6
  292. ansible/modules/reboot.py +6 -6
  293. ansible/modules/replace.py +10 -14
  294. ansible/modules/rpm_key.py +7 -8
  295. ansible/modules/script.py +4 -4
  296. ansible/modules/service.py +10 -17
  297. ansible/modules/service_facts.py +87 -10
  298. ansible/modules/set_fact.py +5 -5
  299. ansible/modules/set_stats.py +4 -4
  300. ansible/modules/setup.py +2 -2
  301. ansible/modules/shell.py +6 -6
  302. ansible/modules/slurp.py +16 -19
  303. ansible/modules/stat.py +15 -31
  304. ansible/modules/subversion.py +15 -15
  305. ansible/modules/systemd.py +7 -7
  306. ansible/modules/systemd_service.py +7 -7
  307. ansible/modules/sysvinit.py +9 -9
  308. ansible/modules/tempfile.py +5 -6
  309. ansible/modules/template.py +6 -6
  310. ansible/modules/unarchive.py +38 -17
  311. ansible/modules/uri.py +33 -26
  312. ansible/modules/user.py +45 -32
  313. ansible/modules/validate_argument_spec.py +10 -7
  314. ansible/modules/wait_for.py +70 -60
  315. ansible/modules/wait_for_connection.py +6 -6
  316. ansible/modules/yum_repository.py +10 -9
  317. ansible/parsing/ajson.py +17 -37
  318. ansible/parsing/dataloader.py +99 -54
  319. ansible/parsing/mod_args.py +62 -60
  320. ansible/parsing/plugin_docs.py +21 -86
  321. ansible/parsing/quoting.py +1 -1
  322. ansible/parsing/splitter.py +27 -12
  323. ansible/parsing/utils/addresses.py +24 -24
  324. ansible/parsing/utils/jsonify.py +5 -1
  325. ansible/parsing/utils/yaml.py +32 -61
  326. ansible/parsing/vault/__init__.py +327 -99
  327. ansible/parsing/yaml/__init__.py +0 -18
  328. ansible/parsing/yaml/dumper.py +6 -120
  329. ansible/parsing/yaml/loader.py +6 -39
  330. ansible/parsing/yaml/objects.py +43 -335
  331. ansible/playbook/__init__.py +1 -1
  332. ansible/playbook/attribute.py +8 -3
  333. ansible/playbook/base.py +187 -134
  334. ansible/playbook/block.py +26 -24
  335. ansible/playbook/collectionsearch.py +1 -15
  336. ansible/playbook/conditional.py +3 -77
  337. ansible/playbook/handler.py +8 -2
  338. ansible/playbook/helpers.py +41 -53
  339. ansible/playbook/included_file.py +32 -26
  340. ansible/playbook/loop_control.py +2 -2
  341. ansible/playbook/play.py +85 -44
  342. ansible/playbook/play_context.py +14 -17
  343. ansible/playbook/playbook_include.py +27 -62
  344. ansible/playbook/role/__init__.py +64 -49
  345. ansible/playbook/role/definition.py +15 -17
  346. ansible/playbook/role/include.py +2 -4
  347. ansible/playbook/role/metadata.py +10 -11
  348. ansible/playbook/role_include.py +3 -3
  349. ansible/playbook/taggable.py +28 -12
  350. ansible/playbook/task.py +192 -121
  351. ansible/playbook/task_include.py +5 -5
  352. ansible/plugins/__init__.py +58 -26
  353. ansible/plugins/action/__init__.py +188 -186
  354. ansible/plugins/action/add_host.py +2 -2
  355. ansible/plugins/action/assemble.py +11 -18
  356. ansible/plugins/action/assert.py +55 -67
  357. ansible/plugins/action/async_status.py +7 -2
  358. ansible/plugins/action/copy.py +14 -17
  359. ansible/plugins/action/debug.py +37 -31
  360. ansible/plugins/action/dnf.py +3 -4
  361. ansible/plugins/action/fail.py +1 -1
  362. ansible/plugins/action/fetch.py +7 -8
  363. ansible/plugins/action/gather_facts.py +13 -14
  364. ansible/plugins/action/group_by.py +1 -1
  365. ansible/plugins/action/include_vars.py +10 -11
  366. ansible/plugins/action/package.py +8 -14
  367. ansible/plugins/action/pause.py +2 -2
  368. ansible/plugins/action/script.py +27 -38
  369. ansible/plugins/action/service.py +9 -18
  370. ansible/plugins/action/set_fact.py +3 -12
  371. ansible/plugins/action/set_stats.py +3 -8
  372. ansible/plugins/action/template.py +47 -67
  373. ansible/plugins/action/unarchive.py +6 -16
  374. ansible/plugins/action/uri.py +9 -20
  375. ansible/plugins/action/validate_argument_spec.py +5 -5
  376. ansible/plugins/action/wait_for_connection.py +1 -1
  377. ansible/plugins/become/__init__.py +31 -8
  378. ansible/plugins/become/runas.py +71 -0
  379. ansible/plugins/become/su.py +13 -8
  380. ansible/plugins/become/sudo.py +19 -0
  381. ansible/plugins/cache/__init__.py +52 -63
  382. ansible/plugins/cache/base.py +8 -0
  383. ansible/plugins/cache/jsonfile.py +10 -16
  384. ansible/plugins/cache/memory.py +6 -12
  385. ansible/plugins/callback/__init__.py +294 -201
  386. ansible/plugins/callback/default.py +99 -95
  387. ansible/plugins/callback/junit.py +44 -43
  388. ansible/plugins/callback/minimal.py +28 -25
  389. ansible/plugins/callback/oneline.py +34 -21
  390. ansible/plugins/callback/tree.py +27 -16
  391. ansible/plugins/connection/__init__.py +47 -34
  392. ansible/plugins/connection/local.py +156 -60
  393. ansible/plugins/connection/paramiko_ssh.py +34 -24
  394. ansible/plugins/connection/psrp.py +76 -165
  395. ansible/plugins/connection/ssh.py +326 -86
  396. ansible/plugins/connection/winrm.py +62 -141
  397. ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
  398. ansible/plugins/doc_fragments/action_core.py +6 -6
  399. ansible/plugins/doc_fragments/backup.py +2 -2
  400. ansible/plugins/doc_fragments/checksum_common.py +27 -0
  401. ansible/plugins/doc_fragments/constructed.py +8 -4
  402. ansible/plugins/doc_fragments/decrypt.py +2 -2
  403. ansible/plugins/doc_fragments/default_callback.py +2 -2
  404. ansible/plugins/doc_fragments/files.py +2 -2
  405. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  406. ansible/plugins/doc_fragments/result_format_callback.py +2 -2
  407. ansible/plugins/doc_fragments/return_common.py +2 -2
  408. ansible/plugins/doc_fragments/template_common.py +4 -4
  409. ansible/plugins/doc_fragments/url.py +17 -1
  410. ansible/plugins/doc_fragments/url_windows.py +2 -2
  411. ansible/plugins/doc_fragments/validate.py +2 -2
  412. ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
  413. ansible/plugins/filter/__init__.py +6 -2
  414. ansible/plugins/filter/b64decode.yml +22 -0
  415. ansible/plugins/filter/b64encode.yml +22 -0
  416. ansible/plugins/filter/bool.yml +11 -4
  417. ansible/plugins/filter/core.py +245 -120
  418. ansible/plugins/filter/encryption.py +42 -34
  419. ansible/plugins/filter/flatten.yml +3 -2
  420. ansible/plugins/filter/human_to_bytes.yml +1 -1
  421. ansible/plugins/filter/mathstuff.py +30 -37
  422. ansible/plugins/filter/password_hash.yml +8 -0
  423. ansible/plugins/filter/pow.yml +1 -1
  424. ansible/plugins/filter/regex_search.yml +1 -4
  425. ansible/plugins/filter/root.yml +1 -1
  426. ansible/plugins/filter/split.yml +1 -1
  427. ansible/plugins/filter/strftime.yml +3 -3
  428. ansible/plugins/filter/to_nice_yaml.yml +0 -4
  429. ansible/plugins/filter/to_uuid.yml +1 -1
  430. ansible/plugins/filter/to_yaml.yml +0 -4
  431. ansible/plugins/filter/unvault.yml +1 -1
  432. ansible/plugins/filter/urls.py +1 -1
  433. ansible/plugins/filter/urlsplit.py +8 -9
  434. ansible/plugins/filter/vault.yml +14 -9
  435. ansible/plugins/filter/win_basename.yml +6 -1
  436. ansible/plugins/filter/win_dirname.yml +5 -0
  437. ansible/plugins/inventory/__init__.py +107 -86
  438. ansible/plugins/inventory/advanced_host_list.py +7 -5
  439. ansible/plugins/inventory/auto.py +11 -4
  440. ansible/plugins/inventory/constructed.py +21 -24
  441. ansible/plugins/inventory/generator.py +16 -11
  442. ansible/plugins/inventory/host_list.py +7 -5
  443. ansible/plugins/inventory/ini.py +78 -44
  444. ansible/plugins/inventory/script.py +190 -120
  445. ansible/plugins/inventory/toml.py +16 -126
  446. ansible/plugins/inventory/yaml.py +10 -8
  447. ansible/plugins/list.py +72 -19
  448. ansible/plugins/loader.py +383 -198
  449. ansible/plugins/lookup/__init__.py +21 -4
  450. ansible/plugins/lookup/config.py +21 -35
  451. ansible/plugins/lookup/csvfile.py +19 -73
  452. ansible/plugins/lookup/dict.py +1 -6
  453. ansible/plugins/lookup/env.py +12 -9
  454. ansible/plugins/lookup/file.py +5 -8
  455. ansible/plugins/lookup/first_found.py +87 -55
  456. ansible/plugins/lookup/indexed_items.py +1 -10
  457. ansible/plugins/lookup/ini.py +14 -13
  458. ansible/plugins/lookup/items.py +1 -1
  459. ansible/plugins/lookup/lines.py +8 -1
  460. ansible/plugins/lookup/list.py +1 -1
  461. ansible/plugins/lookup/nested.py +2 -18
  462. ansible/plugins/lookup/password.py +5 -5
  463. ansible/plugins/lookup/pipe.py +5 -7
  464. ansible/plugins/lookup/sequence.py +18 -8
  465. ansible/plugins/lookup/subelements.py +1 -4
  466. ansible/plugins/lookup/template.py +47 -36
  467. ansible/plugins/lookup/together.py +0 -12
  468. ansible/plugins/lookup/unvault.py +1 -5
  469. ansible/plugins/lookup/url.py +4 -10
  470. ansible/plugins/lookup/vars.py +16 -24
  471. ansible/plugins/shell/__init__.py +58 -4
  472. ansible/plugins/shell/cmd.py +2 -2
  473. ansible/plugins/shell/powershell.py +106 -31
  474. ansible/plugins/shell/sh.py +13 -7
  475. ansible/plugins/strategy/__init__.py +168 -193
  476. ansible/plugins/strategy/debug.py +2 -2
  477. ansible/plugins/strategy/free.py +16 -31
  478. ansible/plugins/strategy/host_pinned.py +2 -2
  479. ansible/plugins/strategy/linear.py +41 -41
  480. ansible/plugins/terminal/__init__.py +4 -4
  481. ansible/plugins/test/__init__.py +7 -2
  482. ansible/plugins/test/core.py +75 -35
  483. ansible/plugins/test/files.py +1 -1
  484. ansible/plugins/test/finished.yml +1 -1
  485. ansible/plugins/test/mathstuff.py +3 -3
  486. ansible/plugins/test/uri.py +5 -8
  487. ansible/plugins/vars/host_group_vars.py +7 -14
  488. ansible/release.py +2 -2
  489. ansible/template/__init__.py +353 -943
  490. ansible/utils/__init__.py +0 -18
  491. ansible/utils/collection_loader/__init__.py +54 -5
  492. ansible/utils/collection_loader/_collection_config.py +5 -6
  493. ansible/utils/collection_loader/_collection_finder.py +82 -96
  494. ansible/utils/collection_loader/_collection_meta.py +15 -8
  495. ansible/utils/display.py +485 -73
  496. ansible/utils/encrypt.py +27 -19
  497. ansible/utils/fqcn.py +2 -2
  498. ansible/utils/galaxy.py +2 -2
  499. ansible/utils/hashing.py +8 -10
  500. ansible/utils/helpers.py +2 -2
  501. ansible/utils/listify.py +10 -8
  502. ansible/utils/lock.py +2 -2
  503. ansible/utils/path.py +10 -12
  504. ansible/utils/plugin_docs.py +16 -14
  505. ansible/utils/py3compat.py +2 -7
  506. ansible/utils/sentinel.py +4 -62
  507. ansible/utils/singleton.py +2 -0
  508. ansible/utils/ssh_functions.py +6 -2
  509. ansible/utils/unsafe_proxy.py +23 -332
  510. ansible/utils/vars.py +55 -8
  511. ansible/utils/version.py +2 -2
  512. ansible/vars/clean.py +5 -5
  513. ansible/vars/hostvars.py +60 -90
  514. ansible/vars/manager.py +220 -285
  515. ansible/vars/plugins.py +4 -4
  516. ansible/vars/reserved.py +13 -12
  517. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/METADATA +4 -3
  518. ansible_core-2.19.0.dist-info/RECORD +1097 -0
  519. ansible_core-2.19.0.dist-info/licenses/licenses/BSD-3-Clause.txt +28 -0
  520. ansible_test/_data/completion/docker.txt +7 -7
  521. ansible_test/_data/completion/remote.txt +6 -6
  522. ansible_test/_data/completion/windows.txt +1 -0
  523. ansible_test/_data/requirements/ansible.txt +2 -2
  524. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  525. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  526. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  527. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  528. ansible_test/_data/requirements/sanity.pylint.txt +5 -5
  529. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  530. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  531. ansible_test/_data/requirements/units.txt +1 -0
  532. ansible_test/_internal/__init__.py +6 -0
  533. ansible_test/_internal/ansible_util.py +3 -1
  534. ansible_test/_internal/become.py +1 -0
  535. ansible_test/_internal/bootstrap.py +1 -0
  536. ansible_test/_internal/cache.py +1 -0
  537. ansible_test/_internal/cgroup.py +1 -0
  538. ansible_test/_internal/ci/__init__.py +1 -0
  539. ansible_test/_internal/ci/azp.py +1 -0
  540. ansible_test/_internal/ci/local.py +1 -0
  541. ansible_test/_internal/classification/__init__.py +1 -0
  542. ansible_test/_internal/classification/common.py +1 -0
  543. ansible_test/_internal/classification/csharp.py +1 -0
  544. ansible_test/_internal/classification/powershell.py +1 -0
  545. ansible_test/_internal/classification/python.py +1 -0
  546. ansible_test/_internal/cli/__init__.py +1 -0
  547. ansible_test/_internal/cli/actions.py +1 -0
  548. ansible_test/_internal/cli/argparsing/__init__.py +1 -0
  549. ansible_test/_internal/cli/argparsing/actions.py +1 -0
  550. ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
  551. ansible_test/_internal/cli/argparsing/parsers.py +1 -0
  552. ansible_test/_internal/cli/commands/__init__.py +11 -5
  553. ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
  554. ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
  555. ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
  556. ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
  557. ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
  558. ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
  559. ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
  560. ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
  561. ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
  562. ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
  563. ansible_test/_internal/cli/commands/coverage/html.py +1 -0
  564. ansible_test/_internal/cli/commands/coverage/report.py +1 -0
  565. ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
  566. ansible_test/_internal/cli/commands/env.py +1 -0
  567. ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
  568. ansible_test/_internal/cli/commands/integration/network.py +1 -0
  569. ansible_test/_internal/cli/commands/integration/posix.py +1 -0
  570. ansible_test/_internal/cli/commands/integration/windows.py +1 -0
  571. ansible_test/_internal/cli/commands/sanity.py +9 -0
  572. ansible_test/_internal/cli/commands/shell.py +1 -0
  573. ansible_test/_internal/cli/commands/units.py +1 -0
  574. ansible_test/_internal/cli/compat.py +1 -0
  575. ansible_test/_internal/cli/completers.py +1 -0
  576. ansible_test/_internal/cli/converters.py +1 -0
  577. ansible_test/_internal/cli/environments.py +52 -5
  578. ansible_test/_internal/cli/epilog.py +1 -0
  579. ansible_test/_internal/cli/parsers/__init__.py +1 -0
  580. ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
  581. ansible_test/_internal/cli/parsers/helpers.py +1 -0
  582. ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
  583. ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
  584. ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
  585. ansible_test/_internal/commands/__init__.py +1 -0
  586. ansible_test/_internal/commands/coverage/__init__.py +3 -2
  587. ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
  588. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
  589. ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
  590. ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
  591. ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
  592. ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
  593. ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
  594. ansible_test/_internal/commands/coverage/combine.py +2 -1
  595. ansible_test/_internal/commands/coverage/erase.py +1 -0
  596. ansible_test/_internal/commands/coverage/html.py +1 -0
  597. ansible_test/_internal/commands/coverage/report.py +1 -0
  598. ansible_test/_internal/commands/coverage/xml.py +1 -0
  599. ansible_test/_internal/commands/env/__init__.py +2 -0
  600. ansible_test/_internal/commands/integration/__init__.py +22 -5
  601. ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
  602. ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
  603. ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
  604. ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
  605. ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
  606. ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
  607. ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
  608. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
  609. ansible_test/_internal/commands/integration/cloud/httptester.py +3 -2
  610. ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
  611. ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
  612. ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
  613. ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
  614. ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
  615. ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
  616. ansible_test/_internal/commands/integration/coverage.py +8 -2
  617. ansible_test/_internal/commands/integration/filters.py +1 -0
  618. ansible_test/_internal/commands/integration/network.py +1 -0
  619. ansible_test/_internal/commands/integration/posix.py +1 -0
  620. ansible_test/_internal/commands/integration/windows.py +1 -0
  621. ansible_test/_internal/commands/sanity/__init__.py +19 -2
  622. ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
  623. ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
  624. ansible_test/_internal/commands/sanity/compile.py +1 -0
  625. ansible_test/_internal/commands/sanity/ignores.py +1 -0
  626. ansible_test/_internal/commands/sanity/import.py +1 -0
  627. ansible_test/_internal/commands/sanity/integration_aliases.py +12 -0
  628. ansible_test/_internal/commands/sanity/pep8.py +1 -0
  629. ansible_test/_internal/commands/sanity/pslint.py +1 -0
  630. ansible_test/_internal/commands/sanity/pylint.py +25 -26
  631. ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
  632. ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
  633. ansible_test/_internal/commands/sanity/yamllint.py +1 -0
  634. ansible_test/_internal/commands/shell/__init__.py +44 -4
  635. ansible_test/_internal/commands/units/__init__.py +5 -1
  636. ansible_test/_internal/compat/__init__.py +1 -0
  637. ansible_test/_internal/compat/packaging.py +1 -0
  638. ansible_test/_internal/compat/yaml.py +1 -0
  639. ansible_test/_internal/completion.py +1 -0
  640. ansible_test/_internal/config.py +23 -13
  641. ansible_test/_internal/connections.py +1 -0
  642. ansible_test/_internal/constants.py +1 -0
  643. ansible_test/_internal/containers.py +1 -0
  644. ansible_test/_internal/content_config.py +1 -0
  645. ansible_test/_internal/core_ci.py +1 -0
  646. ansible_test/_internal/coverage_util.py +11 -10
  647. ansible_test/_internal/data.py +1 -0
  648. ansible_test/_internal/debugging.py +166 -0
  649. ansible_test/_internal/delegation.py +22 -13
  650. ansible_test/_internal/dev/__init__.py +1 -0
  651. ansible_test/_internal/dev/container_probe.py +1 -0
  652. ansible_test/_internal/diff.py +3 -2
  653. ansible_test/_internal/docker_util.py +2 -1
  654. ansible_test/_internal/encoding.py +1 -0
  655. ansible_test/_internal/executor.py +1 -0
  656. ansible_test/_internal/git.py +1 -0
  657. ansible_test/_internal/host_configs.py +1 -0
  658. ansible_test/_internal/host_profiles.py +260 -16
  659. ansible_test/_internal/http.py +1 -0
  660. ansible_test/_internal/init.py +1 -0
  661. ansible_test/_internal/inventory.py +39 -3
  662. ansible_test/_internal/io.py +1 -0
  663. ansible_test/_internal/metadata.py +95 -4
  664. ansible_test/_internal/payload.py +1 -0
  665. ansible_test/_internal/processes.py +80 -0
  666. ansible_test/_internal/provider/__init__.py +1 -0
  667. ansible_test/_internal/provider/layout/__init__.py +1 -0
  668. ansible_test/_internal/provider/layout/ansible.py +1 -0
  669. ansible_test/_internal/provider/layout/collection.py +1 -0
  670. ansible_test/_internal/provider/layout/unsupported.py +1 -0
  671. ansible_test/_internal/provider/source/__init__.py +1 -0
  672. ansible_test/_internal/provider/source/git.py +1 -0
  673. ansible_test/_internal/provider/source/installed.py +1 -0
  674. ansible_test/_internal/provider/source/unsupported.py +1 -0
  675. ansible_test/_internal/provider/source/unversioned.py +1 -0
  676. ansible_test/_internal/provisioning.py +11 -4
  677. ansible_test/_internal/pypi_proxy.py +6 -5
  678. ansible_test/_internal/python_requirements.py +28 -0
  679. ansible_test/_internal/ssh.py +2 -5
  680. ansible_test/_internal/target.py +9 -0
  681. ansible_test/_internal/test.py +3 -2
  682. ansible_test/_internal/thread.py +3 -1
  683. ansible_test/_internal/timeout.py +2 -1
  684. ansible_test/_internal/util.py +41 -12
  685. ansible_test/_internal/util_common.py +18 -5
  686. ansible_test/_internal/venv.py +1 -0
  687. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
  688. ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
  689. ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
  690. ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
  691. ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
  692. ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
  693. ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
  694. ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
  695. ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
  696. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
  697. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
  698. ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
  699. ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
  700. ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
  701. ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
  702. ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
  703. ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
  704. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +8 -5
  705. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +8 -5
  706. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +8 -5
  707. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +4 -5
  708. ansible_test/_util/controller/sanity/pylint/config/default.cfg +8 -7
  709. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +541 -0
  710. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py +137 -0
  711. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
  712. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
  713. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
  714. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
  715. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
  716. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
  717. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
  718. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
  719. ansible_test/_util/controller/tools/collection_detail.py +1 -0
  720. ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
  721. ansible_test/_util/target/injector/python.py +8 -0
  722. ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
  723. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
  724. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
  725. ansible_test/_util/target/sanity/compile/compile.py +1 -0
  726. ansible_test/_util/target/sanity/import/importer.py +15 -16
  727. ansible_test/_util/target/setup/bootstrap.sh +9 -20
  728. ansible_test/_util/target/setup/probe_cgroups.py +1 -0
  729. ansible_test/_util/target/setup/quiet_pip.py +1 -0
  730. ansible_test/_util/target/setup/requirements.py +38 -36
  731. ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
  732. ansible_test/_util/target/tools/yamlcheck.py +2 -1
  733. ansible/compat/selectors.py +0 -32
  734. ansible/errors/yaml_strings.py +0 -138
  735. ansible/executor/action_write_locks.py +0 -44
  736. ansible/executor/discovery/python_target.py +0 -47
  737. ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
  738. ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
  739. ansible/module_utils/compat/importlib.py +0 -26
  740. ansible/module_utils/compat/selectors.py +0 -32
  741. ansible/module_utils/pycompat24.py +0 -73
  742. ansible/parsing/yaml/constructor.py +0 -178
  743. ansible/template/native_helpers.py +0 -251
  744. ansible/template/template.py +0 -43
  745. ansible/template/vars.py +0 -77
  746. ansible/utils/native_jinja.py +0 -11
  747. ansible/vars/fact_cache.py +0 -71
  748. ansible_core-2.18.7.dist-info/RECORD +0 -992
  749. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +0 -411
  750. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/WHEEL +0 -0
  751. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/entry_points.txt +0 -0
  752. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/licenses/COPYING +0 -0
  753. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  754. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  755. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  756. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  757. {ansible_core-2.18.7.dist-info → ansible_core-2.19.0.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- DOCUMENTATION = '''
9
+ DOCUMENTATION = """
10
10
  name: ssh
11
11
  short_description: connect via SSH client binary
12
12
  description:
@@ -62,10 +62,27 @@ DOCUMENTATION = '''
62
62
  - name: ansible_password
63
63
  - name: ansible_ssh_pass
64
64
  - name: ansible_ssh_password
65
+ password_mechanism:
66
+ description: Mechanism to use for handling ssh password prompt
67
+ type: string
68
+ default: ssh_askpass
69
+ choices:
70
+ - ssh_askpass
71
+ - sshpass
72
+ - disable
73
+ version_added: '2.19'
74
+ env:
75
+ - name: ANSIBLE_SSH_PASSWORD_MECHANISM
76
+ ini:
77
+ - {key: password_mechanism, section: ssh_connection}
78
+ vars:
79
+ - name: ansible_ssh_password_mechanism
65
80
  sshpass_prompt:
66
81
  description:
67
- - Password prompt that sshpass should search for. Supported by sshpass 1.06 and up.
82
+ - Password prompt that C(sshpass)/C(SSH_ASKPASS) should search for.
83
+ - Supported by sshpass 1.06 and up when O(password_mechanism) set to V(sshpass).
68
84
  - Defaults to C(Enter PIN for) when pkcs11_provider is set.
85
+ - Defaults to C(assword) when O(password_mechanism) set to V(ssh_askpass).
69
86
  default: ''
70
87
  type: string
71
88
  ini:
@@ -248,7 +265,6 @@ DOCUMENTATION = '''
248
265
  vars:
249
266
  - name: ansible_pipelining
250
267
  - name: ansible_ssh_pipelining
251
-
252
268
  private_key_file:
253
269
  description:
254
270
  - Path to private key file to use for authentication.
@@ -264,7 +280,27 @@ DOCUMENTATION = '''
264
280
  cli:
265
281
  - name: private_key_file
266
282
  option: '--private-key'
267
-
283
+ private_key:
284
+ description:
285
+ - Private key contents in PEM format. Requires the C(SSH_AGENT) configuration to be enabled.
286
+ type: string
287
+ env:
288
+ - name: ANSIBLE_PRIVATE_KEY
289
+ vars:
290
+ - name: ansible_private_key
291
+ - name: ansible_ssh_private_key
292
+ version_added: '2.19'
293
+ private_key_passphrase:
294
+ description:
295
+ - Private key passphrase, dependent on O(private_key).
296
+ - This does NOT have any effect when used with O(private_key_file).
297
+ type: string
298
+ env:
299
+ - name: ANSIBLE_PRIVATE_KEY_PASSPHRASE
300
+ vars:
301
+ - name: ansible_private_key_passphrase
302
+ - name: ansible_ssh_private_key_passphrase
303
+ version_added: '2.19'
268
304
  control_path:
269
305
  description:
270
306
  - This is the location to save SSH's ControlPath sockets, it uses SSH's variable substitution.
@@ -296,7 +332,9 @@ DOCUMENTATION = '''
296
332
  version_added: '2.7'
297
333
  sftp_batch_mode:
298
334
  default: true
299
- description: 'TODO: write it'
335
+ description:
336
+ - When set to C(True), sftp will be run in batch mode, allowing detection of transfer errors.
337
+ - When set to C(False), sftp will not be run in batch mode, preventing detection of transfer errors.
300
338
  env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
301
339
  ini:
302
340
  - {key: sftp_batch_mode, section: ssh_connection}
@@ -357,41 +395,69 @@ DOCUMENTATION = '''
357
395
  type: string
358
396
  description:
359
397
  - "PKCS11 SmartCard provider such as opensc, example: /usr/local/lib/opensc-pkcs11.so"
360
- - Requires sshpass version 1.06+, sshpass must support the -P option.
361
398
  env: [{name: ANSIBLE_PKCS11_PROVIDER}]
362
399
  ini:
363
400
  - {key: pkcs11_provider, section: ssh_connection}
364
401
  vars:
365
402
  - name: ansible_ssh_pkcs11_provider
366
- '''
403
+ verbosity:
404
+ version_added: '2.19'
405
+ default: 0
406
+ type: int
407
+ description:
408
+ - Requested verbosity level for the SSH CLI.
409
+ env: [{name: ANSIBLE_SSH_VERBOSITY}]
410
+ ini:
411
+ - {key: verbosity, section: ssh_connection}
412
+ vars:
413
+ - name: ansible_ssh_verbosity
414
+ """
367
415
 
368
416
  import collections.abc as c
417
+ import argparse
369
418
  import errno
419
+ import contextlib
370
420
  import fcntl
371
421
  import hashlib
372
422
  import io
423
+ import json
373
424
  import os
425
+ import pathlib
374
426
  import pty
375
427
  import re
376
428
  import selectors
377
429
  import shlex
430
+ import shutil
378
431
  import subprocess
432
+ import sys
433
+ import tempfile
379
434
  import time
380
435
  import typing as t
381
-
382
436
  from functools import wraps
437
+ from multiprocessing.shared_memory import SharedMemory
438
+
439
+ from ansible import constants as C
383
440
  from ansible.errors import (
384
441
  AnsibleAuthenticationFailure,
385
442
  AnsibleConnectionFailure,
386
443
  AnsibleError,
387
444
  AnsibleFileNotFound,
388
445
  )
389
- from ansible.module_utils.six import PY3, text_type, binary_type
446
+ from ansible.module_utils.six import text_type, binary_type
390
447
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
391
448
  from ansible.plugins.connection import ConnectionBase, BUFSIZE
392
449
  from ansible.plugins.shell.powershell import _replace_stderr_clixml
393
450
  from ansible.utils.display import Display
394
451
  from ansible.utils.path import unfrackpath, makedirs_safe
452
+ from ansible._internal._ssh import _ssh_agent
453
+
454
+ try:
455
+ from cryptography.hazmat.primitives import serialization
456
+ except ImportError:
457
+ HAS_CRYPTOGRAPHY = False
458
+ else:
459
+ HAS_CRYPTOGRAPHY = True
460
+
395
461
 
396
462
  display = Display()
397
463
 
@@ -408,9 +474,14 @@ b_NOT_SSH_ERRORS = (b'Traceback (most recent call last):', # Python-2.6 when th
408
474
  SSHPASS_AVAILABLE = None
409
475
  SSH_DEBUG = re.compile(r'^debug\d+: .*')
410
476
 
477
+ _HAS_RESOURCE_TRACK = sys.version_info[:2] >= (3, 13)
478
+
479
+ PKCS11_DEFAULT_PROMPT = 'Enter PIN for '
480
+ SSH_ASKPASS_DEFAULT_PROMPT = 'assword'
481
+
411
482
 
412
483
  class AnsibleControlPersistBrokenPipeError(AnsibleError):
413
- ''' ControlPersist broken pipe '''
484
+ """ ControlPersist broken pipe """
414
485
  pass
415
486
 
416
487
 
@@ -450,6 +521,7 @@ def _handle_error(
450
521
  'Upgrade sshpass to use sshpass_prompt, or otherwise switch to ssh keys.'
451
522
  raise AnsibleError('{0} {1}'.format(msg, details))
452
523
  msg = '{0} {1}'.format(msg, details)
524
+ raise AnsibleConnectionFailure(msg)
453
525
 
454
526
  if return_tuple[0] == 255:
455
527
  SSH_ERROR = True
@@ -496,9 +568,10 @@ def _ssh_retry(
496
568
  remaining_tries = int(self.get_option('reconnection_retries')) + 1
497
569
  cmd_summary = u"%s..." % to_text(args[0])
498
570
  conn_password = self.get_option('password') or self._play_context.password
571
+ is_sshpass = self.get_option('password_mechanism') == 'sshpass'
499
572
  for attempt in range(remaining_tries):
500
573
  cmd = t.cast(list[bytes], args[0])
501
- if attempt != 0 and conn_password and isinstance(cmd, list):
574
+ if attempt != 0 and is_sshpass and conn_password and isinstance(cmd, list):
502
575
  # If this is a retry, the fd/pipe for sshpass is closed, and we need a new one
503
576
  self.sshpass_pipe = os.pipe()
504
577
  cmd[1] = b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')
@@ -517,7 +590,7 @@ def _ssh_retry(
517
590
  except (AnsibleControlPersistBrokenPipeError):
518
591
  # Retry one more time because of the ControlPersist broken pipe (see #16731)
519
592
  cmd = t.cast(list[bytes], args[0])
520
- if conn_password and isinstance(cmd, list):
593
+ if is_sshpass and conn_password and isinstance(cmd, list):
521
594
  # This is a retry, so the fd/pipe for sshpass is closed, and we need a new one
522
595
  self.sshpass_pipe = os.pipe()
523
596
  cmd[1] = b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict')
@@ -558,8 +631,26 @@ def _ssh_retry(
558
631
  return wrapped
559
632
 
560
633
 
634
+ def _clean_shm(func):
635
+ def inner(self, *args, **kwargs):
636
+ try:
637
+ ret = func(self, *args, **kwargs)
638
+ finally:
639
+ if self.shm:
640
+ self.shm.close()
641
+ with contextlib.suppress(FileNotFoundError):
642
+ self.shm.unlink()
643
+ if not _HAS_RESOURCE_TRACK:
644
+ # deprecated: description='unneeded due to track argument for SharedMemory' python_version='3.12'
645
+ # There is a resource tracking issue where the resource is deleted, but tracking still has a record
646
+ # This will effectively overwrite the record and remove it
647
+ SharedMemory(name=self.shm.name, create=True, size=1).unlink()
648
+ return ret
649
+ return inner
650
+
651
+
561
652
  class Connection(ConnectionBase):
562
- ''' ssh based connections '''
653
+ """ ssh based connections """
563
654
 
564
655
  transport = 'ssh'
565
656
  has_pipelining = True
@@ -573,6 +664,8 @@ class Connection(ConnectionBase):
573
664
  self.user = self._play_context.remote_user
574
665
  self.control_path: str | None = None
575
666
  self.control_path_dir: str | None = None
667
+ self.shm: SharedMemory | None = None
668
+ self.sshpass_pipe: tuple[int, int] | None = None
576
669
 
577
670
  # Windows operates differently from a POSIX connection/shell plugin,
578
671
  # we need to set various properties to ensure SSH on Windows continues
@@ -583,6 +676,13 @@ class Connection(ConnectionBase):
583
676
  self.module_implementation_preferences = ('.ps1', '.exe', '')
584
677
  self.allow_executable = False
585
678
 
679
+ # parser to discover 'passed options', used later on for pipelining resolution
680
+ self._tty_parser = argparse.ArgumentParser()
681
+ self._tty_parser.add_argument('-t', action='count')
682
+ self._tty_parser.add_argument('-o', action='append')
683
+
684
+ self._populated_agent: pathlib.Path | None = None
685
+
586
686
  # The connection is created by running ssh/scp/sftp from the exec_command,
587
687
  # put_file, and fetch_file methods, so we don't need to do any connection
588
688
  # management here.
@@ -598,7 +698,7 @@ class Connection(ConnectionBase):
598
698
  connection: ConnectionBase | None = None,
599
699
  pid: int | None = None,
600
700
  ) -> str:
601
- '''Make a hash for the controlpath based on con attributes'''
701
+ """Make a hash for the controlpath based on con attributes"""
602
702
  pstring = '%s-%s-%s' % (host, port, user)
603
703
  if connection:
604
704
  pstring += '-%s' % connection
@@ -614,28 +714,21 @@ class Connection(ConnectionBase):
614
714
  def _sshpass_available() -> bool:
615
715
  global SSHPASS_AVAILABLE
616
716
 
617
- # We test once if sshpass is available, and remember the result. It
618
- # would be nice to use distutils.spawn.find_executable for this, but
619
- # distutils isn't always available; shutils.which() is Python3-only.
717
+ # We test once if sshpass is available, and remember the result.
620
718
 
621
719
  if SSHPASS_AVAILABLE is None:
622
- try:
623
- p = subprocess.Popen(["sshpass"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
624
- p.communicate()
625
- SSHPASS_AVAILABLE = True
626
- except OSError:
627
- SSHPASS_AVAILABLE = False
720
+ SSHPASS_AVAILABLE = shutil.which('sshpass') is not None
628
721
 
629
722
  return SSHPASS_AVAILABLE
630
723
 
631
724
  @staticmethod
632
725
  def _persistence_controls(b_command: list[bytes]) -> tuple[bool, bool]:
633
- '''
726
+ """
634
727
  Takes a command array and scans it for ControlPersist and ControlPath
635
728
  settings and returns two booleans indicating whether either was found.
636
729
  This could be smarter, e.g. returning false if ControlPersist is 'no',
637
730
  but for now we do it simple way.
638
- '''
731
+ """
639
732
 
640
733
  controlpersist = False
641
734
  controlpath = False
@@ -664,8 +757,54 @@ class Connection(ConnectionBase):
664
757
  display.vvvvv(u'SSH: %s: (%s)' % (explanation, ')('.join(to_text(a) for a in b_args)), host=self.host)
665
758
  b_command += b_args
666
759
 
760
+ def _populate_agent(self) -> pathlib.Path:
761
+ """Adds configured private key identity to the SSH agent. Returns a path to a file containing the public key."""
762
+ if self._populated_agent:
763
+ return self._populated_agent
764
+
765
+ if (auth_sock := C.config.get_config_value('SSH_AGENT')) == 'none':
766
+ raise AnsibleError('Cannot utilize private_key with SSH_AGENT disabled')
767
+
768
+ key_data = self.get_option('private_key')
769
+ passphrase = self.get_option('private_key_passphrase')
770
+
771
+ private_key, public_key, fingerprint = _ssh_agent.key_data_into_crypto_objects(
772
+ to_bytes(key_data),
773
+ to_bytes(passphrase) if passphrase else None,
774
+ )
775
+
776
+ with _ssh_agent.SshAgentClient(auth_sock) as client:
777
+ if public_key not in client:
778
+ display.vvv(f'SSH: SSH_AGENT adding {fingerprint} to agent', host=self.host)
779
+ client.add(
780
+ private_key,
781
+ f'[added by ansible: PID={os.getpid()}, UID={os.getuid()}, EUID={os.geteuid()}, TIME={time.time()}]',
782
+ C.config.get_config_value('SSH_AGENT_KEY_LIFETIME'),
783
+ )
784
+ else:
785
+ display.vvv(f'SSH: SSH_AGENT {fingerprint} exists in agent', host=self.host)
786
+ # Write the public key to disk, to be provided as IdentityFile.
787
+ # This allows ssh to pick an explicit key in the agent to use,
788
+ # preventing ssh from attempting all keys in the agent.
789
+ pubkey_path = self._populated_agent = pathlib.Path(C.DEFAULT_LOCAL_TMP).joinpath(
790
+ fingerprint.replace('/', '-') + '.pub'
791
+ )
792
+ if os.path.exists(pubkey_path):
793
+ return pubkey_path
794
+
795
+ with tempfile.NamedTemporaryFile(dir=C.DEFAULT_LOCAL_TMP, delete=False) as f:
796
+ f.write(public_key.public_bytes(
797
+ encoding=serialization.Encoding.OpenSSH,
798
+ format=serialization.PublicFormat.OpenSSH
799
+ ))
800
+ # move atomically to prevent race conditions, silently succeeds if the target exists
801
+ os.rename(f.name, pubkey_path)
802
+ os.chmod(pubkey_path, mode=0o400)
803
+
804
+ return self._populated_agent
805
+
667
806
  def _build_command(self, binary: str, subsystem: str, *other_args: bytes | str) -> list[bytes]:
668
- '''
807
+ """
669
808
  Takes a executable (ssh, scp, sftp or wrapper) and optional extra arguments and returns the remote command
670
809
  wrapped in local ssh shell commands and ready for execution.
671
810
 
@@ -673,21 +812,22 @@ class Connection(ConnectionBase):
673
812
  :arg subsystem: type of executable provided, ssh/sftp/scp, needed because wrappers for ssh might have diff names.
674
813
  :arg other_args: dict of, value pairs passed as arguments to the ssh binary
675
814
 
676
- '''
815
+ """
677
816
 
678
817
  b_command = []
679
818
  conn_password = self.get_option('password') or self._play_context.password
819
+ pkcs11_provider = self.get_option("pkcs11_provider")
820
+ password_mechanism = self.get_option('password_mechanism')
680
821
 
681
822
  #
682
823
  # First, the command to invoke
683
824
  #
684
825
 
685
- # If we want to use password authentication, we have to set up a pipe to
826
+ # If we want to use sshpass for password authentication, we have to set up a pipe to
686
827
  # write the password to sshpass.
687
- pkcs11_provider = self.get_option("pkcs11_provider")
688
- if conn_password or pkcs11_provider:
828
+ if password_mechanism == 'sshpass' and (conn_password or pkcs11_provider):
689
829
  if not self._sshpass_available():
690
- raise AnsibleError("to use the 'ssh' connection type with passwords or pkcs11_provider, you must install the sshpass program")
830
+ raise AnsibleError("to use the password_mechanism=sshpass, you must install the sshpass program")
691
831
  if not conn_password and pkcs11_provider:
692
832
  raise AnsibleError("to use pkcs11_provider you must specify a password/pin")
693
833
 
@@ -697,7 +837,7 @@ class Connection(ConnectionBase):
697
837
  password_prompt = self.get_option('sshpass_prompt')
698
838
  if not password_prompt and pkcs11_provider:
699
839
  # Set default password prompt for pkcs11_provider to make it clear its a PIN
700
- password_prompt = 'Enter PIN for '
840
+ password_prompt = PKCS11_DEFAULT_PROMPT
701
841
 
702
842
  if password_prompt:
703
843
  b_command += [b'-P', to_bytes(password_prompt, errors='surrogate_or_strict')]
@@ -720,16 +860,16 @@ class Connection(ConnectionBase):
720
860
  # sftp batch mode allows us to correctly catch failed transfers, but can
721
861
  # be disabled if the client side doesn't support the option. However,
722
862
  # sftp batch mode does not prompt for passwords so it must be disabled
723
- # if not using controlpersist and using sshpass
863
+ # if not using controlpersist and using password auth
724
864
  b_args: t.Iterable[bytes]
725
865
  if subsystem == 'sftp' and self.get_option('sftp_batch_mode'):
726
866
  if conn_password:
727
867
  b_args = [b'-o', b'BatchMode=no']
728
- self._add_args(b_command, b_args, u'disable batch mode for sshpass')
868
+ self._add_args(b_command, b_args, u'disable batch mode for password auth')
729
869
  b_command += [b'-b', b'-']
730
870
 
731
- if display.verbosity:
732
- b_command.append(b'-' + (b'v' * display.verbosity))
871
+ if (verbosity := self.get_option('verbosity')) > 0:
872
+ b_command.append(b'-' + (b'v' * verbosity))
733
873
 
734
874
  # Next, we add ssh_args
735
875
  ssh_args = self.get_option('ssh_args')
@@ -748,8 +888,14 @@ class Connection(ConnectionBase):
748
888
  b_args = (b"-o", b"Port=" + to_bytes(self.port, nonstring='simplerepr', errors='surrogate_or_strict'))
749
889
  self._add_args(b_command, b_args, u"ANSIBLE_REMOTE_PORT/remote_port/ansible_port set")
750
890
 
751
- key = self.get_option('private_key_file')
752
- if key:
891
+ if self.get_option('private_key'):
892
+ try:
893
+ key = self._populate_agent()
894
+ except Exception as e:
895
+ raise AnsibleAuthenticationFailure('Failed to add configured private key into ssh-agent.') from e
896
+ b_args = (b'-o', b'IdentitiesOnly=yes', b'-o', to_bytes(f'IdentityFile="{key}"', errors='surrogate_or_strict'))
897
+ self._add_args(b_command, b_args, "ANSIBLE_PRIVATE_KEY/private_key set")
898
+ elif key := self.get_option('private_key_file'):
753
899
  b_args = (b"-o", b'IdentityFile="' + to_bytes(os.path.expanduser(key), errors='surrogate_or_strict') + b'"')
754
900
  self._add_args(b_command, b_args, u"ANSIBLE_PRIVATE_KEY_FILE/private_key_file/ansible_ssh_private_key_file set")
755
901
 
@@ -815,6 +961,13 @@ class Connection(ConnectionBase):
815
961
  b_args = (b"-o", b'ControlPath="%s"' % to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict'))
816
962
  self._add_args(b_command, b_args, u"found only ControlPersist; added ControlPath")
817
963
 
964
+ if password_mechanism == "ssh_askpass":
965
+ self._add_args(
966
+ b_command,
967
+ (b"-o", b"NumberOfPasswordPrompts=1"),
968
+ "Restrict number of password prompts in case incorrect password is provided.",
969
+ )
970
+
818
971
  # Finally, we add any caller-supplied extras.
819
972
  if other_args:
820
973
  b_command += [to_bytes(a) for a in other_args]
@@ -822,27 +975,24 @@ class Connection(ConnectionBase):
822
975
  return b_command
823
976
 
824
977
  def _send_initial_data(self, fh: io.IOBase, in_data: bytes, ssh_process: subprocess.Popen) -> None:
825
- '''
978
+ """
826
979
  Writes initial data to the stdin filehandle of the subprocess and closes
827
980
  it. (The handle must be closed; otherwise, for example, "sftp -b -" will
828
981
  just hang forever waiting for more commands.)
829
- '''
982
+ """
830
983
 
831
984
  display.debug(u'Sending initial data')
832
985
 
833
986
  try:
834
987
  fh.write(to_bytes(in_data))
835
988
  fh.close()
836
- except (OSError, IOError) as e:
989
+ except OSError as ex:
837
990
  # The ssh connection may have already terminated at this point, with a more useful error
838
991
  # Only raise AnsibleConnectionFailure if the ssh process is still alive
839
992
  time.sleep(0.001)
840
993
  ssh_process.poll()
841
994
  if getattr(ssh_process, 'returncode', None) is None:
842
- raise AnsibleConnectionFailure(
843
- 'Data could not be sent to remote host "%s". Make sure this host can be reached '
844
- 'over ssh: %s' % (self.host, to_native(e)), orig_exc=e
845
- )
995
+ raise AnsibleConnectionFailure(f'Data could not be sent to remote host {self.host!r}. Make sure this host can be reached over SSH.') from ex
846
996
 
847
997
  display.debug(u'Sent initial data (%d bytes)' % len(in_data))
848
998
 
@@ -852,20 +1002,20 @@ class Connection(ConnectionBase):
852
1002
  """ Terminate a process, ignoring errors """
853
1003
  try:
854
1004
  p.terminate()
855
- except (OSError, IOError):
1005
+ except OSError:
856
1006
  pass
857
1007
 
858
1008
  # This is separate from _run() because we need to do the same thing for stdout
859
1009
  # and stderr.
860
1010
  def _examine_output(self, source: str, state: str, b_chunk: bytes, sudoable: bool) -> tuple[bytes, bytes]:
861
- '''
1011
+ """
862
1012
  Takes a string, extracts complete lines from it, tests to see if they
863
1013
  are a prompt, error message, etc., and sets appropriate flags in self.
864
1014
  Prompt and success lines are removed.
865
1015
 
866
1016
  Returns the processed (i.e. possibly-edited) output and the unprocessed
867
1017
  remainder (to be processed with the next chunk) as strings.
868
- '''
1018
+ """
869
1019
 
870
1020
  output = []
871
1021
  for b_line in b_chunk.splitlines(True):
@@ -906,15 +1056,69 @@ class Connection(ConnectionBase):
906
1056
 
907
1057
  return b''.join(output), remainder
908
1058
 
1059
+ def _init_shm(self) -> dict[str, t.Any]:
1060
+ env = os.environ.copy()
1061
+ popen_kwargs: dict[str, t.Any] = {}
1062
+
1063
+ if self.get_option('password_mechanism') != 'ssh_askpass':
1064
+ return popen_kwargs
1065
+
1066
+ conn_password = self.get_option('password') or self._play_context.password
1067
+ pkcs11_provider = self.get_option("pkcs11_provider")
1068
+ if not conn_password and pkcs11_provider:
1069
+ raise AnsibleError("to use pkcs11_provider you must specify a password/pin")
1070
+
1071
+ if not conn_password:
1072
+ return popen_kwargs
1073
+
1074
+ kwargs = {}
1075
+ if _HAS_RESOURCE_TRACK:
1076
+ # deprecated: description='track argument for SharedMemory always available' python_version='3.12'
1077
+ kwargs['track'] = False
1078
+ self.shm = shm = SharedMemory(create=True, size=16384, **kwargs) # type: ignore[arg-type]
1079
+
1080
+ sshpass_prompt = self.get_option('sshpass_prompt')
1081
+ if not sshpass_prompt and pkcs11_provider:
1082
+ sshpass_prompt = PKCS11_DEFAULT_PROMPT
1083
+ elif not sshpass_prompt:
1084
+ sshpass_prompt = SSH_ASKPASS_DEFAULT_PROMPT
1085
+
1086
+ data = json.dumps({
1087
+ 'password': conn_password,
1088
+ 'prompt': sshpass_prompt,
1089
+ }).encode('utf-8')
1090
+ shm.buf[:len(data)] = bytearray(data)
1091
+ shm.close()
1092
+
1093
+ env['_ANSIBLE_SSH_ASKPASS_SHM'] = str(self.shm.name)
1094
+ adhoc = pathlib.Path(sys.argv[0]).with_name('ansible')
1095
+ env['SSH_ASKPASS'] = str(adhoc) if adhoc.is_file() else 'ansible'
1096
+
1097
+ # SSH_ASKPASS_REQUIRE was added in openssh 8.4, prior to 8.4 there must be no tty, and DISPLAY must be set
1098
+ env['SSH_ASKPASS_REQUIRE'] = 'force'
1099
+ if not env.get('DISPLAY'):
1100
+ # If the user has DISPLAY set, assume it is there for a reason
1101
+ env['DISPLAY'] = '-'
1102
+
1103
+ popen_kwargs['env'] = env
1104
+ # start_new_session runs setsid which detaches the tty to support the use of ASKPASS prior to openssh 8.4
1105
+ popen_kwargs['start_new_session'] = True
1106
+
1107
+ return popen_kwargs
1108
+
1109
+ @_clean_shm
909
1110
  def _bare_run(self, cmd: list[bytes], in_data: bytes | None, sudoable: bool = True, checkrc: bool = True) -> tuple[int, bytes, bytes]:
910
- '''
1111
+ """
911
1112
  Starts the command and communicates with it until it ends.
912
- '''
1113
+ """
913
1114
 
914
1115
  # We don't use _shell.quote as this is run on the controller and independent from the shell plugin chosen
915
1116
  display_cmd = u' '.join(shlex.quote(to_text(c)) for c in cmd)
916
1117
  display.vvv(u'SSH: EXEC {0}'.format(display_cmd), host=self.host)
917
1118
 
1119
+ conn_password = self.get_option('password') or self._play_context.password
1120
+ password_mechanism = self.get_option('password_mechanism')
1121
+
918
1122
  # Start the given command. If we don't need to pipeline data, we can try
919
1123
  # to use a pseudo-tty (ssh will have been invoked with -tt). If we are
920
1124
  # pipelining data, or can't create a pty, we fall back to using plain
@@ -927,39 +1131,30 @@ class Connection(ConnectionBase):
927
1131
  else:
928
1132
  cmd = list(map(to_bytes, cmd))
929
1133
 
930
- conn_password = self.get_option('password') or self._play_context.password
1134
+ popen_kwargs = self._init_shm()
1135
+
1136
+ if self.sshpass_pipe:
1137
+ popen_kwargs['pass_fds'] = self.sshpass_pipe
931
1138
 
932
1139
  if not in_data:
933
1140
  try:
934
1141
  # Make sure stdin is a proper pty to avoid tcgetattr errors
935
1142
  master, slave = pty.openpty()
936
- if PY3 and conn_password:
937
- # pylint: disable=unexpected-keyword-arg
938
- p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe)
939
- else:
940
- p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1143
+ p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs)
941
1144
  stdin = os.fdopen(master, 'wb', 0)
942
1145
  os.close(slave)
943
- except (OSError, IOError):
1146
+ except OSError:
944
1147
  p = None
945
1148
 
946
1149
  if not p:
947
1150
  try:
948
- if PY3 and conn_password:
949
- # pylint: disable=unexpected-keyword-arg
950
- p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
951
- stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe)
952
- else:
953
- p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
954
- stderr=subprocess.PIPE)
1151
+ p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1152
+ stderr=subprocess.PIPE, **popen_kwargs)
955
1153
  stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
956
- except (OSError, IOError) as e:
957
- raise AnsibleError('Unable to execute ssh command line on a controller due to: %s' % to_native(e))
1154
+ except OSError as ex:
1155
+ raise AnsibleError('Unable to execute ssh command line on a controller.') from ex
958
1156
 
959
- # If we are using SSH password authentication, write the password into
960
- # the pipe we opened in _build_command.
961
-
962
- if conn_password:
1157
+ if password_mechanism == 'sshpass' and conn_password:
963
1158
  os.close(self.sshpass_pipe[0])
964
1159
  try:
965
1160
  os.write(self.sshpass_pipe[1], to_bytes(conn_password) + b'\n')
@@ -983,7 +1178,7 @@ class Connection(ConnectionBase):
983
1178
 
984
1179
  # Are we requesting privilege escalation? Right now, we may be invoked
985
1180
  # to execute sftp/scp with sudoable=True, but we can request escalation
986
- # only when using ssh. Otherwise we can send initial data straightaway.
1181
+ # only when using ssh. Otherwise, we can send initial data straight away.
987
1182
 
988
1183
  state = states.index('ready_to_send')
989
1184
  if to_bytes(self.get_option('ssh_executable')) in cmd and sudoable:
@@ -1047,7 +1242,9 @@ class Connection(ConnectionBase):
1047
1242
  if poll is not None:
1048
1243
  break
1049
1244
  self._terminate_process(p)
1050
- raise AnsibleError('Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, to_native(b_stdout)))
1245
+ raise AnsibleConnectionFailure('Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, to_native(b_stdout)))
1246
+
1247
+ display.vvvvv(f'SSH: Timeout ({timeout}s) waiting for the output', host=self.host)
1051
1248
 
1052
1249
  # Read whatever output is available on stdout and stderr, and stop
1053
1250
  # listening to the pipe if it's been closed.
@@ -1117,23 +1314,23 @@ class Connection(ConnectionBase):
1117
1314
 
1118
1315
  if states[state] == 'awaiting_escalation':
1119
1316
  if self._flags['become_success']:
1120
- display.vvv(u'Escalation succeeded')
1317
+ display.vvv(u'Escalation succeeded', host=self.host)
1121
1318
  self._flags['become_success'] = False
1122
1319
  state += 1
1123
1320
  elif self._flags['become_error']:
1124
- display.vvv(u'Escalation failed')
1321
+ display.vvv(u'Escalation failed', host=self.host)
1125
1322
  self._terminate_process(p)
1126
1323
  self._flags['become_error'] = False
1127
1324
  raise AnsibleError('Incorrect %s password' % self.become.name)
1128
1325
  elif self._flags['become_nopasswd_error']:
1129
- display.vvv(u'Escalation requires password')
1326
+ display.vvv(u'Escalation requires password', host=self.host)
1130
1327
  self._terminate_process(p)
1131
1328
  self._flags['become_nopasswd_error'] = False
1132
1329
  raise AnsibleError('Missing %s password' % self.become.name)
1133
1330
  elif self._flags['become_prompt']:
1134
1331
  # This shouldn't happen, because we should see the "Sorry,
1135
1332
  # try again" message first.
1136
- display.vvv(u'Escalation prompt repeated')
1333
+ display.vvv(u'Escalation prompt repeated', host=self.host)
1137
1334
  self._terminate_process(p)
1138
1335
  self._flags['become_prompt'] = False
1139
1336
  raise AnsibleError('Incorrect %s password' % self.become.name)
@@ -1176,10 +1373,15 @@ class Connection(ConnectionBase):
1176
1373
  p.stdout.close()
1177
1374
  p.stderr.close()
1178
1375
 
1179
- if self.get_option('host_key_checking'):
1180
- if cmd[0] == b"sshpass" and p.returncode == 6:
1181
- raise AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support '
1182
- 'this. Please add this host\'s fingerprint to your known_hosts file to manage this host.')
1376
+ conn_password = self.get_option('password') or self._play_context.password
1377
+ hostkey_fail = any((
1378
+ (cmd[0] == b"sshpass" and p.returncode == 6),
1379
+ b"read_passphrase: can't open /dev/tty" in b_stderr,
1380
+ b"Host key verification failed" in b_stderr,
1381
+ ))
1382
+ if password_mechanism and self.get_option('host_key_checking') and conn_password and hostkey_fail:
1383
+ raise AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled. '
1384
+ 'Please add this host\'s fingerprint to your known_hosts file to manage this host.')
1183
1385
 
1184
1386
  controlpersisterror = b'Bad configuration option: ControlPersist' in b_stderr or b'unknown configuration option: ControlPersist' in b_stderr
1185
1387
  if p.returncode != 0 and controlpersisterror:
@@ -1292,7 +1494,7 @@ class Connection(ConnectionBase):
1292
1494
  # Main public methods
1293
1495
  #
1294
1496
  def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: bool = True) -> tuple[int, bytes, bytes]:
1295
- ''' run a command on the remote host '''
1497
+ """ run a command on the remote host """
1296
1498
 
1297
1499
  super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
1298
1500
 
@@ -1333,7 +1535,7 @@ class Connection(ConnectionBase):
1333
1535
  return (returncode, stdout, stderr)
1334
1536
 
1335
1537
  def put_file(self, in_path: str, out_path: str) -> tuple[int, bytes, bytes]: # type: ignore[override] # Used by tests and would break API
1336
- ''' transfer a file from local to remote '''
1538
+ """ transfer a file from local to remote """
1337
1539
 
1338
1540
  super(Connection, self).put_file(in_path, out_path)
1339
1541
 
@@ -1349,7 +1551,7 @@ class Connection(ConnectionBase):
1349
1551
  return self._file_transport_command(in_path, out_path, 'put')
1350
1552
 
1351
1553
  def fetch_file(self, in_path: str, out_path: str) -> tuple[int, bytes, bytes]: # type: ignore[override] # Used by tests and would break API
1352
- ''' fetch a file from remote to local '''
1554
+ """ fetch a file from remote to local """
1353
1555
 
1354
1556
  super(Connection, self).fetch_file(in_path, out_path)
1355
1557
 
@@ -1372,18 +1574,18 @@ class Connection(ConnectionBase):
1372
1574
  # only run the reset if the ControlPath already exists or if it isn't configured and ControlPersist is set
1373
1575
  # 'check' will determine this.
1374
1576
  cmd = self._build_command(self.get_option('ssh_executable'), 'ssh', '-O', 'check', self.host)
1375
- display.vvv(u'sending connection check: %s' % to_text(cmd))
1577
+ display.vvv(u'sending connection check: %s' % to_text(cmd), host=self.host)
1376
1578
  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1377
1579
  stdout, stderr = p.communicate()
1378
1580
  status_code = p.wait()
1379
1581
  if status_code != 0:
1380
- display.vvv(u"No connection to reset: %s" % to_text(stderr))
1582
+ display.vvv(u"No connection to reset: %s" % to_text(stderr), host=self.host)
1381
1583
  else:
1382
1584
  run_reset = True
1383
1585
 
1384
1586
  if run_reset:
1385
1587
  cmd = self._build_command(self.get_option('ssh_executable'), 'ssh', '-O', 'stop', self.host)
1386
- display.vvv(u'sending connection stop: %s' % to_text(cmd))
1588
+ display.vvv(u'sending connection stop: %s' % to_text(cmd), host=self.host)
1387
1589
  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1388
1590
  stdout, stderr = p.communicate()
1389
1591
  status_code = p.wait()
@@ -1394,3 +1596,41 @@ class Connection(ConnectionBase):
1394
1596
 
1395
1597
  def close(self) -> None:
1396
1598
  self._connected = False
1599
+
1600
+ @property
1601
+ def has_tty(self):
1602
+ return self._is_tty_requested()
1603
+
1604
+ def _is_tty_requested(self):
1605
+
1606
+ # check if we require tty (only from our args, cannot see options in configuration files)
1607
+ opts = []
1608
+ for opt in ('ssh_args', 'ssh_common_args', 'ssh_extra_args'):
1609
+ attr = self.get_option(opt)
1610
+ if attr is not None:
1611
+ opts.extend(self._split_ssh_args(attr))
1612
+
1613
+ args, dummy = self._tty_parser.parse_known_args(opts)
1614
+
1615
+ if args.t:
1616
+ return True
1617
+
1618
+ for arg in args.o or []:
1619
+ if '=' in arg:
1620
+ val = arg.split('=', 1)
1621
+ else:
1622
+ val = arg.split(maxsplit=1)
1623
+
1624
+ if val[0].lower().strip() == 'requesttty':
1625
+ if val[1].lower().strip() in ('yes', 'force'):
1626
+ return True
1627
+
1628
+ return False
1629
+
1630
+ def is_pipelining_enabled(self, wrap_async=False):
1631
+ """ override parent method and ensure we don't request a tty """
1632
+
1633
+ if self._is_tty_requested():
1634
+ return False
1635
+ else:
1636
+ return super(Connection, self).is_pipelining_enabled(wrap_async)