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
@@ -6,9 +6,10 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import enum
|
9
|
-
import inspect
|
10
9
|
import traceback
|
11
10
|
|
11
|
+
from . import _stack
|
12
|
+
|
12
13
|
|
13
14
|
class TracebackEvent(enum.Enum):
|
14
15
|
"""The events for which tracebacks can be enabled."""
|
@@ -16,6 +17,7 @@ class TracebackEvent(enum.Enum):
|
|
16
17
|
ERROR = enum.auto()
|
17
18
|
WARNING = enum.auto()
|
18
19
|
DEPRECATED = enum.auto()
|
20
|
+
DEPRECATED_VALUE = enum.auto() # implies DEPRECATED
|
19
21
|
|
20
22
|
|
21
23
|
def traceback_for() -> list[str]:
|
@@ -28,24 +30,25 @@ def is_traceback_enabled(event: TracebackEvent) -> bool:
|
|
28
30
|
return _is_traceback_enabled(event)
|
29
31
|
|
30
32
|
|
31
|
-
def maybe_capture_traceback(event: TracebackEvent) -> str | None:
|
33
|
+
def maybe_capture_traceback(msg: str, event: TracebackEvent) -> str | None:
|
32
34
|
"""
|
33
35
|
Optionally capture a traceback for the current call stack, formatted as a string, if the specified traceback event is enabled.
|
34
|
-
|
36
|
+
Frames marked with the `_skip_stackwalk` local are omitted.
|
35
37
|
"""
|
38
|
+
_skip_stackwalk = True
|
39
|
+
|
36
40
|
if not is_traceback_enabled(event):
|
37
41
|
return None
|
38
42
|
|
39
43
|
tb_lines = []
|
40
44
|
|
41
|
-
if
|
45
|
+
if frame_info := _stack.caller_frame():
|
42
46
|
# DTFIX-FUTURE: rewrite target-side tracebacks to point at controller-side paths?
|
43
|
-
frames = inspect.getouterframes(current_frame)
|
44
|
-
ignore_frame_count = 2 # ignore this function and its caller
|
45
47
|
tb_lines.append('Traceback (most recent call last):\n')
|
46
|
-
tb_lines.extend(traceback.format_stack(
|
48
|
+
tb_lines.extend(traceback.format_stack(frame_info.frame))
|
49
|
+
tb_lines.append(f'Message: {msg}\n')
|
47
50
|
else:
|
48
|
-
tb_lines.append('
|
51
|
+
tb_lines.append('(frame not found)\n') # pragma: nocover
|
49
52
|
|
50
53
|
return ''.join(tb_lines)
|
51
54
|
|
ansible/module_utils/basic.py
CHANGED
@@ -75,7 +75,7 @@ except ImportError:
|
|
75
75
|
# Python2 & 3 way to get NoneType
|
76
76
|
NoneType = type(None)
|
77
77
|
|
78
|
-
from ._internal import _traceback, _errors, _debugging, _deprecator
|
78
|
+
from ._internal import _traceback, _errors, _debugging, _deprecator, _messages
|
79
79
|
|
80
80
|
from .common.text.converters import (
|
81
81
|
to_native,
|
@@ -161,10 +161,10 @@ from ansible.module_utils.common.validation import (
|
|
161
161
|
safe_eval,
|
162
162
|
)
|
163
163
|
from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses
|
164
|
-
from ansible.module_utils.common import messages as _messages
|
165
164
|
from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean
|
166
165
|
from ansible.module_utils.common.warnings import (
|
167
166
|
deprecate,
|
167
|
+
error_as_warning,
|
168
168
|
get_deprecations,
|
169
169
|
get_warnings,
|
170
170
|
warn,
|
@@ -505,9 +505,34 @@ class AnsibleModule(object):
|
|
505
505
|
|
506
506
|
return self._tmpdir
|
507
507
|
|
508
|
-
def warn(
|
509
|
-
|
510
|
-
|
508
|
+
def warn(
|
509
|
+
self,
|
510
|
+
warning: str,
|
511
|
+
*,
|
512
|
+
help_text: str | None = None,
|
513
|
+
) -> None:
|
514
|
+
_skip_stackwalk = True
|
515
|
+
|
516
|
+
warn(
|
517
|
+
warning=warning,
|
518
|
+
help_text=help_text,
|
519
|
+
)
|
520
|
+
|
521
|
+
def error_as_warning(
|
522
|
+
self,
|
523
|
+
msg: str | None,
|
524
|
+
exception: BaseException,
|
525
|
+
*,
|
526
|
+
help_text: str | None = None,
|
527
|
+
) -> None:
|
528
|
+
"""Display an exception as a warning."""
|
529
|
+
_skip_stackwalk = True
|
530
|
+
|
531
|
+
error_as_warning(
|
532
|
+
msg=msg,
|
533
|
+
exception=exception,
|
534
|
+
help_text=help_text,
|
535
|
+
)
|
511
536
|
|
512
537
|
def deprecate(
|
513
538
|
self,
|
@@ -1399,7 +1424,7 @@ class AnsibleModule(object):
|
|
1399
1424
|
# deprecate(
|
1400
1425
|
# msg="The `AnsibleModule.jsonify' method is deprecated.",
|
1401
1426
|
# version="2.27",
|
1402
|
-
# # help_text="", # DTFIX-
|
1427
|
+
# # help_text="", # DTFIX-FUTURE: fill in this help text
|
1403
1428
|
# )
|
1404
1429
|
|
1405
1430
|
try:
|
@@ -1528,16 +1553,25 @@ class AnsibleModule(object):
|
|
1528
1553
|
)
|
1529
1554
|
|
1530
1555
|
if isinstance(exception, BaseException):
|
1531
|
-
# Include a `_messages.
|
1532
|
-
# The `msg` is included in the
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1556
|
+
# Include a `_messages.Event` in the result.
|
1557
|
+
# The `msg` is included in the chain to ensure it is not lost when looking only at `exception` from the result.
|
1558
|
+
|
1559
|
+
kwargs.update(
|
1560
|
+
exception=_messages.ErrorSummary(
|
1561
|
+
event=_messages.Event(
|
1562
|
+
msg=msg,
|
1563
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
|
1564
|
+
chain=_messages.EventChain(
|
1565
|
+
msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
|
1566
|
+
traceback_reason="The above exception was the direct cause of the following error:",
|
1567
|
+
event=_errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR)),
|
1568
|
+
),
|
1569
|
+
),
|
1570
|
+
),
|
1571
|
+
)
|
1538
1572
|
elif _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR):
|
1539
1573
|
# Include only a formatted traceback string in the result.
|
1540
|
-
# The controller will combine this with `msg` to create an `_messages.
|
1574
|
+
# The controller will combine this with `msg` to create an `_messages.ErrorSummary`.
|
1541
1575
|
|
1542
1576
|
formatted_traceback: str | None
|
1543
1577
|
|
@@ -1546,7 +1580,7 @@ class AnsibleModule(object):
|
|
1546
1580
|
elif exception is _UNSET and (current_exception := t.cast(t.Optional[BaseException], sys.exc_info()[1])):
|
1547
1581
|
formatted_traceback = _traceback.maybe_extract_traceback(current_exception, _traceback.TracebackEvent.ERROR)
|
1548
1582
|
else:
|
1549
|
-
formatted_traceback = _traceback.maybe_capture_traceback(_traceback.TracebackEvent.ERROR)
|
1583
|
+
formatted_traceback = _traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR)
|
1550
1584
|
|
1551
1585
|
if formatted_traceback:
|
1552
1586
|
kwargs.update(exception=formatted_traceback)
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
from copy import deepcopy
|
8
8
|
|
9
|
+
from ansible.module_utils.datatag import deprecator_from_collection_name
|
9
10
|
from ansible.module_utils.common.parameters import (
|
10
11
|
_ADDITIONAL_CHECKS,
|
11
12
|
_get_legal_inputs,
|
@@ -22,7 +23,6 @@ from ansible.module_utils.common.parameters import (
|
|
22
23
|
|
23
24
|
from ansible.module_utils.common.text.converters import to_native
|
24
25
|
from ansible.module_utils.common.warnings import deprecate, warn
|
25
|
-
from ansible.module_utils.common import messages as _messages
|
26
26
|
|
27
27
|
from ansible.module_utils.common.validation import (
|
28
28
|
check_mutually_exclusive,
|
@@ -306,7 +306,7 @@ class ModuleArgumentSpecValidator(ArgumentSpecValidator):
|
|
306
306
|
msg=d['msg'],
|
307
307
|
version=d.get('version'),
|
308
308
|
date=d.get('date'),
|
309
|
-
deprecator=
|
309
|
+
deprecator=deprecator_from_collection_name(d.get('collection_name')),
|
310
310
|
)
|
311
311
|
|
312
312
|
for w in result._warnings:
|
@@ -6,6 +6,7 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
|
9
|
+
from ansible.module_utils.common import warnings as _warnings
|
9
10
|
from ansible.module_utils.six import binary_type, text_type
|
10
11
|
from ansible.module_utils.six.moves.collections_abc import Hashable, Mapping, MutableMapping, Sequence # pylint: disable=unused-import
|
11
12
|
|
@@ -102,6 +103,11 @@ def count(seq):
|
|
102
103
|
code is run on Python 2.6.* where collections.Counter is not available. It should be
|
103
104
|
deprecated and replaced when support for Python < 2.7 is dropped.
|
104
105
|
"""
|
106
|
+
_warnings.deprecate(
|
107
|
+
msg="The `ansible.module_utils.common.collections.count` function is deprecated.",
|
108
|
+
version="2.23",
|
109
|
+
help_text="Use `collections.Counter` from the Python standard library instead.",
|
110
|
+
)
|
105
111
|
if not is_iterable(seq):
|
106
112
|
raise Exception('Argument provided is not an iterable')
|
107
113
|
counters = dict()
|
@@ -20,7 +20,7 @@ def __getattr__(name: str) -> object:
|
|
20
20
|
# _warnings.deprecate(
|
21
21
|
# msg="The `AnsibleJSONEncoder` type is deprecated.",
|
22
22
|
# version="2.27",
|
23
|
-
# help_text="Use a profile-based encoder instead.", # DTFIX-
|
23
|
+
# help_text="Use a profile-based encoder instead.", # DTFIX-FUTURE: improve this help text
|
24
24
|
# )
|
25
25
|
|
26
26
|
return _get_legacy_encoder()
|
@@ -31,7 +31,7 @@ def __getattr__(name: str) -> object:
|
|
31
31
|
# _warnings.deprecate(
|
32
32
|
# msg="The `AnsibleJSONDecoder` type is deprecated.",
|
33
33
|
# version="2.27",
|
34
|
-
# help_text="Use a profile-based decoder instead.", # DTFIX-
|
34
|
+
# help_text="Use a profile-based decoder instead.", # DTFIX-FUTURE: improve this help text
|
35
35
|
# )
|
36
36
|
|
37
37
|
return _tagless.Decoder
|
@@ -245,7 +245,7 @@ def jsonify(data, **kwargs):
|
|
245
245
|
# deprecate(
|
246
246
|
# msg="The `jsonify` function is deprecated.",
|
247
247
|
# version="2.27",
|
248
|
-
# # help_text="", # DTFIX-
|
248
|
+
# # help_text="", # DTFIX-FUTURE: fill in this help text
|
249
249
|
# )
|
250
250
|
|
251
251
|
return json.dumps(data, cls=_common_json._get_legacy_encoder(), _decode_bytes=True, **kwargs)
|
@@ -257,7 +257,7 @@ def container_to_bytes(d, encoding='utf-8', errors='surrogate_or_strict'):
|
|
257
257
|
Specialized for json return because this only handles, lists, tuples,
|
258
258
|
and dict container types (the containers that the json module returns)
|
259
259
|
"""
|
260
|
-
# DTFIX-
|
260
|
+
# DTFIX-FUTURE: deprecate
|
261
261
|
|
262
262
|
if isinstance(d, text_type):
|
263
263
|
return to_bytes(d, encoding=encoding, errors=errors)
|
@@ -277,7 +277,7 @@ def container_to_text(d, encoding='utf-8', errors='surrogate_or_strict'):
|
|
277
277
|
Specialized for json return because this only handles, lists, tuples,
|
278
278
|
and dict container types (the containers that the json module returns)
|
279
279
|
"""
|
280
|
-
# DTFIX-
|
280
|
+
# DTFIX-FUTURE: deprecate
|
281
281
|
|
282
282
|
if isinstance(d, binary_type):
|
283
283
|
# Warning, can traceback
|
@@ -402,7 +402,7 @@ def check_type_list(value):
|
|
402
402
|
if isinstance(value, list):
|
403
403
|
return value
|
404
404
|
|
405
|
-
# DTFIX-
|
405
|
+
# DTFIX-FUTURE: deprecate legacy comma split functionality, eventually replace with `_check_type_list_strict`
|
406
406
|
if isinstance(value, string_types):
|
407
407
|
return value.split(",")
|
408
408
|
elif isinstance(value, int) or isinstance(value, float):
|
@@ -6,20 +6,77 @@ from __future__ import annotations as _annotations
|
|
6
6
|
|
7
7
|
import typing as _t
|
8
8
|
|
9
|
-
from ansible.module_utils._internal import _traceback, _deprecator
|
10
|
-
from ansible.module_utils.common import messages as _messages
|
9
|
+
from ansible.module_utils._internal import _traceback, _deprecator, _event_utils, _messages, _errors
|
11
10
|
from ansible.module_utils import _internal
|
12
11
|
|
13
12
|
|
14
|
-
def warn(
|
13
|
+
def warn(
|
14
|
+
warning: str,
|
15
|
+
*,
|
16
|
+
help_text: str | None = None,
|
17
|
+
obj: object | None = None,
|
18
|
+
) -> None:
|
15
19
|
"""Record a warning to be returned with the module result."""
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
_skip_stackwalk = True
|
21
|
+
|
22
|
+
if _internal.is_controller:
|
23
|
+
_display = _internal.import_controller_module('ansible.utils.display').Display()
|
24
|
+
_display.warning(
|
25
|
+
msg=warning,
|
26
|
+
help_text=help_text,
|
27
|
+
obj=obj,
|
28
|
+
)
|
29
|
+
|
30
|
+
return
|
31
|
+
|
32
|
+
warning = _messages.WarningSummary(
|
33
|
+
event=_messages.Event(
|
34
|
+
msg=warning,
|
35
|
+
help_text=help_text,
|
36
|
+
formatted_traceback=_traceback.maybe_capture_traceback(warning, _traceback.TracebackEvent.WARNING),
|
37
|
+
),
|
38
|
+
)
|
39
|
+
|
40
|
+
_global_warnings[warning] = None
|
41
|
+
|
42
|
+
|
43
|
+
def error_as_warning(
|
44
|
+
msg: str | None,
|
45
|
+
exception: BaseException,
|
46
|
+
*,
|
47
|
+
help_text: str | None = None,
|
48
|
+
obj: object = None,
|
49
|
+
) -> None:
|
50
|
+
"""Display an exception as a warning."""
|
51
|
+
_skip_stackwalk = True
|
52
|
+
|
53
|
+
if _internal.is_controller:
|
54
|
+
_display = _internal.import_controller_module('ansible.utils.display').Display()
|
55
|
+
_display.error_as_warning(
|
56
|
+
msg=msg,
|
57
|
+
exception=exception,
|
58
|
+
help_text=help_text,
|
59
|
+
obj=obj,
|
60
|
+
)
|
61
|
+
|
62
|
+
return
|
63
|
+
|
64
|
+
event = _errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
|
65
|
+
|
66
|
+
warning = _messages.WarningSummary(
|
67
|
+
event=_messages.Event(
|
68
|
+
msg=msg,
|
69
|
+
help_text=help_text,
|
70
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
|
71
|
+
chain=_messages.EventChain(
|
72
|
+
msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
|
73
|
+
traceback_reason=_errors.TRACEBACK_REASON_EXCEPTION_DIRECT_WARNING,
|
74
|
+
event=event,
|
75
|
+
),
|
20
76
|
),
|
21
|
-
|
22
|
-
|
77
|
+
)
|
78
|
+
|
79
|
+
_global_warnings[warning] = None
|
23
80
|
|
24
81
|
|
25
82
|
def deprecate(
|
@@ -57,30 +114,30 @@ def deprecate(
|
|
57
114
|
|
58
115
|
return
|
59
116
|
|
60
|
-
|
61
|
-
|
62
|
-
|
117
|
+
warning = _messages.DeprecationSummary(
|
118
|
+
event=_messages.Event(
|
119
|
+
msg=msg,
|
120
|
+
help_text=help_text,
|
121
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED),
|
63
122
|
),
|
64
|
-
formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
|
65
123
|
version=version,
|
66
124
|
date=date,
|
67
125
|
deprecator=deprecator,
|
68
|
-
)
|
126
|
+
)
|
127
|
+
|
128
|
+
_global_deprecations[warning] = None
|
69
129
|
|
70
130
|
|
71
131
|
def get_warning_messages() -> tuple[str, ...]:
|
72
132
|
"""Return a tuple of warning messages accumulated over this run."""
|
73
|
-
#
|
74
|
-
return tuple(item.
|
75
|
-
|
76
|
-
|
77
|
-
_DEPRECATION_MESSAGE_KEYS = frozenset({'msg', 'date', 'version', 'collection_name'})
|
133
|
+
# DTFIX7: add future deprecation comment
|
134
|
+
return tuple(_event_utils.format_event_brief_message(item.event) for item in _global_warnings)
|
78
135
|
|
79
136
|
|
80
137
|
def get_deprecation_messages() -> tuple[dict[str, _t.Any], ...]:
|
81
138
|
"""Return a tuple of deprecation warning messages accumulated over this run."""
|
82
|
-
#
|
83
|
-
return tuple(
|
139
|
+
# DTFIX7: add future deprecation comment
|
140
|
+
return tuple(_event_utils.deprecation_as_dict(item) for item in _global_deprecations)
|
84
141
|
|
85
142
|
|
86
143
|
def get_warnings() -> list[_messages.WarningSummary]:
|
@@ -94,7 +151,7 @@ def get_deprecations() -> list[_messages.DeprecationSummary]:
|
|
94
151
|
|
95
152
|
|
96
153
|
_global_warnings: dict[_messages.WarningSummary, object] = {}
|
97
|
-
"""Global, ordered, de-
|
154
|
+
"""Global, ordered, de-duplicated storage of accumulated warnings for the current module run."""
|
98
155
|
|
99
156
|
_global_deprecations: dict[_messages.DeprecationSummary, object] = {}
|
100
|
-
"""Global, ordered, de-
|
157
|
+
"""Global, ordered, de-duplicated storage of accumulated deprecations for the current module run."""
|
@@ -24,7 +24,7 @@ except ImportError:
|
|
24
24
|
else:
|
25
25
|
HAS_YAML = True
|
26
26
|
|
27
|
-
# DTFIX-
|
27
|
+
# DTFIX-FUTURE: refactor this to share the implementation with the controller version
|
28
28
|
# use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
|
29
29
|
# then tests can be consolidated intead of having two nearly identical copies
|
30
30
|
|
ansible/module_utils/datatag.py
CHANGED
@@ -3,13 +3,15 @@ from __future__ import annotations as _annotations
|
|
3
3
|
|
4
4
|
import typing as _t
|
5
5
|
|
6
|
-
from ._internal import _datatag, _deprecator
|
6
|
+
from ._internal import _datatag, _deprecator, _traceback, _messages
|
7
7
|
from ._internal._datatag import _tags
|
8
|
-
from .common import messages as _messages
|
9
8
|
|
10
9
|
_T = _t.TypeVar('_T')
|
11
10
|
|
12
11
|
|
12
|
+
deprecator_from_collection_name = _deprecator.deprecator_from_collection_name
|
13
|
+
|
14
|
+
|
13
15
|
def deprecate_value(
|
14
16
|
value: _T,
|
15
17
|
msg: str,
|
@@ -36,6 +38,7 @@ def deprecate_value(
|
|
36
38
|
date=date,
|
37
39
|
version=version,
|
38
40
|
deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
|
41
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED_VALUE),
|
39
42
|
)
|
40
43
|
|
41
44
|
return deprecated.tag(value)
|
@@ -310,9 +310,22 @@ class DistributionFiles:
|
|
310
310
|
suse_facts['distribution_release'] = release.group(1)
|
311
311
|
suse_facts['distribution_version'] = collected_facts['distribution_version'] + '.' + release.group(1)
|
312
312
|
|
313
|
-
#
|
314
|
-
|
315
|
-
|
313
|
+
# Check VARIANT_ID first for SLES4SAP or SL-Micro
|
314
|
+
variant_id_match = re.search(r'^VARIANT_ID="?([^"\n]*)"?', data, re.MULTILINE)
|
315
|
+
if variant_id_match:
|
316
|
+
variant_id = variant_id_match.group(1)
|
317
|
+
if variant_id in ('server-sap', 'sles-sap'):
|
318
|
+
suse_facts['distribution'] = 'SLES_SAP'
|
319
|
+
elif variant_id == 'transactional':
|
320
|
+
suse_facts['distribution'] = 'SL-Micro'
|
321
|
+
else:
|
322
|
+
# Fallback for older SLES 15 using baseproduct symlink
|
323
|
+
if os.path.islink('/etc/products.d/baseproduct'):
|
324
|
+
resolved = os.path.realpath('/etc/products.d/baseproduct')
|
325
|
+
if resolved.endswith('SLES_SAP.prod'):
|
326
|
+
suse_facts['distribution'] = 'SLES_SAP'
|
327
|
+
elif resolved.endswith('SL-Micro.prod'):
|
328
|
+
suse_facts['distribution'] = 'SL-Micro'
|
316
329
|
|
317
330
|
return True, suse_facts
|
318
331
|
|
@@ -201,7 +201,7 @@ class LinuxVirtual(Virtual):
|
|
201
201
|
virtual_facts['virtualization_type'] = 'virtualbox'
|
202
202
|
found_virt = True
|
203
203
|
|
204
|
-
if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner'):
|
204
|
+
if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner', 'Linode'):
|
205
205
|
guest_tech.add('kvm')
|
206
206
|
if not found_virt:
|
207
207
|
virtual_facts['virtualization_type'] = 'kvm'
|
ansible/module_utils/service.py
CHANGED
@@ -36,7 +36,7 @@ import select
|
|
36
36
|
import shlex
|
37
37
|
import subprocess
|
38
38
|
|
39
|
-
from ansible.module_utils.six import
|
39
|
+
from ansible.module_utils.six import b
|
40
40
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
41
41
|
|
42
42
|
|
@@ -187,12 +187,8 @@ def daemonize(module, cmd):
|
|
187
187
|
if pid == 0:
|
188
188
|
os.close(pipe[0])
|
189
189
|
|
190
|
-
# if command is string deal with py2 vs py3 conversions for shlex
|
191
190
|
if not isinstance(cmd, list):
|
192
|
-
|
193
|
-
cmd = shlex.split(to_bytes(cmd, errors=errors))
|
194
|
-
else:
|
195
|
-
cmd = shlex.split(to_text(cmd, errors=errors))
|
191
|
+
cmd = shlex.split(to_text(cmd, errors=errors))
|
196
192
|
|
197
193
|
# make sure we always use byte strings
|
198
194
|
run_cmd = []
|
@@ -247,9 +243,6 @@ def daemonize(module, cmd):
|
|
247
243
|
break
|
248
244
|
return_data += to_bytes(data, errors=errors)
|
249
245
|
|
250
|
-
# Note: no need to specify encoding on py3 as this module sends the
|
251
|
-
# pickle to itself (thus same python interpreter so we aren't mixing
|
252
|
-
# py2 and py3)
|
253
246
|
return pickle.loads(to_bytes(return_data, errors=errors))
|
254
247
|
|
255
248
|
|
@@ -88,8 +88,8 @@ options:
|
|
88
88
|
description:
|
89
89
|
- Whether to automatically try to install the Python apt library or not, if it is not already installed.
|
90
90
|
Without this library, the module does not work.
|
91
|
-
- Runs C(apt-get install
|
92
|
-
- Only works with the system Python
|
91
|
+
- Runs C(apt-get install python3-apt).
|
92
|
+
- Only works with the system Python. If you are using a Python on the remote that is not
|
93
93
|
the system Python, set O(install_python_apt=false) and ensure that the Python apt library
|
94
94
|
for your Python version is installed some other way.
|
95
95
|
type: bool
|
@@ -98,8 +98,7 @@ author:
|
|
98
98
|
- Alexander Saltanov (@sashka)
|
99
99
|
version_added: "0.7"
|
100
100
|
requirements:
|
101
|
-
-
|
102
|
-
- python3-apt (python 3)
|
101
|
+
- python3-apt
|
103
102
|
- apt-key or gpg
|
104
103
|
"""
|
105
104
|
|
@@ -232,14 +231,15 @@ class SourcesList(object):
|
|
232
231
|
self.files_mapping = {} # internal DS for tracking symlinks
|
233
232
|
# Repositories that we're adding -- used to implement mode param
|
234
233
|
self.new_repos = set()
|
235
|
-
self.default_file =
|
234
|
+
self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist')
|
236
235
|
|
237
236
|
# read sources.list if it exists
|
238
237
|
if os.path.isfile(self.default_file):
|
239
238
|
self.load(self.default_file)
|
240
239
|
|
241
240
|
# read sources.list.d
|
242
|
-
|
241
|
+
self.sources_dir = apt_pkg.config.find_dir('Dir::Etc::sourceparts')
|
242
|
+
for file in glob.iglob(f'{self.sources_dir}/*.list'):
|
243
243
|
if os.path.islink(file):
|
244
244
|
self.files_mapping[file] = os.readlink(file)
|
245
245
|
self.load(file)
|
@@ -255,7 +255,7 @@ class SourcesList(object):
|
|
255
255
|
if '/' in filename:
|
256
256
|
return filename
|
257
257
|
else:
|
258
|
-
return os.path.abspath(os.path.join(self.
|
258
|
+
return os.path.abspath(os.path.join(self.sources_dir, filename))
|
259
259
|
|
260
260
|
def _suggest_filename(self, line):
|
261
261
|
def _cleanup_filename(s):
|
@@ -313,28 +313,6 @@ class SourcesList(object):
|
|
313
313
|
|
314
314
|
return valid, enabled, source, comment
|
315
315
|
|
316
|
-
@staticmethod
|
317
|
-
def _apt_cfg_file(filespec):
|
318
|
-
"""
|
319
|
-
Wrapper for `apt_pkg` module for running with Python 2.5
|
320
|
-
"""
|
321
|
-
try:
|
322
|
-
result = apt_pkg.config.find_file(filespec)
|
323
|
-
except AttributeError:
|
324
|
-
result = apt_pkg.Config.FindFile(filespec)
|
325
|
-
return result
|
326
|
-
|
327
|
-
@staticmethod
|
328
|
-
def _apt_cfg_dir(dirspec):
|
329
|
-
"""
|
330
|
-
Wrapper for `apt_pkg` module for running with Python 2.5
|
331
|
-
"""
|
332
|
-
try:
|
333
|
-
result = apt_pkg.config.find_dir(dirspec)
|
334
|
-
except AttributeError:
|
335
|
-
result = apt_pkg.Config.FindDir(dirspec)
|
336
|
-
return result
|
337
|
-
|
338
316
|
def load(self, file):
|
339
317
|
group = []
|
340
318
|
f = open(file, 'r')
|
ansible/modules/async_status.py
CHANGED
@@ -28,6 +28,8 @@ options:
|
|
28
28
|
type: str
|
29
29
|
choices: [ cleanup, status ]
|
30
30
|
default: status
|
31
|
+
notes:
|
32
|
+
- The RV(started) and RV(finished) return values were updated to return V(True) or V(False) instead of V(1) or V(0) in ansible-core 2.19.
|
31
33
|
extends_documentation_fragment:
|
32
34
|
- action_common_attributes
|
33
35
|
- action_common_attributes.flow
|
@@ -85,15 +87,15 @@ ansible_job_id:
|
|
85
87
|
type: str
|
86
88
|
sample: '360874038559.4169'
|
87
89
|
finished:
|
88
|
-
description: Whether the asynchronous job has finished
|
90
|
+
description: Whether the asynchronous job has finished or not
|
89
91
|
returned: always
|
90
|
-
type:
|
91
|
-
sample:
|
92
|
+
type: bool
|
93
|
+
sample: true
|
92
94
|
started:
|
93
|
-
description: Whether the asynchronous job has started
|
95
|
+
description: Whether the asynchronous job has started or not
|
94
96
|
returned: always
|
95
|
-
type:
|
96
|
-
sample:
|
97
|
+
type: bool
|
98
|
+
sample: true
|
97
99
|
stdout:
|
98
100
|
description: Any output returned by async_wrapper
|
99
101
|
returned: always
|
@@ -134,7 +136,7 @@ def main():
|
|
134
136
|
log_path = os.path.join(async_dir, jid)
|
135
137
|
|
136
138
|
if not os.path.exists(log_path):
|
137
|
-
module.fail_json(msg="could not find job", ansible_job_id=jid, started=
|
139
|
+
module.fail_json(msg="could not find job", ansible_job_id=jid, started=True, finished=True)
|
138
140
|
|
139
141
|
if mode == 'cleanup':
|
140
142
|
os.unlink(log_path)
|
@@ -151,16 +153,16 @@ def main():
|
|
151
153
|
except Exception:
|
152
154
|
if not data:
|
153
155
|
# file not written yet? That means it is running
|
154
|
-
module.exit_json(results_file=log_path, ansible_job_id=jid, started=
|
156
|
+
module.exit_json(results_file=log_path, ansible_job_id=jid, started=True, finished=False)
|
155
157
|
else:
|
156
158
|
module.fail_json(ansible_job_id=jid, results_file=log_path,
|
157
|
-
msg="Could not parse job output: %s" % data, started=
|
159
|
+
msg="Could not parse job output: %s" % data, started=True, finished=True)
|
158
160
|
|
159
161
|
if 'started' not in data:
|
160
|
-
data['finished'] =
|
162
|
+
data['finished'] = True
|
161
163
|
data['ansible_job_id'] = jid
|
162
164
|
elif 'finished' not in data:
|
163
|
-
data['finished'] =
|
165
|
+
data['finished'] = False
|
164
166
|
|
165
167
|
# just write the module output directly to stdout and exit; bypass other processing done by exit_json since it's already been done
|
166
168
|
print(f"\n{json.dumps(data)}") # pylint: disable=ansible-bad-function
|