ansible-core 2.19.2__py3-none-any.whl → 2.20.0b1__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.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/_internal/__init__.py +1 -4
- ansible/_internal/_ansiballz/_builder.py +1 -3
- ansible/_internal/_collection_proxy.py +7 -9
- ansible/_internal/_display_utils.py +145 -0
- ansible/_internal/_json/__init__.py +3 -4
- ansible/_internal/_templating/_engine.py +1 -1
- ansible/_internal/_templating/_jinja_plugins.py +1 -2
- ansible/_internal/_wrapt.py +105 -301
- ansible/cli/__init__.py +11 -10
- ansible/cli/adhoc.py +1 -2
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/config.py +5 -6
- ansible/cli/doc.py +67 -67
- ansible/cli/galaxy.py +15 -24
- ansible/cli/inventory.py +0 -1
- ansible/cli/playbook.py +0 -1
- ansible/cli/pull.py +0 -1
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
- ansible/config/base.yml +1 -25
- ansible/config/manager.py +0 -2
- ansible/executor/play_iterator.py +42 -20
- ansible/executor/playbook_executor.py +0 -9
- ansible/executor/powershell/async_watchdog.ps1 +24 -4
- ansible/executor/task_executor.py +32 -22
- ansible/executor/task_queue_manager.py +1 -3
- ansible/galaxy/api.py +33 -80
- ansible/galaxy/collection/__init__.py +4 -17
- ansible/galaxy/dependency_resolution/dataclasses.py +0 -10
- ansible/galaxy/dependency_resolution/providers.py +1 -2
- ansible/galaxy/role.py +1 -33
- ansible/inventory/manager.py +2 -3
- ansible/keyword_desc.yml +0 -3
- ansible/module_utils/_internal/_datatag/__init__.py +2 -10
- ansible/module_utils/_internal/_no_six.py +86 -0
- ansible/module_utils/_text.py +28 -8
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/basic.py +27 -24
- ansible/module_utils/common/_collections_compat.py +11 -2
- ansible/module_utils/common/collections.py +8 -3
- ansible/module_utils/common/dict_transformations.py +1 -2
- ansible/module_utils/common/network.py +4 -2
- ansible/module_utils/common/parameters.py +32 -41
- ansible/module_utils/common/text/converters.py +109 -23
- ansible/module_utils/common/text/formatters.py +6 -2
- ansible/module_utils/common/validation.py +11 -9
- ansible/module_utils/connection.py +8 -3
- ansible/module_utils/facts/hardware/linux.py +23 -7
- ansible/module_utils/facts/hardware/netbsd.py +1 -1
- ansible/module_utils/facts/hardware/sunos.py +2 -1
- ansible/module_utils/facts/packages.py +6 -2
- ansible/module_utils/facts/system/distribution.py +2 -1
- ansible/module_utils/facts/system/env.py +6 -3
- ansible/module_utils/facts/system/local.py +3 -1
- ansible/module_utils/parsing/convert_bool.py +6 -2
- ansible/module_utils/service.py +2 -3
- ansible/module_utils/six/__init__.py +11 -6
- ansible/module_utils/urls.py +6 -2
- ansible/module_utils/yumdnf.py +0 -5
- ansible/modules/apt.py +18 -13
- ansible/modules/apt_repository.py +1 -1
- ansible/modules/assemble.py +5 -9
- ansible/modules/blockinfile.py +39 -23
- ansible/modules/cron.py +26 -35
- ansible/modules/deb822_repository.py +83 -12
- ansible/modules/dnf.py +3 -7
- ansible/modules/dnf5.py +4 -6
- ansible/modules/expect.py +0 -3
- ansible/modules/find.py +1 -2
- ansible/modules/get_url.py +1 -1
- ansible/modules/git.py +4 -5
- ansible/modules/include_vars.py +1 -1
- ansible/modules/lineinfile.py +71 -63
- ansible/modules/package_facts.py +1 -1
- ansible/modules/pip.py +8 -2
- ansible/modules/replace.py +6 -6
- ansible/modules/service.py +3 -4
- ansible/modules/stat.py +20 -0
- ansible/modules/uri.py +9 -10
- ansible/modules/user.py +1 -2
- ansible/modules/wait_for.py +2 -2
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +1 -16
- ansible/parsing/dataloader.py +24 -31
- ansible/parsing/mod_args.py +3 -0
- ansible/parsing/vault/__init__.py +1 -2
- ansible/playbook/base.py +8 -56
- ansible/playbook/block.py +0 -60
- ansible/playbook/collectionsearch.py +1 -2
- ansible/playbook/handler.py +1 -7
- ansible/playbook/helpers.py +0 -7
- ansible/playbook/included_file.py +1 -1
- ansible/playbook/play.py +103 -37
- ansible/playbook/play_context.py +4 -0
- ansible/playbook/role/__init__.py +10 -65
- ansible/playbook/role/definition.py +3 -4
- ansible/playbook/role/include.py +2 -3
- ansible/playbook/role/metadata.py +1 -12
- ansible/playbook/role/requirement.py +1 -2
- ansible/playbook/role_include.py +1 -2
- ansible/playbook/taggable.py +16 -5
- ansible/playbook/task.py +51 -55
- ansible/plugins/action/__init__.py +20 -19
- ansible/plugins/action/add_host.py +1 -2
- ansible/plugins/action/fetch.py +2 -4
- ansible/plugins/action/group_by.py +1 -2
- ansible/plugins/action/include_vars.py +20 -22
- ansible/plugins/action/script.py +1 -3
- ansible/plugins/action/template.py +1 -2
- ansible/plugins/action/uri.py +4 -2
- ansible/plugins/cache/__init__.py +1 -0
- ansible/plugins/callback/__init__.py +13 -6
- ansible/plugins/connection/__init__.py +3 -7
- ansible/plugins/connection/local.py +2 -3
- ansible/plugins/connection/psrp.py +0 -2
- ansible/plugins/connection/ssh.py +2 -7
- ansible/plugins/connection/winrm.py +0 -2
- ansible/plugins/doc_fragments/result_format_callback.py +15 -0
- ansible/plugins/filter/core.py +4 -5
- ansible/plugins/filter/encryption.py +3 -27
- ansible/plugins/filter/mathstuff.py +1 -2
- ansible/plugins/filter/to_nice_yaml.yml +31 -3
- ansible/plugins/filter/to_yaml.yml +29 -12
- ansible/plugins/inventory/__init__.py +1 -2
- ansible/plugins/inventory/script.py +2 -1
- ansible/plugins/inventory/toml.py +3 -6
- ansible/plugins/inventory/yaml.py +1 -2
- ansible/plugins/list.py +10 -3
- ansible/plugins/loader.py +6 -6
- ansible/plugins/lookup/password.py +1 -2
- ansible/plugins/lookup/subelements.py +2 -3
- ansible/plugins/lookup/url.py +1 -1
- ansible/plugins/lookup/varnames.py +1 -2
- ansible/plugins/shell/__init__.py +9 -4
- ansible/plugins/shell/powershell.py +8 -24
- ansible/plugins/strategy/__init__.py +6 -3
- ansible/plugins/test/core.py +4 -1
- ansible/plugins/test/regex.yml +18 -6
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -7
- ansible/utils/collection_loader/_collection_config.py +5 -0
- ansible/utils/collection_loader/_collection_finder.py +11 -14
- ansible/utils/context_objects.py +7 -4
- ansible/utils/display.py +28 -167
- ansible/utils/encrypt.py +0 -5
- ansible/utils/helpers.py +6 -2
- ansible/utils/jsonrpc.py +7 -3
- ansible/utils/plugin_docs.py +49 -38
- ansible/utils/ssh_functions.py +0 -19
- ansible/utils/unsafe_proxy.py +7 -7
- ansible/vars/clean.py +2 -3
- ansible/vars/manager.py +27 -20
- ansible/vars/plugins.py +1 -31
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/METADATA +3 -3
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/RECORD +200 -200
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/network.txt +0 -1
- ansible_test/_data/completion/remote.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_internal/cache.py +2 -5
- ansible_test/_internal/cli/compat.py +1 -1
- ansible_test/_internal/commands/coverage/combine.py +1 -3
- ansible_test/_internal/commands/integration/__init__.py +3 -7
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/coverage.py +1 -3
- ansible_test/_internal/commands/integration/filters.py +5 -10
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
- ansible_test/_internal/commands/units/__init__.py +1 -13
- ansible_test/_internal/completion.py +2 -5
- ansible_test/_internal/config.py +2 -7
- ansible_test/_internal/coverage_util.py +1 -1
- ansible_test/_internal/delegation.py +2 -0
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/host_profiles.py +6 -11
- ansible_test/_internal/provider/__init__.py +2 -5
- ansible_test/_internal/provisioning.py +2 -5
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/target.py +2 -6
- ansible_test/_internal/thread.py +1 -4
- ansible_test/_internal/util.py +9 -14
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +30 -27
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/setup/bootstrap.sh +0 -6
- ansible/utils/py3compat.py +0 -27
- ansible_test/_data/pytest/config/legacy.ini +0 -4
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,9 @@ from ansible.errors import (
|
|
|
19
19
|
AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleTaskError,
|
|
20
20
|
AnsibleValueOmittedError,
|
|
21
21
|
)
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
from ansible._internal import _display_utils
|
|
24
|
+
from ansible.executor.task_result import _RawTaskResult, _SUB_PRESERVE
|
|
23
25
|
from ansible._internal._datatag import _utils
|
|
24
26
|
from ansible.module_utils._internal import _messages
|
|
25
27
|
from ansible.module_utils.datatag import native_type_name, deprecator_from_collection_name
|
|
@@ -27,7 +29,6 @@ from ansible._internal._datatag._tags import TrustedAsTemplate
|
|
|
27
29
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
28
30
|
from ansible.module_utils.common.text.converters import to_text, to_native
|
|
29
31
|
from ansible.module_utils.connection import write_to_stream
|
|
30
|
-
from ansible.module_utils.six import string_types
|
|
31
32
|
from ansible.playbook.task import Task
|
|
32
33
|
from ansible.plugins import get_plugin_class
|
|
33
34
|
from ansible.plugins.loader import become_loader, cliconf_loader, connection_loader, httpapi_loader, netconf_loader, terminal_loader
|
|
@@ -35,7 +36,7 @@ from ansible._internal._templating._jinja_plugins import _invoke_lookup, _Direct
|
|
|
35
36
|
from ansible._internal._templating._engine import TemplateEngine
|
|
36
37
|
from ansible.template import Templar
|
|
37
38
|
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
|
38
|
-
from ansible.utils.display import Display
|
|
39
|
+
from ansible.utils.display import Display
|
|
39
40
|
from ansible.utils.vars import combine_vars
|
|
40
41
|
from ansible.vars.clean import namespace_facts, clean_facts
|
|
41
42
|
from ansible.vars.manager import _deprecate_top_level_fact
|
|
@@ -48,6 +49,7 @@ display = Display()
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
RETURN_VARS = [x for x in C.MAGIC_VARIABLE_MAPPING.items() if 'become' not in x and '_pass' not in x]
|
|
52
|
+
_INJECT_FACTS, _INJECT_FACTS_ORIGIN = C.config.get_config_value_and_origin('INJECT_FACTS_AS_VARS')
|
|
51
53
|
|
|
52
54
|
__all__ = ['TaskExecutor']
|
|
53
55
|
|
|
@@ -340,7 +342,7 @@ class TaskExecutor:
|
|
|
340
342
|
})
|
|
341
343
|
|
|
342
344
|
# if plugin is loaded, get resolved name, otherwise leave original task connection
|
|
343
|
-
if self._connection and not isinstance(self._connection,
|
|
345
|
+
if self._connection and not isinstance(self._connection, str):
|
|
344
346
|
task_fields['connection'] = getattr(self._connection, 'ansible_name')
|
|
345
347
|
|
|
346
348
|
tr = _RawTaskResult(
|
|
@@ -416,7 +418,7 @@ class TaskExecutor:
|
|
|
416
418
|
def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
|
|
417
419
|
result: dict[str, t.Any]
|
|
418
420
|
|
|
419
|
-
with
|
|
421
|
+
with _display_utils.DeferredWarningContext(variables=variables) as warning_ctx:
|
|
420
422
|
try:
|
|
421
423
|
# DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
|
|
422
424
|
result = self._execute_internal(templar, variables)
|
|
@@ -431,7 +433,7 @@ class TaskExecutor:
|
|
|
431
433
|
|
|
432
434
|
self._task.update_result_no_log(templar, result)
|
|
433
435
|
|
|
434
|
-
# The warnings/deprecations in the result have already been captured in the
|
|
436
|
+
# The warnings/deprecations in the result have already been captured in the DeferredWarningContext by _apply_task_result_compat.
|
|
435
437
|
# The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
|
|
436
438
|
# These are then used to supersede the entries in the result.
|
|
437
439
|
|
|
@@ -664,8 +666,11 @@ class TaskExecutor:
|
|
|
664
666
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
|
665
667
|
af = result['ansible_facts']
|
|
666
668
|
vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
|
|
667
|
-
if
|
|
668
|
-
|
|
669
|
+
if _INJECT_FACTS:
|
|
670
|
+
if _INJECT_FACTS_ORIGIN == 'default':
|
|
671
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
|
672
|
+
else:
|
|
673
|
+
cleaned_toplevel = clean_facts(af)
|
|
669
674
|
vars_copy.update(cleaned_toplevel)
|
|
670
675
|
|
|
671
676
|
# set the failed property if it was missing.
|
|
@@ -759,9 +764,13 @@ class TaskExecutor:
|
|
|
759
764
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
|
760
765
|
af = result['ansible_facts']
|
|
761
766
|
variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
|
|
762
|
-
if
|
|
763
|
-
|
|
764
|
-
|
|
767
|
+
if _INJECT_FACTS:
|
|
768
|
+
if _INJECT_FACTS_ORIGIN == 'default':
|
|
769
|
+
# This happens x2 due to loops and being able to use values in subsequent iterations
|
|
770
|
+
# these copies are later discared in favor of 'total/final' one on loop end.
|
|
771
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
|
772
|
+
else:
|
|
773
|
+
cleaned_toplevel = clean_facts(af)
|
|
765
774
|
variables.update(cleaned_toplevel)
|
|
766
775
|
|
|
767
776
|
# save the notification target in the result, if it was specified, as
|
|
@@ -774,21 +783,25 @@ class TaskExecutor:
|
|
|
774
783
|
# on the results side without having to do any further templating
|
|
775
784
|
# also now add connection vars results when delegating
|
|
776
785
|
if self._task.delegate_to:
|
|
777
|
-
result["_ansible_delegated_vars"] = {
|
|
778
|
-
|
|
779
|
-
|
|
786
|
+
result["_ansible_delegated_vars"] = {
|
|
787
|
+
"ansible_delegated_host": self._task.delegate_to,
|
|
788
|
+
"ansible_connection": current_connection,
|
|
789
|
+
}
|
|
780
790
|
|
|
781
791
|
# note: here for callbacks that rely on this info to display delegation
|
|
782
|
-
for
|
|
783
|
-
if
|
|
784
|
-
|
|
792
|
+
for k in plugin_vars:
|
|
793
|
+
if k not in _SUB_PRESERVE["_ansible_delegated_vars"]:
|
|
794
|
+
continue
|
|
795
|
+
|
|
796
|
+
for o in C.config.get_plugin_options_from_var("connection", current_connection, k):
|
|
797
|
+
result["_ansible_delegated_vars"][k] = self._connection.get_option(o)
|
|
785
798
|
|
|
786
799
|
# and return
|
|
787
800
|
display.debug("attempt loop complete, returning result")
|
|
788
801
|
return result
|
|
789
802
|
|
|
790
803
|
@staticmethod
|
|
791
|
-
def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx:
|
|
804
|
+
def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _display_utils.DeferredWarningContext) -> None:
|
|
792
805
|
"""Apply backward-compatibility mutations to the supplied task result."""
|
|
793
806
|
if warnings := result.get('warnings'):
|
|
794
807
|
if isinstance(warnings, list):
|
|
@@ -956,9 +969,6 @@ class TaskExecutor:
|
|
|
956
969
|
|
|
957
970
|
self._play_context.connection = current_connection
|
|
958
971
|
|
|
959
|
-
# TODO: play context has logic to update the connection for 'smart'
|
|
960
|
-
# (default value, will chose between ssh and paramiko) and 'persistent'
|
|
961
|
-
# (really paramiko), eventually this should move to task object itself.
|
|
962
972
|
conn_type = self._play_context.connection
|
|
963
973
|
|
|
964
974
|
connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
|
|
@@ -1213,7 +1223,7 @@ def start_connection(play_context, options, task_uuid):
|
|
|
1213
1223
|
)
|
|
1214
1224
|
|
|
1215
1225
|
write_to_stream(p.stdin, options)
|
|
1216
|
-
write_to_stream(p.stdin, play_context.
|
|
1226
|
+
write_to_stream(p.stdin, play_context.dump_attrs())
|
|
1217
1227
|
|
|
1218
1228
|
(stdout, stderr) = p.communicate()
|
|
1219
1229
|
|
|
@@ -58,8 +58,6 @@ STDERR_FILENO = 2
|
|
|
58
58
|
|
|
59
59
|
display = Display()
|
|
60
60
|
|
|
61
|
-
_T = t.TypeVar('_T')
|
|
62
|
-
|
|
63
61
|
|
|
64
62
|
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
|
|
65
63
|
class CallbackSend:
|
|
@@ -413,7 +411,7 @@ class TaskQueueManager:
|
|
|
413
411
|
return defunct
|
|
414
412
|
|
|
415
413
|
@staticmethod
|
|
416
|
-
def _first_arg_of_type(value_type: t.Type[
|
|
414
|
+
def _first_arg_of_type[T](value_type: t.Type[T], args: t.Sequence) -> T | None:
|
|
417
415
|
return next((arg for arg in args if isinstance(arg, value_type)), None)
|
|
418
416
|
|
|
419
417
|
@lock_decorator(attr='_callback_lock')
|
ansible/galaxy/api.py
CHANGED
|
@@ -25,7 +25,6 @@ from ansible.errors import AnsibleError
|
|
|
25
25
|
from ansible.galaxy.user_agent import user_agent
|
|
26
26
|
from ansible.module_utils.api import retry_with_delays_and_condition
|
|
27
27
|
from ansible.module_utils.api import generate_jittered_backoff
|
|
28
|
-
from ansible.module_utils.six import string_types
|
|
29
28
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
30
29
|
from ansible.module_utils.urls import open_url, prepare_multipart
|
|
31
30
|
from ansible.utils.display import Display
|
|
@@ -114,13 +113,7 @@ def g_connect(versions):
|
|
|
114
113
|
# url + '/api/' appended.
|
|
115
114
|
self.api_server = n_url
|
|
116
115
|
|
|
117
|
-
|
|
118
|
-
# it isn't returned in the available_versions dict.
|
|
119
|
-
available_versions = data.get('available_versions', {u'v1': u'v1/'})
|
|
120
|
-
if list(available_versions.keys()) == [u'v1']:
|
|
121
|
-
available_versions[u'v2'] = u'v2/'
|
|
122
|
-
|
|
123
|
-
self._available_api_versions = available_versions
|
|
116
|
+
self._available_api_versions = available_versions = data['available_versions']
|
|
124
117
|
display.vvvv("Found API version '%s' with Galaxy server %s (%s)"
|
|
125
118
|
% (', '.join(available_versions.keys()), self.name, self.api_server))
|
|
126
119
|
|
|
@@ -132,15 +125,6 @@ def g_connect(versions):
|
|
|
132
125
|
% (method.__name__, ", ".join(versions), ", ".join(available_versions),
|
|
133
126
|
self.name, self.api_server))
|
|
134
127
|
|
|
135
|
-
# Warn only when we know we are talking to a collections API
|
|
136
|
-
if common_versions == {'v2'}:
|
|
137
|
-
display.deprecated(
|
|
138
|
-
'The v2 Ansible Galaxy API is deprecated and no longer supported. '
|
|
139
|
-
'Ensure that you have configured the ansible-galaxy CLI to utilize an '
|
|
140
|
-
'updated and supported version of Ansible Galaxy.',
|
|
141
|
-
version='2.20',
|
|
142
|
-
)
|
|
143
|
-
|
|
144
128
|
return method(self, *args, **kwargs)
|
|
145
129
|
return wrapped
|
|
146
130
|
return decorator
|
|
@@ -214,11 +198,7 @@ class GalaxyError(AnsibleError):
|
|
|
214
198
|
err_info = {}
|
|
215
199
|
|
|
216
200
|
url_split = self.url.split('/')
|
|
217
|
-
if '
|
|
218
|
-
galaxy_msg = err_info.get('message', http_error.reason)
|
|
219
|
-
code = err_info.get('code', 'Unknown')
|
|
220
|
-
full_error_msg = u"%s (HTTP Code: %d, Message: %s Code: %s)" % (message, self.http_code, galaxy_msg, code)
|
|
221
|
-
elif 'v3' in url_split:
|
|
201
|
+
if 'v3' in url_split:
|
|
222
202
|
errors = err_info.get('errors', [])
|
|
223
203
|
if not errors:
|
|
224
204
|
errors = [{}] # Defaults are set below, we just need to make sure 1 error is present.
|
|
@@ -340,7 +320,7 @@ class GalaxyAPI:
|
|
|
340
320
|
return self._priority > other_galaxy_api._priority
|
|
341
321
|
|
|
342
322
|
@property # type: ignore[misc] # https://github.com/python/mypy/issues/1362
|
|
343
|
-
@g_connect(['v1', '
|
|
323
|
+
@g_connect(['v1', 'v3'])
|
|
344
324
|
def available_api_versions(self):
|
|
345
325
|
# Calling g_connect will populate self._available_api_versions
|
|
346
326
|
return self._available_api_versions
|
|
@@ -595,11 +575,11 @@ class GalaxyAPI:
|
|
|
595
575
|
page_size = kwargs.get('page_size', None)
|
|
596
576
|
author = kwargs.get('author', None)
|
|
597
577
|
|
|
598
|
-
if tags and isinstance(tags,
|
|
578
|
+
if tags and isinstance(tags, str):
|
|
599
579
|
tags = tags.split(',')
|
|
600
580
|
search_url += '&tags_autocomplete=' + '+'.join(tags)
|
|
601
581
|
|
|
602
|
-
if platforms and isinstance(platforms,
|
|
582
|
+
if platforms and isinstance(platforms, str):
|
|
603
583
|
platforms = platforms.split(',')
|
|
604
584
|
search_url += '&platforms_autocomplete=' + '+'.join(platforms)
|
|
605
585
|
|
|
@@ -645,7 +625,7 @@ class GalaxyAPI:
|
|
|
645
625
|
|
|
646
626
|
# Collection APIs #
|
|
647
627
|
|
|
648
|
-
@g_connect(['
|
|
628
|
+
@g_connect(['v3'])
|
|
649
629
|
def publish_collection(self, collection_path):
|
|
650
630
|
"""
|
|
651
631
|
Publishes a collection to a Galaxy server and returns the import task URI.
|
|
@@ -680,19 +660,15 @@ class GalaxyAPI:
|
|
|
680
660
|
'Content-length': len(b_form_data),
|
|
681
661
|
}
|
|
682
662
|
|
|
683
|
-
|
|
684
|
-
n_url = _urljoin(self.api_server, self.available_api_versions['v3'], 'artifacts', 'collections') + '/'
|
|
685
|
-
else:
|
|
686
|
-
n_url = _urljoin(self.api_server, self.available_api_versions['v2'], 'collections') + '/'
|
|
687
|
-
|
|
663
|
+
n_url = _urljoin(self.api_server, self.available_api_versions['v3'], 'artifacts', 'collections') + '/'
|
|
688
664
|
resp = self._call_galaxy(n_url, args=b_form_data, headers=headers, method='POST', auth_required=True,
|
|
689
665
|
error_context_msg='Error when publishing collection to %s (%s)'
|
|
690
666
|
% (self.name, self.api_server))
|
|
691
667
|
|
|
692
|
-
return resp['task']
|
|
668
|
+
return urljoin(self.api_server, resp['task'])
|
|
693
669
|
|
|
694
|
-
@g_connect(['
|
|
695
|
-
def wait_import_task(self,
|
|
670
|
+
@g_connect(['v3'])
|
|
671
|
+
def wait_import_task(self, task_url, timeout=0):
|
|
696
672
|
"""
|
|
697
673
|
Waits until the import process on the Galaxy server has completed or the timeout is reached.
|
|
698
674
|
|
|
@@ -703,22 +679,14 @@ class GalaxyAPI:
|
|
|
703
679
|
state = 'waiting'
|
|
704
680
|
data = None
|
|
705
681
|
|
|
706
|
-
|
|
707
|
-
if 'v3' in self.available_api_versions:
|
|
708
|
-
full_url = _urljoin(self.api_server, self.available_api_versions['v3'],
|
|
709
|
-
'imports/collections', task_id, '/')
|
|
710
|
-
else:
|
|
711
|
-
full_url = _urljoin(self.api_server, self.available_api_versions['v2'],
|
|
712
|
-
'collection-imports', task_id, '/')
|
|
713
|
-
|
|
714
|
-
display.display("Waiting until Galaxy import task %s has completed" % full_url)
|
|
682
|
+
display.display("Waiting until Galaxy import task %s has completed" % task_url)
|
|
715
683
|
start = time.time()
|
|
716
684
|
wait = C.GALAXY_COLLECTION_IMPORT_POLL_INTERVAL
|
|
717
685
|
|
|
718
686
|
while timeout == 0 or (time.time() - start) < timeout:
|
|
719
687
|
try:
|
|
720
|
-
data = self._call_galaxy(
|
|
721
|
-
error_context_msg='Error when getting import task results at %s' %
|
|
688
|
+
data = self._call_galaxy(task_url, method='GET', auth_required=True,
|
|
689
|
+
error_context_msg='Error when getting import task results at %s' % task_url)
|
|
722
690
|
except GalaxyError as e:
|
|
723
691
|
if e.http_code != 404:
|
|
724
692
|
raise
|
|
@@ -740,7 +708,7 @@ class GalaxyAPI:
|
|
|
740
708
|
wait = min(30, wait * C.GALAXY_COLLECTION_IMPORT_POLL_FACTOR)
|
|
741
709
|
if state == 'waiting':
|
|
742
710
|
raise AnsibleError("Timeout while waiting for the Galaxy import process to finish, check progress at '%s'"
|
|
743
|
-
% to_native(
|
|
711
|
+
% to_native(task_url))
|
|
744
712
|
|
|
745
713
|
for message in data.get('messages', []):
|
|
746
714
|
level = message['level']
|
|
@@ -754,10 +722,10 @@ class GalaxyAPI:
|
|
|
754
722
|
if state == 'failed':
|
|
755
723
|
code = to_native(data['error'].get('code', 'UNKNOWN'))
|
|
756
724
|
description = to_native(
|
|
757
|
-
data['error'].get('description', "Unknown error, see %s for more details" %
|
|
725
|
+
data['error'].get('description', "Unknown error, see %s for more details" % task_url))
|
|
758
726
|
raise AnsibleError("Galaxy import process failed: %s (Code: %s)" % (description, code))
|
|
759
727
|
|
|
760
|
-
@g_connect(['
|
|
728
|
+
@g_connect(['v3'])
|
|
761
729
|
def get_collection_metadata(self, namespace, name):
|
|
762
730
|
"""
|
|
763
731
|
Gets the collection information from the Galaxy server about a specific Collection.
|
|
@@ -766,18 +734,11 @@ class GalaxyAPI:
|
|
|
766
734
|
:param name: The collection name.
|
|
767
735
|
return: CollectionMetadata about the collection.
|
|
768
736
|
"""
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
]
|
|
775
|
-
else:
|
|
776
|
-
api_path = self.available_api_versions['v2']
|
|
777
|
-
field_map = [
|
|
778
|
-
('created_str', 'created'),
|
|
779
|
-
('modified_str', 'modified'),
|
|
780
|
-
]
|
|
737
|
+
api_path = self.available_api_versions['v3']
|
|
738
|
+
field_map = [
|
|
739
|
+
('created_str', 'created_at'),
|
|
740
|
+
('modified_str', 'updated_at'),
|
|
741
|
+
]
|
|
781
742
|
|
|
782
743
|
info_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, '/')
|
|
783
744
|
error_context_msg = 'Error when getting the collection info for %s.%s from %s (%s)' \
|
|
@@ -790,7 +751,7 @@ class GalaxyAPI:
|
|
|
790
751
|
|
|
791
752
|
return CollectionMetadata(namespace, name, **metadata)
|
|
792
753
|
|
|
793
|
-
@g_connect(['
|
|
754
|
+
@g_connect(['v3'])
|
|
794
755
|
def get_collection_version_metadata(self, namespace, name, version):
|
|
795
756
|
"""
|
|
796
757
|
Gets the collection information from the Galaxy server about a specific Collection version.
|
|
@@ -800,7 +761,7 @@ class GalaxyAPI:
|
|
|
800
761
|
:param version: Version of the collection to get the information for.
|
|
801
762
|
:return: CollectionVersionMetadata about the collection at the version requested.
|
|
802
763
|
"""
|
|
803
|
-
api_path = self.available_api_versions
|
|
764
|
+
api_path = self.available_api_versions['v3']
|
|
804
765
|
url_paths = [self.api_server, api_path, 'collections', namespace, name, 'versions', version, '/']
|
|
805
766
|
|
|
806
767
|
n_collection_url = _urljoin(*url_paths)
|
|
@@ -824,7 +785,7 @@ class GalaxyAPI:
|
|
|
824
785
|
download_url, data['artifact']['sha256'],
|
|
825
786
|
data['metadata']['dependencies'], data['href'], signatures)
|
|
826
787
|
|
|
827
|
-
@g_connect(['
|
|
788
|
+
@g_connect(['v3'])
|
|
828
789
|
def get_collection_versions(self, namespace, name):
|
|
829
790
|
"""
|
|
830
791
|
Gets a list of available versions for a collection on a Galaxy server.
|
|
@@ -833,17 +794,10 @@ class GalaxyAPI:
|
|
|
833
794
|
:param name: The collection name.
|
|
834
795
|
:return: A list of versions that are available.
|
|
835
796
|
"""
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
api_path = self.available_api_versions['v3']
|
|
839
|
-
pagination_path = ['links', 'next']
|
|
840
|
-
relative_link = True # AH pagination results are relative an not an absolute URI.
|
|
841
|
-
else:
|
|
842
|
-
api_path = self.available_api_versions['v2']
|
|
843
|
-
pagination_path = ['next']
|
|
797
|
+
api_path = self.available_api_versions['v3']
|
|
798
|
+
pagination_path = ['links', 'next']
|
|
844
799
|
|
|
845
|
-
|
|
846
|
-
versions_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, 'versions', '/?%s=%d' % (page_size_name, COLLECTION_PAGE_SIZE))
|
|
800
|
+
versions_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, 'versions', '/?limit=%d' % COLLECTION_PAGE_SIZE)
|
|
847
801
|
versions_url_info = urlparse(versions_url)
|
|
848
802
|
cache_key = versions_url_info.path
|
|
849
803
|
|
|
@@ -898,11 +852,10 @@ class GalaxyAPI:
|
|
|
898
852
|
|
|
899
853
|
if not next_link:
|
|
900
854
|
break
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
next_link = urljoin(self.api_server, next_link)
|
|
855
|
+
next_link_info = urlparse(next_link)
|
|
856
|
+
if not next_link_info.scheme and not next_link_info.path.startswith('/'):
|
|
857
|
+
raise AnsibleError(f'Invalid non absolute pagination link: {next_link}')
|
|
858
|
+
next_link = urljoin(self.api_server, next_link)
|
|
906
859
|
|
|
907
860
|
data = self._call_galaxy(to_native(next_link, errors='surrogate_or_strict'),
|
|
908
861
|
error_context_msg=error_context_msg, cache=True, cache_key=cache_key)
|
|
@@ -910,7 +863,7 @@ class GalaxyAPI:
|
|
|
910
863
|
|
|
911
864
|
return versions
|
|
912
865
|
|
|
913
|
-
@g_connect(['
|
|
866
|
+
@g_connect(['v3'])
|
|
914
867
|
def get_collection_signatures(self, namespace, name, version):
|
|
915
868
|
"""
|
|
916
869
|
Gets the collection signatures from the Galaxy server about a specific Collection version.
|
|
@@ -920,7 +873,7 @@ class GalaxyAPI:
|
|
|
920
873
|
:param version: Version of the collection to get the information for.
|
|
921
874
|
:return: A list of signature strings.
|
|
922
875
|
"""
|
|
923
|
-
api_path = self.available_api_versions
|
|
876
|
+
api_path = self.available_api_versions['v3']
|
|
924
877
|
url_paths = [self.api_server, api_path, 'collections', namespace, name, 'versions', version, '/']
|
|
925
878
|
|
|
926
879
|
n_collection_url = _urljoin(*url_paths)
|
|
@@ -339,12 +339,12 @@ def verify_local_collection(local_collection, remote_collection, artifacts_manag
|
|
|
339
339
|
]
|
|
340
340
|
|
|
341
341
|
# Find any paths not in the FILES.json
|
|
342
|
-
for root, dirs,
|
|
343
|
-
for name in
|
|
342
|
+
for root, dirs, filenames in os.walk(b_collection_path):
|
|
343
|
+
for name in filenames:
|
|
344
344
|
full_path = os.path.join(root, name)
|
|
345
345
|
path = to_text(full_path[len(b_collection_path) + 1::], errors='surrogate_or_strict')
|
|
346
346
|
if any(fnmatch.fnmatch(full_path, b_pattern) for b_pattern in b_ignore_patterns):
|
|
347
|
-
display.v("Ignoring verification for %s" % full_path)
|
|
347
|
+
display.v("Ignoring verification for %s" % to_text(full_path))
|
|
348
348
|
continue
|
|
349
349
|
|
|
350
350
|
if full_path not in collection_files:
|
|
@@ -623,24 +623,11 @@ def publish_collection(collection_path, api, wait, timeout):
|
|
|
623
623
|
import_uri = api.publish_collection(collection_path)
|
|
624
624
|
|
|
625
625
|
if wait:
|
|
626
|
-
# Galaxy returns a url fragment which differs between v2 and v3. The second to last entry is
|
|
627
|
-
# always the task_id, though.
|
|
628
|
-
# v2: {"task": "https://galaxy-dev.ansible.com/api/v2/collection-imports/35573/"}
|
|
629
|
-
# v3: {"task": "/api/automation-hub/v3/imports/collections/838d1308-a8f4-402c-95cb-7823f3806cd8/"}
|
|
630
|
-
task_id = None
|
|
631
|
-
for path_segment in reversed(import_uri.split('/')):
|
|
632
|
-
if path_segment:
|
|
633
|
-
task_id = path_segment
|
|
634
|
-
break
|
|
635
|
-
|
|
636
|
-
if not task_id:
|
|
637
|
-
raise AnsibleError("Publishing the collection did not return valid task info. Cannot wait for task status. Returned task info: '%s'" % import_uri)
|
|
638
|
-
|
|
639
626
|
with _display_progress(
|
|
640
627
|
"Collection has been published to the Galaxy server "
|
|
641
628
|
"{api.name!s} {api.api_server!s}".format(api=api),
|
|
642
629
|
):
|
|
643
|
-
api.wait_import_task(
|
|
630
|
+
api.wait_import_task(import_uri, timeout)
|
|
644
631
|
display.display("Collection has been successfully published and imported to the Galaxy server %s %s"
|
|
645
632
|
% (api.name, api.api_server))
|
|
646
633
|
else:
|
|
@@ -26,9 +26,6 @@ if t.TYPE_CHECKING:
|
|
|
26
26
|
'_ComputedReqKindsMixin',
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
import ansible
|
|
30
|
-
import ansible.release
|
|
31
|
-
|
|
32
29
|
from ansible.errors import AnsibleError, AnsibleAssertionError
|
|
33
30
|
from ansible.galaxy.api import GalaxyAPI
|
|
34
31
|
from ansible.galaxy.collection import HAS_PACKAGING, PkgReq
|
|
@@ -42,7 +39,6 @@ _ALLOW_CONCRETE_POINTER_IN_SOURCE = False # NOTE: This is a feature flag
|
|
|
42
39
|
_GALAXY_YAML = b'galaxy.yml'
|
|
43
40
|
_MANIFEST_JSON = b'MANIFEST.json'
|
|
44
41
|
_SOURCE_METADATA_FILE = b'GALAXY.yml'
|
|
45
|
-
_ANSIBLE_PACKAGE_PATH = pathlib.Path(ansible.__file__).parent
|
|
46
42
|
|
|
47
43
|
display = Display()
|
|
48
44
|
|
|
@@ -229,12 +225,6 @@ class _ComputedReqKindsMixin:
|
|
|
229
225
|
dir_path = dir_path.rstrip(to_bytes(os.path.sep))
|
|
230
226
|
if not _is_collection_dir(dir_path):
|
|
231
227
|
dir_pathlib = pathlib.Path(to_text(dir_path))
|
|
232
|
-
|
|
233
|
-
# special handling for bundled collections without manifests, e.g., ansible._protomatter
|
|
234
|
-
if dir_pathlib.is_relative_to(_ANSIBLE_PACKAGE_PATH):
|
|
235
|
-
req_name = f'{dir_pathlib.parent.name}.{dir_pathlib.name}'
|
|
236
|
-
return cls(req_name, ansible.release.__version__, dir_path, 'dir', None)
|
|
237
|
-
|
|
238
228
|
display.warning(
|
|
239
229
|
u"Collection at '{path!s}' does not have a {manifest_json!s} "
|
|
240
230
|
u'file, nor has it {galaxy_yml!s}: cannot detect version.'.
|
|
@@ -24,7 +24,6 @@ from ansible.galaxy.dependency_resolution.versioning import (
|
|
|
24
24
|
is_pre_release,
|
|
25
25
|
meets_requirements,
|
|
26
26
|
)
|
|
27
|
-
from ansible.module_utils.six import string_types
|
|
28
27
|
from ansible.utils.version import SemanticVersion, LooseVersion
|
|
29
28
|
|
|
30
29
|
try:
|
|
@@ -278,7 +277,7 @@ class CollectionDependencyProviderBase(AbstractProvider):
|
|
|
278
277
|
# NOTE: Another known mistake is setting a minor part of the SemVer notation
|
|
279
278
|
# NOTE: skipping the "patch" bit like "1.0" which is assumed non-compliant even
|
|
280
279
|
# NOTE: after the conversion to string.
|
|
281
|
-
if not isinstance(version,
|
|
280
|
+
if not isinstance(version, str):
|
|
282
281
|
raise ValueError(version_err)
|
|
283
282
|
elif version != '*':
|
|
284
283
|
try:
|
ansible/galaxy/role.py
CHANGED
|
@@ -23,7 +23,6 @@ from __future__ import annotations
|
|
|
23
23
|
|
|
24
24
|
import errno
|
|
25
25
|
import datetime
|
|
26
|
-
import functools
|
|
27
26
|
import os
|
|
28
27
|
import tarfile
|
|
29
28
|
import tempfile
|
|
@@ -46,32 +45,6 @@ from ansible.utils.path import is_subpath, unfrackpath
|
|
|
46
45
|
display = Display()
|
|
47
46
|
|
|
48
47
|
|
|
49
|
-
@functools.cache
|
|
50
|
-
def _check_working_data_filter() -> bool:
|
|
51
|
-
"""
|
|
52
|
-
Check if tarfile.data_filter implementation is working
|
|
53
|
-
for the current Python version or not
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
# Implemented the following code to circumvent broken implementation of data_filter
|
|
57
|
-
# in tarfile. See for more information - https://github.com/python/cpython/issues/107845
|
|
58
|
-
# deprecated: description='probing broken data filter implementation' python_version='3.11'
|
|
59
|
-
ret = False
|
|
60
|
-
if hasattr(tarfile, 'data_filter'):
|
|
61
|
-
# We explicitly check if tarfile.data_filter is broken or not
|
|
62
|
-
ti = tarfile.TarInfo('docs/README.md')
|
|
63
|
-
ti.type = tarfile.SYMTYPE
|
|
64
|
-
ti.linkname = '../README.md'
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
tarfile.data_filter(ti, '/foo')
|
|
68
|
-
except tarfile.LinkOutsideDestinationError:
|
|
69
|
-
pass
|
|
70
|
-
else:
|
|
71
|
-
ret = True
|
|
72
|
-
return ret
|
|
73
|
-
|
|
74
|
-
|
|
75
48
|
class GalaxyRole(object):
|
|
76
49
|
|
|
77
50
|
SUPPORTED_SCMS = set(['git', 'hg'])
|
|
@@ -418,12 +391,7 @@ class GalaxyRole(object):
|
|
|
418
391
|
relative_path = os.path.join(*full_path.replace(relative_path_dir, "", 1).split(os.sep))
|
|
419
392
|
setattr(member, attr, relative_path)
|
|
420
393
|
|
|
421
|
-
|
|
422
|
-
# deprecated: description='extract fallback without filter' python_version='3.11'
|
|
423
|
-
role_tar_file.extract(member, to_native(self.path), filter='data') # type: ignore[call-arg]
|
|
424
|
-
else:
|
|
425
|
-
# Remove along with manual path filter once Python 3.12 is minimum supported version
|
|
426
|
-
role_tar_file.extract(member, to_native(self.path))
|
|
394
|
+
role_tar_file.extract(member, to_native(self.path), filter='data')
|
|
427
395
|
|
|
428
396
|
# write out the install info file for later use
|
|
429
397
|
self._write_galaxy_install_info()
|
ansible/inventory/manager.py
CHANGED
|
@@ -33,7 +33,6 @@ from ansible._internal import _json, _wrapt
|
|
|
33
33
|
from ansible._internal._json import EncryptedStringBehavior
|
|
34
34
|
from ansible.errors import AnsibleError, AnsibleOptionsError
|
|
35
35
|
from ansible.inventory.data import InventoryData
|
|
36
|
-
from ansible.module_utils.six import string_types
|
|
37
36
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
|
38
37
|
from ansible.parsing.utils.addresses import parse_address
|
|
39
38
|
from ansible.plugins.loader import inventory_loader
|
|
@@ -112,7 +111,7 @@ def split_host_pattern(pattern):
|
|
|
112
111
|
results = (split_host_pattern(p) for p in pattern)
|
|
113
112
|
# flatten the results
|
|
114
113
|
return list(itertools.chain.from_iterable(results))
|
|
115
|
-
elif not isinstance(pattern,
|
|
114
|
+
elif not isinstance(pattern, str):
|
|
116
115
|
pattern = to_text(pattern, errors='surrogate_or_strict')
|
|
117
116
|
|
|
118
117
|
# If it's got commas in it, we'll treat it as a straightforward
|
|
@@ -162,7 +161,7 @@ class InventoryManager(object):
|
|
|
162
161
|
# the inventory dirs, files, script paths or lists of hosts
|
|
163
162
|
if sources is None:
|
|
164
163
|
self._sources = []
|
|
165
|
-
elif isinstance(sources,
|
|
164
|
+
elif isinstance(sources, str):
|
|
166
165
|
self._sources = [sources]
|
|
167
166
|
else:
|
|
168
167
|
self._sources = sources
|
ansible/keyword_desc.yml
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
accelerate: "*DEPRECATED*, set to True to use accelerate connection plugin."
|
|
2
|
-
accelerate_ipv6: "*DEPRECATED*, set to True to force accelerate plugin to use ipv6 for its connection."
|
|
3
|
-
accelerate_port: "*DEPRECATED*, set to override default port use for accelerate connection."
|
|
4
1
|
action: "The 'action' to execute for a task, it normally translates into a C(module) or action plugin."
|
|
5
2
|
args: "A secondary way to add arguments into a task. Takes a dictionary in which keys map to options and values."
|
|
6
3
|
always: List of tasks, in a block, that execute no matter if there is an error in the block or not.
|
|
@@ -500,17 +500,9 @@ class AnsibleDatatagBase(AnsibleSerializableDataclass, metaclass=abc.ABCMeta):
|
|
|
500
500
|
_known_tag_type_map: t.Dict[str, t.Type[AnsibleDatatagBase]] = {}
|
|
501
501
|
_known_tag_types: t.Set[t.Type[AnsibleDatatagBase]] = set()
|
|
502
502
|
|
|
503
|
-
if sys.version_info >= (3, 9):
|
|
504
|
-
# Include the key and value types in the type hints on Python 3.9 and later.
|
|
505
|
-
# Earlier versions do not support subscriptable dict.
|
|
506
|
-
# deprecated: description='always use subscriptable dict' python_version='3.8'
|
|
507
|
-
class _AnsibleTagsMapping(dict[type[_TAnsibleDatatagBase], _TAnsibleDatatagBase]):
|
|
508
|
-
__slots__ = _NO_INSTANCE_STORAGE
|
|
509
503
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
class _AnsibleTagsMapping(dict):
|
|
513
|
-
__slots__ = _NO_INSTANCE_STORAGE
|
|
504
|
+
class _AnsibleTagsMapping(dict[type[_TAnsibleDatatagBase], _TAnsibleDatatagBase]):
|
|
505
|
+
__slots__ = _NO_INSTANCE_STORAGE
|
|
514
506
|
|
|
515
507
|
|
|
516
508
|
class _EmptyROInternalTagsMapping(dict):
|