ansible-core 2.19.0b5__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.
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_templating/_jinja_bits.py +7 -4
- ansible/_internal/_templating/_jinja_plugins.py +5 -2
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +6 -0
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/cli/__init__.py +7 -12
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +2 -2
- ansible/cli/inventory.py +5 -7
- ansible/config/base.yml +24 -0
- ansible/errors/__init__.py +2 -1
- ansible/executor/module_common.py +67 -39
- ansible/executor/process/worker.py +2 -2
- ansible/galaxy/api.py +1 -4
- ansible/galaxy/collection/__init__.py +1 -6
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
- ansible/galaxy/role.py +2 -2
- ansible/module_utils/_internal/__init__.py +7 -4
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +10 -38
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +23 -1
- ansible/module_utils/_internal/_deprecator.py +27 -33
- ansible/module_utils/_internal/_json/_profiles/__init__.py +1 -0
- ansible/module_utils/_internal/_messages.py +26 -2
- ansible/module_utils/_internal/_plugin_info.py +14 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +46 -56
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/module_utils/service.py +1 -1
- ansible/module_utils/urls.py +4 -4
- ansible/modules/apt_repository.py +10 -10
- ansible/modules/assemble.py +2 -2
- ansible/modules/async_wrapper.py +7 -17
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/replace.py +2 -2
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +5 -7
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +28 -30
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/vault/__init__.py +6 -10
- ansible/playbook/base.py +7 -2
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/taggable.py +19 -5
- ansible/playbook/task.py +2 -0
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/template.py +8 -2
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +4 -4
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +8 -6
- ansible/plugins/connection/winrm.py +1 -1
- ansible/plugins/filter/core.py +19 -21
- ansible/plugins/filter/encryption.py +10 -2
- ansible/plugins/list.py +5 -4
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/powershell.py +3 -2
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +2 -2
- ansible/release.py +1 -1
- ansible/template/__init__.py +9 -53
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +23 -12
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -7
- ansible/utils/path.py +5 -7
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/vars/plugins.py +3 -3
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +117 -108
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/host_profiles.py +62 -10
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +20 -12
- ansible_test/_util/target/setup/requirements.py +3 -9
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
ansible/parsing/dataloader.py
CHANGED
@@ -217,7 +217,7 @@ class DataLoader:
|
|
217
217
|
except FileNotFoundError as ex:
|
218
218
|
# DTFIX-FUTURE: why not just let the builtin one fly?
|
219
219
|
raise AnsibleFileNotFound("Unable to retrieve file contents.", file_name=file_name) from ex
|
220
|
-
except
|
220
|
+
except OSError as ex:
|
221
221
|
raise AnsibleParserError(f"An error occurred while trying to read the file {file_name!r}.") from ex
|
222
222
|
|
223
223
|
data = Origin(path=file_name).tag(data)
|
@@ -448,7 +448,7 @@ class DataLoader:
|
|
448
448
|
|
449
449
|
return real_path
|
450
450
|
|
451
|
-
except
|
451
|
+
except OSError as ex:
|
452
452
|
raise AnsibleParserError(f"an error occurred while trying to read the file {to_text(real_path)!r}.") from ex
|
453
453
|
|
454
454
|
def cleanup_tmp_file(self, file_path: str) -> None:
|
@@ -17,7 +17,6 @@
|
|
17
17
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
|
-
import errno
|
21
20
|
import fcntl
|
22
21
|
import functools
|
23
22
|
import os
|
@@ -414,8 +413,8 @@ class FileVaultSecret(VaultSecret):
|
|
414
413
|
try:
|
415
414
|
with open(filename, "rb") as f:
|
416
415
|
vault_pass = f.read().strip()
|
417
|
-
except
|
418
|
-
raise AnsibleError("Could not read vault password file
|
416
|
+
except OSError as ex:
|
417
|
+
raise AnsibleError(f"Could not read vault password file {filename!r}.") from ex
|
419
418
|
|
420
419
|
b_vault_data, dummy = self.loader._decrypt_if_vault_data(vault_pass)
|
421
420
|
|
@@ -1071,13 +1070,10 @@ class VaultEditor:
|
|
1071
1070
|
try:
|
1072
1071
|
# create file with secure permissions
|
1073
1072
|
fd = os.open(thefile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_TRUNC, mode)
|
1074
|
-
except
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
raise AnsibleError('Vault file got recreated while we were operating on it: %s' % to_native(ose))
|
1079
|
-
|
1080
|
-
raise AnsibleError('Problem creating temporary vault file: %s' % to_native(ose))
|
1073
|
+
except FileExistsError as ex:
|
1074
|
+
raise AnsibleError('Vault file got recreated while we were operating on it.') from ex
|
1075
|
+
except OSError as ex:
|
1076
|
+
raise AnsibleError('Problem creating temporary vault file.') from ex
|
1081
1077
|
|
1082
1078
|
try:
|
1083
1079
|
# now write to the file and ensure ours is only data in it
|
ansible/playbook/base.py
CHANGED
@@ -83,6 +83,11 @@ class _ClassProperty:
|
|
83
83
|
|
84
84
|
class FieldAttributeBase:
|
85
85
|
|
86
|
+
_post_validate_object = False
|
87
|
+
"""
|
88
|
+
`False` skips FieldAttribute post-validation on intermediate objects and mixins for attributes without `always_post_validate`.
|
89
|
+
Leaf objects (e.g., `Task`) should set this attribute `True` to opt-in to post-validation.
|
90
|
+
"""
|
86
91
|
fattributes = _ClassProperty()
|
87
92
|
|
88
93
|
@classmethod
|
@@ -566,8 +571,8 @@ class FieldAttributeBase:
|
|
566
571
|
# only import_role is checked here because import_tasks never reaches this point
|
567
572
|
return Sentinel
|
568
573
|
|
569
|
-
#
|
570
|
-
if not attribute.always_post_validate and self.
|
574
|
+
# Skip post validation unless always_post_validate is True, or the object requires post validation.
|
575
|
+
if not attribute.always_post_validate and not self._post_validate_object:
|
571
576
|
# Intermediate objects like Play() won't have their fields validated by
|
572
577
|
# default, as their values are often inherited by other objects and validated
|
573
578
|
# later, so we don't want them to fail out early
|
@@ -144,7 +144,9 @@ class IncludedFile:
|
|
144
144
|
parent_include_dir = parent_include._role_path
|
145
145
|
else:
|
146
146
|
try:
|
147
|
-
|
147
|
+
# FUTURE: Since the parent include path has already been resolved, it should be used here.
|
148
|
+
# Unfortunately it's not currently stored anywhere, so it must be calculated again.
|
149
|
+
parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params')))
|
148
150
|
except AnsibleError as e:
|
149
151
|
parent_include_dir = ''
|
150
152
|
display.warning(
|
ansible/playbook/play_context.py
CHANGED
ansible/playbook/taggable.py
CHANGED
@@ -17,6 +17,8 @@
|
|
17
17
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
|
+
import typing as t
|
21
|
+
|
20
22
|
from ansible.errors import AnsibleError
|
21
23
|
from ansible.module_utils.six import string_types
|
22
24
|
from ansible.module_utils.common.sentinel import Sentinel
|
@@ -25,7 +27,7 @@ from ansible.playbook.attribute import FieldAttribute
|
|
25
27
|
from ansible._internal._templating._engine import TemplateEngine
|
26
28
|
|
27
29
|
|
28
|
-
def _flatten_tags(tags: list) -> list:
|
30
|
+
def _flatten_tags(tags: list[str | int]) -> list[str | int]:
|
29
31
|
rv = set()
|
30
32
|
for tag in tags:
|
31
33
|
if isinstance(tag, list):
|
@@ -49,16 +51,28 @@ class Taggable:
|
|
49
51
|
|
50
52
|
raise AnsibleError('tags must be specified as a list', obj=ds)
|
51
53
|
|
54
|
+
def _get_all_taggable_objects(self) -> t.Iterable[Taggable]:
|
55
|
+
obj = self
|
56
|
+
while obj is not None:
|
57
|
+
yield obj
|
58
|
+
|
59
|
+
if (role := getattr(obj, "_role", Sentinel)) is not Sentinel:
|
60
|
+
yield role # type: ignore[misc]
|
61
|
+
|
62
|
+
obj = obj._parent
|
63
|
+
|
64
|
+
yield self.get_play()
|
65
|
+
|
52
66
|
def evaluate_tags(self, only_tags, skip_tags, all_vars):
|
53
|
-
"""
|
67
|
+
"""Check if the current item should be executed depending on the specified tags.
|
54
68
|
|
69
|
+
NOTE this method is assumed to be called only on Task objects.
|
70
|
+
"""
|
55
71
|
if self.tags:
|
56
72
|
templar = TemplateEngine(loader=self._loader, variables=all_vars)
|
57
|
-
obj
|
58
|
-
while obj is not None:
|
73
|
+
for obj in self._get_all_taggable_objects():
|
59
74
|
if (_tags := getattr(obj, "_tags", Sentinel)) is not Sentinel:
|
60
75
|
obj._tags = _flatten_tags(templar.template(_tags))
|
61
|
-
obj = obj._parent
|
62
76
|
tags = set(self.tags)
|
63
77
|
else:
|
64
78
|
# this makes isdisjoint work for untagged
|
ansible/playbook/task.py
CHANGED
@@ -65,6 +65,8 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
|
|
65
65
|
Task.something(...)
|
66
66
|
"""
|
67
67
|
|
68
|
+
_post_validate_object = True
|
69
|
+
|
68
70
|
# =================================================================================
|
69
71
|
# ATTRIBUTES
|
70
72
|
# load_<attribute_name> and
|
ansible/plugins/action/fetch.py
CHANGED
@@ -119,7 +119,7 @@ class ActionModule(ActionBase):
|
|
119
119
|
|
120
120
|
if 'not found' in slurpres.get('msg', ''):
|
121
121
|
result['msg'] = "the remote file does not exist, not transferring, ignored"
|
122
|
-
elif slurpres.get('msg', '').startswith('source is a directory'):
|
122
|
+
elif slurpres.get('msg', '').lower().startswith('source is a directory'):
|
123
123
|
result['msg'] = "remote file is a directory, fetch cannot work on directories"
|
124
124
|
|
125
125
|
return result
|
@@ -180,8 +180,8 @@ class ActionModule(ActionBase):
|
|
180
180
|
try:
|
181
181
|
with open(to_bytes(dest, errors='surrogate_or_strict'), 'wb') as f:
|
182
182
|
f.write(remote_data)
|
183
|
-
except
|
184
|
-
raise AnsibleActionFail("Failed to fetch the file
|
183
|
+
except OSError as ex:
|
184
|
+
raise AnsibleActionFail("Failed to fetch the file.") from ex
|
185
185
|
new_checksum = secure_hash(dest)
|
186
186
|
# For backwards compatibility. We'll return None on FIPS enabled systems
|
187
187
|
try:
|
@@ -25,7 +25,8 @@ from ansible.module_utils.common.text.converters import to_bytes, to_text, to_na
|
|
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
|
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):
|
@@ -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(
|
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,
|
@@ -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
|
112
|
-
raise AnsibleError("
|
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
|
164
|
-
display.
|
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
|
169
|
-
display.
|
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
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
237
|
+
except OSError:
|
240
238
|
pass # TODO: only pass on non existing?
|
241
239
|
|
242
240
|
def flush(self):
|
ansible/plugins/callback/tree.py
CHANGED
@@ -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
|
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
|
77
|
-
self._display.
|
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
|
84
|
-
self._display.
|
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))
|
@@ -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
|
118
|
-
display.debug("Unable to open pty:
|
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
|
275
|
-
raise AnsibleError("
|
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
|
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
|
571
|
-
raise AnsibleError("
|
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
|
597
|
-
raise AnsibleError("
|
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:
|
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}
|
@@ -977,7 +979,7 @@ class Connection(ConnectionBase):
|
|
977
979
|
try:
|
978
980
|
fh.write(to_bytes(in_data))
|
979
981
|
fh.close()
|
980
|
-
except
|
982
|
+
except OSError as ex:
|
981
983
|
# The ssh connection may have already terminated at this point, with a more useful error
|
982
984
|
# Only raise AnsibleConnectionFailure if the ssh process is still alive
|
983
985
|
time.sleep(0.001)
|
@@ -993,7 +995,7 @@ class Connection(ConnectionBase):
|
|
993
995
|
""" Terminate a process, ignoring errors """
|
994
996
|
try:
|
995
997
|
p.terminate()
|
996
|
-
except
|
998
|
+
except OSError:
|
997
999
|
pass
|
998
1000
|
|
999
1001
|
# This is separate from _run() because we need to do the same thing for stdout
|
@@ -1134,7 +1136,7 @@ class Connection(ConnectionBase):
|
|
1134
1136
|
p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs)
|
1135
1137
|
stdin = os.fdopen(master, 'wb', 0)
|
1136
1138
|
os.close(slave)
|
1137
|
-
except
|
1139
|
+
except OSError:
|
1138
1140
|
p = None
|
1139
1141
|
|
1140
1142
|
if not p:
|
@@ -1142,8 +1144,8 @@ class Connection(ConnectionBase):
|
|
1142
1144
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1143
1145
|
stderr=subprocess.PIPE, **popen_kwargs)
|
1144
1146
|
stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
|
1145
|
-
except
|
1146
|
-
raise AnsibleError('Unable to execute ssh command line on a controller
|
1147
|
+
except OSError as ex:
|
1148
|
+
raise AnsibleError('Unable to execute ssh command line on a controller.') from ex
|
1147
1149
|
|
1148
1150
|
if password_mechanism == 'sshpass' and conn_password:
|
1149
1151
|
os.close(self.sshpass_pipe[0])
|
ansible/plugins/filter/core.py
CHANGED
@@ -32,10 +32,10 @@ from ansible.module_utils.common.json import get_encoder, get_decoder
|
|
32
32
|
from ansible.module_utils.six import string_types, integer_types, text_type
|
33
33
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
34
34
|
from ansible.module_utils.common.collections import is_sequence
|
35
|
-
from ansible.module_utils.common.yaml import yaml_load, yaml_load_all
|
36
35
|
from ansible.parsing.yaml.dumper import AnsibleDumper
|
37
36
|
from ansible.template import accept_args_markers, accept_lazy_markers
|
38
37
|
from ansible._internal._templating._jinja_common import MarkerError, UndefinedMarker, validate_arg_type
|
38
|
+
from ansible._internal._yaml import _loader as _yaml_loader
|
39
39
|
from ansible.utils.display import Display
|
40
40
|
from ansible.utils.encrypt import do_encrypt, PASSLIB_AVAILABLE
|
41
41
|
from ansible.utils.hashing import md5s, checksum_s
|
@@ -47,13 +47,13 @@ display = Display()
|
|
47
47
|
UUID_NAMESPACE_ANSIBLE = uuid.UUID('361E6D51-FAEC-444A-9079-341386DA8E2E')
|
48
48
|
|
49
49
|
|
50
|
-
|
50
|
+
@accept_lazy_markers
|
51
|
+
def to_yaml(a, *_args, default_flow_style: bool | None = None, **kwargs) -> str:
|
51
52
|
"""Serialize input as terse flow-style YAML."""
|
52
|
-
|
53
|
-
|
54
|
-
return yaml.dump(a, Dumper=dumper, allow_unicode=True, default_flow_style=default_flow_style, **kwargs)
|
53
|
+
return yaml.dump(a, Dumper=AnsibleDumper, allow_unicode=True, default_flow_style=default_flow_style, **kwargs)
|
55
54
|
|
56
55
|
|
56
|
+
@accept_lazy_markers
|
57
57
|
def to_nice_yaml(a, indent=4, *_args, default_flow_style=False, **kwargs) -> str:
|
58
58
|
"""Serialize input as verbose multi-line YAML."""
|
59
59
|
return to_yaml(a, indent=indent, default_flow_style=default_flow_style, **kwargs)
|
@@ -98,6 +98,7 @@ _valid_bool_false = {'no', 'off', 'false', '0'}
|
|
98
98
|
def to_bool(value: object) -> bool:
|
99
99
|
"""Convert well-known input values to a boolean value."""
|
100
100
|
value_to_check: object
|
101
|
+
|
101
102
|
if isinstance(value, str):
|
102
103
|
value_to_check = value.lower() # accept mixed case variants
|
103
104
|
elif isinstance(value, int): # bool is also an int
|
@@ -105,14 +106,17 @@ def to_bool(value: object) -> bool:
|
|
105
106
|
else:
|
106
107
|
value_to_check = value
|
107
108
|
|
108
|
-
|
109
|
-
|
109
|
+
try:
|
110
|
+
if value_to_check in _valid_bool_true:
|
111
|
+
return True
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
+
if value_to_check in _valid_bool_false:
|
114
|
+
return False
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
+
# if we're still here, the value is unsupported- always fire a deprecation warning
|
117
|
+
result = value_to_check == 1 # backwards compatibility with the old code which checked: value in ('yes', 'on', '1', 'true', 1)
|
118
|
+
except TypeError:
|
119
|
+
result = False
|
116
120
|
|
117
121
|
# NB: update the doc string to reflect reality once this fallback is removed
|
118
122
|
display.deprecated(
|
@@ -250,11 +254,8 @@ def from_yaml(data):
|
|
250
254
|
if data is None:
|
251
255
|
return None
|
252
256
|
|
253
|
-
if isinstance(data,
|
254
|
-
|
255
|
-
# string wrapper class, so that CSafeLoader can
|
256
|
-
# read the data
|
257
|
-
return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
|
257
|
+
if isinstance(data, str):
|
258
|
+
return yaml.load(data, Loader=_yaml_loader.AnsibleInstrumentedLoader) # type: ignore[arg-type]
|
258
259
|
|
259
260
|
display.deprecated(f"The from_yaml filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
|
260
261
|
return data
|
@@ -264,11 +265,8 @@ def from_yaml_all(data):
|
|
264
265
|
if data is None:
|
265
266
|
return [] # backward compatibility; ensure consistent result between classic/native Jinja for None/empty string input
|
266
267
|
|
267
|
-
if isinstance(data,
|
268
|
-
|
269
|
-
# string wrapper class, so that CSafeLoader can
|
270
|
-
# read the data
|
271
|
-
return yaml_load_all(text_type(to_text(data, errors='surrogate_or_strict')))
|
268
|
+
if isinstance(data, str):
|
269
|
+
return yaml.load_all(data, Loader=_yaml_loader.AnsibleInstrumentedLoader) # type: ignore[arg-type]
|
272
270
|
|
273
271
|
display.deprecated(f"The from_yaml_all filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
|
274
272
|
return data
|
@@ -21,7 +21,11 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
|
|
21
21
|
raise TypeError(f"Can only vault strings, instead we got {type(data)}.")
|
22
22
|
|
23
23
|
if vaultid is not None:
|
24
|
-
display.deprecated(
|
24
|
+
display.deprecated(
|
25
|
+
msg="Use of undocumented `vaultid`.",
|
26
|
+
version="2.20",
|
27
|
+
help_text="Use `vault_id` instead.",
|
28
|
+
)
|
25
29
|
|
26
30
|
if vault_id == 'filter_default':
|
27
31
|
vault_id = vaultid
|
@@ -58,7 +62,11 @@ def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
|
|
58
62
|
raise TypeError(f"Vault should be in the form of a string, instead we got {type(vault)}.")
|
59
63
|
|
60
64
|
if vaultid is not None:
|
61
|
-
display.deprecated(
|
65
|
+
display.deprecated(
|
66
|
+
msg="Use of undocumented `vaultid`.",
|
67
|
+
version="2.20",
|
68
|
+
help_text="Use `vault_id` instead.",
|
69
|
+
)
|
62
70
|
|
63
71
|
if vault_id == 'filter_default':
|
64
72
|
vault_id = vaultid
|
ansible/plugins/list.py
CHANGED
@@ -58,7 +58,7 @@ def get_composite_name(collection, name, path, depth):
|
|
58
58
|
return '.'.join(composite)
|
59
59
|
|
60
60
|
|
61
|
-
def _list_plugins_from_paths(ptype, dirs, collection, depth=0):
|
61
|
+
def _list_plugins_from_paths(ptype, dirs, collection, depth=0, docs=False):
|
62
62
|
# TODO: update to use importlib.resources
|
63
63
|
|
64
64
|
plugins = {}
|
@@ -93,14 +93,15 @@ def _list_plugins_from_paths(ptype, dirs, collection, depth=0):
|
|
93
93
|
continue
|
94
94
|
|
95
95
|
# actually recurse dirs
|
96
|
-
plugins.update(_list_plugins_from_paths(ptype, [to_native(full_path)], collection, depth=depth + 1))
|
96
|
+
plugins.update(_list_plugins_from_paths(ptype, [to_native(full_path)], collection, depth=depth + 1, docs=docs))
|
97
97
|
else:
|
98
98
|
if any([
|
99
99
|
plugin in C.IGNORE_FILES, # general files to ignore
|
100
100
|
to_native(b_ext) in C.REJECT_EXTS, # general extensions to ignore
|
101
|
-
b_ext in (b'.yml', b'.yaml', b'.json'), # ignore docs files
|
101
|
+
b_ext in (b'.yml', b'.yaml', b'.json'), # ignore docs files
|
102
102
|
plugin in IGNORE.get(bkey, ()), # plugin in reject list
|
103
103
|
os.path.islink(full_path), # skip aliases, author should document in 'aliases' field
|
104
|
+
not docs and b_ext in (b''), # ignore no ext when looking for docs files
|
104
105
|
]):
|
105
106
|
continue
|
106
107
|
|
@@ -179,7 +180,7 @@ def _list_collection_plugins_with_info(
|
|
179
180
|
|
180
181
|
# raise Exception('bad acr for %s, %s' % (collection, ptype))
|
181
182
|
|
182
|
-
plugin_paths.update(_list_plugins_from_paths(ptype, dirs, collection))
|
183
|
+
plugin_paths.update(_list_plugins_from_paths(ptype, dirs, collection, docs=True))
|
183
184
|
|
184
185
|
plugins = {}
|
185
186
|
if ptype in ('module',):
|
@@ -22,7 +22,7 @@ DOCUMENTATION = """
|
|
22
22
|
default: true
|
23
23
|
deprecated:
|
24
24
|
why: This option is no longer used in the Ansible Core code base.
|
25
|
-
version: "2.
|
25
|
+
version: "2.23"
|
26
26
|
alternatives: Jinja2 native mode is now the default and only option, which is mutually exclusive with this option.
|
27
27
|
variable_start_string:
|
28
28
|
description: The string marking the beginning of a print statement.
|
@@ -45,7 +45,7 @@ DOCUMENTATION = """
|
|
45
45
|
type: bool
|
46
46
|
deprecated:
|
47
47
|
why: This option is no longer used in the Ansible Core code base.
|
48
|
-
version: "2.
|
48
|
+
version: "2.23"
|
49
49
|
alternatives: Jinja2 native mode is now the default and only option.
|
50
50
|
template_vars:
|
51
51
|
description: A dictionary, the keys become additional variables available for templating.
|
@@ -105,7 +105,8 @@ import os
|
|
105
105
|
|
106
106
|
from ansible.errors import AnsibleError
|
107
107
|
from ansible.plugins.lookup import LookupBase
|
108
|
-
from ansible.template import
|
108
|
+
from ansible.template import trust_as_template
|
109
|
+
from ansible._internal._templating import _template_vars
|
109
110
|
from ansible.utils.display import Display
|
110
111
|
|
111
112
|
|
@@ -157,7 +158,11 @@ class LookupModule(LookupBase):
|
|
157
158
|
# argument.
|
158
159
|
# FIXME: why isn't this a chainmap with a sacrificial bottom layer?
|
159
160
|
vars = deepcopy(variables)
|
160
|
-
vars.update(generate_ansible_template_vars(
|
161
|
+
vars.update(_template_vars.generate_ansible_template_vars(
|
162
|
+
path=term,
|
163
|
+
fullpath=lookupfile,
|
164
|
+
include_ansible_managed='ansible_managed' not in vars, # do not clobber ansible_managed when set by the user
|
165
|
+
))
|
161
166
|
vars.update(lookup_template_vars)
|
162
167
|
|
163
168
|
overrides = dict(
|
@@ -325,8 +325,9 @@ class ShellModule(ShellBase):
|
|
325
325
|
|
326
326
|
def checksum(self, path, *args, **kwargs):
|
327
327
|
display.deprecated(
|
328
|
-
"The
|
329
|
-
version="2.23"
|
328
|
+
msg="The `ShellModule.checksum` method is deprecated.",
|
329
|
+
version="2.23",
|
330
|
+
help_text="Use `ActionBase._execute_remote_stat()` instead.",
|
330
331
|
)
|
331
332
|
path = self._escape(self._unquote(path))
|
332
333
|
script = """
|