ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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.
Files changed (225) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  5. ansible/_internal/_collection_proxy.py +1 -1
  6. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  7. ansible/_internal/_errors/_captured.py +25 -30
  8. ansible/_internal/_errors/_error_factory.py +89 -0
  9. ansible/_internal/_errors/_error_utils.py +240 -0
  10. ansible/_internal/_errors/_task_timeout.py +28 -0
  11. ansible/_internal/_event_formatting.py +127 -0
  12. ansible/_internal/_json/__init__.py +5 -5
  13. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  14. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  15. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  16. ansible/_internal/_ssh/__init__.py +0 -0
  17. ansible/_internal/_ssh/_agent_launch.py +91 -0
  18. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  19. ansible/_internal/_templating/__init__.py +5 -3
  20. ansible/_internal/_templating/_datatag.py +2 -1
  21. ansible/_internal/_templating/_engine.py +3 -4
  22. ansible/_internal/_templating/_jinja_bits.py +28 -20
  23. ansible/_internal/_templating/_jinja_common.py +18 -27
  24. ansible/_internal/_templating/_jinja_plugins.py +36 -5
  25. ansible/_internal/_templating/_lazy_containers.py +5 -5
  26. ansible/_internal/_templating/_template_vars.py +72 -0
  27. ansible/_internal/_templating/_transform.py +26 -19
  28. ansible/_internal/_templating/_utils.py +1 -1
  29. ansible/_internal/_yaml/_constructor.py +4 -4
  30. ansible/_internal/_yaml/_dumper.py +26 -18
  31. ansible/_internal/_yaml/_errors.py +7 -7
  32. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  33. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  34. ansible/cli/__init__.py +11 -93
  35. ansible/cli/arguments/option_helpers.py +3 -4
  36. ansible/cli/console.py +1 -1
  37. ansible/cli/doc.py +86 -30
  38. ansible/cli/inventory.py +5 -7
  39. ansible/compat/importlib_resources.py +9 -12
  40. ansible/config/base.yml +46 -0
  41. ansible/errors/__init__.py +98 -50
  42. ansible/executor/module_common.py +75 -49
  43. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  44. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  45. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  46. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  47. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  48. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  49. ansible/executor/powershell/module_manifest.py +52 -0
  50. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  51. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  52. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  53. ansible/executor/process/worker.py +40 -115
  54. ansible/executor/task_executor.py +26 -61
  55. ansible/executor/task_result.py +2 -4
  56. ansible/galaxy/api.py +1 -4
  57. ansible/galaxy/collection/__init__.py +2 -10
  58. ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
  59. ansible/galaxy/role.py +2 -2
  60. ansible/inventory/manager.py +1 -1
  61. ansible/module_utils/_internal/__init__.py +7 -7
  62. ansible/module_utils/_internal/_ambient_context.py +3 -3
  63. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  64. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  65. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  66. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  67. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
  68. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  69. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  70. ansible/module_utils/_internal/_datatag/__init__.py +43 -15
  71. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  72. ansible/module_utils/_internal/_deprecator.py +67 -55
  73. ansible/module_utils/_internal/_errors.py +88 -17
  74. ansible/module_utils/_internal/_event_utils.py +61 -0
  75. ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
  76. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  77. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  78. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  79. ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
  80. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  81. ansible/module_utils/_internal/_plugin_info.py +15 -2
  82. ansible/module_utils/_internal/_stack.py +22 -0
  83. ansible/module_utils/_internal/_text_utils.py +6 -0
  84. ansible/module_utils/_internal/_traceback.py +11 -8
  85. ansible/module_utils/ansible_release.py +1 -1
  86. ansible/module_utils/basic.py +95 -71
  87. ansible/module_utils/common/arg_spec.py +2 -2
  88. ansible/module_utils/common/collections.py +6 -0
  89. ansible/module_utils/common/json.py +2 -2
  90. ansible/module_utils/common/respawn.py +4 -41
  91. ansible/module_utils/common/text/converters.py +3 -3
  92. ansible/module_utils/common/validation.py +1 -1
  93. ansible/module_utils/common/warnings.py +80 -23
  94. ansible/module_utils/common/yaml.py +1 -1
  95. ansible/module_utils/connection.py +8 -11
  96. ansible/module_utils/datatag.py +5 -2
  97. ansible/module_utils/facts/hardware/linux.py +1 -1
  98. ansible/module_utils/facts/sysctl.py +4 -6
  99. ansible/module_utils/facts/system/caps.py +2 -2
  100. ansible/module_utils/facts/system/distribution.py +16 -3
  101. ansible/module_utils/facts/system/local.py +1 -1
  102. ansible/module_utils/facts/virtual/linux.py +2 -2
  103. ansible/module_utils/service.py +3 -10
  104. ansible/module_utils/urls.py +4 -4
  105. ansible/modules/apt_repository.py +17 -39
  106. ansible/modules/assemble.py +2 -2
  107. ansible/modules/async_status.py +13 -11
  108. ansible/modules/async_wrapper.py +12 -22
  109. ansible/modules/command.py +3 -3
  110. ansible/modules/copy.py +4 -4
  111. ansible/modules/cron.py +1 -1
  112. ansible/modules/dnf5.py +14 -22
  113. ansible/modules/file.py +16 -17
  114. ansible/modules/find.py +3 -3
  115. ansible/modules/get_url.py +17 -0
  116. ansible/modules/git.py +9 -7
  117. ansible/modules/hostname.py +0 -1
  118. ansible/modules/known_hosts.py +12 -14
  119. ansible/modules/package.py +6 -0
  120. ansible/modules/replace.py +2 -2
  121. ansible/modules/service.py +3 -9
  122. ansible/modules/slurp.py +10 -13
  123. ansible/modules/stat.py +5 -7
  124. ansible/modules/unarchive.py +6 -6
  125. ansible/modules/user.py +1 -1
  126. ansible/modules/wait_for.py +28 -30
  127. ansible/modules/yum_repository.py +4 -3
  128. ansible/parsing/ajson.py +3 -5
  129. ansible/parsing/dataloader.py +6 -6
  130. ansible/parsing/mod_args.py +1 -1
  131. ansible/parsing/plugin_docs.py +2 -2
  132. ansible/parsing/utils/yaml.py +3 -3
  133. ansible/parsing/vault/__init__.py +10 -14
  134. ansible/playbook/base.py +7 -2
  135. ansible/playbook/included_file.py +3 -1
  136. ansible/playbook/play_context.py +2 -0
  137. ansible/playbook/playbook_include.py +1 -1
  138. ansible/playbook/taggable.py +19 -8
  139. ansible/playbook/task.py +2 -0
  140. ansible/plugins/__init__.py +0 -25
  141. ansible/plugins/action/__init__.py +8 -31
  142. ansible/plugins/action/add_host.py +1 -1
  143. ansible/plugins/action/assemble.py +8 -16
  144. ansible/plugins/action/async_status.py +7 -2
  145. ansible/plugins/action/copy.py +8 -7
  146. ansible/plugins/action/fetch.py +3 -3
  147. ansible/plugins/action/gather_facts.py +8 -8
  148. ansible/plugins/action/package.py +5 -8
  149. ansible/plugins/action/script.py +8 -15
  150. ansible/plugins/action/service.py +3 -7
  151. ansible/plugins/action/template.py +11 -10
  152. ansible/plugins/action/unarchive.py +5 -15
  153. ansible/plugins/action/uri.py +9 -20
  154. ansible/plugins/cache/__init__.py +17 -19
  155. ansible/plugins/callback/__init__.py +4 -6
  156. ansible/plugins/callback/junit.py +4 -2
  157. ansible/plugins/callback/tree.py +5 -5
  158. ansible/plugins/connection/local.py +6 -6
  159. ansible/plugins/connection/paramiko_ssh.py +5 -5
  160. ansible/plugins/connection/ssh.py +25 -15
  161. ansible/plugins/connection/winrm.py +6 -3
  162. ansible/plugins/doc_fragments/constructed.py +2 -2
  163. ansible/plugins/filter/core.py +32 -27
  164. ansible/plugins/filter/encryption.py +14 -6
  165. ansible/plugins/inventory/__init__.py +11 -10
  166. ansible/plugins/inventory/script.py +1 -1
  167. ansible/plugins/list.py +73 -19
  168. ansible/plugins/loader.py +7 -7
  169. ansible/plugins/lookup/csvfile.py +16 -71
  170. ansible/plugins/lookup/first_found.py +2 -1
  171. ansible/plugins/lookup/template.py +9 -4
  172. ansible/plugins/shell/__init__.py +56 -2
  173. ansible/plugins/shell/powershell.py +67 -9
  174. ansible/plugins/shell/sh.py +10 -5
  175. ansible/plugins/strategy/__init__.py +3 -3
  176. ansible/plugins/test/core.py +22 -16
  177. ansible/plugins/test/finished.yml +1 -1
  178. ansible/plugins/test/uri.py +2 -5
  179. ansible/release.py +1 -1
  180. ansible/template/__init__.py +38 -54
  181. ansible/utils/collection_loader/_collection_finder.py +3 -3
  182. ansible/utils/display.py +124 -138
  183. ansible/utils/galaxy.py +2 -2
  184. ansible/utils/hashing.py +6 -8
  185. ansible/utils/listify.py +6 -4
  186. ansible/utils/path.py +5 -7
  187. ansible/utils/py3compat.py +2 -1
  188. ansible/utils/ssh_functions.py +3 -2
  189. ansible/utils/unsafe_proxy.py +1 -1
  190. ansible/vars/hostvars.py +1 -1
  191. ansible/vars/plugins.py +3 -3
  192. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
  193. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
  194. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
  195. ansible_test/_data/completion/docker.txt +3 -3
  196. ansible_test/_data/completion/remote.txt +1 -0
  197. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  198. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  199. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  200. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  201. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  202. ansible_test/_internal/commands/integration/coverage.py +7 -2
  203. ansible_test/_internal/host_profiles.py +62 -10
  204. ansible_test/_internal/provisioning.py +10 -4
  205. ansible_test/_internal/ssh.py +1 -5
  206. ansible_test/_internal/thread.py +2 -1
  207. ansible_test/_internal/timeout.py +1 -1
  208. ansible_test/_internal/util.py +40 -12
  209. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  210. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  211. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  212. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  213. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  214. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  215. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  216. ansible_test/_util/target/setup/requirements.py +3 -9
  217. ansible/_internal/_errors/_utils.py +0 -310
  218. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
  219. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
  220. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  221. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  222. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  223. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  224. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  225. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,7 @@ import pathlib
21
21
  import re
22
22
  import shlex
23
23
 
24
- from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail, AnsibleActionSkip
24
+ from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
25
25
  from ansible.executor.powershell import module_manifest as ps_manifest
26
26
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
27
27
  from ansible.plugins.action import ActionBase
@@ -53,7 +53,7 @@ class ActionModule(ActionBase):
53
53
  mutually_exclusive=[['_raw_params', 'cmd']],
54
54
  )
55
55
 
56
- result = super(ActionModule, self).run(tmp, task_vars)
56
+ super(ActionModule, self).run(tmp, task_vars)
57
57
  del tmp # tmp no longer has any effect
58
58
 
59
59
  try:
@@ -105,16 +105,11 @@ class ActionModule(ActionBase):
105
105
  # check mode is supported if 'creates' or 'removes' are provided
106
106
  # the task has already been skipped if a change would not occur
107
107
  if new_module_args['creates'] or new_module_args['removes']:
108
- result['changed'] = True
109
- raise _AnsibleActionDone(result=result)
108
+ return dict(changed=True)
110
109
  # If the script doesn't return changed in the result, it defaults to True,
111
110
  # but since the script may override 'changed', just skip instead of guessing.
112
111
  else:
113
- result['changed'] = False
114
- raise AnsibleActionSkip('Check mode is not supported for this task.', result=result)
115
-
116
- # now we execute script, always assume changed.
117
- result['changed'] = True
112
+ raise AnsibleActionSkip('Check mode is not supported for this task.', result=dict(changed=False))
118
113
 
119
114
  # transfer the file to a remote tmp location
120
115
  tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir,
@@ -168,14 +163,12 @@ class ActionModule(ActionBase):
168
163
  # full manual exec of KEEP_REMOTE_FILES
169
164
  script_cmd = self._connection._shell.build_module_command(env_string='', shebang='#!powershell', cmd='')
170
165
 
171
- result.update(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir))
166
+ # now we execute script, always assume changed.
167
+ result = dict(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir), changed=True)
172
168
 
173
169
  if 'rc' in result and result['rc'] != 0:
174
- raise AnsibleActionFail('non-zero return code')
170
+ raise AnsibleActionFail('non-zero return code', result=result)
175
171
 
176
- except AnsibleAction as e:
177
- result.update(e.result)
172
+ return result
178
173
  finally:
179
174
  self._remove_tmp_path(self._connection._shell.tmpdir)
180
-
181
- return result
@@ -16,7 +16,7 @@
16
16
  # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17
17
  from __future__ import annotations
18
18
 
19
- from ansible.errors import AnsibleAction, AnsibleActionFail
19
+ from ansible.errors import AnsibleActionFail
20
20
  from ansible.executor.module_common import _apply_action_arg_defaults
21
21
  from ansible.plugins.action import ActionBase
22
22
 
@@ -39,7 +39,7 @@ class ActionModule(ActionBase):
39
39
  self._supports_check_mode = True
40
40
  self._supports_async = True
41
41
 
42
- result = super(ActionModule, self).run(tmp, task_vars)
42
+ super(ActionModule, self).run(tmp, task_vars)
43
43
  del tmp # tmp no longer has any effect
44
44
 
45
45
  module = self._task.args.get('use', 'auto').lower()
@@ -84,14 +84,10 @@ class ActionModule(ActionBase):
84
84
  module = 'ansible.legacy.' + module
85
85
 
86
86
  self._display.vvvv("Running %s" % module)
87
- result.update(self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
87
+ return self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
88
88
  else:
89
89
  raise AnsibleActionFail('Could not detect which service manager to use. Try gathering facts or setting the "use" option.')
90
90
 
91
- except AnsibleAction as e:
92
- result.update(e.result)
93
91
  finally:
94
92
  if not self._task.async_val:
95
93
  self._remove_tmp_path(self._connection._shell.tmpdir)
96
-
97
- return result
@@ -20,12 +20,13 @@ from jinja2.defaults import (
20
20
 
21
21
  from ansible import constants as C
22
22
  from ansible.config.manager import ensure_type
23
- from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionFail
23
+ from ansible.errors import AnsibleError, AnsibleActionFail
24
24
  from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
25
25
  from ansible.module_utils.parsing.convert_bool import boolean
26
26
  from ansible.module_utils.six import string_types
27
27
  from ansible.plugins.action import ActionBase
28
- from ansible.template import generate_ansible_template_vars, trust_as_template
28
+ from ansible.template import trust_as_template
29
+ from ansible._internal._templating import _template_vars
29
30
 
30
31
 
31
32
  class ActionModule(ActionBase):
@@ -39,7 +40,7 @@ class ActionModule(ActionBase):
39
40
  if task_vars is None:
40
41
  task_vars = dict()
41
42
 
42
- result = super(ActionModule, self).run(tmp, task_vars)
43
+ super(ActionModule, self).run(tmp, task_vars)
43
44
  del tmp # tmp no longer has any effect
44
45
 
45
46
  # Options type validation
@@ -115,7 +116,12 @@ class ActionModule(ActionBase):
115
116
 
116
117
  # add ansible 'template' vars
117
118
  temp_vars = task_vars.copy()
118
- temp_vars.update(generate_ansible_template_vars(self._task.args.get('src', None), fullpath=source, dest_path=dest))
119
+ temp_vars.update(_template_vars.generate_ansible_template_vars(
120
+ path=self._task.args.get('src', None),
121
+ fullpath=source,
122
+ dest_path=dest,
123
+ include_ansible_managed='ansible_managed' not in temp_vars, # do not clobber ansible_managed when set by the user
124
+ ))
119
125
 
120
126
  overrides = dict(
121
127
  block_start_string=block_start_string,
@@ -167,13 +173,8 @@ class ActionModule(ActionBase):
167
173
  loader=self._loader,
168
174
  templar=self._templar,
169
175
  shared_loader_obj=self._shared_loader_obj)
170
- result.update(copy_action.run(task_vars=task_vars))
176
+ return copy_action.run(task_vars=task_vars)
171
177
  finally:
172
178
  shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
173
-
174
- except AnsibleAction as e:
175
- result.update(e.result)
176
179
  finally:
177
180
  self._remove_tmp_path(self._connection._shell.tmpdir)
178
-
179
- return result
@@ -19,8 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  import os
21
21
 
22
- from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionFail, AnsibleActionSkip
23
- from ansible.module_utils.common.text.converters import to_text
22
+ from ansible.errors import AnsibleActionFail, AnsibleActionSkip
24
23
  from ansible.module_utils.parsing.convert_bool import boolean
25
24
  from ansible.plugins.action import ActionBase
26
25
 
@@ -34,7 +33,7 @@ class ActionModule(ActionBase):
34
33
  if task_vars is None:
35
34
  task_vars = dict()
36
35
 
37
- result = super(ActionModule, self).run(tmp, task_vars)
36
+ super(ActionModule, self).run(tmp, task_vars)
38
37
  del tmp # tmp no longer has any effect
39
38
 
40
39
  source = self._task.args.get('src', None)
@@ -68,15 +67,9 @@ class ActionModule(ActionBase):
68
67
  source = os.path.expanduser(source)
69
68
 
70
69
  if not remote_src:
71
- try:
72
- source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
73
- except AnsibleError as e:
74
- raise AnsibleActionFail(to_text(e))
70
+ source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
75
71
 
76
- try:
77
- remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
78
- except AnsibleError as e:
79
- raise AnsibleActionFail(to_text(e))
72
+ remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
80
73
 
81
74
  if not remote_stat['exists'] or not remote_stat['isdir']:
82
75
  raise AnsibleActionFail("dest '%s' must be an existing dir" % dest)
@@ -102,9 +95,6 @@ class ActionModule(ActionBase):
102
95
 
103
96
  # execute the unarchive module now, with the updated args (using ansible.legacy prefix to eliminate collections
104
97
  # collisions with local override
105
- result.update(self._execute_module(module_name='ansible.legacy.unarchive', module_args=new_module_args, task_vars=task_vars))
106
- except AnsibleAction as e:
107
- result.update(e.result)
98
+ return self._execute_module(module_name='ansible.legacy.unarchive', module_args=new_module_args, task_vars=task_vars)
108
99
  finally:
109
100
  self._remove_tmp_path(self._connection._shell.tmpdir)
110
- return result
@@ -5,11 +5,10 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import collections.abc as _c
8
9
  import os
9
10
 
10
- from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail
11
- from ansible.module_utils.common.text.converters import to_native
12
- from ansible.module_utils.common.collections import Mapping, MutableMapping
11
+ from ansible.errors import AnsibleActionFail
13
12
  from ansible.module_utils.parsing.convert_bool import boolean
14
13
  from ansible.plugins.action import ActionBase
15
14
 
@@ -25,7 +24,7 @@ class ActionModule(ActionBase):
25
24
  if task_vars is None:
26
25
  task_vars = dict()
27
26
 
28
- result = super(ActionModule, self).run(tmp, task_vars)
27
+ super(ActionModule, self).run(tmp, task_vars)
29
28
  del tmp # tmp no longer has any effect
30
29
 
31
30
  body_format = self._task.args.get('body_format', 'raw')
@@ -38,38 +37,31 @@ class ActionModule(ActionBase):
38
37
  # everything is remote, so we just execute the module
39
38
  # without changing any of the module arguments
40
39
  # call with ansible.legacy prefix to prevent collections collisions while allowing local override
41
- raise _AnsibleActionDone(result=self._execute_module(module_name='ansible.legacy.uri',
42
- task_vars=task_vars, wrap_async=self._task.async_val))
40
+ return self._execute_module(module_name='ansible.legacy.uri', task_vars=task_vars, wrap_async=self._task.async_val)
43
41
 
44
42
  kwargs = {}
45
43
 
46
44
  if src:
47
- try:
48
- src = self._find_needle('files', src)
49
- except AnsibleError as e:
50
- raise AnsibleActionFail(to_native(e))
45
+ src = self._find_needle('files', src)
51
46
 
52
47
  tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, os.path.basename(src))
53
48
  kwargs['src'] = tmp_src
54
49
  self._transfer_file(src, tmp_src)
55
50
  self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
56
51
  elif body_format == 'form-multipart':
57
- if not isinstance(body, Mapping):
52
+ if not isinstance(body, _c.Mapping):
58
53
  raise AnsibleActionFail(
59
54
  'body must be mapping, cannot be type %s' % body.__class__.__name__
60
55
  )
61
56
  for field, value in body.items():
62
- if not isinstance(value, MutableMapping):
57
+ if not isinstance(value, _c.MutableMapping):
63
58
  continue
64
59
  content = value.get('content')
65
60
  filename = value.get('filename')
66
61
  if not filename or content:
67
62
  continue
68
63
 
69
- try:
70
- filename = self._find_needle('files', filename)
71
- except AnsibleError as e:
72
- raise AnsibleActionFail(to_native(e))
64
+ filename = self._find_needle('files', filename)
73
65
 
74
66
  tmp_src = self._connection._shell.join_path(
75
67
  self._connection._shell.tmpdir,
@@ -83,10 +75,7 @@ class ActionModule(ActionBase):
83
75
  new_module_args = self._task.args | kwargs
84
76
 
85
77
  # call with ansible.legacy prefix to prevent collections collisions while allowing local override
86
- result.update(self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
87
- except AnsibleAction as e:
88
- result.update(e.result)
78
+ return self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
89
79
  finally:
90
80
  if not self._task.async_val:
91
81
  self._remove_tmp_path(self._connection._shell.tmpdir)
92
- return result
@@ -18,7 +18,6 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  import copy
21
- import errno
22
21
  import os
23
22
  import tempfile
24
23
  import time
@@ -108,8 +107,8 @@ class BaseFileCacheModule(BaseCacheModule):
108
107
  if not os.path.exists(self._cache_dir):
109
108
  try:
110
109
  os.makedirs(self._cache_dir)
111
- except (OSError, IOError) as e:
112
- raise AnsibleError("error in '%s' cache plugin while trying to create cache dir %s : %s" % (self.plugin_name, self._cache_dir, to_bytes(e)))
110
+ except OSError as ex:
111
+ raise AnsibleError(f"Error in {self.plugin_name!r} cache plugin while trying to create cache dir {self._cache_dir!r}.") from ex
113
112
  else:
114
113
  for x in (os.R_OK, os.W_OK, os.X_OK):
115
114
  if not os.access(self._cache_dir, x):
@@ -160,13 +159,13 @@ class BaseFileCacheModule(BaseCacheModule):
160
159
  try:
161
160
  try:
162
161
  self._dump(value, tmpfile_path)
163
- except (OSError, IOError) as e:
164
- display.warning("error in '%s' cache plugin while trying to write to '%s' : %s" % (self.plugin_name, tmpfile_path, to_bytes(e)))
162
+ except OSError as ex:
163
+ display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to write to {tmpfile_path!r}.", exception=ex)
165
164
  try:
166
165
  os.rename(tmpfile_path, cachefile)
167
166
  os.chmod(cachefile, mode=S_IRWU_RG_RO)
168
- except (OSError, IOError) as e:
169
- display.warning("error in '%s' cache plugin while trying to move '%s' to '%s' : %s" % (self.plugin_name, tmpfile_path, cachefile, to_bytes(e)))
167
+ except OSError as ex:
168
+ display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to move {tmpfile_path!r} to {cachefile!r}.", exception=ex)
170
169
  finally:
171
170
  try:
172
171
  os.unlink(tmpfile_path)
@@ -181,12 +180,12 @@ class BaseFileCacheModule(BaseCacheModule):
181
180
  cachefile = self._get_cache_file_name(key)
182
181
  try:
183
182
  st = os.stat(cachefile)
184
- except (OSError, IOError) as e:
185
- if e.errno == errno.ENOENT:
186
- return False
187
- else:
188
- display.warning("error in '%s' cache plugin while trying to stat %s : %s" % (self.plugin_name, cachefile, to_bytes(e)))
189
- return False
183
+ except FileNotFoundError:
184
+ return False
185
+ except OSError as ex:
186
+ display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to stat {cachefile!r}.", exception=ex)
187
+
188
+ return False
190
189
 
191
190
  if time.time() - st.st_mtime <= self._timeout:
192
191
  return False
@@ -223,11 +222,10 @@ class BaseFileCacheModule(BaseCacheModule):
223
222
  try:
224
223
  os.stat(cachefile)
225
224
  return True
226
- except (OSError, IOError) as e:
227
- if e.errno == errno.ENOENT:
228
- return False
229
- else:
230
- display.warning("error in '%s' cache plugin while trying to stat %s : %s" % (self.plugin_name, cachefile, to_bytes(e)))
225
+ except FileNotFoundError:
226
+ return False
227
+ except OSError as ex:
228
+ display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to stat {cachefile!r}.", exception=ex)
231
229
 
232
230
  def delete(self, key):
233
231
  try:
@@ -236,7 +234,7 @@ class BaseFileCacheModule(BaseCacheModule):
236
234
  pass
237
235
  try:
238
236
  os.remove(self._get_cache_file_name(key))
239
- except (OSError, IOError):
237
+ except OSError:
240
238
  pass # TODO: only pass on non existing?
241
239
 
242
240
  def flush(self):
@@ -280,8 +280,6 @@ class CallbackBase(AnsiblePlugin):
280
280
  # that want to further modify the result, or use custom serialization
281
281
  return abridged_result
282
282
 
283
- # DTFIX-RELEASE: Switch to stock json/yaml serializers here? We should always have a transformed plain-types result.
284
-
285
283
  if result_format == 'json':
286
284
  return json.dumps(abridged_result, cls=_fallback_to_str.Encoder, indent=indent, ensure_ascii=False, sort_keys=sort_keys)
287
285
 
@@ -310,7 +308,7 @@ class CallbackBase(AnsiblePlugin):
310
308
  ' ' * (indent or 4)
311
309
  )
312
310
 
313
- # DTFIX-RELEASE: add test to exercise this case
311
+ # DTFIX5: add test to exercise this case
314
312
  raise ValueError(f'Unsupported result_format {result_format!r}.')
315
313
 
316
314
  def _handle_warnings(self, res: _c.MutableMapping[str, t.Any]) -> None:
@@ -318,7 +316,7 @@ class CallbackBase(AnsiblePlugin):
318
316
  if res.pop('warnings', None) and self._current_task_result and (warnings := self._current_task_result.warnings):
319
317
  # display warnings from the current task result if `warnings` was not removed from `result` (or made falsey)
320
318
  for warning in warnings:
321
- # DTFIX-RELEASE: what to do about propagating wrap_text from the original display.warning call?
319
+ # DTFIX3: what to do about propagating wrap_text from the original display.warning call?
322
320
  self._display._warning(warning, wrap_text=False)
323
321
 
324
322
  if res.pop('deprecations', None) and self._current_task_result and (deprecations := self._current_task_result.deprecations):
@@ -333,7 +331,7 @@ class CallbackBase(AnsiblePlugin):
333
331
 
334
332
  def _handle_warnings_and_exception(self, result: CallbackTaskResult) -> None:
335
333
  """Standardized handling of warnings/deprecations and exceptions from a task/item result."""
336
- # DTFIX-RELEASE: make/doc/porting-guide a public version of this method?
334
+ # DTFIX5: make/doc/porting-guide a public version of this method?
337
335
  try:
338
336
  use_stderr = self.get_option('display_failed_stderr')
339
337
  except KeyError:
@@ -374,7 +372,7 @@ class CallbackBase(AnsiblePlugin):
374
372
  ' '
375
373
  )
376
374
 
377
- # DTFIX-RELEASE: add test to exercise this case
375
+ # DTFIX5: add test to exercise this case
378
376
  raise ValueError(f'Unsupported result_format {result_format!r}.')
379
377
 
380
378
  def _get_diff(self, difflist):
@@ -90,6 +90,8 @@ import typing as t
90
90
 
91
91
  from ansible import constants
92
92
  from ansible.module_utils.common.text.converters import to_bytes, to_text
93
+ from ansible.module_utils._internal import _event_utils
94
+ from ansible._internal import _event_formatting
93
95
  from ansible.playbook.task import Task
94
96
  from ansible.plugins.callback import CallbackBase
95
97
  from ansible.executor.task_result import CallbackTaskResult
@@ -248,8 +250,8 @@ class CallbackModule(CallbackBase):
248
250
 
249
251
  if host_data.status == 'failed':
250
252
  if error_summary := task_result.exception:
251
- message = error_summary._format()
252
- output = error_summary.formatted_traceback
253
+ message = _event_utils.format_event_brief_message(error_summary.event)
254
+ output = _event_formatting.format_event_traceback(error_summary.event)
253
255
  test_case.errors.append(TestError(message=message, output=output))
254
256
  elif 'msg' in res:
255
257
  message = res['msg']
@@ -31,7 +31,7 @@ import os
31
31
 
32
32
  from ansible.constants import TREE_DIR
33
33
  from ansible.executor.task_result import CallbackTaskResult
34
- from ansible.module_utils.common.text.converters import to_bytes, to_text
34
+ from ansible.module_utils.common.text.converters import to_bytes
35
35
  from ansible.plugins.callback import CallbackBase
36
36
  from ansible.utils.path import makedirs_safe, unfrackpath
37
37
  from ansible.module_utils._internal import _deprecator
@@ -73,15 +73,15 @@ class CallbackModule(CallbackBase):
73
73
  buf = to_bytes(buf)
74
74
  try:
75
75
  makedirs_safe(self.tree)
76
- except (OSError, IOError) as e:
77
- self._display.warning(u"Unable to access or create the configured directory (%s): %s" % (to_text(self.tree), to_text(e)))
76
+ except OSError as ex:
77
+ self._display.error_as_warning(f"Unable to access or create the configured directory {self.tree!r}.", exception=ex)
78
78
 
79
79
  try:
80
80
  path = to_bytes(os.path.join(self.tree, hostname))
81
81
  with open(path, 'wb+') as fd:
82
82
  fd.write(buf)
83
- except (OSError, IOError) as e:
84
- self._display.warning(u"Unable to write to %s's file: %s" % (hostname, to_text(e)))
83
+ except OSError as ex:
84
+ self._display.error_as_warning(f"Unable to write to {hostname!r}'s file.", exception=ex)
85
85
 
86
86
  def result_to_tree(self, result: CallbackTaskResult) -> None:
87
87
  self.write_tree_file(result.host.get_name(), self._dump_results(result.result))
@@ -101,9 +101,9 @@ class Connection(ConnectionBase):
101
101
  display.debug("opening command with Popen()")
102
102
 
103
103
  if isinstance(cmd, (text_type, binary_type)):
104
- cmd = to_bytes(cmd)
104
+ cmd = to_text(cmd)
105
105
  else:
106
- cmd = map(to_bytes, cmd)
106
+ cmd = map(to_text, cmd)
107
107
 
108
108
  pty_primary = None
109
109
  stdin = subprocess.PIPE
@@ -114,8 +114,8 @@ class Connection(ConnectionBase):
114
114
  # privileges or the command otherwise needs a pty.
115
115
  try:
116
116
  pty_primary, stdin = pty.openpty()
117
- except (IOError, OSError) as e:
118
- display.debug("Unable to open pty: %s" % to_native(e))
117
+ except OSError as ex:
118
+ display.debug(f"Unable to open pty: {ex}")
119
119
 
120
120
  p = subprocess.Popen(
121
121
  cmd,
@@ -271,8 +271,8 @@ class Connection(ConnectionBase):
271
271
  shutil.copyfile(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
272
272
  except shutil.Error:
273
273
  raise AnsibleError("failed to copy: {0} and {1} are the same".format(to_native(in_path), to_native(out_path)))
274
- except IOError as e:
275
- raise AnsibleError("failed to transfer file to {0}: {1}".format(to_native(out_path), to_native(e)))
274
+ except OSError as ex:
275
+ raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
276
276
 
277
277
  def fetch_file(self, in_path: str, out_path: str) -> None:
278
278
  """ fetch a file from local to local -- for compatibility """
@@ -413,7 +413,7 @@ class Connection(ConnectionBase):
413
413
  # TODO: check if we need to look at several possible locations, possible for loop
414
414
  ssh.load_system_host_keys(ssh_known_hosts)
415
415
  break
416
- except IOError:
416
+ except OSError:
417
417
  pass # file was not found, but not required to function
418
418
  ssh.load_system_host_keys()
419
419
 
@@ -567,8 +567,8 @@ class Connection(ConnectionBase):
567
567
 
568
568
  try:
569
569
  self.sftp.put(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
570
- except IOError:
571
- raise AnsibleError("failed to transfer file to %s" % out_path)
570
+ except OSError as ex:
571
+ raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
572
572
 
573
573
  def _connect_sftp(self) -> paramiko.sftp_client.SFTPClient:
574
574
 
@@ -593,8 +593,8 @@ class Connection(ConnectionBase):
593
593
 
594
594
  try:
595
595
  self.sftp.get(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
596
- except IOError:
597
- raise AnsibleError("failed to transfer file from %s" % in_path)
596
+ except OSError as ex:
597
+ raise AnsibleError(f"Failed to transfer file from {in_path!r}.") from ex
598
598
 
599
599
  def _any_keys_added(self) -> bool:
600
600
 
@@ -332,7 +332,9 @@ DOCUMENTATION = """
332
332
  version_added: '2.7'
333
333
  sftp_batch_mode:
334
334
  default: true
335
- 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.
336
338
  env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
337
339
  ini:
338
340
  - {key: sftp_batch_mode, section: ssh_connection}
@@ -398,6 +400,17 @@ DOCUMENTATION = """
398
400
  - {key: pkcs11_provider, section: ssh_connection}
399
401
  vars:
400
402
  - name: ansible_ssh_pkcs11_provider
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
401
414
  """
402
415
 
403
416
  import collections.abc as c
@@ -436,7 +449,7 @@ from ansible.plugins.connection import ConnectionBase, BUFSIZE
436
449
  from ansible.plugins.shell.powershell import _replace_stderr_clixml
437
450
  from ansible.utils.display import Display
438
451
  from ansible.utils.path import unfrackpath, makedirs_safe
439
- from ansible.utils._ssh_agent import SshAgentClient, _key_data_into_crypto_objects
452
+ from ansible._internal._ssh import _ssh_agent
440
453
 
441
454
  try:
442
455
  from cryptography.hazmat.primitives import serialization
@@ -755,12 +768,12 @@ class Connection(ConnectionBase):
755
768
  key_data = self.get_option('private_key')
756
769
  passphrase = self.get_option('private_key_passphrase')
757
770
 
758
- private_key, public_key, fingerprint = _key_data_into_crypto_objects(
771
+ private_key, public_key, fingerprint = _ssh_agent.key_data_into_crypto_objects(
759
772
  to_bytes(key_data),
760
773
  to_bytes(passphrase) if passphrase else None,
761
774
  )
762
775
 
763
- with SshAgentClient(auth_sock) as client:
776
+ with _ssh_agent.SshAgentClient(auth_sock) as client:
764
777
  if public_key not in client:
765
778
  display.vvv(f'SSH: SSH_AGENT adding {fingerprint} to agent', host=self.host)
766
779
  client.add(
@@ -855,8 +868,8 @@ class Connection(ConnectionBase):
855
868
  self._add_args(b_command, b_args, u'disable batch mode for password auth')
856
869
  b_command += [b'-b', b'-']
857
870
 
858
- if display.verbosity:
859
- b_command.append(b'-' + (b'v' * display.verbosity))
871
+ if (verbosity := self.get_option('verbosity')) > 0:
872
+ b_command.append(b'-' + (b'v' * verbosity))
860
873
 
861
874
  # Next, we add ssh_args
862
875
  ssh_args = self.get_option('ssh_args')
@@ -879,10 +892,7 @@ class Connection(ConnectionBase):
879
892
  try:
880
893
  key = self._populate_agent()
881
894
  except Exception as e:
882
- raise AnsibleAuthenticationFailure(
883
- 'Failed to add configured private key into ssh-agent',
884
- orig_exc=e,
885
- )
895
+ raise AnsibleAuthenticationFailure('Failed to add configured private key into ssh-agent.') from e
886
896
  b_args = (b'-o', b'IdentitiesOnly=yes', b'-o', to_bytes(f'IdentityFile="{key}"', errors='surrogate_or_strict'))
887
897
  self._add_args(b_command, b_args, "ANSIBLE_PRIVATE_KEY/private_key set")
888
898
  elif key := self.get_option('private_key_file'):
@@ -969,7 +979,7 @@ class Connection(ConnectionBase):
969
979
  try:
970
980
  fh.write(to_bytes(in_data))
971
981
  fh.close()
972
- except (OSError, IOError) as ex:
982
+ except OSError as ex:
973
983
  # The ssh connection may have already terminated at this point, with a more useful error
974
984
  # Only raise AnsibleConnectionFailure if the ssh process is still alive
975
985
  time.sleep(0.001)
@@ -985,7 +995,7 @@ class Connection(ConnectionBase):
985
995
  """ Terminate a process, ignoring errors """
986
996
  try:
987
997
  p.terminate()
988
- except (OSError, IOError):
998
+ except OSError:
989
999
  pass
990
1000
 
991
1001
  # This is separate from _run() because we need to do the same thing for stdout
@@ -1126,7 +1136,7 @@ class Connection(ConnectionBase):
1126
1136
  p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs)
1127
1137
  stdin = os.fdopen(master, 'wb', 0)
1128
1138
  os.close(slave)
1129
- except (OSError, IOError):
1139
+ except OSError:
1130
1140
  p = None
1131
1141
 
1132
1142
  if not p:
@@ -1134,8 +1144,8 @@ class Connection(ConnectionBase):
1134
1144
  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1135
1145
  stderr=subprocess.PIPE, **popen_kwargs)
1136
1146
  stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
1137
- except (OSError, IOError) as e:
1138
- raise AnsibleError('Unable to execute ssh command line on a controller due to: %s' % to_native(e))
1147
+ except OSError as ex:
1148
+ raise AnsibleError('Unable to execute ssh command line on a controller.') from ex
1139
1149
 
1140
1150
  if password_mechanism == 'sshpass' and conn_password:
1141
1151
  os.close(self.sshpass_pipe[0])
@@ -723,8 +723,11 @@ class Connection(ConnectionBase):
723
723
  super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
724
724
 
725
725
  encoded_prefix = self._shell._encode_script('', as_list=False, strict_mode=False, preserve_rc=False)
726
- if cmd.startswith(encoded_prefix):
727
- # Avoid double encoding the script
726
+ if cmd.startswith(encoded_prefix) or cmd.startswith("type "):
727
+ # Avoid double encoding the script, the first means we are already
728
+ # running the standard PowerShell command, the latter is used for
729
+ # the no pipeline case where it uses type to pipe the script into
730
+ # powershell which is known to work without re-encoding as pwsh.
728
731
  cmd_parts = cmd.split(" ")
729
732
  else:
730
733
  cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)
@@ -825,7 +828,7 @@ class Connection(ConnectionBase):
825
828
  stderr = to_text(b_stderr)
826
829
 
827
830
  if status_code != 0:
828
- raise IOError(stderr)
831
+ raise OSError(stderr)
829
832
  if stdout.strip() == '[DIR]':
830
833
  data = None
831
834
  else: