ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b5__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.
- ansible/_internal/__init__.py +1 -1
- ansible/_internal/_collection_proxy.py +1 -1
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +25 -30
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +5 -5
- ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
- ansible/_internal/_json/_profiles/_legacy.py +3 -11
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
- ansible/_internal/_templating/__init__.py +5 -3
- ansible/_internal/_templating/_datatag.py +2 -1
- ansible/_internal/_templating/_engine.py +3 -4
- ansible/_internal/_templating/_jinja_bits.py +21 -16
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +31 -3
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_transform.py +20 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_yaml/_dumper.py +1 -1
- ansible/_internal/_yaml/_errors.py +7 -7
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
- ansible/cli/__init__.py +5 -82
- ansible/cli/arguments/option_helpers.py +2 -3
- ansible/cli/doc.py +84 -28
- ansible/cli/inventory.py +1 -1
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +22 -0
- ansible/errors/__init__.py +96 -49
- ansible/executor/module_common.py +8 -10
- ansible/executor/powershell/async_watchdog.ps1 +2 -2
- ansible/executor/powershell/async_wrapper.ps1 +3 -3
- ansible/executor/powershell/become_wrapper.ps1 +20 -2
- ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
- ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
- ansible/executor/powershell/exec_wrapper.ps1 +219 -6
- ansible/executor/powershell/module_manifest.py +52 -0
- ansible/executor/powershell/module_wrapper.ps1 +47 -21
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/process/worker.py +38 -113
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/collection/__init__.py +1 -4
- ansible/inventory/manager.py +1 -1
- ansible/module_utils/_internal/__init__.py +0 -3
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- ansible/module_utils/_internal/_ansiballz.py +4 -2
- ansible/module_utils/_internal/_datatag/__init__.py +20 -14
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +66 -48
- ansible/module_utils/_internal/_errors.py +88 -17
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +21 -4
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
- ansible/module_utils/{common/messages.py → _internal/_messages.py} +28 -47
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +1 -1
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +11 -8
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +49 -15
- ansible/module_utils/common/arg_spec.py +2 -2
- ansible/module_utils/common/collections.py +6 -0
- ansible/module_utils/common/json.py +2 -2
- ansible/module_utils/common/text/converters.py +3 -3
- ansible/module_utils/common/validation.py +1 -1
- ansible/module_utils/common/warnings.py +80 -23
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/datatag.py +5 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/module_utils/service.py +2 -9
- ansible/modules/apt_repository.py +7 -29
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +5 -5
- ansible/modules/dnf5.py +14 -22
- ansible/modules/hostname.py +0 -1
- ansible/modules/service.py +3 -9
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +4 -4
- ansible/parsing/mod_args.py +1 -1
- ansible/parsing/plugin_docs.py +2 -2
- ansible/parsing/utils/yaml.py +3 -3
- ansible/parsing/vault/__init__.py +4 -4
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +0 -3
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +8 -31
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +8 -16
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +8 -7
- ansible/plugins/action/gather_facts.py +8 -8
- ansible/plugins/action/package.py +5 -8
- ansible/plugins/action/script.py +8 -15
- ansible/plugins/action/service.py +3 -7
- ansible/plugins/action/template.py +3 -8
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/connection/local.py +2 -2
- ansible/plugins/connection/ssh.py +17 -9
- ansible/plugins/connection/winrm.py +5 -2
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +13 -6
- ansible/plugins/filter/encryption.py +4 -4
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +69 -16
- ansible/plugins/loader.py +7 -7
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +66 -9
- ansible/plugins/shell/sh.py +9 -5
- ansible/plugins/test/core.py +21 -15
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +30 -2
- ansible/utils/display.py +103 -128
- ansible/utils/hashing.py +0 -1
- ansible/utils/listify.py +6 -4
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +162 -151
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/WHEEL +1 -1
- ansible_test/_data/completion/docker.txt +3 -3
- ansible_test/_data/completion/remote.txt +1 -0
- ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/util.py +20 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -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
|
19
|
+
from ansible.errors import AnsibleActionFail
|
20
20
|
from ansible.executor.module_common import _apply_action_arg_defaults
|
21
21
|
from ansible.module_utils.facts.system.pkg_mgr import PKG_MGRS
|
22
22
|
from ansible.plugins.action import ActionBase
|
@@ -38,7 +38,7 @@ class ActionModule(ActionBase):
|
|
38
38
|
self._supports_check_mode = True
|
39
39
|
self._supports_async = True
|
40
40
|
|
41
|
-
|
41
|
+
super(ActionModule, self).run(tmp, task_vars)
|
42
42
|
|
43
43
|
module = self._task.args.get('use', 'auto')
|
44
44
|
|
@@ -99,11 +99,8 @@ class ActionModule(ActionBase):
|
|
99
99
|
module = 'ansible.legacy.' + module
|
100
100
|
|
101
101
|
display.vvvv("Running %s" % module)
|
102
|
-
|
102
|
+
return self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
|
103
103
|
else:
|
104
104
|
raise AnsibleActionFail('Could not detect which package manager to use. Try gathering facts or setting the "use" option.')
|
105
|
-
|
106
|
-
|
107
|
-
result.update(e.result)
|
108
|
-
|
109
|
-
return result
|
105
|
+
finally:
|
106
|
+
pass # avoid de-dent all on refactor
|
ansible/plugins/action/script.py
CHANGED
@@ -21,7 +21,7 @@ import pathlib
|
|
21
21
|
import re
|
22
22
|
import shlex
|
23
23
|
|
24
|
-
from ansible.errors import AnsibleError,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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,7 +20,7 @@ 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,
|
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
|
@@ -39,7 +39,7 @@ class ActionModule(ActionBase):
|
|
39
39
|
if task_vars is None:
|
40
40
|
task_vars = dict()
|
41
41
|
|
42
|
-
|
42
|
+
super(ActionModule, self).run(tmp, task_vars)
|
43
43
|
del tmp # tmp no longer has any effect
|
44
44
|
|
45
45
|
# Options type validation
|
@@ -167,13 +167,8 @@ class ActionModule(ActionBase):
|
|
167
167
|
loader=self._loader,
|
168
168
|
templar=self._templar,
|
169
169
|
shared_loader_obj=self._shared_loader_obj)
|
170
|
-
|
170
|
+
return copy_action.run(task_vars=task_vars)
|
171
171
|
finally:
|
172
172
|
shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
|
173
|
-
|
174
|
-
except AnsibleAction as e:
|
175
|
-
result.update(e.result)
|
176
173
|
finally:
|
177
174
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
ansible/plugins/action/uri.py
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
252
|
-
output = error_summary.
|
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']
|
@@ -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 =
|
104
|
+
cmd = to_text(cmd)
|
105
105
|
else:
|
106
|
-
cmd = map(
|
106
|
+
cmd = map(to_text, cmd)
|
107
107
|
|
108
108
|
pty_primary = None
|
109
109
|
stdin = subprocess.PIPE
|
@@ -398,6 +398,17 @@ DOCUMENTATION = """
|
|
398
398
|
- {key: pkcs11_provider, section: ssh_connection}
|
399
399
|
vars:
|
400
400
|
- name: ansible_ssh_pkcs11_provider
|
401
|
+
verbosity:
|
402
|
+
version_added: '2.19'
|
403
|
+
default: 0
|
404
|
+
type: int
|
405
|
+
description:
|
406
|
+
- Requested verbosity level for the SSH CLI.
|
407
|
+
env: [{name: ANSIBLE_SSH_VERBOSITY}]
|
408
|
+
ini:
|
409
|
+
- {key: verbosity, section: ssh_connection}
|
410
|
+
vars:
|
411
|
+
- name: ansible_ssh_verbosity
|
401
412
|
"""
|
402
413
|
|
403
414
|
import collections.abc as c
|
@@ -436,7 +447,7 @@ from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
|
436
447
|
from ansible.plugins.shell.powershell import _replace_stderr_clixml
|
437
448
|
from ansible.utils.display import Display
|
438
449
|
from ansible.utils.path import unfrackpath, makedirs_safe
|
439
|
-
from ansible.
|
450
|
+
from ansible._internal._ssh import _ssh_agent
|
440
451
|
|
441
452
|
try:
|
442
453
|
from cryptography.hazmat.primitives import serialization
|
@@ -755,12 +766,12 @@ class Connection(ConnectionBase):
|
|
755
766
|
key_data = self.get_option('private_key')
|
756
767
|
passphrase = self.get_option('private_key_passphrase')
|
757
768
|
|
758
|
-
private_key, public_key, fingerprint =
|
769
|
+
private_key, public_key, fingerprint = _ssh_agent.key_data_into_crypto_objects(
|
759
770
|
to_bytes(key_data),
|
760
771
|
to_bytes(passphrase) if passphrase else None,
|
761
772
|
)
|
762
773
|
|
763
|
-
with SshAgentClient(auth_sock) as client:
|
774
|
+
with _ssh_agent.SshAgentClient(auth_sock) as client:
|
764
775
|
if public_key not in client:
|
765
776
|
display.vvv(f'SSH: SSH_AGENT adding {fingerprint} to agent', host=self.host)
|
766
777
|
client.add(
|
@@ -855,8 +866,8 @@ class Connection(ConnectionBase):
|
|
855
866
|
self._add_args(b_command, b_args, u'disable batch mode for password auth')
|
856
867
|
b_command += [b'-b', b'-']
|
857
868
|
|
858
|
-
if
|
859
|
-
b_command.append(b'-' + (b'v' *
|
869
|
+
if (verbosity := self.get_option('verbosity')) > 0:
|
870
|
+
b_command.append(b'-' + (b'v' * verbosity))
|
860
871
|
|
861
872
|
# Next, we add ssh_args
|
862
873
|
ssh_args = self.get_option('ssh_args')
|
@@ -879,10 +890,7 @@ class Connection(ConnectionBase):
|
|
879
890
|
try:
|
880
891
|
key = self._populate_agent()
|
881
892
|
except Exception as e:
|
882
|
-
raise AnsibleAuthenticationFailure(
|
883
|
-
'Failed to add configured private key into ssh-agent',
|
884
|
-
orig_exc=e,
|
885
|
-
)
|
893
|
+
raise AnsibleAuthenticationFailure('Failed to add configured private key into ssh-agent.') from e
|
886
894
|
b_args = (b'-o', b'IdentitiesOnly=yes', b'-o', to_bytes(f'IdentityFile="{key}"', errors='surrogate_or_strict'))
|
887
895
|
self._add_args(b_command, b_args, "ANSIBLE_PRIVATE_KEY/private_key set")
|
888
896
|
elif key := self.get_option('private_key_file'):
|
@@ -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)
|
@@ -47,13 +47,13 @@ options:
|
|
47
47
|
- The key from input dictionary used to generate groups.
|
48
48
|
default_value:
|
49
49
|
description:
|
50
|
-
- The default value when the host variable's value is an empty string.
|
50
|
+
- The default value when the host variable's value is V(None) or an empty string.
|
51
51
|
- This option is mutually exclusive with O(keyed_groups[].trailing_separator).
|
52
52
|
type: str
|
53
53
|
version_added: '2.12'
|
54
54
|
trailing_separator:
|
55
55
|
description:
|
56
|
-
- Set this option to V(false) to omit the O(keyed_groups[].separator) after the host variable when the value is an empty string.
|
56
|
+
- Set this option to V(false) to omit the O(keyed_groups[].separator) after the host variable when the value is V(None) or an empty string.
|
57
57
|
- This option is mutually exclusive with O(keyed_groups[].default_value).
|
58
58
|
type: bool
|
59
59
|
default: true
|
ansible/plugins/filter/core.py
CHANGED
@@ -34,7 +34,7 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_
|
|
34
34
|
from ansible.module_utils.common.collections import is_sequence
|
35
35
|
from ansible.module_utils.common.yaml import yaml_load, yaml_load_all
|
36
36
|
from ansible.parsing.yaml.dumper import AnsibleDumper
|
37
|
-
from ansible.
|
37
|
+
from ansible.template import accept_args_markers, accept_lazy_markers
|
38
38
|
from ansible._internal._templating._jinja_common import MarkerError, UndefinedMarker, validate_arg_type
|
39
39
|
from ansible.utils.display import Display
|
40
40
|
from ansible.utils.encrypt import do_encrypt, PASSLIB_AVAILABLE
|
@@ -247,20 +247,30 @@ def regex_escape(string, re_type='python'):
|
|
247
247
|
|
248
248
|
|
249
249
|
def from_yaml(data):
|
250
|
+
if data is None:
|
251
|
+
return None
|
252
|
+
|
250
253
|
if isinstance(data, string_types):
|
251
254
|
# The ``text_type`` call here strips any custom
|
252
255
|
# string wrapper class, so that CSafeLoader can
|
253
256
|
# read the data
|
254
257
|
return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
|
258
|
+
|
259
|
+
display.deprecated(f"The from_yaml filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
|
255
260
|
return data
|
256
261
|
|
257
262
|
|
258
263
|
def from_yaml_all(data):
|
264
|
+
if data is None:
|
265
|
+
return [] # backward compatibility; ensure consistent result between classic/native Jinja for None/empty string input
|
266
|
+
|
259
267
|
if isinstance(data, string_types):
|
260
268
|
# The ``text_type`` call here strips any custom
|
261
269
|
# string wrapper class, so that CSafeLoader can
|
262
270
|
# read the data
|
263
271
|
return yaml_load_all(text_type(to_text(data, errors='surrogate_or_strict')))
|
272
|
+
|
273
|
+
display.deprecated(f"The from_yaml_all filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
|
264
274
|
return data
|
265
275
|
|
266
276
|
|
@@ -338,7 +348,7 @@ def to_uuid(string, namespace=UUID_NAMESPACE_ANSIBLE):
|
|
338
348
|
@accept_args_markers
|
339
349
|
def mandatory(a: object, msg: str | None = None) -> object:
|
340
350
|
"""Make a variable mandatory."""
|
341
|
-
# DTFIX-
|
351
|
+
# DTFIX-FUTURE: deprecate this filter; there are much better ways via undef, etc...
|
342
352
|
# also remember to remove unit test checking for _undefined_name
|
343
353
|
if isinstance(a, UndefinedMarker):
|
344
354
|
if msg is not None:
|
@@ -654,7 +664,7 @@ def _cleansed_groupby(*args, **kwargs):
|
|
654
664
|
|
655
665
|
return res
|
656
666
|
|
657
|
-
# DTFIX-
|
667
|
+
# DTFIX-FUTURE: make these dumb wrappers more dynamic
|
658
668
|
|
659
669
|
|
660
670
|
@accept_args_markers
|
@@ -806,7 +816,6 @@ class FilterModule(object):
|
|
806
816
|
'groupby': _cleansed_groupby,
|
807
817
|
|
808
818
|
# Jinja builtins that need special arg handling
|
809
|
-
# DTFIX-RELEASE: document these now that they're overridden, or hide them so they don't show up as undocumented
|
810
819
|
'd': ansible_default, # replaces the implementation instead of wrapping it
|
811
820
|
'default': ansible_default, # replaces the implementation instead of wrapping it
|
812
821
|
'map': wrapped_map,
|
@@ -815,5 +824,3 @@ class FilterModule(object):
|
|
815
824
|
'reject': wrapped_reject,
|
816
825
|
'rejectattr': wrapped_rejectattr,
|
817
826
|
}
|
818
|
-
|
819
|
-
# DTFIX-RELEASE: document protomatter plugins, or hide them from ansible-doc/galaxy (not related to this code, but needed some place to put this comment)
|
@@ -4,10 +4,10 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
from ansible.errors import AnsibleError
|
6
6
|
from ansible.module_utils.common.text.converters import to_native, to_bytes
|
7
|
-
from ansible.
|
8
|
-
from ansible._internal._templating._jinja_common import get_first_marker_arg, VaultExceptionMarker
|
7
|
+
from ansible._internal._templating._jinja_common import VaultExceptionMarker
|
9
8
|
from ansible._internal._datatag._tags import VaultedValue
|
10
9
|
from ansible.parsing.vault import is_encrypted, VaultSecret, VaultLib, VaultHelper
|
10
|
+
from ansible import template as _template
|
11
11
|
from ansible.utils.display import Display
|
12
12
|
|
13
13
|
display = Display()
|
@@ -43,12 +43,12 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
|
|
43
43
|
return vault
|
44
44
|
|
45
45
|
|
46
|
-
@accept_args_markers
|
46
|
+
@_template.accept_args_markers
|
47
47
|
def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
|
48
48
|
if isinstance(vault, VaultExceptionMarker):
|
49
49
|
vault = vault._disarm()
|
50
50
|
|
51
|
-
if (first_marker := get_first_marker_arg((vault, secret, vault_id, vaultid), {})) is not None:
|
51
|
+
if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id, vaultid), {})) is not None:
|
52
52
|
return first_marker
|
53
53
|
|
54
54
|
if not isinstance(secret, (str, bytes)):
|
@@ -334,7 +334,6 @@ class Cacheable(_plugin_info.HasPluginInfo, _ConfigurablePlugin):
|
|
334
334
|
|
335
335
|
def _get_cache_prefix(self, path: str) -> str:
|
336
336
|
"""Return a predictable unique key based on the given path."""
|
337
|
-
# DTFIX-RELEASE: choose a better hashing approach
|
338
337
|
return 'k' + hashlib.sha256(f'{self.ansible_name}{path}'.encode(), usedforsecurity=False).hexdigest()[:6]
|
339
338
|
|
340
339
|
def clear_cache(self) -> None:
|
@@ -402,6 +401,8 @@ class Constructable(_BaseInventoryPlugin):
|
|
402
401
|
|
403
402
|
def _add_host_to_keyed_groups(self, keys, variables, host, strict=False, fetch_hostvars=True):
|
404
403
|
""" helper to create groups for plugins based on variable values and add the corresponding hosts to it"""
|
404
|
+
should_default_value = (None, '')
|
405
|
+
|
405
406
|
if keys and isinstance(keys, list):
|
406
407
|
for keyed in keys:
|
407
408
|
if keyed and isinstance(keyed, dict):
|
@@ -418,7 +419,9 @@ class Constructable(_BaseInventoryPlugin):
|
|
418
419
|
trailing_separator = keyed.get('trailing_separator')
|
419
420
|
if trailing_separator is not None and default_value_name is not None:
|
420
421
|
raise AnsibleParserError("parameters are mutually exclusive for keyed groups: default_value|trailing_separator")
|
421
|
-
|
422
|
+
|
423
|
+
use_default = key in should_default_value and default_value_name is not None
|
424
|
+
if key or use_default:
|
422
425
|
prefix = keyed.get('prefix', '')
|
423
426
|
sep = keyed.get('separator', '_')
|
424
427
|
raw_parent_name = keyed.get('parent_group', None)
|
@@ -434,23 +437,21 @@ class Constructable(_BaseInventoryPlugin):
|
|
434
437
|
continue
|
435
438
|
|
436
439
|
new_raw_group_names = []
|
437
|
-
if
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
else:
|
442
|
-
new_raw_group_names.append(key)
|
440
|
+
if use_default:
|
441
|
+
new_raw_group_names.append(default_value_name)
|
442
|
+
elif isinstance(key, string_types):
|
443
|
+
new_raw_group_names.append(key)
|
443
444
|
elif isinstance(key, list):
|
444
445
|
for name in key:
|
445
446
|
# if list item is empty, 'default_value' will be used as group name
|
446
|
-
if name
|
447
|
+
if name in should_default_value and default_value_name is not None:
|
447
448
|
new_raw_group_names.append(default_value_name)
|
448
449
|
else:
|
449
450
|
new_raw_group_names.append(name)
|
450
451
|
elif isinstance(key, Mapping):
|
451
452
|
for (gname, gval) in key.items():
|
452
453
|
bare_name = '%s%s%s' % (gname, sep, gval)
|
453
|
-
if gval
|
454
|
+
if gval in should_default_value:
|
454
455
|
# key's value is empty
|
455
456
|
if default_value_name is not None:
|
456
457
|
bare_name = '%s%s%s' % (gname, sep, default_value_name)
|
@@ -367,7 +367,7 @@ def run_command(path: str, options: list[str], origin: Origin) -> tuple[str, str
|
|
367
367
|
if stderr and not stderr.endswith('\n'):
|
368
368
|
stderr += '\n'
|
369
369
|
|
370
|
-
# DTFIX-
|
370
|
+
# DTFIX-FUTURE: another use case for the "not quite help text, definitely not message" diagnostic output on errors
|
371
371
|
stderr_help_text = f'Standard error from inventory script:\n{stderr}' if stderr.strip() else None
|
372
372
|
|
373
373
|
if sp.returncode != 0:
|