ansible-core 2.19.0b1__py3-none-any.whl → 2.19.0b3__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.py +1 -4
- ansible/_internal/_collection_proxy.py +47 -0
- ansible/_internal/_errors/_handler.py +4 -4
- ansible/_internal/_json/__init__.py +47 -4
- ansible/_internal/_json/_profiles/_legacy.py +2 -3
- ansible/_internal/_templating/_datatag.py +3 -4
- ansible/_internal/_templating/_engine.py +6 -1
- ansible/_internal/_templating/_jinja_bits.py +4 -4
- ansible/_internal/_templating/_jinja_plugins.py +7 -17
- ansible/cli/__init__.py +12 -5
- ansible/cli/arguments/option_helpers.py +4 -1
- ansible/cli/doc.py +14 -8
- ansible/config/base.yml +17 -20
- ansible/config/manager.py +2 -2
- ansible/constants.py +0 -62
- ansible/errors/__init__.py +6 -2
- ansible/executor/module_common.py +11 -7
- ansible/executor/process/worker.py +31 -26
- ansible/executor/task_executor.py +38 -31
- ansible/executor/task_queue_manager.py +62 -52
- ansible/executor/task_result.py +168 -72
- ansible/galaxy/api.py +1 -1
- ansible/galaxy/collection/__init__.py +3 -3
- ansible/inventory/manager.py +2 -1
- ansible/module_utils/_internal/_ansiballz.py +4 -30
- ansible/module_utils/_internal/_datatag/_tags.py +3 -25
- ansible/module_utils/_internal/_deprecator.py +134 -0
- ansible/module_utils/_internal/_plugin_info.py +25 -0
- ansible/module_utils/_internal/_validation.py +14 -0
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +68 -23
- ansible/module_utils/common/arg_spec.py +8 -3
- ansible/module_utils/common/messages.py +40 -23
- ansible/module_utils/common/process.py +0 -1
- ansible/module_utils/common/respawn.py +0 -7
- ansible/module_utils/common/warnings.py +13 -13
- ansible/module_utils/datatag.py +13 -13
- ansible/modules/async_status.py +1 -1
- ansible/modules/dnf5.py +1 -1
- ansible/modules/get_url.py +1 -1
- ansible/parsing/utils/jsonify.py +40 -0
- ansible/parsing/yaml/objects.py +16 -5
- ansible/playbook/included_file.py +25 -12
- ansible/playbook/task.py +0 -2
- ansible/plugins/__init__.py +18 -8
- ansible/plugins/action/__init__.py +6 -14
- ansible/plugins/action/gather_facts.py +2 -4
- ansible/plugins/callback/__init__.py +173 -86
- ansible/plugins/callback/default.py +79 -79
- ansible/plugins/callback/junit.py +20 -19
- ansible/plugins/callback/minimal.py +17 -17
- ansible/plugins/callback/oneline.py +23 -16
- ansible/plugins/callback/tree.py +13 -6
- ansible/plugins/connection/local.py +1 -1
- ansible/plugins/connection/paramiko_ssh.py +9 -2
- ansible/plugins/doc_fragments/action_core.py +1 -1
- ansible/plugins/filter/core.py +12 -2
- ansible/plugins/inventory/__init__.py +2 -2
- ansible/plugins/loader.py +194 -130
- ansible/plugins/lookup/url.py +2 -2
- ansible/plugins/strategy/__init__.py +76 -82
- ansible/plugins/strategy/free.py +4 -4
- ansible/plugins/strategy/linear.py +11 -9
- ansible/plugins/test/core.py +1 -1
- ansible/release.py +1 -1
- ansible/template/__init__.py +8 -6
- ansible/utils/collection_loader/_collection_meta.py +5 -3
- ansible/utils/display.py +141 -79
- ansible/utils/py3compat.py +1 -7
- ansible/utils/ssh_functions.py +4 -1
- ansible/utils/vars.py +23 -0
- ansible/vars/clean.py +1 -1
- ansible/vars/manager.py +18 -27
- ansible/vars/plugins.py +4 -4
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/RECORD +89 -85
- ansible_test/_internal/commands/sanity/pylint.py +1 -0
- ansible_test/_internal/docker_util.py +4 -3
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +475 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py +137 -0
- ansible/module_utils/_internal/_dataclass_annotation_patch.py +0 -64
- ansible/module_utils/_internal/_plugin_exec_context.py +0 -49
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +0 -399
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/COPYING +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b1.dist-info → ansible_core-2.19.0b3.dist-info}/top_level.txt +0 -0
ansible/_internal/_ansiballz.py
CHANGED
@@ -42,7 +42,6 @@ def _ansiballz_main(
|
|
42
42
|
module_fqn: str,
|
43
43
|
params: str,
|
44
44
|
profile: str,
|
45
|
-
plugin_info_dict: dict[str, object],
|
46
45
|
date_time: datetime.datetime,
|
47
46
|
coverage_config: str | None,
|
48
47
|
coverage_output: str | None,
|
@@ -142,7 +141,6 @@ def _ansiballz_main(
|
|
142
141
|
run_module(
|
143
142
|
json_params=json_params,
|
144
143
|
profile=profile,
|
145
|
-
plugin_info_dict=plugin_info_dict,
|
146
144
|
module_fqn=module_fqn,
|
147
145
|
modlib_path=modlib_path,
|
148
146
|
coverage_config=coverage_config,
|
@@ -230,13 +228,12 @@ def _ansiballz_main(
|
|
230
228
|
run_module(
|
231
229
|
json_params=json_params,
|
232
230
|
profile=profile,
|
233
|
-
plugin_info_dict=plugin_info_dict,
|
234
231
|
module_fqn=module_fqn,
|
235
232
|
modlib_path=modlib_path,
|
236
233
|
)
|
237
234
|
|
238
235
|
else:
|
239
|
-
print('
|
236
|
+
print(f'FATAL: Unknown debug command {command!r}. Doing nothing.')
|
240
237
|
|
241
238
|
#
|
242
239
|
# See comments in the debug() method for information on debugging
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from __future__ import annotations as _annotations
|
2
|
+
|
3
|
+
import collections.abc as _c
|
4
|
+
import typing as _t
|
5
|
+
|
6
|
+
_T_co = _t.TypeVar('_T_co', covariant=True)
|
7
|
+
|
8
|
+
|
9
|
+
class SequenceProxy(_c.Sequence[_T_co]):
|
10
|
+
"""A read-only sequence proxy."""
|
11
|
+
|
12
|
+
# DTFIX-RELEASE: needs unit test coverage
|
13
|
+
|
14
|
+
__slots__ = ('__value',)
|
15
|
+
|
16
|
+
def __init__(self, value: _c.Sequence[_T_co]) -> None:
|
17
|
+
self.__value = value
|
18
|
+
|
19
|
+
@_t.overload
|
20
|
+
def __getitem__(self, index: int) -> _T_co: ...
|
21
|
+
|
22
|
+
@_t.overload
|
23
|
+
def __getitem__(self, index: slice) -> _c.Sequence[_T_co]: ...
|
24
|
+
|
25
|
+
def __getitem__(self, index: int | slice) -> _T_co | _c.Sequence[_T_co]:
|
26
|
+
if isinstance(index, slice):
|
27
|
+
return self.__class__(self.__value[index])
|
28
|
+
|
29
|
+
return self.__value[index]
|
30
|
+
|
31
|
+
def __len__(self) -> int:
|
32
|
+
return len(self.__value)
|
33
|
+
|
34
|
+
def __contains__(self, item: object) -> bool:
|
35
|
+
return item in self.__value
|
36
|
+
|
37
|
+
def __iter__(self) -> _t.Iterator[_T_co]:
|
38
|
+
yield from self.__value
|
39
|
+
|
40
|
+
def __reversed__(self) -> _c.Iterator[_T_co]:
|
41
|
+
return reversed(self.__value)
|
42
|
+
|
43
|
+
def index(self, *args) -> int:
|
44
|
+
return self.__value.index(*args)
|
45
|
+
|
46
|
+
def count(self, value: object) -> int:
|
47
|
+
return self.__value.count(value)
|
@@ -16,8 +16,8 @@ class ErrorAction(enum.Enum):
|
|
16
16
|
"""Action to take when an error is encountered."""
|
17
17
|
|
18
18
|
IGNORE = enum.auto()
|
19
|
-
|
20
|
-
|
19
|
+
WARNING = enum.auto()
|
20
|
+
ERROR = enum.auto()
|
21
21
|
|
22
22
|
@classmethod
|
23
23
|
def from_config(cls, setting: str, variables: dict[str, t.Any] | None = None) -> t.Self:
|
@@ -75,9 +75,9 @@ class ErrorHandler:
|
|
75
75
|
yield
|
76
76
|
except args as ex:
|
77
77
|
match self.action:
|
78
|
-
case ErrorAction.
|
78
|
+
case ErrorAction.WARNING:
|
79
79
|
display.error_as_warning(msg=None, exception=ex)
|
80
|
-
case ErrorAction.
|
80
|
+
case ErrorAction.ERROR:
|
81
81
|
raise
|
82
82
|
case _: # ErrorAction.IGNORE
|
83
83
|
pass
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
+
import enum
|
7
8
|
import json
|
8
9
|
import typing as t
|
9
10
|
|
@@ -19,7 +20,9 @@ from ansible.module_utils._internal._datatag import (
|
|
19
20
|
from ansible.module_utils._internal._json._profiles import _tagless
|
20
21
|
from ansible.parsing.vault import EncryptedString
|
21
22
|
from ansible._internal._datatag._tags import Origin, TrustedAsTemplate
|
23
|
+
from ansible._internal._templating import _transform
|
22
24
|
from ansible.module_utils import _internal
|
25
|
+
from ansible.module_utils._internal import _datatag
|
23
26
|
|
24
27
|
_T = t.TypeVar('_T')
|
25
28
|
_sentinel = object()
|
@@ -52,6 +55,19 @@ class StateTrackingMixIn(HasCurrent):
|
|
52
55
|
return self._stack[1:] + [self._current]
|
53
56
|
|
54
57
|
|
58
|
+
class EncryptedStringBehavior(enum.Enum):
|
59
|
+
"""How `AnsibleVariableVisitor` will handle instances of `EncryptedString`."""
|
60
|
+
|
61
|
+
PRESERVE = enum.auto()
|
62
|
+
"""Preserves the unmodified `EncryptedString` instance."""
|
63
|
+
DECRYPT = enum.auto()
|
64
|
+
"""Replaces the value with its decrypted plaintext."""
|
65
|
+
REDACT = enum.auto()
|
66
|
+
"""Replaces the value with a placeholder string."""
|
67
|
+
FAIL = enum.auto()
|
68
|
+
"""Raises an `AnsibleVariableTypeError` error."""
|
69
|
+
|
70
|
+
|
55
71
|
class AnsibleVariableVisitor:
|
56
72
|
"""Utility visitor base class to recursively apply various behaviors and checks to variable object graphs."""
|
57
73
|
|
@@ -63,7 +79,9 @@ class AnsibleVariableVisitor:
|
|
63
79
|
convert_mapping_to_dict: bool = False,
|
64
80
|
convert_sequence_to_list: bool = False,
|
65
81
|
convert_custom_scalars: bool = False,
|
66
|
-
|
82
|
+
convert_to_native_values: bool = False,
|
83
|
+
apply_transforms: bool = False,
|
84
|
+
encrypted_string_behavior: EncryptedStringBehavior = EncryptedStringBehavior.DECRYPT,
|
67
85
|
):
|
68
86
|
super().__init__() # supports StateTrackingMixIn
|
69
87
|
|
@@ -72,7 +90,16 @@ class AnsibleVariableVisitor:
|
|
72
90
|
self.convert_mapping_to_dict = convert_mapping_to_dict
|
73
91
|
self.convert_sequence_to_list = convert_sequence_to_list
|
74
92
|
self.convert_custom_scalars = convert_custom_scalars
|
75
|
-
self.
|
93
|
+
self.convert_to_native_values = convert_to_native_values
|
94
|
+
self.apply_transforms = apply_transforms
|
95
|
+
self.encrypted_string_behavior = encrypted_string_behavior
|
96
|
+
|
97
|
+
if apply_transforms:
|
98
|
+
from ansible._internal._templating import _engine
|
99
|
+
|
100
|
+
self._template_engine = _engine.TemplateEngine()
|
101
|
+
else:
|
102
|
+
self._template_engine = None
|
76
103
|
|
77
104
|
self._current: t.Any = None # supports StateTrackingMixIn
|
78
105
|
|
@@ -113,9 +140,19 @@ class AnsibleVariableVisitor:
|
|
113
140
|
|
114
141
|
value_type = type(value)
|
115
142
|
|
143
|
+
if self.apply_transforms and value_type in _transform._type_transform_mapping:
|
144
|
+
value = self._template_engine.transform(value)
|
145
|
+
value_type = type(value)
|
146
|
+
|
147
|
+
# DTFIX-RELEASE: need to handle native copy for keys too
|
148
|
+
if self.convert_to_native_values and isinstance(value, _datatag.AnsibleTaggedObject):
|
149
|
+
value = value._native_copy()
|
150
|
+
value_type = type(value)
|
151
|
+
|
116
152
|
result: _T
|
117
153
|
|
118
154
|
# DTFIX-RELEASE: the visitor is ignoring dict/mapping keys except for debugging and schema-aware checking, it should be doing type checks on keys
|
155
|
+
# keep in mind the allowed types for keys is a more restrictive set than for values (str and taggged str only, not EncryptedString)
|
119
156
|
# DTFIX-RELEASE: some type lists being consulted (the ones from datatag) are probably too permissive, and perhaps should not be dynamic
|
120
157
|
|
121
158
|
if (result := self._early_visit(value, value_type)) is not _sentinel:
|
@@ -127,8 +164,14 @@ class AnsibleVariableVisitor:
|
|
127
164
|
elif value_type in _ANSIBLE_ALLOWED_NON_SCALAR_COLLECTION_VAR_TYPES:
|
128
165
|
with self: # supports StateTrackingMixIn
|
129
166
|
result = AnsibleTagHelper.tag_copy(value, (self._visit(k, v) for k, v in enumerate(t.cast(t.Iterable, value))), value_type=value_type)
|
130
|
-
elif self.
|
131
|
-
|
167
|
+
elif self.encrypted_string_behavior != EncryptedStringBehavior.FAIL and isinstance(value, EncryptedString):
|
168
|
+
match self.encrypted_string_behavior:
|
169
|
+
case EncryptedStringBehavior.REDACT:
|
170
|
+
result = "<redacted>" # type: ignore[assignment]
|
171
|
+
case EncryptedStringBehavior.PRESERVE:
|
172
|
+
result = value # type: ignore[assignment]
|
173
|
+
case EncryptedStringBehavior.DECRYPT:
|
174
|
+
result = str(value) # type: ignore[assignment]
|
132
175
|
elif self.convert_mapping_to_dict and _internal.is_intermediate_mapping(value):
|
133
176
|
with self: # supports StateTrackingMixIn
|
134
177
|
result = {k: self._visit(k, v) for k, v in value.items()} # type: ignore[assignment]
|
@@ -8,13 +8,12 @@ from __future__ import annotations as _annotations
|
|
8
8
|
import datetime as _datetime
|
9
9
|
import typing as _t
|
10
10
|
|
11
|
+
from ansible._internal import _json
|
11
12
|
from ansible._internal._datatag import _tags
|
12
13
|
from ansible.module_utils._internal import _datatag
|
13
14
|
from ansible.module_utils._internal._json import _profiles
|
14
15
|
from ansible.parsing import vault as _vault
|
15
16
|
|
16
|
-
from ... import _json
|
17
|
-
|
18
17
|
|
19
18
|
class _Untrusted:
|
20
19
|
"""
|
@@ -48,7 +47,7 @@ class _LegacyVariableVisitor(_json.AnsibleVariableVisitor):
|
|
48
47
|
convert_mapping_to_dict=convert_mapping_to_dict,
|
49
48
|
convert_sequence_to_list=convert_sequence_to_list,
|
50
49
|
convert_custom_scalars=convert_custom_scalars,
|
51
|
-
|
50
|
+
encrypted_string_behavior=_json.EncryptedStringBehavior.PRESERVE,
|
52
51
|
)
|
53
52
|
|
54
53
|
self.invert_trust = invert_trust
|
@@ -12,7 +12,6 @@ from ansible.utils.display import Display
|
|
12
12
|
from ._access import NotifiableAccessContextBase
|
13
13
|
from ._utils import TemplateContext
|
14
14
|
|
15
|
-
|
16
15
|
display = Display()
|
17
16
|
|
18
17
|
|
@@ -57,10 +56,10 @@ class DeprecatedAccessAuditContext(NotifiableAccessContextBase):
|
|
57
56
|
display._deprecated_with_plugin_info(
|
58
57
|
msg=msg,
|
59
58
|
help_text=item.deprecated.help_text,
|
60
|
-
version=item.deprecated.
|
61
|
-
date=item.deprecated.
|
59
|
+
version=item.deprecated.version,
|
60
|
+
date=item.deprecated.date,
|
62
61
|
obj=item.template,
|
63
|
-
|
62
|
+
deprecator=item.deprecated.deprecator,
|
64
63
|
)
|
65
64
|
|
66
65
|
return result
|
@@ -566,7 +566,12 @@ class TemplateEngine:
|
|
566
566
|
)
|
567
567
|
|
568
568
|
if _TemplateConfig.allow_broken_conditionals:
|
569
|
-
_display.deprecated(
|
569
|
+
_display.deprecated(
|
570
|
+
msg=msg,
|
571
|
+
obj=conditional,
|
572
|
+
help_text=self._BROKEN_CONDITIONAL_ALLOWED_FRAGMENT,
|
573
|
+
version='2.23',
|
574
|
+
)
|
570
575
|
|
571
576
|
return bool_result
|
572
577
|
|
@@ -985,12 +985,12 @@ def _maybe_finalize_scalar(o: t.Any) -> t.Any:
|
|
985
985
|
|
986
986
|
match _TemplateConfig.unknown_type_conversion_handler.action:
|
987
987
|
# we don't want to show the object value, and it can't be Origin-tagged; send the current template value for best effort
|
988
|
-
case ErrorAction.
|
988
|
+
case ErrorAction.WARNING:
|
989
989
|
display.warning(
|
990
990
|
msg=f'Type {native_type_name(o)!r} is unsupported in variable storage, converting to {native_type_name(target_type)!r}.',
|
991
991
|
obj=TemplateContext.current(optional=True).template_value,
|
992
992
|
)
|
993
|
-
case ErrorAction.
|
993
|
+
case ErrorAction.ERROR:
|
994
994
|
raise AnsibleVariableTypeError.from_value(obj=TemplateContext.current(optional=True).template_value)
|
995
995
|
|
996
996
|
return target_type(o)
|
@@ -1006,12 +1006,12 @@ def _finalize_fallback_collection(
|
|
1006
1006
|
) -> t.Collection[t.Any]:
|
1007
1007
|
match _TemplateConfig.unknown_type_conversion_handler.action:
|
1008
1008
|
# we don't want to show the object value, and it can't be Origin-tagged; send the current template value for best effort
|
1009
|
-
case ErrorAction.
|
1009
|
+
case ErrorAction.WARNING:
|
1010
1010
|
display.warning(
|
1011
1011
|
msg=f'Type {native_type_name(o)!r} is unsupported in variable storage, converting to {native_type_name(target_type)!r}.',
|
1012
1012
|
obj=TemplateContext.current(optional=True).template_value,
|
1013
1013
|
)
|
1014
|
-
case ErrorAction.
|
1014
|
+
case ErrorAction.ERROR:
|
1015
1015
|
raise AnsibleVariableTypeError.from_value(obj=TemplateContext.current(optional=True).template_value)
|
1016
1016
|
|
1017
1017
|
return _finalize_collection(o, mode, finalizer, target_type)
|
@@ -8,12 +8,7 @@ import datetime
|
|
8
8
|
import functools
|
9
9
|
import typing as t
|
10
10
|
|
11
|
-
from ansible.errors import (
|
12
|
-
AnsibleTemplatePluginError,
|
13
|
-
)
|
14
|
-
|
15
11
|
from ansible.module_utils._internal._ambient_context import AmbientContextBase
|
16
|
-
from ansible.module_utils._internal._plugin_exec_context import PluginExecContext
|
17
12
|
from ansible.module_utils.common.collections import is_sequence
|
18
13
|
from ansible.module_utils._internal._datatag import AnsibleTagHelper
|
19
14
|
from ansible._internal._datatag._tags import TrustedAsTemplate
|
@@ -115,7 +110,7 @@ class JinjaPluginIntercept(c.MutableMapping):
|
|
115
110
|
return first_marker
|
116
111
|
|
117
112
|
try:
|
118
|
-
with JinjaCallContext(accept_lazy_markers=instance.accept_lazy_markers)
|
113
|
+
with JinjaCallContext(accept_lazy_markers=instance.accept_lazy_markers):
|
119
114
|
return instance.j2_function(*lazify_container_args(args), **lazify_container_kwargs(kwargs))
|
120
115
|
except MarkerError as ex:
|
121
116
|
return ex.source
|
@@ -216,10 +211,7 @@ def _invoke_lookup(*, plugin_name: str, lookup_terms: list, lookup_kwargs: dict[
|
|
216
211
|
wantlist = lookup_kwargs.pop('wantlist', False)
|
217
212
|
errors = lookup_kwargs.pop('errors', 'strict')
|
218
213
|
|
219
|
-
with (
|
220
|
-
JinjaCallContext(accept_lazy_markers=instance.accept_lazy_markers),
|
221
|
-
PluginExecContext(executing_plugin=instance),
|
222
|
-
):
|
214
|
+
with JinjaCallContext(accept_lazy_markers=instance.accept_lazy_markers):
|
223
215
|
try:
|
224
216
|
if _TemplateConfig.allow_embedded_templates:
|
225
217
|
# for backwards compat, only trust constant templates in lookup terms
|
@@ -263,15 +255,13 @@ def _invoke_lookup(*, plugin_name: str, lookup_terms: list, lookup_kwargs: dict[
|
|
263
255
|
return ex.source
|
264
256
|
except Exception as ex:
|
265
257
|
# DTFIX-RELEASE: convert this to the new error/warn/ignore context manager
|
266
|
-
if isinstance(ex, AnsibleTemplatePluginError):
|
267
|
-
msg = f'Lookup failed but the error is being ignored: {ex}'
|
268
|
-
else:
|
269
|
-
msg = f'An unhandled exception occurred while running the lookup plugin {plugin_name!r}. Error was a {type(ex)}, original message: {ex}'
|
270
|
-
|
271
258
|
if errors == 'warn':
|
272
|
-
_display.
|
259
|
+
_display.error_as_warning(
|
260
|
+
msg=f'An error occurred while running the lookup plugin {plugin_name!r}.',
|
261
|
+
exception=ex,
|
262
|
+
)
|
273
263
|
elif errors == 'ignore':
|
274
|
-
_display.display(
|
264
|
+
_display.display(f'An error of type {type(ex)} occurred while running the lookup plugin {plugin_name!r}: {ex}', log_only=True)
|
275
265
|
else:
|
276
266
|
raise AnsibleTemplatePluginRuntimeError('lookup', plugin_name) from ex
|
277
267
|
|
ansible/cli/__init__.py
CHANGED
@@ -10,7 +10,6 @@ import os
|
|
10
10
|
import signal
|
11
11
|
import sys
|
12
12
|
|
13
|
-
|
14
13
|
# We overload the ``ansible`` adhoc command to provide the functionality for
|
15
14
|
# ``SSH_ASKPASS``. This code is here, and not in ``adhoc.py`` to bypass
|
16
15
|
# unnecessary code. The program provided to ``SSH_ASKPASS`` can only be invoked
|
@@ -89,18 +88,25 @@ from ansible import _internal # do not remove or defer; ensures controller-spec
|
|
89
88
|
|
90
89
|
_internal.setup()
|
91
90
|
|
91
|
+
from ansible.errors import AnsibleError, ExitCode
|
92
|
+
|
92
93
|
try:
|
93
94
|
from ansible import constants as C
|
94
95
|
from ansible.utils.display import Display
|
95
96
|
display = Display()
|
96
97
|
except Exception as ex:
|
97
|
-
|
98
|
+
if isinstance(ex, AnsibleError):
|
99
|
+
ex_msg = ' '.join((ex.message, ex._help_text)).strip()
|
100
|
+
else:
|
101
|
+
ex_msg = str(ex)
|
102
|
+
|
103
|
+
print(f'ERROR: {ex_msg}\n\n{"".join(traceback.format_exception(ex))}', file=sys.stderr)
|
98
104
|
sys.exit(5)
|
99
105
|
|
100
106
|
|
101
107
|
from ansible import context
|
108
|
+
from ansible.utils import display as _display
|
102
109
|
from ansible.cli.arguments import option_helpers as opt_help
|
103
|
-
from ansible.errors import AnsibleError, ExitCode
|
104
110
|
from ansible.inventory.manager import InventoryManager
|
105
111
|
from ansible.module_utils.six import string_types
|
106
112
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
@@ -116,6 +122,7 @@ from ansible.utils.collection_loader import AnsibleCollectionConfig
|
|
116
122
|
from ansible.utils.collection_loader._collection_finder import _get_collection_name_from_path
|
117
123
|
from ansible.utils.path import unfrackpath
|
118
124
|
from ansible.vars.manager import VariableManager
|
125
|
+
from ansible.module_utils._internal import _deprecator
|
119
126
|
|
120
127
|
try:
|
121
128
|
import argcomplete
|
@@ -139,7 +146,7 @@ def _launch_ssh_agent() -> None:
|
|
139
146
|
return
|
140
147
|
case 'auto':
|
141
148
|
try:
|
142
|
-
ssh_agent_bin = get_bin_path('ssh-agent'
|
149
|
+
ssh_agent_bin = get_bin_path('ssh-agent')
|
143
150
|
except ValueError as e:
|
144
151
|
raise AnsibleError('SSH_AGENT set to auto, but cannot find ssh-agent binary') from e
|
145
152
|
ssh_agent_dir = os.path.join(C.DEFAULT_LOCAL_TMP, 'ssh_agent')
|
@@ -251,7 +258,7 @@ class CLI(ABC):
|
|
251
258
|
else:
|
252
259
|
display.v(u"No config file found; using defaults")
|
253
260
|
|
254
|
-
|
261
|
+
_display._report_config_warnings(_deprecator.ANSIBLE_CORE_DEPRECATOR)
|
255
262
|
|
256
263
|
@staticmethod
|
257
264
|
def split_vault_id(vault_id):
|
@@ -56,7 +56,10 @@ class DeprecatedArgument:
|
|
56
56
|
|
57
57
|
from ansible.utils.display import Display
|
58
58
|
|
59
|
-
Display().deprecated(
|
59
|
+
Display().deprecated( # pylint: disable=ansible-invalid-deprecated-version
|
60
|
+
msg=f'The {option!r} argument is deprecated.',
|
61
|
+
version=self.version,
|
62
|
+
)
|
60
63
|
|
61
64
|
|
62
65
|
class ArgumentParser(argparse.ArgumentParser):
|
ansible/cli/doc.py
CHANGED
@@ -1172,12 +1172,16 @@ class DocCLI(CLI, RoleMixin):
|
|
1172
1172
|
return 'version %s' % (version_added, )
|
1173
1173
|
|
1174
1174
|
@staticmethod
|
1175
|
-
def warp_fill(text, limit, initial_indent='', subsequent_indent='', **kwargs):
|
1175
|
+
def warp_fill(text, limit, initial_indent='', subsequent_indent='', initial_extra=0, **kwargs):
|
1176
1176
|
result = []
|
1177
1177
|
for paragraph in text.split('\n\n'):
|
1178
|
-
|
1179
|
-
|
1178
|
+
wrapped = textwrap.fill(paragraph, limit, initial_indent=initial_indent + ' ' * initial_extra, subsequent_indent=subsequent_indent,
|
1179
|
+
break_on_hyphens=False, break_long_words=False, drop_whitespace=True, **kwargs)
|
1180
|
+
if initial_extra and wrapped.startswith(' ' * initial_extra):
|
1181
|
+
wrapped = wrapped[initial_extra:]
|
1182
|
+
result.append(wrapped)
|
1180
1183
|
initial_indent = subsequent_indent
|
1184
|
+
initial_extra = 0
|
1181
1185
|
return '\n'.join(result)
|
1182
1186
|
|
1183
1187
|
@staticmethod
|
@@ -1209,20 +1213,23 @@ class DocCLI(CLI, RoleMixin):
|
|
1209
1213
|
text.append('')
|
1210
1214
|
|
1211
1215
|
# TODO: push this to top of for and sort by size, create indent on largest key?
|
1212
|
-
inline_indent =
|
1213
|
-
|
1216
|
+
inline_indent = ' ' * max((len(opt_indent) - len(o)) - len(base_indent), 2)
|
1217
|
+
extra_indent = base_indent + ' ' * (len(o) + 3)
|
1218
|
+
sub_indent = inline_indent + extra_indent
|
1214
1219
|
if is_sequence(opt['description']):
|
1215
1220
|
for entry_idx, entry in enumerate(opt['description'], 1):
|
1216
1221
|
if not isinstance(entry, string_types):
|
1217
1222
|
raise AnsibleError("Expected string in description of %s at index %s, got %s" % (o, entry_idx, type(entry)))
|
1218
1223
|
if entry_idx == 1:
|
1219
|
-
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(entry), limit,
|
1224
|
+
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(entry), limit,
|
1225
|
+
initial_indent=inline_indent, subsequent_indent=sub_indent, initial_extra=len(extra_indent)))
|
1220
1226
|
else:
|
1221
1227
|
text.append(DocCLI.warp_fill(DocCLI.tty_ify(entry), limit, initial_indent=sub_indent, subsequent_indent=sub_indent))
|
1222
1228
|
else:
|
1223
1229
|
if not isinstance(opt['description'], string_types):
|
1224
1230
|
raise AnsibleError("Expected string in description of %s, got %s" % (o, type(opt['description'])))
|
1225
|
-
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(opt['description']), limit,
|
1231
|
+
text.append(key + DocCLI.warp_fill(DocCLI.tty_ify(opt['description']), limit,
|
1232
|
+
initial_indent=inline_indent, subsequent_indent=sub_indent, initial_extra=len(extra_indent)))
|
1226
1233
|
del opt['description']
|
1227
1234
|
|
1228
1235
|
suboptions = []
|
@@ -1328,7 +1335,6 @@ class DocCLI(CLI, RoleMixin):
|
|
1328
1335
|
'This was unintentionally allowed when plugin attributes were added, '
|
1329
1336
|
'but the feature does not map well to role argument specs.',
|
1330
1337
|
version='2.20',
|
1331
|
-
collection_name='ansible.builtin',
|
1332
1338
|
)
|
1333
1339
|
text.append("")
|
1334
1340
|
text.append(_format("ATTRIBUTES:", 'bold'))
|
ansible/config/base.yml
CHANGED
@@ -9,6 +9,18 @@ _ANSIBLE_CONNECTION_PATH:
|
|
9
9
|
- For internal use only.
|
10
10
|
type: path
|
11
11
|
version_added: "2.18"
|
12
|
+
_CALLBACK_DISPATCH_ERROR_BEHAVIOR:
|
13
|
+
name: Callback dispatch error behavior
|
14
|
+
default: warning
|
15
|
+
description:
|
16
|
+
- Action to take when a callback dispatch results in an error.
|
17
|
+
type: choices
|
18
|
+
choices: &basic_error
|
19
|
+
error: issue a 'fatal' error and stop the play
|
20
|
+
warning: issue a warning but continue
|
21
|
+
ignore: just continue silently
|
22
|
+
env: [ { name: _ANSIBLE_CALLBACK_DISPATCH_ERROR_BEHAVIOR } ]
|
23
|
+
version_added: '2.19'
|
12
24
|
ALLOW_BROKEN_CONDITIONALS:
|
13
25
|
# This config option will be deprecated once it no longer has any effect (2.23).
|
14
26
|
name: Allow broken conditionals
|
@@ -224,18 +236,6 @@ CACHE_PLUGIN_TIMEOUT:
|
|
224
236
|
- {key: fact_caching_timeout, section: defaults}
|
225
237
|
type: integer
|
226
238
|
yaml: {key: facts.cache.timeout}
|
227
|
-
_CALLBACK_DISPATCH_ERROR_BEHAVIOR:
|
228
|
-
name: Callback dispatch error behavior
|
229
|
-
default: warn
|
230
|
-
description:
|
231
|
-
- Action to take when a callback dispatch results in an error.
|
232
|
-
type: choices
|
233
|
-
choices: &choices_ignore_warn_fail
|
234
|
-
- ignore
|
235
|
-
- warn
|
236
|
-
- fail
|
237
|
-
env: [ { name: _ANSIBLE_CALLBACK_DISPATCH_ERROR_BEHAVIOR } ]
|
238
|
-
version_added: '2.19'
|
239
239
|
COLLECTIONS_SCAN_SYS_PATH:
|
240
240
|
name: Scan PYTHONPATH for installed collections
|
241
241
|
description: A boolean to enable or disable scanning the sys.path for installed collections.
|
@@ -268,10 +268,7 @@ COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH:
|
|
268
268
|
- When a collection is loaded that does not support the running Ansible version (with the collection metadata key `requires_ansible`).
|
269
269
|
env: [{name: ANSIBLE_COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH}]
|
270
270
|
ini: [{key: collections_on_ansible_version_mismatch, section: defaults}]
|
271
|
-
choices:
|
272
|
-
error: issue a 'fatal' error and stop the play
|
273
|
-
warning: issue a warning but continue
|
274
|
-
ignore: just continue silently
|
271
|
+
choices: *basic_error
|
275
272
|
default: warning
|
276
273
|
COLOR_CHANGED:
|
277
274
|
name: Color for 'changed' task status
|
@@ -2058,13 +2055,13 @@ TASK_TIMEOUT:
|
|
2058
2055
|
version_added: '2.10'
|
2059
2056
|
_TEMPLAR_UNKNOWN_TYPE_CONVERSION:
|
2060
2057
|
name: Templar unknown type conversion behavior
|
2061
|
-
default:
|
2058
|
+
default: warning
|
2062
2059
|
description:
|
2063
2060
|
- Action to take when an unknown type is converted for variable storage during template finalization.
|
2064
2061
|
- This setting has no effect on the inability to store unsupported variable types as the result of templating.
|
2065
2062
|
- Experimental diagnostic feature, subject to change.
|
2066
2063
|
type: choices
|
2067
|
-
choices: *
|
2064
|
+
choices: *basic_error
|
2068
2065
|
env: [{name: _ANSIBLE_TEMPLAR_UNKNOWN_TYPE_CONVERSION}]
|
2069
2066
|
version_added: '2.19'
|
2070
2067
|
_TEMPLAR_UNKNOWN_TYPE_ENCOUNTERED:
|
@@ -2074,7 +2071,7 @@ _TEMPLAR_UNKNOWN_TYPE_ENCOUNTERED:
|
|
2074
2071
|
- Action to take when an unknown type is encountered inside a template pipeline.
|
2075
2072
|
- Experimental diagnostic feature, subject to change.
|
2076
2073
|
type: choices
|
2077
|
-
choices: *
|
2074
|
+
choices: *basic_error
|
2078
2075
|
env: [{name: _ANSIBLE_TEMPLAR_UNKNOWN_TYPE_ENCOUNTERED}]
|
2079
2076
|
version_added: '2.19'
|
2080
2077
|
_TEMPLAR_UNTRUSTED_TEMPLATE_BEHAVIOR:
|
@@ -2086,7 +2083,7 @@ _TEMPLAR_UNTRUSTED_TEMPLATE_BEHAVIOR:
|
|
2086
2083
|
- This setting has no effect on expressions.
|
2087
2084
|
- Experimental diagnostic feature, subject to change.
|
2088
2085
|
type: choices
|
2089
|
-
choices: *
|
2086
|
+
choices: *basic_error
|
2090
2087
|
env: [{name: _ANSIBLE_TEMPLAR_UNTRUSTED_TEMPLATE_BEHAVIOR}]
|
2091
2088
|
version_added: '2.19'
|
2092
2089
|
WORKER_SHUTDOWN_POLL_COUNT:
|
ansible/config/manager.py
CHANGED
@@ -480,9 +480,9 @@ class ConfigManager(object):
|
|
480
480
|
else:
|
481
481
|
ret = self._plugins.get(plugin_type, {}).get(name, {})
|
482
482
|
|
483
|
-
if ignore_private:
|
483
|
+
if ignore_private: # ignore 'test' config entries, they should not change runtime behaviors
|
484
484
|
for cdef in list(ret.keys()):
|
485
|
-
if cdef.startswith('
|
485
|
+
if cdef.startswith('_Z_'):
|
486
486
|
del ret[cdef]
|
487
487
|
return ret
|
488
488
|
|
ansible/constants.py
CHANGED
@@ -10,9 +10,7 @@ from string import ascii_letters, digits
|
|
10
10
|
|
11
11
|
from ansible.config.manager import ConfigManager
|
12
12
|
from ansible.module_utils.common.text.converters import to_text
|
13
|
-
from ansible.module_utils.common.collections import Sequence
|
14
13
|
from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE
|
15
|
-
from ansible.release import __version__
|
16
14
|
from ansible.utils.fqcn import add_internal_fqcns
|
17
15
|
|
18
16
|
# initialize config manager/config data to read/store global settings
|
@@ -20,68 +18,11 @@ from ansible.utils.fqcn import add_internal_fqcns
|
|
20
18
|
config = ConfigManager()
|
21
19
|
|
22
20
|
|
23
|
-
def _warning(msg):
|
24
|
-
""" display is not guaranteed here, nor it being the full class, but try anyways, fallback to sys.stderr.write """
|
25
|
-
try:
|
26
|
-
from ansible.utils.display import Display
|
27
|
-
Display().warning(msg)
|
28
|
-
except Exception:
|
29
|
-
import sys
|
30
|
-
sys.stderr.write(' [WARNING] %s\n' % (msg))
|
31
|
-
|
32
|
-
|
33
|
-
def _deprecated(msg, version):
|
34
|
-
""" display is not guaranteed here, nor it being the full class, but try anyways, fallback to sys.stderr.write """
|
35
|
-
try:
|
36
|
-
from ansible.utils.display import Display
|
37
|
-
Display().deprecated(msg, version=version)
|
38
|
-
except Exception:
|
39
|
-
import sys
|
40
|
-
sys.stderr.write(' [DEPRECATED] %s, to be removed in %s\n' % (msg, version))
|
41
|
-
|
42
|
-
|
43
|
-
def handle_config_noise(display=None):
|
44
|
-
|
45
|
-
if display is not None:
|
46
|
-
w = display.warning
|
47
|
-
d = display.deprecated
|
48
|
-
else:
|
49
|
-
w = _warning
|
50
|
-
d = _deprecated
|
51
|
-
|
52
|
-
while config.WARNINGS:
|
53
|
-
warn = config.WARNINGS.pop()
|
54
|
-
w(warn)
|
55
|
-
|
56
|
-
while config.DEPRECATED:
|
57
|
-
# tuple with name and options
|
58
|
-
dep = config.DEPRECATED.pop(0)
|
59
|
-
msg = config.get_deprecated_msg_from_config(dep[1])
|
60
|
-
# use tabs only for ansible-doc?
|
61
|
-
msg = msg.replace("\t", "")
|
62
|
-
d(f"{dep[0]} option. {msg}", version=dep[1]['version'])
|
63
|
-
|
64
|
-
|
65
21
|
def set_constant(name, value, export=vars()):
|
66
22
|
""" sets constants and returns resolved options dict """
|
67
23
|
export[name] = value
|
68
24
|
|
69
25
|
|
70
|
-
class _DeprecatedSequenceConstant(Sequence):
|
71
|
-
def __init__(self, value, msg, version):
|
72
|
-
self._value = value
|
73
|
-
self._msg = msg
|
74
|
-
self._version = version
|
75
|
-
|
76
|
-
def __len__(self):
|
77
|
-
_deprecated(self._msg, self._version)
|
78
|
-
return len(self._value)
|
79
|
-
|
80
|
-
def __getitem__(self, y):
|
81
|
-
_deprecated(self._msg, self._version)
|
82
|
-
return self._value[y]
|
83
|
-
|
84
|
-
|
85
26
|
# CONSTANTS ### yes, actual ones
|
86
27
|
|
87
28
|
# The following are hard-coded action names
|
@@ -245,6 +186,3 @@ MAGIC_VARIABLE_MAPPING = dict(
|
|
245
186
|
# POPULATE SETTINGS FROM CONFIG ###
|
246
187
|
for setting in config.get_configuration_definitions():
|
247
188
|
set_constant(setting, config.get_config_value(setting, variables=vars()))
|
248
|
-
|
249
|
-
# emit any warnings or deprecations
|
250
|
-
handle_config_noise()
|