ansible-core 2.19.2rc1__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.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/METADATA +3 -3
- {ansible_core-2.19.2rc1.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.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.2rc1.dist-info → ansible_core-2.20.0b1.dist-info}/top_level.txt +0 -0
|
@@ -13,25 +13,13 @@ from ansible.utils.display import Display
|
|
|
13
13
|
display = Display()
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=False
|
|
16
|
+
def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=False):
|
|
17
17
|
if not isinstance(secret, (str, bytes)):
|
|
18
18
|
raise TypeError(f"Secret passed is required to be a string, instead we got {type(secret)}.")
|
|
19
19
|
|
|
20
20
|
if not isinstance(data, (str, bytes)):
|
|
21
21
|
raise TypeError(f"Can only vault strings, instead we got {type(data)}.")
|
|
22
22
|
|
|
23
|
-
if vaultid is not None:
|
|
24
|
-
display.deprecated(
|
|
25
|
-
msg="Use of undocumented `vaultid`.",
|
|
26
|
-
version="2.20",
|
|
27
|
-
help_text="Use `vault_id` instead.",
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
if vault_id == 'filter_default':
|
|
31
|
-
vault_id = vaultid
|
|
32
|
-
else:
|
|
33
|
-
display.warning("Ignoring vaultid as vault_id is already set.")
|
|
34
|
-
|
|
35
23
|
vs = VaultSecret(to_bytes(secret))
|
|
36
24
|
vl = VaultLib()
|
|
37
25
|
try:
|
|
@@ -48,11 +36,11 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
|
|
|
48
36
|
|
|
49
37
|
|
|
50
38
|
@_template.accept_args_markers
|
|
51
|
-
def do_unvault(vault, secret, vault_id='filter_default'
|
|
39
|
+
def do_unvault(vault, secret, vault_id='filter_default'):
|
|
52
40
|
if isinstance(vault, VaultExceptionMarker):
|
|
53
41
|
vault = vault._disarm()
|
|
54
42
|
|
|
55
|
-
if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id
|
|
43
|
+
if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id), {})) is not None:
|
|
56
44
|
return first_marker
|
|
57
45
|
|
|
58
46
|
if not isinstance(secret, (str, bytes)):
|
|
@@ -61,18 +49,6 @@ def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
|
|
|
61
49
|
if not isinstance(vault, (str, bytes)):
|
|
62
50
|
raise TypeError(f"Vault should be in the form of a string, instead we got {type(vault)}.")
|
|
63
51
|
|
|
64
|
-
if vaultid is not None:
|
|
65
|
-
display.deprecated(
|
|
66
|
-
msg="Use of undocumented `vaultid`.",
|
|
67
|
-
version="2.20",
|
|
68
|
-
help_text="Use `vault_id` instead.",
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
if vault_id == 'filter_default':
|
|
72
|
-
vault_id = vaultid
|
|
73
|
-
else:
|
|
74
|
-
display.warning("Ignoring vaultid as vault_id is already set.")
|
|
75
|
-
|
|
76
52
|
vs = VaultSecret(to_bytes(secret))
|
|
77
53
|
vl = VaultLib([(vault_id, vs)])
|
|
78
54
|
|
|
@@ -29,7 +29,6 @@ from jinja2.filters import pass_environment
|
|
|
29
29
|
|
|
30
30
|
from ansible.errors import AnsibleError
|
|
31
31
|
from ansible.module_utils.common.text import formatters
|
|
32
|
-
from ansible.module_utils.six import binary_type, text_type
|
|
33
32
|
from ansible.utils.display import Display
|
|
34
33
|
|
|
35
34
|
try:
|
|
@@ -180,7 +179,7 @@ def rekey_on_member(data, key, duplicates='error'):
|
|
|
180
179
|
|
|
181
180
|
if isinstance(data, Mapping):
|
|
182
181
|
iterate_over = data.values()
|
|
183
|
-
elif isinstance(data, Iterable) and not isinstance(data, (
|
|
182
|
+
elif isinstance(data, Iterable) and not isinstance(data, (str, bytes)):
|
|
184
183
|
iterate_over = data
|
|
185
184
|
else:
|
|
186
185
|
raise AnsibleError("Type is not a valid list, set, or dict")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
DOCUMENTATION:
|
|
2
|
-
name:
|
|
2
|
+
name: to_nice_yaml
|
|
3
3
|
author: core team
|
|
4
4
|
version_added: 'historical'
|
|
5
5
|
short_description: Convert variable to YAML string
|
|
@@ -20,10 +20,38 @@ DOCUMENTATION:
|
|
|
20
20
|
description: Affects sorting of dictionary keys.
|
|
21
21
|
default: True
|
|
22
22
|
type: bool
|
|
23
|
-
|
|
23
|
+
default_style:
|
|
24
|
+
description:
|
|
25
|
+
- Indicates the style of the scalar.
|
|
26
|
+
choices:
|
|
27
|
+
- ''
|
|
28
|
+
- "'"
|
|
29
|
+
- '"'
|
|
30
|
+
- '|'
|
|
31
|
+
- '>'
|
|
32
|
+
type: string
|
|
33
|
+
canonical:
|
|
34
|
+
description:
|
|
35
|
+
- If set to V(True), export tag type to the output.
|
|
36
|
+
type: bool
|
|
37
|
+
width:
|
|
38
|
+
description: Set the preferred line width.
|
|
39
|
+
type: int
|
|
40
|
+
line_break:
|
|
41
|
+
description: Specify the line break.
|
|
42
|
+
type: string
|
|
43
|
+
encoding:
|
|
44
|
+
description: Specify the output encoding.
|
|
45
|
+
type: string
|
|
46
|
+
explicit_start:
|
|
47
|
+
description: If set to V(True), adds an explicit start using "---".
|
|
48
|
+
type: bool
|
|
49
|
+
explicit_end:
|
|
50
|
+
description: If set to V(True), adds an explicit end using "...".
|
|
51
|
+
type: bool
|
|
24
52
|
notes:
|
|
25
53
|
- More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
|
|
26
|
-
- 'These parameters to C(yaml.dump) will be ignored, as they are overridden internally: I(default_flow_style)'
|
|
54
|
+
- 'These parameters to C(yaml.dump) will be ignored, as they are overridden internally: I(default_flow_style), I(allow_unicode).'
|
|
27
55
|
|
|
28
56
|
EXAMPLES: |
|
|
29
57
|
# dump variable in a template to create a YAML document
|
|
@@ -20,21 +20,38 @@ DOCUMENTATION:
|
|
|
20
20
|
description: Affects sorting of dictionary keys.
|
|
21
21
|
default: True
|
|
22
22
|
type: bool
|
|
23
|
+
default_style:
|
|
24
|
+
description:
|
|
25
|
+
- Indicates the style of the scalar.
|
|
26
|
+
choices:
|
|
27
|
+
- ''
|
|
28
|
+
- "'"
|
|
29
|
+
- '"'
|
|
30
|
+
- '|'
|
|
31
|
+
- '>'
|
|
32
|
+
type: string
|
|
33
|
+
canonical:
|
|
34
|
+
description:
|
|
35
|
+
- If set to V(True), export tag type to the output.
|
|
36
|
+
type: bool
|
|
37
|
+
width:
|
|
38
|
+
description: Set the preferred line width.
|
|
39
|
+
type: integer
|
|
40
|
+
line_break:
|
|
41
|
+
description: Specify the line break.
|
|
42
|
+
type: string
|
|
43
|
+
encoding:
|
|
44
|
+
description: Specify the output encoding.
|
|
45
|
+
type: string
|
|
46
|
+
explicit_start:
|
|
47
|
+
description: If set to V(True), adds an explicit start using "---".
|
|
48
|
+
type: bool
|
|
49
|
+
explicit_end:
|
|
50
|
+
description: If set to V(True), adds an explicit end using "...".
|
|
51
|
+
type: bool
|
|
23
52
|
notes:
|
|
24
53
|
- More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
|
|
25
54
|
|
|
26
|
-
# TODO: find docs for these
|
|
27
|
-
#default_flow_style
|
|
28
|
-
#default_style
|
|
29
|
-
#canonical=None,
|
|
30
|
-
#width=None,
|
|
31
|
-
#line_break=None,
|
|
32
|
-
#encoding=None,
|
|
33
|
-
#explicit_start=None,
|
|
34
|
-
#explicit_end=None,
|
|
35
|
-
#version=None,
|
|
36
|
-
#tags=None
|
|
37
|
-
|
|
38
55
|
EXAMPLES: |
|
|
39
56
|
# dump variable in a template to create a YAML document
|
|
40
57
|
{{ github_workflow | to_yaml }}
|
|
@@ -34,7 +34,6 @@ from ansible.parsing.dataloader import DataLoader
|
|
|
34
34
|
from ansible.plugins import AnsiblePlugin, _ConfigurablePlugin
|
|
35
35
|
from ansible.plugins.cache import CachePluginAdjudicator
|
|
36
36
|
from ansible.module_utils.common.text.converters import to_bytes, to_native
|
|
37
|
-
from ansible.module_utils.six import string_types
|
|
38
37
|
from ansible.utils.display import Display
|
|
39
38
|
from ansible.utils.vars import combine_vars, load_extra_vars
|
|
40
39
|
|
|
@@ -439,7 +438,7 @@ class Constructable(_BaseInventoryPlugin):
|
|
|
439
438
|
new_raw_group_names = []
|
|
440
439
|
if use_default:
|
|
441
440
|
new_raw_group_names.append(default_value_name)
|
|
442
|
-
elif isinstance(key,
|
|
441
|
+
elif isinstance(key, str):
|
|
443
442
|
new_raw_group_names.append(key)
|
|
444
443
|
elif isinstance(key, list):
|
|
445
444
|
for name in key:
|
|
@@ -256,9 +256,10 @@ class InventoryModule(BaseInventoryPlugin):
|
|
|
256
256
|
group = self.inventory.add_group(group)
|
|
257
257
|
|
|
258
258
|
if not isinstance(data, dict):
|
|
259
|
+
original_type = native_type_name(data)
|
|
259
260
|
data = {'hosts': data}
|
|
260
261
|
display.deprecated(
|
|
261
|
-
msg=f"Group {group!r} was converted to {native_type_name(dict)!r} from {
|
|
262
|
+
msg=f"Group {group!r} was converted to {native_type_name(dict)!r} from {original_type!r}.",
|
|
262
263
|
version='2.23',
|
|
263
264
|
obj=origin,
|
|
264
265
|
)
|
|
@@ -89,8 +89,6 @@ import tomllib
|
|
|
89
89
|
from collections.abc import MutableMapping, MutableSequence
|
|
90
90
|
|
|
91
91
|
from ansible.errors import AnsibleFileNotFound, AnsibleParserError
|
|
92
|
-
from ansible.module_utils.common.text.converters import to_bytes, to_native
|
|
93
|
-
from ansible.module_utils.six import string_types
|
|
94
92
|
from ansible.plugins.inventory import BaseFileInventoryPlugin
|
|
95
93
|
from ansible.utils.display import Display
|
|
96
94
|
|
|
@@ -147,11 +145,10 @@ class InventoryModule(BaseFileInventoryPlugin):
|
|
|
147
145
|
)
|
|
148
146
|
|
|
149
147
|
def _load_file(self, file_name):
|
|
150
|
-
if not file_name or not isinstance(file_name,
|
|
151
|
-
raise AnsibleParserError("Invalid filename: '%s'" %
|
|
148
|
+
if not file_name or not isinstance(file_name, str):
|
|
149
|
+
raise AnsibleParserError("Invalid filename: '%s'" % file_name)
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
if not self.loader.path_exists(b_file_name):
|
|
151
|
+
if not self.loader.path_exists(file_name):
|
|
155
152
|
raise AnsibleFileNotFound("Unable to retrieve file contents", file_name=file_name)
|
|
156
153
|
|
|
157
154
|
try:
|
|
@@ -70,7 +70,6 @@ import os
|
|
|
70
70
|
from collections.abc import MutableMapping
|
|
71
71
|
|
|
72
72
|
from ansible.errors import AnsibleError, AnsibleParserError
|
|
73
|
-
from ansible.module_utils.six import string_types
|
|
74
73
|
from ansible.module_utils.common.text.converters import to_native, to_text
|
|
75
74
|
from ansible.plugins.inventory import BaseFileInventoryPlugin
|
|
76
75
|
|
|
@@ -136,7 +135,7 @@ class InventoryModule(BaseFileInventoryPlugin):
|
|
|
136
135
|
for section in ['vars', 'children', 'hosts']:
|
|
137
136
|
if section in group_data:
|
|
138
137
|
# convert strings to dicts as these are allowed
|
|
139
|
-
if isinstance(group_data[section],
|
|
138
|
+
if isinstance(group_data[section], str):
|
|
140
139
|
group_data[section] = {group_data[section]: None}
|
|
141
140
|
|
|
142
141
|
if not isinstance(group_data[section], (MutableMapping, NoneType)): # type: ignore[misc]
|
ansible/plugins/list.py
CHANGED
|
@@ -105,18 +105,25 @@ def _list_plugins_from_paths(ptype, dirs, collection, depth=0, docs=False):
|
|
|
105
105
|
]):
|
|
106
106
|
continue
|
|
107
107
|
|
|
108
|
+
resource_dir = to_native(os.path.dirname(full_path))
|
|
109
|
+
resource_name = get_composite_name(collection, plugin, resource_dir, depth)
|
|
110
|
+
|
|
108
111
|
if ptype in ('test', 'filter'):
|
|
112
|
+
# NOTE: pass the composite resource to ensure any relative
|
|
113
|
+
# imports it contains are interpreted in the correct context
|
|
114
|
+
if collection:
|
|
115
|
+
resource_name = '.'.join(resource_name.split('.')[2:])
|
|
109
116
|
try:
|
|
110
|
-
file_plugins = _list_j2_plugins_from_file(collection, full_path, ptype,
|
|
117
|
+
file_plugins = _list_j2_plugins_from_file(collection, full_path, ptype, resource_name)
|
|
111
118
|
except KeyError as e:
|
|
112
119
|
display.warning('Skipping file %s: %s' % (full_path, to_native(e)))
|
|
113
120
|
continue
|
|
114
121
|
|
|
115
122
|
for plugin in file_plugins:
|
|
116
|
-
plugin_name = get_composite_name(collection, plugin.ansible_name,
|
|
123
|
+
plugin_name = get_composite_name(collection, plugin.ansible_name, resource_dir, depth)
|
|
117
124
|
plugins[plugin_name] = full_path
|
|
118
125
|
else:
|
|
119
|
-
plugin_name =
|
|
126
|
+
plugin_name = resource_name
|
|
120
127
|
plugins[plugin_name] = full_path
|
|
121
128
|
else:
|
|
122
129
|
display.debug("Skip listing plugins in '{0}' as it is not a directory".format(path))
|
ansible/plugins/loader.py
CHANGED
|
@@ -27,7 +27,6 @@ from ansible import _internal, constants as C
|
|
|
27
27
|
from ansible.errors import AnsibleError, AnsiblePluginCircularRedirect, AnsiblePluginRemovedError, AnsibleCollectionUnsupportedVersionError
|
|
28
28
|
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
|
|
29
29
|
from ansible.module_utils.datatag import deprecator_from_collection_name
|
|
30
|
-
from ansible.module_utils.six import string_types
|
|
31
30
|
from ansible.parsing.yaml.loader import AnsibleLoader
|
|
32
31
|
from ansible._internal._yaml._loader import AnsibleInstrumentedLoader
|
|
33
32
|
from ansible.plugins import get_plugin_class, MODULE_CACHE, PATH_CACHE, PLUGIN_PATH_CACHE, AnsibleJinja2Plugin
|
|
@@ -36,6 +35,7 @@ from ansible.utils.collection_loader._collection_finder import _AnsibleCollectio
|
|
|
36
35
|
from ansible.utils.display import Display
|
|
37
36
|
from ansible.utils.plugin_docs import add_fragments
|
|
38
37
|
from ansible._internal._datatag import _tags
|
|
38
|
+
from ansible._internal import _display_utils
|
|
39
39
|
|
|
40
40
|
from . import _AnsiblePluginInfoMixin
|
|
41
41
|
from .filter import AnsibleJinja2Filter
|
|
@@ -96,7 +96,7 @@ def get_shell_plugin(shell_type=None, executable=None):
|
|
|
96
96
|
|
|
97
97
|
# mostly for backwards compat
|
|
98
98
|
if executable:
|
|
99
|
-
if isinstance(executable,
|
|
99
|
+
if isinstance(executable, str):
|
|
100
100
|
shell_filename = os.path.basename(executable)
|
|
101
101
|
try:
|
|
102
102
|
shell = shell_loader.get(shell_filename)
|
|
@@ -517,7 +517,7 @@ class PluginLoader:
|
|
|
517
517
|
# filename, cn = find_plugin_docfile( name, type_name, self, [os.path.dirname(path)], C.YAML_DOC_EXTENSIONS)
|
|
518
518
|
|
|
519
519
|
if dstring:
|
|
520
|
-
add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'))
|
|
520
|
+
add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'), section='DOCUMENTATION')
|
|
521
521
|
|
|
522
522
|
if 'options' in dstring and isinstance(dstring['options'], dict):
|
|
523
523
|
C.config.initialize_plugin_configuration_definitions(type_name, name, dstring['options'])
|
|
@@ -606,7 +606,7 @@ class PluginLoader:
|
|
|
606
606
|
warning_text = tombstone.get('warning_text') or ''
|
|
607
607
|
warning_plugin_type = "module" if self.type == "modules" else f'{self.type} plugin'
|
|
608
608
|
warning_text = f'The {fq_name!r} {warning_plugin_type} has been removed.{" " if warning_text else ""}{warning_text}'
|
|
609
|
-
removed_msg =
|
|
609
|
+
removed_msg = _display_utils.get_deprecation_message_with_plugin_info(
|
|
610
610
|
msg=warning_text,
|
|
611
611
|
version=removal_version,
|
|
612
612
|
date=removal_date,
|
|
@@ -1411,7 +1411,7 @@ class Jinja2Loader(PluginLoader):
|
|
|
1411
1411
|
removal_version = tombstone_entry.get('removal_version')
|
|
1412
1412
|
warning_text = f'The {key!r} {self.type} plugin has been removed.{" " if warning_text else ""}{warning_text}'
|
|
1413
1413
|
|
|
1414
|
-
exc_msg =
|
|
1414
|
+
exc_msg = _display_utils.get_deprecation_message_with_plugin_info(
|
|
1415
1415
|
msg=warning_text,
|
|
1416
1416
|
version=removal_version,
|
|
1417
1417
|
date=removal_date,
|
|
@@ -1674,7 +1674,7 @@ def _configure_collection_loader(prefix_collections_path=None):
|
|
|
1674
1674
|
|
|
1675
1675
|
# insert the internal ansible._protomatter collection up front
|
|
1676
1676
|
paths = [os.path.dirname(_internal.__file__)] + list(prefix_collections_path) + C.COLLECTIONS_PATHS
|
|
1677
|
-
finder = _AnsibleCollectionFinder(paths, C.COLLECTIONS_SCAN_SYS_PATH)
|
|
1677
|
+
finder = _AnsibleCollectionFinder(paths, C.COLLECTIONS_SCAN_SYS_PATH, internal_collections=paths[0])
|
|
1678
1678
|
finder._install()
|
|
1679
1679
|
|
|
1680
1680
|
# this should succeed now
|
|
@@ -134,7 +134,6 @@ import hashlib
|
|
|
134
134
|
|
|
135
135
|
from ansible.errors import AnsibleError, AnsibleAssertionError
|
|
136
136
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
137
|
-
from ansible.module_utils.six import string_types
|
|
138
137
|
from ansible.parsing.splitter import parse_kv
|
|
139
138
|
from ansible.plugins.lookup import LookupBase
|
|
140
139
|
from ansible.utils.encrypt import BaseHash, do_encrypt, random_password, random_salt
|
|
@@ -335,7 +334,7 @@ class LookupModule(LookupBase):
|
|
|
335
334
|
|
|
336
335
|
# chars still might need more
|
|
337
336
|
chars = params.get('chars', self.get_option('chars'))
|
|
338
|
-
if chars and isinstance(chars,
|
|
337
|
+
if chars and isinstance(chars, str):
|
|
339
338
|
tmp_chars = []
|
|
340
339
|
if u',,' in chars:
|
|
341
340
|
tmp_chars.append(u',')
|
|
@@ -83,7 +83,6 @@ _list:
|
|
|
83
83
|
"""
|
|
84
84
|
|
|
85
85
|
from ansible.errors import AnsibleError
|
|
86
|
-
from ansible.module_utils.six import string_types
|
|
87
86
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
88
87
|
from ansible.plugins.lookup import LookupBase
|
|
89
88
|
|
|
@@ -104,7 +103,7 @@ class LookupModule(LookupBase):
|
|
|
104
103
|
_raise_terms_error()
|
|
105
104
|
|
|
106
105
|
# first term should be a list (or dict), second a string holding the subkey
|
|
107
|
-
if not isinstance(terms[0], (list, dict)) or not isinstance(terms[1],
|
|
106
|
+
if not isinstance(terms[0], (list, dict)) or not isinstance(terms[1], str):
|
|
108
107
|
_raise_terms_error("first a dict or a list, second a string pointing to the subkey")
|
|
109
108
|
subelements = terms[1].split(".")
|
|
110
109
|
|
|
@@ -122,7 +121,7 @@ class LookupModule(LookupBase):
|
|
|
122
121
|
flags = {}
|
|
123
122
|
if len(terms) == 3:
|
|
124
123
|
flags = terms[2]
|
|
125
|
-
if not isinstance(flags, dict) and not all(isinstance(key,
|
|
124
|
+
if not isinstance(flags, dict) and not all(isinstance(key, str) and key in FLAGS for key in flags):
|
|
126
125
|
_raise_terms_error("the optional third item must be a dict with flags %s" % FLAGS)
|
|
127
126
|
|
|
128
127
|
# build_items
|
ansible/plugins/lookup/url.py
CHANGED
|
@@ -164,7 +164,7 @@ options:
|
|
|
164
164
|
description:
|
|
165
165
|
- SSL/TLS Ciphers to use for the request
|
|
166
166
|
- 'When a list is provided, all ciphers are joined in order with C(:)'
|
|
167
|
-
- See the L(OpenSSL Cipher List Format,https://
|
|
167
|
+
- See the L(OpenSSL Cipher List Format,https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-list-format)
|
|
168
168
|
for more details.
|
|
169
169
|
- The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions
|
|
170
170
|
type: list
|
|
@@ -52,7 +52,6 @@ import re
|
|
|
52
52
|
|
|
53
53
|
from ansible.errors import AnsibleError
|
|
54
54
|
from ansible.module_utils.common.text.converters import to_native
|
|
55
|
-
from ansible.module_utils.six import string_types
|
|
56
55
|
from ansible.plugins.lookup import LookupBase
|
|
57
56
|
|
|
58
57
|
|
|
@@ -69,7 +68,7 @@ class LookupModule(LookupBase):
|
|
|
69
68
|
variable_names = list(variables.keys())
|
|
70
69
|
for term in terms:
|
|
71
70
|
|
|
72
|
-
if not isinstance(term,
|
|
71
|
+
if not isinstance(term, str):
|
|
73
72
|
raise AnsibleError('Invalid setting identifier, "%s" is not a string, it is a %s' % (term, type(term)))
|
|
74
73
|
|
|
75
74
|
try:
|
|
@@ -24,12 +24,12 @@ import secrets
|
|
|
24
24
|
import shlex
|
|
25
25
|
import time
|
|
26
26
|
|
|
27
|
-
from collections.abc import Mapping, Sequence
|
|
28
|
-
|
|
29
27
|
from ansible.errors import AnsibleError
|
|
30
28
|
from ansible.module_utils.common.text.converters import to_native
|
|
31
|
-
from ansible.module_utils.six import text_type, string_types
|
|
32
29
|
from ansible.plugins import AnsiblePlugin
|
|
30
|
+
from ansible.utils.display import Display
|
|
31
|
+
|
|
32
|
+
display = Display()
|
|
33
33
|
|
|
34
34
|
_USER_HOME_PATH_RE = re.compile(r'^~[_.A-Za-z0-9][-_.A-Za-z0-9]*$')
|
|
35
35
|
|
|
@@ -84,7 +84,7 @@ class ShellBase(AnsiblePlugin):
|
|
|
84
84
|
return 'ansible-tmp-%s-%s-%s' % (time.time(), os.getpid(), secrets.randbelow(2**48))
|
|
85
85
|
|
|
86
86
|
def env_prefix(self, **kwargs):
|
|
87
|
-
return ' '.join(['%s=%s' % (k, self.quote(
|
|
87
|
+
return ' '.join(['%s=%s' % (k, self.quote(str(v))) for k, v in kwargs.items()])
|
|
88
88
|
|
|
89
89
|
def join_path(self, *args):
|
|
90
90
|
return os.path.join(*args)
|
|
@@ -272,6 +272,11 @@ class ShellBase(AnsiblePlugin):
|
|
|
272
272
|
|
|
273
273
|
def wrap_for_exec(self, cmd):
|
|
274
274
|
"""wrap script execution with any necessary decoration (eg '&' for quoted powershell script paths)"""
|
|
275
|
+
display.deprecated(
|
|
276
|
+
msg='The Shell.wrap_for_exec method is deprecated.',
|
|
277
|
+
help_text="Contact plugin author to update their plugin to not use this method.",
|
|
278
|
+
version='2.24',
|
|
279
|
+
)
|
|
275
280
|
return cmd
|
|
276
281
|
|
|
277
282
|
def quote(self, cmd):
|
|
@@ -192,7 +192,7 @@ class ShellModule(ShellBase):
|
|
|
192
192
|
|
|
193
193
|
def join_path(self, *args):
|
|
194
194
|
# use normpath() to remove doubled slashed and convert forward to backslashes
|
|
195
|
-
parts = [ntpath.normpath(
|
|
195
|
+
parts = [ntpath.normpath(arg) for arg in args]
|
|
196
196
|
|
|
197
197
|
# Because ntpath.join treats any component that begins with a backslash as an absolute path,
|
|
198
198
|
# we have to strip slashes from at least the beginning, otherwise join will ignore all previous
|
|
@@ -210,7 +210,6 @@ class ShellModule(ShellBase):
|
|
|
210
210
|
|
|
211
211
|
def path_has_trailing_slash(self, path):
|
|
212
212
|
# Allow Windows paths to be specified using either slash.
|
|
213
|
-
path = self._unquote(path)
|
|
214
213
|
return path.endswith('/') or path.endswith('\\')
|
|
215
214
|
|
|
216
215
|
def chmod(self, paths, mode):
|
|
@@ -223,11 +222,11 @@ class ShellModule(ShellBase):
|
|
|
223
222
|
raise NotImplementedError('set_user_facl is not implemented for Powershell')
|
|
224
223
|
|
|
225
224
|
def remove(self, path, recurse=False):
|
|
226
|
-
|
|
225
|
+
quoted_path = self._escape(path)
|
|
227
226
|
if recurse:
|
|
228
|
-
return self._encode_script("""Remove-Item '%s' -Force -Recurse;""" %
|
|
227
|
+
return self._encode_script("""Remove-Item '%s' -Force -Recurse;""" % quoted_path)
|
|
229
228
|
else:
|
|
230
|
-
return self._encode_script("""Remove-Item '%s' -Force;""" %
|
|
229
|
+
return self._encode_script("""Remove-Item '%s' -Force;""" % quoted_path)
|
|
231
230
|
|
|
232
231
|
def mkdtemp(
|
|
233
232
|
self,
|
|
@@ -240,7 +239,6 @@ class ShellModule(ShellBase):
|
|
|
240
239
|
# compatibility in case other action plugins outside Ansible calls this.
|
|
241
240
|
if not basefile:
|
|
242
241
|
basefile = self.__class__._generate_temp_dir_name()
|
|
243
|
-
basefile = self._escape(self._unquote(basefile))
|
|
244
242
|
basetmpdir = self._escape(tmpdir if tmpdir else self.get_option('remote_tmp'))
|
|
245
243
|
|
|
246
244
|
script = f"""
|
|
@@ -263,7 +261,6 @@ class ShellModule(ShellBase):
|
|
|
263
261
|
if not basefile:
|
|
264
262
|
basefile = self.__class__._generate_temp_dir_name()
|
|
265
263
|
|
|
266
|
-
basefile = self._unquote(basefile)
|
|
267
264
|
basetmpdir = tmpdir if tmpdir else self.get_option('remote_tmp')
|
|
268
265
|
|
|
269
266
|
script, stdin = _bootstrap_powershell_script("powershell_mkdtemp.ps1", {
|
|
@@ -283,7 +280,6 @@ class ShellModule(ShellBase):
|
|
|
283
280
|
) -> str:
|
|
284
281
|
# This is not called in Ansible anymore but it is kept for backwards
|
|
285
282
|
# compatibility in case other actions plugins outside Ansible called this.
|
|
286
|
-
user_home_path = self._unquote(user_home_path)
|
|
287
283
|
if user_home_path == '~':
|
|
288
284
|
script = 'Write-Output (Get-Location).Path'
|
|
289
285
|
elif user_home_path.startswith('~\\'):
|
|
@@ -297,7 +293,6 @@ class ShellModule(ShellBase):
|
|
|
297
293
|
user_home_path: str,
|
|
298
294
|
username: str = '',
|
|
299
295
|
) -> _ShellCommand:
|
|
300
|
-
user_home_path = self._unquote(user_home_path)
|
|
301
296
|
script, stdin = _bootstrap_powershell_script("powershell_expand_user.ps1", {
|
|
302
297
|
'Path': user_home_path,
|
|
303
298
|
})
|
|
@@ -308,7 +303,7 @@ class ShellModule(ShellBase):
|
|
|
308
303
|
)
|
|
309
304
|
|
|
310
305
|
def exists(self, path):
|
|
311
|
-
path = self._escape(
|
|
306
|
+
path = self._escape(path)
|
|
312
307
|
script = """
|
|
313
308
|
If (Test-Path '%s')
|
|
314
309
|
{
|
|
@@ -329,7 +324,7 @@ class ShellModule(ShellBase):
|
|
|
329
324
|
version="2.23",
|
|
330
325
|
help_text="Use `ActionBase._execute_remote_stat()` instead.",
|
|
331
326
|
)
|
|
332
|
-
path = self._escape(
|
|
327
|
+
path = self._escape(path)
|
|
333
328
|
script = """
|
|
334
329
|
If (Test-Path -PathType Leaf '%(path)s')
|
|
335
330
|
{
|
|
@@ -364,7 +359,7 @@ class ShellModule(ShellBase):
|
|
|
364
359
|
if arg_path:
|
|
365
360
|
# Running a module without the exec_wrapper and with an argument
|
|
366
361
|
# file.
|
|
367
|
-
script_path =
|
|
362
|
+
script_path = cmd_parts[0]
|
|
368
363
|
if not script_path.lower().endswith('.ps1'):
|
|
369
364
|
script_path += '.ps1'
|
|
370
365
|
|
|
@@ -387,7 +382,6 @@ class ShellModule(ShellBase):
|
|
|
387
382
|
cmd_parts.insert(0, shebang[2:])
|
|
388
383
|
elif not shebang:
|
|
389
384
|
# The module is assumed to be a binary
|
|
390
|
-
cmd_parts[0] = self._unquote(cmd_parts[0])
|
|
391
385
|
cmd_parts.append(arg_path)
|
|
392
386
|
script = """
|
|
393
387
|
Try
|
|
@@ -428,19 +422,9 @@ class ShellModule(ShellBase):
|
|
|
428
422
|
return self._encode_script(script, preserve_rc=False)
|
|
429
423
|
|
|
430
424
|
def wrap_for_exec(self, cmd):
|
|
425
|
+
super().wrap_for_exec(cmd)
|
|
431
426
|
return '& %s; exit $LASTEXITCODE' % cmd
|
|
432
427
|
|
|
433
|
-
def _unquote(self, value):
|
|
434
|
-
"""Remove any matching quotes that wrap the given value."""
|
|
435
|
-
value = to_text(value or '')
|
|
436
|
-
m = re.match(r'^\s*?\'(.*?)\'\s*?$', value)
|
|
437
|
-
if m:
|
|
438
|
-
return m.group(1)
|
|
439
|
-
m = re.match(r'^\s*?"(.*?)"\s*?$', value)
|
|
440
|
-
if m:
|
|
441
|
-
return m.group(1)
|
|
442
|
-
return value
|
|
443
|
-
|
|
444
428
|
def _escape(self, value):
|
|
445
429
|
"""Return value escaped for use in PowerShell single quotes."""
|
|
446
430
|
# There are 5 chars that need to be escaped in a single quote.
|
|
@@ -53,6 +53,9 @@ from ansible.utils.sentinel import Sentinel
|
|
|
53
53
|
from ansible.utils.vars import combine_vars
|
|
54
54
|
from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
|
|
55
55
|
|
|
56
|
+
if t.TYPE_CHECKING:
|
|
57
|
+
from ansible.playbook.role_include import IncludeRole
|
|
58
|
+
|
|
56
59
|
display = Display()
|
|
57
60
|
|
|
58
61
|
__all__ = ['StrategyBase']
|
|
@@ -581,7 +584,7 @@ class StrategyBase:
|
|
|
581
584
|
self._variable_manager.set_nonpersistent_facts(
|
|
582
585
|
original_host.name,
|
|
583
586
|
dict(
|
|
584
|
-
ansible_failed_task=original_task.
|
|
587
|
+
ansible_failed_task=original_task.dump_attrs(),
|
|
585
588
|
ansible_failed_result=task_result._return_data,
|
|
586
589
|
),
|
|
587
590
|
)
|
|
@@ -799,7 +802,7 @@ class StrategyBase:
|
|
|
799
802
|
|
|
800
803
|
return ret_results
|
|
801
804
|
|
|
802
|
-
def _copy_included_file(self, included_file: IncludedFile) ->
|
|
805
|
+
def _copy_included_file(self, included_file: IncludedFile) -> TaskInclude | IncludeRole:
|
|
803
806
|
"""
|
|
804
807
|
A proven safe and performant way to create a copy of an included file
|
|
805
808
|
"""
|
|
@@ -900,7 +903,7 @@ class StrategyBase:
|
|
|
900
903
|
display.warning("%s task does not support when conditional" % task_name)
|
|
901
904
|
|
|
902
905
|
def _execute_meta(self, task: Task, play_context, iterator, target_host: Host):
|
|
903
|
-
task.
|
|
906
|
+
task._resolved_action = 'ansible.builtin.meta' # _post_validate_args is never called for meta actions, so resolved_action hasn't been set
|
|
904
907
|
|
|
905
908
|
# meta tasks store their args in the _raw_params field of args,
|
|
906
909
|
# since they do not use k=v pairs, so get that
|
ansible/plugins/test/core.py
CHANGED
|
@@ -142,6 +142,9 @@ def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='s
|
|
|
142
142
|
This is likely only useful for `search` and `match` which already
|
|
143
143
|
have their own filters.
|
|
144
144
|
"""
|
|
145
|
+
valid_match_types = ('search', 'match', 'fullmatch')
|
|
146
|
+
if match_type not in valid_match_types:
|
|
147
|
+
raise errors.AnsibleTemplatePluginError(f"Invalid match_type specified. Expected one of: {', '.join(valid_match_types)}.", obj=match_type)
|
|
145
148
|
value = to_text(value, errors='surrogate_or_strict')
|
|
146
149
|
flags = 0
|
|
147
150
|
if ignorecase:
|
|
@@ -149,7 +152,7 @@ def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='s
|
|
|
149
152
|
if multiline:
|
|
150
153
|
flags |= re.M
|
|
151
154
|
_re = re.compile(pattern, flags=flags)
|
|
152
|
-
return bool(getattr(_re, match_type
|
|
155
|
+
return bool(getattr(_re, match_type)(value))
|
|
153
156
|
|
|
154
157
|
|
|
155
158
|
@accept_args_markers
|
ansible/plugins/test/regex.yml
CHANGED
|
@@ -3,7 +3,7 @@ DOCUMENTATION:
|
|
|
3
3
|
author: Ansible Core
|
|
4
4
|
short_description: Does string match regular expression from the start
|
|
5
5
|
description:
|
|
6
|
-
- Compare string against regular expression using Python's match or search functions.
|
|
6
|
+
- Compare string against regular expression using Python's match, fullmatch or search functions.
|
|
7
7
|
options:
|
|
8
8
|
_input:
|
|
9
9
|
description: String to match.
|
|
@@ -22,14 +22,26 @@ DOCUMENTATION:
|
|
|
22
22
|
type: boolean
|
|
23
23
|
default: False
|
|
24
24
|
match_type:
|
|
25
|
-
description:
|
|
25
|
+
description:
|
|
26
|
+
- Decide which function to be used to do the matching.
|
|
26
27
|
type: string
|
|
27
|
-
choices: [match, search]
|
|
28
|
+
choices: [match, search, fullmatch]
|
|
28
29
|
default: search
|
|
29
30
|
|
|
30
|
-
EXAMPLES:
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
EXAMPLES:
|
|
32
|
+
- name: check if string matches regex
|
|
33
|
+
assert:
|
|
34
|
+
that:
|
|
35
|
+
- 'url is regex("example\.com/\w+/foo")'
|
|
36
|
+
vars:
|
|
37
|
+
url: "https://example.com/users/foo/resources/bar"
|
|
38
|
+
|
|
39
|
+
- name: check if string matches regex ignoring case
|
|
40
|
+
assert:
|
|
41
|
+
that:
|
|
42
|
+
- 'url is regex("EXAMPLE\.COM/\w+/foo", ignorecase=True)'
|
|
43
|
+
vars:
|
|
44
|
+
url: "https://Example.com/users/foo/resources/bar"
|
|
33
45
|
|
|
34
46
|
RETURN:
|
|
35
47
|
_value:
|