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/utils/display.py
CHANGED
@@ -18,7 +18,6 @@
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
20
|
import dataclasses
|
21
|
-
import datetime
|
22
21
|
|
23
22
|
try:
|
24
23
|
import curses
|
@@ -50,9 +49,10 @@ from functools import wraps
|
|
50
49
|
from struct import unpack, pack
|
51
50
|
|
52
51
|
from ansible import constants as C
|
52
|
+
from ansible.constants import config
|
53
53
|
from ansible.errors import AnsibleAssertionError, AnsiblePromptInterrupt, AnsiblePromptNoninteractive, AnsibleError
|
54
54
|
from ansible._internal._errors import _utils
|
55
|
-
from ansible.module_utils._internal import _ambient_context,
|
55
|
+
from ansible.module_utils._internal import _ambient_context, _deprecator
|
56
56
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
57
57
|
from ansible._internal._datatag._tags import TrustedAsTemplate
|
58
58
|
from ansible.module_utils.common.messages import ErrorSummary, WarningSummary, DeprecationSummary, Detail, SummaryBase, PluginInfo
|
@@ -76,8 +76,6 @@ _LIBC.wcswidth.argtypes = (ctypes.c_wchar_p, ctypes.c_int)
|
|
76
76
|
# Max for c_int
|
77
77
|
_MAX_INT = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1
|
78
78
|
|
79
|
-
_UNSET = t.cast(t.Any, ...)
|
80
|
-
|
81
79
|
MOVE_TO_BOL = b'\r'
|
82
80
|
CLEAR_TO_EOL = b'\x1b[K'
|
83
81
|
|
@@ -555,7 +553,7 @@ class Display(metaclass=Singleton):
|
|
555
553
|
msg: str,
|
556
554
|
version: str | None = None,
|
557
555
|
removed: bool = False,
|
558
|
-
date: str |
|
556
|
+
date: str | None = None,
|
559
557
|
collection_name: str | None = None,
|
560
558
|
) -> str:
|
561
559
|
"""Return a deprecation message and help text for non-display purposes (e.g., exception messages)."""
|
@@ -570,7 +568,7 @@ class Display(metaclass=Singleton):
|
|
570
568
|
version=version,
|
571
569
|
removed=removed,
|
572
570
|
date=date,
|
573
|
-
|
571
|
+
deprecator=PluginInfo._from_collection_name(collection_name),
|
574
572
|
)
|
575
573
|
|
576
574
|
if removed:
|
@@ -582,57 +580,63 @@ class Display(metaclass=Singleton):
|
|
582
580
|
|
583
581
|
def _get_deprecation_message_with_plugin_info(
|
584
582
|
self,
|
583
|
+
*,
|
585
584
|
msg: str,
|
586
|
-
version: str | None
|
585
|
+
version: str | None,
|
587
586
|
removed: bool = False,
|
588
|
-
date: str |
|
589
|
-
|
587
|
+
date: str | None,
|
588
|
+
deprecator: PluginInfo | None,
|
590
589
|
) -> str:
|
591
590
|
"""Internal use only. Return a deprecation message and help text for display."""
|
592
|
-
|
593
|
-
|
594
|
-
if msg and msg[-1] not in ['!', '?', '.']:
|
595
|
-
msg += '.'
|
591
|
+
# DTFIX-RELEASE: the logic for omitting date/version doesn't apply to the payload, so it shows up in vars in some cases when it should not
|
596
592
|
|
597
593
|
if removed:
|
598
594
|
removal_fragment = 'This feature was removed'
|
599
|
-
help_text = 'Please update your playbooks.'
|
600
595
|
else:
|
601
596
|
removal_fragment = 'This feature will be removed'
|
602
|
-
help_text = ''
|
603
597
|
|
604
|
-
if
|
605
|
-
|
598
|
+
if not deprecator or deprecator.type == _deprecator.INDETERMINATE_DEPRECATOR.type:
|
599
|
+
collection = None
|
600
|
+
plugin_fragment = ''
|
601
|
+
elif deprecator.type == _deprecator.PluginInfo._COLLECTION_ONLY_TYPE:
|
602
|
+
collection = deprecator.resolved_name
|
603
|
+
plugin_fragment = ''
|
606
604
|
else:
|
607
|
-
|
605
|
+
parts = deprecator.resolved_name.split('.')
|
606
|
+
plugin_name = parts[-1]
|
607
|
+
# DTFIX-RELEASE: normalize 'modules' -> 'module' before storing it so we can eliminate the normalization here
|
608
|
+
plugin_type = "module" if deprecator.type in ("module", "modules") else f'{deprecator.type} plugin'
|
608
609
|
|
609
|
-
|
610
|
-
|
611
|
-
elif version:
|
612
|
-
when = 'in version {0}.'.format(version)
|
613
|
-
else:
|
614
|
-
when = 'in a future release.'
|
610
|
+
collection = '.'.join(parts[:2]) if len(parts) > 2 else None
|
611
|
+
plugin_fragment = f'{plugin_type} {plugin_name!r}'
|
615
612
|
|
616
|
-
|
613
|
+
if collection and plugin_fragment:
|
614
|
+
plugin_fragment += ' in'
|
617
615
|
|
618
|
-
|
616
|
+
if collection == 'ansible.builtin':
|
617
|
+
collection_fragment = 'ansible-core'
|
618
|
+
elif collection:
|
619
|
+
collection_fragment = f'collection {collection!r}'
|
620
|
+
else:
|
621
|
+
collection_fragment = ''
|
619
622
|
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
if plugin_info.type in ("module", "modules"):
|
627
|
-
# DTFIX-RELEASE: pluginloader or AnsiblePlugin needs a "type desc" property that doesn't suffer from legacy "inconsistencies" like this
|
628
|
-
plugin_type = "module"
|
629
|
-
elif plugin_info.type == "collection":
|
630
|
-
# not a real plugin type, but used for tombstone errors generated by plugin loader
|
631
|
-
plugin_type = plugin_info.type
|
623
|
+
if not collection:
|
624
|
+
when_fragment = 'in the future' if not removed else ''
|
625
|
+
elif date:
|
626
|
+
when_fragment = f'in a release after {date}'
|
627
|
+
elif version:
|
628
|
+
when_fragment = f'version {version}'
|
632
629
|
else:
|
633
|
-
|
630
|
+
when_fragment = 'in a future release' if not removed else ''
|
631
|
+
|
632
|
+
if plugin_fragment or collection_fragment:
|
633
|
+
from_fragment = 'from'
|
634
|
+
else:
|
635
|
+
from_fragment = ''
|
636
|
+
|
637
|
+
deprecation_msg = ' '.join(f for f in [removal_fragment, from_fragment, plugin_fragment, collection_fragment, when_fragment] if f) + '.'
|
634
638
|
|
635
|
-
return
|
639
|
+
return _join_sentences(msg, deprecation_msg)
|
636
640
|
|
637
641
|
def _wrap_message(self, msg: str, wrap_text: bool) -> str:
|
638
642
|
if wrap_text and self._wrap_stderr:
|
@@ -661,20 +665,24 @@ class Display(metaclass=Singleton):
|
|
661
665
|
msg: str,
|
662
666
|
version: str | None = None,
|
663
667
|
removed: bool = False,
|
664
|
-
date: str |
|
665
|
-
collection_name: str | None =
|
668
|
+
date: str | None = None,
|
669
|
+
collection_name: str | None = None,
|
666
670
|
*,
|
671
|
+
deprecator: PluginInfo | None = None,
|
667
672
|
help_text: str | None = None,
|
668
673
|
obj: t.Any = None,
|
669
674
|
) -> None:
|
670
|
-
"""
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
+
"""
|
676
|
+
Display a deprecation warning message, if enabled.
|
677
|
+
Most callers do not need to provide `collection_name` or `deprecator` -- but provide only one if needed.
|
678
|
+
Specify `version` or `date`, but not both.
|
679
|
+
If `date` is a string, it must be in the form `YYYY-MM-DD`.
|
680
|
+
"""
|
675
681
|
# DTFIX-RELEASE: are there any deprecation calls where the feature is switching from enabled to disabled, rather than being removed entirely?
|
676
682
|
# DTFIX-RELEASE: are there deprecated features which should going through deferred deprecation instead?
|
677
683
|
|
684
|
+
_skip_stackwalk = True
|
685
|
+
|
678
686
|
self._deprecated_with_plugin_info(
|
679
687
|
msg=msg,
|
680
688
|
version=version,
|
@@ -682,37 +690,36 @@ class Display(metaclass=Singleton):
|
|
682
690
|
date=date,
|
683
691
|
help_text=help_text,
|
684
692
|
obj=obj,
|
685
|
-
|
693
|
+
deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
|
686
694
|
)
|
687
695
|
|
688
696
|
def _deprecated_with_plugin_info(
|
689
697
|
self,
|
698
|
+
*,
|
690
699
|
msg: str,
|
691
|
-
version: str | None
|
700
|
+
version: str | None,
|
692
701
|
removed: bool = False,
|
693
|
-
date: str |
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
plugin: PluginInfo | None = None,
|
702
|
+
date: str | None,
|
703
|
+
help_text: str | None,
|
704
|
+
obj: t.Any,
|
705
|
+
deprecator: PluginInfo | None,
|
698
706
|
) -> None:
|
699
707
|
"""
|
700
708
|
This is the internal pre-proxy half of the `deprecated` implementation.
|
701
709
|
Any logic that must occur on workers needs to be implemented here.
|
702
710
|
"""
|
711
|
+
_skip_stackwalk = True
|
712
|
+
|
703
713
|
if removed:
|
704
|
-
|
714
|
+
formatted_msg = self._get_deprecation_message_with_plugin_info(
|
705
715
|
msg=msg,
|
706
716
|
version=version,
|
707
717
|
removed=removed,
|
708
718
|
date=date,
|
709
|
-
|
710
|
-
)
|
711
|
-
|
712
|
-
if not _DeferredWarningContext.deprecation_warnings_enabled():
|
713
|
-
return
|
719
|
+
deprecator=deprecator,
|
720
|
+
)
|
714
721
|
|
715
|
-
|
722
|
+
raise AnsibleError(formatted_msg)
|
716
723
|
|
717
724
|
if source_context := _utils.SourceContext.from_value(obj):
|
718
725
|
formatted_source_context = str(source_context)
|
@@ -728,8 +735,8 @@ class Display(metaclass=Singleton):
|
|
728
735
|
),
|
729
736
|
),
|
730
737
|
version=version,
|
731
|
-
date=
|
732
|
-
|
738
|
+
date=date,
|
739
|
+
deprecator=deprecator,
|
733
740
|
formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
|
734
741
|
)
|
735
742
|
|
@@ -746,6 +753,11 @@ class Display(metaclass=Singleton):
|
|
746
753
|
# This is the post-proxy half of the `deprecated` implementation.
|
747
754
|
# Any logic that must occur in the primary controller process needs to be implemented here.
|
748
755
|
|
756
|
+
if not _DeferredWarningContext.deprecation_warnings_enabled():
|
757
|
+
return
|
758
|
+
|
759
|
+
self.warning('Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg.')
|
760
|
+
|
749
761
|
msg = format_message(warning)
|
750
762
|
msg = f'[DEPRECATION WARNING]: {msg}'
|
751
763
|
|
@@ -983,8 +995,8 @@ class Display(metaclass=Singleton):
|
|
983
995
|
msg: str,
|
984
996
|
private: bool = False,
|
985
997
|
seconds: int | None = None,
|
986
|
-
interrupt_input: c.
|
987
|
-
complete_input: c.
|
998
|
+
interrupt_input: c.Iterable[bytes] | None = None,
|
999
|
+
complete_input: c.Iterable[bytes] | None = None,
|
988
1000
|
) -> bytes:
|
989
1001
|
if self._final_q:
|
990
1002
|
from ansible.executor.process.worker import current_worker
|
@@ -1039,8 +1051,8 @@ class Display(metaclass=Singleton):
|
|
1039
1051
|
self,
|
1040
1052
|
echo: bool = False,
|
1041
1053
|
seconds: int | None = None,
|
1042
|
-
interrupt_input: c.
|
1043
|
-
complete_input: c.
|
1054
|
+
interrupt_input: c.Iterable[bytes] | None = None,
|
1055
|
+
complete_input: c.Iterable[bytes] | None = None,
|
1044
1056
|
) -> bytes:
|
1045
1057
|
if self._final_q:
|
1046
1058
|
raise NotImplementedError
|
@@ -1225,20 +1237,70 @@ def _get_message_lines(message: str, help_text: str | None, formatted_source_con
|
|
1225
1237
|
return message_lines
|
1226
1238
|
|
1227
1239
|
|
1240
|
+
def _join_sentences(first: str | None, second: str | None) -> str:
|
1241
|
+
"""Join two sentences together."""
|
1242
|
+
first = (first or '').strip()
|
1243
|
+
second = (second or '').strip()
|
1244
|
+
|
1245
|
+
if first and first[-1] not in ('!', '?', '.'):
|
1246
|
+
first += '.'
|
1247
|
+
|
1248
|
+
if second and second[-1] not in ('!', '?', '.'):
|
1249
|
+
second += '.'
|
1250
|
+
|
1251
|
+
if first and not second:
|
1252
|
+
return first
|
1253
|
+
|
1254
|
+
if not first and second:
|
1255
|
+
return second
|
1256
|
+
|
1257
|
+
return ' '.join((first, second))
|
1258
|
+
|
1259
|
+
|
1228
1260
|
def format_message(summary: SummaryBase) -> str:
|
1229
|
-
details:
|
1261
|
+
details: c.Sequence[Detail] = summary.details
|
1262
|
+
|
1263
|
+
if isinstance(summary, DeprecationSummary) and details:
|
1264
|
+
# augment the first detail element for deprecations to include additional diagnostic info and help text
|
1265
|
+
detail_list = list(details)
|
1266
|
+
detail = detail_list[0]
|
1267
|
+
|
1268
|
+
deprecation_msg = _display._get_deprecation_message_with_plugin_info(
|
1269
|
+
msg=detail.msg,
|
1270
|
+
version=summary.version,
|
1271
|
+
date=summary.date,
|
1272
|
+
deprecator=summary.deprecator,
|
1273
|
+
)
|
1230
1274
|
|
1231
|
-
|
1232
|
-
details = [detail if idx else dataclasses.replace(
|
1275
|
+
detail_list[0] = dataclasses.replace(
|
1233
1276
|
detail,
|
1234
|
-
msg=
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
),
|
1240
|
-
) for idx, detail in enumerate(summary.details)]
|
1241
|
-
else:
|
1242
|
-
details = summary.details
|
1277
|
+
msg=deprecation_msg,
|
1278
|
+
help_text=detail.help_text,
|
1279
|
+
)
|
1280
|
+
|
1281
|
+
details = detail_list
|
1243
1282
|
|
1244
1283
|
return _format_error_details(details, summary.formatted_traceback)
|
1284
|
+
|
1285
|
+
|
1286
|
+
def _report_config_warnings(deprecator: PluginInfo) -> None:
|
1287
|
+
"""Called by config to report warnings/deprecations collected during a config parse."""
|
1288
|
+
while config.WARNINGS:
|
1289
|
+
warn = config.WARNINGS.pop()
|
1290
|
+
_display.warning(warn)
|
1291
|
+
|
1292
|
+
while config.DEPRECATED:
|
1293
|
+
# tuple with name and options
|
1294
|
+
dep = config.DEPRECATED.pop(0)
|
1295
|
+
msg = config.get_deprecated_msg_from_config(dep[1]).replace("\t", "")
|
1296
|
+
|
1297
|
+
_display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name,ansible-invalid-deprecated-version
|
1298
|
+
msg=f"{dep[0]} option. {msg}",
|
1299
|
+
version=dep[1]['version'],
|
1300
|
+
deprecator=deprecator,
|
1301
|
+
)
|
1302
|
+
|
1303
|
+
|
1304
|
+
# emit any warnings or deprecations
|
1305
|
+
# in the event config fails before display is up, we'll lose warnings -- but that's OK, since everything is broken anyway
|
1306
|
+
_report_config_warnings(_deprecator.ANSIBLE_CORE_DEPRECATOR)
|
ansible/utils/py3compat.py
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
|
7
7
|
from __future__ import annotations
|
8
8
|
|
9
|
-
import inspect
|
10
9
|
import os
|
11
10
|
|
12
11
|
from ansible.utils.display import Display
|
@@ -19,13 +18,8 @@ def __getattr__(name):
|
|
19
18
|
if name != 'environ':
|
20
19
|
raise AttributeError(name)
|
21
20
|
|
22
|
-
caller = inspect.stack()[1]
|
23
|
-
|
24
21
|
display.deprecated(
|
25
|
-
|
26
|
-
'ansible.utils.py3compat.environ is deprecated in favor of os.environ. '
|
27
|
-
f'Accessed by {caller.filename} line number {caller.lineno}'
|
28
|
-
),
|
22
|
+
msg='ansible.utils.py3compat.environ is deprecated in favor of os.environ.',
|
29
23
|
version='2.20',
|
30
24
|
)
|
31
25
|
|
ansible/utils/ssh_functions.py
CHANGED
@@ -56,7 +56,10 @@ def set_default_transport():
|
|
56
56
|
|
57
57
|
# deal with 'smart' connection .. one time ..
|
58
58
|
if C.DEFAULT_TRANSPORT == 'smart':
|
59
|
-
display.deprecated(
|
59
|
+
display.deprecated(
|
60
|
+
msg="The 'smart' option for connections is deprecated. Set the connection plugin directly instead.",
|
61
|
+
version='2.20',
|
62
|
+
)
|
60
63
|
|
61
64
|
# see if SSH can support ControlPersist if not use paramiko
|
62
65
|
if not check_for_controlpersist('ssh') and paramiko is not None:
|
ansible/utils/vars.py
CHANGED
@@ -27,6 +27,7 @@ from json import dumps
|
|
27
27
|
|
28
28
|
from ansible import constants as C
|
29
29
|
from ansible import context
|
30
|
+
from ansible._internal import _json
|
30
31
|
from ansible.errors import AnsibleError, AnsibleOptionsError
|
31
32
|
from ansible.module_utils.datatag import native_type_name
|
32
33
|
from ansible.module_utils.common.text.converters import to_native, to_text
|
@@ -284,3 +285,25 @@ def validate_variable_name(name: object) -> None:
|
|
284
285
|
help_text='Variable names must be strings starting with a letter or underscore character, and contain only letters, numbers and underscores.',
|
285
286
|
obj=name,
|
286
287
|
)
|
288
|
+
|
289
|
+
|
290
|
+
def transform_to_native_types(
|
291
|
+
value: object,
|
292
|
+
redact: bool = True,
|
293
|
+
) -> object:
|
294
|
+
"""
|
295
|
+
Recursively transform the given value to Python native types.
|
296
|
+
Potentially sensitive values such as individually vaulted variables will be redacted unless ``redact=False`` is passed.
|
297
|
+
Which values are considered potentially sensitive may change in future releases.
|
298
|
+
Types which cannot be converted to Python native types will result in an error.
|
299
|
+
"""
|
300
|
+
avv = _json.AnsibleVariableVisitor(
|
301
|
+
convert_mapping_to_dict=True,
|
302
|
+
convert_sequence_to_list=True,
|
303
|
+
convert_custom_scalars=True,
|
304
|
+
convert_to_native_values=True,
|
305
|
+
apply_transforms=True,
|
306
|
+
encrypted_string_behavior=_json.EncryptedStringBehavior.REDACT if redact else _json.EncryptedStringBehavior.DECRYPT,
|
307
|
+
)
|
308
|
+
|
309
|
+
return avv.visit(value)
|
ansible/vars/clean.py
CHANGED
@@ -42,7 +42,7 @@ def module_response_deepcopy(v):
|
|
42
42
|
backwards compatibility, in case we need to extend this function
|
43
43
|
to handle our specific needs:
|
44
44
|
|
45
|
-
* ``ansible.executor.task_result.
|
45
|
+
* ``ansible.executor.task_result._RawTaskResult.as_callback_task_result``
|
46
46
|
* ``ansible.vars.clean.clean_facts``
|
47
47
|
* ``ansible.vars.namespace_facts``
|
48
48
|
"""
|
ansible/vars/manager.py
CHANGED
@@ -25,6 +25,8 @@ from collections import defaultdict
|
|
25
25
|
from collections.abc import Mapping, MutableMapping
|
26
26
|
|
27
27
|
from ansible import constants as C
|
28
|
+
from ansible.module_utils._internal import _deprecator
|
29
|
+
from ansible.module_utils._internal._datatag import _tags
|
28
30
|
from ansible.errors import (AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleFileNotFound,
|
29
31
|
AnsibleAssertionError, AnsibleValueOmittedError)
|
30
32
|
from ansible.inventory.host import Host
|
@@ -32,7 +34,6 @@ from ansible.inventory.helpers import sort_groups, get_group_vars
|
|
32
34
|
from ansible.inventory.manager import InventoryManager
|
33
35
|
from ansible.module_utils.datatag import native_type_name
|
34
36
|
from ansible.module_utils.six import text_type
|
35
|
-
from ansible.module_utils.datatag import deprecate_value
|
36
37
|
from ansible.parsing.dataloader import DataLoader
|
37
38
|
from ansible._internal._templating._engine import TemplateEngine
|
38
39
|
from ansible.plugins.loader import cache_loader
|
@@ -50,8 +51,12 @@ if t.TYPE_CHECKING:
|
|
50
51
|
display = Display()
|
51
52
|
|
52
53
|
# deprecated: description='enable top-level facts deprecation' core_version='2.20'
|
53
|
-
#
|
54
|
-
#
|
54
|
+
# _DEPRECATE_TOP_LEVEL_FACT_TAG = _tags.Deprecated(
|
55
|
+
# msg='Top-level facts are deprecated.',
|
56
|
+
# version='2.24',
|
57
|
+
# deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR,
|
58
|
+
# help_text='Use `ansible_facts` instead.',
|
59
|
+
# )
|
55
60
|
|
56
61
|
|
57
62
|
def _deprecate_top_level_fact(value: t.Any) -> t.Any:
|
@@ -61,7 +66,7 @@ def _deprecate_top_level_fact(value: t.Any) -> t.Any:
|
|
61
66
|
Unique tag instances are required to achieve the correct de-duplication within a top-level templating operation.
|
62
67
|
"""
|
63
68
|
# deprecated: description='enable top-level facts deprecation' core_version='2.20'
|
64
|
-
# return
|
69
|
+
# return _DEPRECATE_TOP_LEVEL_FACT_TAG.tag(value)
|
65
70
|
return value
|
66
71
|
|
67
72
|
|
@@ -96,6 +101,13 @@ class VariableManager:
|
|
96
101
|
_ALLOWED = frozenset(['plugins_by_group', 'groups_plugins_play', 'groups_plugins_inventory', 'groups_inventory',
|
97
102
|
'all_plugins_play', 'all_plugins_inventory', 'all_inventory'])
|
98
103
|
|
104
|
+
_PLAY_HOSTS_DEPRECATED_TAG = _tags.Deprecated(
|
105
|
+
msg='The `play_hosts` magic variable is deprecated.',
|
106
|
+
version='2.23',
|
107
|
+
deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR,
|
108
|
+
help_text='Use `ansible_play_batch` instead.',
|
109
|
+
)
|
110
|
+
|
99
111
|
def __init__(self, loader: DataLoader | None = None, inventory: InventoryManager | None = None, version_info: dict[str, str] | None = None) -> None:
|
100
112
|
self._nonpersistent_fact_cache: defaultdict[str, dict] = defaultdict(dict)
|
101
113
|
self._vars_cache: defaultdict[str, dict] = defaultdict(dict)
|
@@ -214,24 +226,6 @@ class VariableManager:
|
|
214
226
|
all_group = self._inventory.groups.get('all')
|
215
227
|
host_groups = sort_groups([g for g in host.get_groups() if g.name != 'all'])
|
216
228
|
|
217
|
-
def _get_plugin_vars(plugin, path, entities):
|
218
|
-
data = {}
|
219
|
-
try:
|
220
|
-
data = plugin.get_vars(self._loader, path, entities)
|
221
|
-
except AttributeError:
|
222
|
-
try:
|
223
|
-
for entity in entities:
|
224
|
-
if isinstance(entity, Host):
|
225
|
-
data |= plugin.get_host_vars(entity.name)
|
226
|
-
else:
|
227
|
-
data |= plugin.get_group_vars(entity.name)
|
228
|
-
except AttributeError:
|
229
|
-
if hasattr(plugin, 'run'):
|
230
|
-
raise AnsibleError("Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
|
231
|
-
else:
|
232
|
-
raise AnsibleError("Invalid vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
|
233
|
-
return data
|
234
|
-
|
235
229
|
# internal functions that actually do the work
|
236
230
|
def _plugins_inventory(entities):
|
237
231
|
""" merges all entities by inventory source """
|
@@ -495,11 +489,8 @@ class VariableManager:
|
|
495
489
|
variables['ansible_play_hosts'] = [x for x in variables['ansible_play_hosts_all'] if x not in play._removed_hosts]
|
496
490
|
variables['ansible_play_batch'] = [x for x in _hosts if x not in play._removed_hosts]
|
497
491
|
|
498
|
-
|
499
|
-
|
500
|
-
msg='Use `ansible_play_batch` instead of `play_hosts`.',
|
501
|
-
removal_version='2.23',
|
502
|
-
)
|
492
|
+
# use a static tag instead of `deprecate_value` to avoid stackwalk in a hot code path
|
493
|
+
variables['play_hosts'] = self._PLAY_HOSTS_DEPRECATED_TAG.tag(variables['ansible_play_batch'])
|
503
494
|
|
504
495
|
# Set options vars
|
505
496
|
for option, option_value in self._options_vars.items():
|
ansible/vars/plugins.py
CHANGED
@@ -34,10 +34,10 @@ def get_plugin_vars(loader, plugin, path, entities):
|
|
34
34
|
except AttributeError:
|
35
35
|
if hasattr(plugin, 'get_host_vars') or hasattr(plugin, 'get_group_vars'):
|
36
36
|
display.deprecated(
|
37
|
-
f"The vars plugin {plugin.ansible_name} from {plugin._original_path} is relying "
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
msg=f"The vars plugin {plugin.ansible_name} from {plugin._original_path} is relying "
|
38
|
+
"on the deprecated entrypoints 'get_host_vars' and 'get_group_vars'. "
|
39
|
+
"This plugin should be updated to inherit from BaseVarsPlugin and define "
|
40
|
+
"a 'get_vars' method as the main entrypoint instead.",
|
41
41
|
version="2.20",
|
42
42
|
)
|
43
43
|
try:
|