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
ansible/modules/uri.py
CHANGED
|
@@ -438,13 +438,12 @@ import os
|
|
|
438
438
|
import re
|
|
439
439
|
import shutil
|
|
440
440
|
import tempfile
|
|
441
|
+
from collections.abc import Mapping, Sequence
|
|
441
442
|
from datetime import datetime, timezone
|
|
443
|
+
from urllib.parse import urlencode, urljoin
|
|
442
444
|
|
|
443
445
|
from ansible.module_utils.basic import AnsibleModule, sanitize_keys
|
|
444
|
-
from ansible.module_utils.six import binary_type, iteritems, string_types
|
|
445
|
-
from ansible.module_utils.six.moves.urllib.parse import urlencode, urljoin
|
|
446
446
|
from ansible.module_utils.common.text.converters import to_native, to_text
|
|
447
|
-
from ansible.module_utils.six.moves.collections_abc import Mapping, Sequence
|
|
448
447
|
from ansible.module_utils.urls import (
|
|
449
448
|
fetch_url,
|
|
450
449
|
get_response_filename,
|
|
@@ -479,7 +478,7 @@ def write_file(module, dest, content, resp):
|
|
|
479
478
|
try:
|
|
480
479
|
fd, tmpsrc = tempfile.mkstemp(dir=module.tmpdir)
|
|
481
480
|
with os.fdopen(fd, 'wb') as f:
|
|
482
|
-
if isinstance(content,
|
|
481
|
+
if isinstance(content, bytes):
|
|
483
482
|
f.write(content)
|
|
484
483
|
else:
|
|
485
484
|
shutil.copyfileobj(content, f)
|
|
@@ -521,14 +520,14 @@ def kv_list(data):
|
|
|
521
520
|
|
|
522
521
|
def form_urlencoded(body):
|
|
523
522
|
""" Convert data into a form-urlencoded string """
|
|
524
|
-
if isinstance(body,
|
|
523
|
+
if isinstance(body, str):
|
|
525
524
|
return body
|
|
526
525
|
|
|
527
526
|
if isinstance(body, (Mapping, Sequence)):
|
|
528
527
|
result = []
|
|
529
528
|
# Turn a list of lists into a list of tuples that urlencode accepts
|
|
530
529
|
for key, values in kv_list(body):
|
|
531
|
-
if isinstance(values,
|
|
530
|
+
if isinstance(values, str) or not isinstance(values, (Mapping, Sequence)):
|
|
532
531
|
values = [values]
|
|
533
532
|
for value in values:
|
|
534
533
|
if value is not None:
|
|
@@ -641,12 +640,12 @@ def main():
|
|
|
641
640
|
|
|
642
641
|
if body_format == 'json':
|
|
643
642
|
# Encode the body unless its a string, then assume it is pre-formatted JSON
|
|
644
|
-
if not isinstance(body,
|
|
643
|
+
if not isinstance(body, str):
|
|
645
644
|
body = json.dumps(body)
|
|
646
645
|
if 'content-type' not in [header.lower() for header in dict_headers]:
|
|
647
646
|
dict_headers['Content-Type'] = 'application/json'
|
|
648
647
|
elif body_format == 'form-urlencoded':
|
|
649
|
-
if not isinstance(body,
|
|
648
|
+
if not isinstance(body, str):
|
|
650
649
|
try:
|
|
651
650
|
body = form_urlencoded(body)
|
|
652
651
|
except ValueError as e:
|
|
@@ -747,7 +746,7 @@ def main():
|
|
|
747
746
|
# In python3, the headers are title cased. Lowercase them to be
|
|
748
747
|
# compatible with the python2 behaviour.
|
|
749
748
|
uresp = {}
|
|
750
|
-
for key, value in
|
|
749
|
+
for key, value in resp.items():
|
|
751
750
|
ukey = key.replace("-", "_").lower()
|
|
752
751
|
uresp[ukey] = value
|
|
753
752
|
|
|
@@ -755,7 +754,7 @@ def main():
|
|
|
755
754
|
uresp['location'] = urljoin(url, uresp['location'])
|
|
756
755
|
|
|
757
756
|
# Default content_encoding to try
|
|
758
|
-
if isinstance(content,
|
|
757
|
+
if isinstance(content, bytes):
|
|
759
758
|
u_content = to_text(content, encoding=content_encoding)
|
|
760
759
|
if maybe_json:
|
|
761
760
|
try:
|
ansible/modules/user.py
CHANGED
|
@@ -2504,11 +2504,10 @@ class DarwinUser(User):
|
|
|
2504
2504
|
Please note that password must be cleartext.
|
|
2505
2505
|
"""
|
|
2506
2506
|
# some documentation on how is stored passwords on OSX:
|
|
2507
|
-
# http://blog.lostpassword.com/2012/07/cracking-mac-os-x-lion-accounts-passwords/
|
|
2508
2507
|
# http://null-byte.wonderhowto.com/how-to/hack-mac-os-x-lion-passwords-0130036/
|
|
2509
2508
|
# http://pastebin.com/RYqxi7Ca
|
|
2510
2509
|
# on OSX 10.8+ hash is SALTED-SHA512-PBKDF2
|
|
2511
|
-
# https://
|
|
2510
|
+
# https://passlib.readthedocs.io/en/stable/lib/passlib.hash.pbkdf2_digest.html
|
|
2512
2511
|
# https://gist.github.com/nueh/8252572
|
|
2513
2512
|
cmd = self._get_dscl()
|
|
2514
2513
|
if self.password:
|
ansible/modules/wait_for.py
CHANGED
|
@@ -216,13 +216,13 @@ elapsed:
|
|
|
216
216
|
type: int
|
|
217
217
|
sample: 23
|
|
218
218
|
match_groups:
|
|
219
|
-
description: Tuple containing all the subgroups of the match as returned by U(https://docs.python.org/3/library/re.html#re.
|
|
219
|
+
description: Tuple containing all the subgroups of the match as returned by U(https://docs.python.org/3/library/re.html#re.Match.groups)
|
|
220
220
|
returned: always
|
|
221
221
|
type: list
|
|
222
222
|
sample: ['match 1', 'match 2']
|
|
223
223
|
match_groupdict:
|
|
224
224
|
description: Dictionary containing all the named subgroups of the match, keyed by the subgroup name,
|
|
225
|
-
as returned by U(https://docs.python.org/3/library/re.html#re.
|
|
225
|
+
as returned by U(https://docs.python.org/3/library/re.html#re.Match.groupdict)
|
|
226
226
|
returned: always
|
|
227
227
|
type: dict
|
|
228
228
|
sample:
|
|
@@ -104,9 +104,10 @@ EXAMPLES = r"""
|
|
|
104
104
|
- cmd.exe /c winrm.cmd quickconfig -quiet -force
|
|
105
105
|
delegate_to: localhost
|
|
106
106
|
|
|
107
|
-
- name: Wait for system to become reachable over WinRM
|
|
107
|
+
- name: Wait for system to become reachable over WinRM, polling every 10 seconds
|
|
108
108
|
ansible.builtin.wait_for_connection:
|
|
109
109
|
timeout: 900
|
|
110
|
+
sleep: 10
|
|
110
111
|
|
|
111
112
|
- name: Gather facts for first time
|
|
112
113
|
ansible.builtin.setup:
|
|
@@ -183,14 +183,6 @@ options:
|
|
|
183
183
|
- This parameter is deprecated as it has no effect with dnf as an underlying package manager
|
|
184
184
|
and will be removed in ansible-core 2.22.
|
|
185
185
|
type: bool
|
|
186
|
-
keepcache:
|
|
187
|
-
description:
|
|
188
|
-
- Either V(1) or V(0). Determines whether or not yum keeps the cache of
|
|
189
|
-
headers and packages after successful installation.
|
|
190
|
-
- This parameter is deprecated as it is only valid in the main configuration
|
|
191
|
-
and will be removed in ansible-core 2.20.
|
|
192
|
-
choices: ['0', '1']
|
|
193
|
-
type: str
|
|
194
186
|
metadata_expire:
|
|
195
187
|
description:
|
|
196
188
|
- Time (in seconds) after which the metadata will expire.
|
|
@@ -466,13 +458,7 @@ class YumRepo:
|
|
|
466
458
|
for key, value in sorted(self.params.items()):
|
|
467
459
|
if value is None:
|
|
468
460
|
continue
|
|
469
|
-
if key == '
|
|
470
|
-
self.module.deprecate(
|
|
471
|
-
"'keepcache' parameter is deprecated as it is only valid in "
|
|
472
|
-
"the main configuration.",
|
|
473
|
-
version='2.20'
|
|
474
|
-
)
|
|
475
|
-
elif key == 'async':
|
|
461
|
+
if key == 'async':
|
|
476
462
|
self.module.deprecate(
|
|
477
463
|
"'async' parameter is deprecated as it has been removed on systems supported by ansible-core",
|
|
478
464
|
version='2.22',
|
|
@@ -557,7 +543,6 @@ def main():
|
|
|
557
543
|
includepkgs=dict(type='list', elements='str'),
|
|
558
544
|
ip_resolve=dict(choices=['4', '6', 'IPv4', 'IPv6', 'whatever']),
|
|
559
545
|
keepalive=dict(type='bool'),
|
|
560
|
-
keepcache=dict(choices=['0', '1']),
|
|
561
546
|
metadata_expire=dict(),
|
|
562
547
|
metadata_expire_filter=dict(
|
|
563
548
|
choices=[
|
ansible/parsing/dataloader.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# Copyright: (c) 2017, Ansible Project
|
|
3
3
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
4
4
|
|
|
5
|
-
from __future__ import annotations
|
|
6
5
|
from __future__ import annotations
|
|
7
6
|
|
|
8
7
|
import copy
|
|
@@ -19,7 +18,6 @@ from ansible._internal._errors import _error_utils
|
|
|
19
18
|
from ansible.module_utils.basic import is_executable
|
|
20
19
|
from ansible._internal._datatag._tags import Origin, TrustedAsTemplate, SourceWasEncrypted
|
|
21
20
|
from ansible.module_utils._internal._datatag import AnsibleTagHelper
|
|
22
|
-
from ansible.module_utils.six import binary_type, text_type
|
|
23
21
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
24
22
|
from ansible.parsing.quoting import unquote
|
|
25
23
|
from ansible.parsing.utils.yaml import from_yaml
|
|
@@ -32,7 +30,7 @@ display = Display()
|
|
|
32
30
|
|
|
33
31
|
# Tries to determine if a path is inside a role, last dir must be 'tasks'
|
|
34
32
|
# this is not perfect but people should really avoid 'tasks' dirs outside roles when using Ansible.
|
|
35
|
-
RE_TASKS = re.compile(
|
|
33
|
+
RE_TASKS = re.compile('(?:^|%s)+tasks%s?$' % (os.path.sep, os.path.sep))
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
class DataLoader:
|
|
@@ -49,28 +47,27 @@ class DataLoader:
|
|
|
49
47
|
Usage:
|
|
50
48
|
|
|
51
49
|
dl = DataLoader()
|
|
52
|
-
# optionally: dl.set_vault_secrets([('default', ansible.parsing.vault.
|
|
50
|
+
# optionally: dl.set_vault_secrets([('default', ansible.parsing.vault.PromptVaultSecret(...),)])
|
|
53
51
|
ds = dl.load('...')
|
|
54
52
|
ds = dl.load_from_file('/path/to/file')
|
|
55
53
|
"""
|
|
56
54
|
|
|
57
|
-
def __init__(self):
|
|
55
|
+
def __init__(self) -> None:
|
|
58
56
|
|
|
59
|
-
self._basedir = '.'
|
|
57
|
+
self._basedir: str = '.'
|
|
60
58
|
|
|
61
59
|
# NOTE: not effective with forks as the main copy does not get updated.
|
|
62
60
|
# avoids rereading files
|
|
63
|
-
self._FILE_CACHE =
|
|
61
|
+
self._FILE_CACHE: dict[str, object] = {}
|
|
64
62
|
|
|
65
63
|
# NOTE: not thread safe, also issues with forks not returning data to main proc
|
|
66
64
|
# so they need to be cleaned independently. See WorkerProcess for example.
|
|
67
65
|
# used to keep track of temp files for cleaning
|
|
68
|
-
self._tempfiles = set()
|
|
66
|
+
self._tempfiles: set[str] = set()
|
|
69
67
|
|
|
70
68
|
# initialize the vault stuff with an empty password
|
|
71
69
|
# TODO: replace with a ref to something that can get the password
|
|
72
70
|
# a creds/auth provider
|
|
73
|
-
self._vaults = {}
|
|
74
71
|
self._vault = VaultLib()
|
|
75
72
|
self.set_vault_secrets(None)
|
|
76
73
|
|
|
@@ -230,23 +227,19 @@ class DataLoader:
|
|
|
230
227
|
|
|
231
228
|
def set_basedir(self, basedir: str) -> None:
|
|
232
229
|
""" sets the base directory, used to find files when a relative path is given """
|
|
233
|
-
|
|
234
|
-
if basedir is not None:
|
|
235
|
-
self._basedir = to_text(basedir)
|
|
230
|
+
self._basedir = basedir
|
|
236
231
|
|
|
237
232
|
def path_dwim(self, given: str) -> str:
|
|
238
233
|
"""
|
|
239
234
|
make relative paths work like folks expect.
|
|
240
235
|
"""
|
|
241
236
|
|
|
242
|
-
given = to_text(given, errors='surrogate_or_strict')
|
|
243
237
|
given = unquote(given)
|
|
244
238
|
|
|
245
|
-
if given.startswith(
|
|
239
|
+
if given.startswith(os.path.sep) or given.startswith('~'):
|
|
246
240
|
path = given
|
|
247
241
|
else:
|
|
248
|
-
|
|
249
|
-
path = os.path.join(basedir, given)
|
|
242
|
+
path = os.path.join(self._basedir, given)
|
|
250
243
|
|
|
251
244
|
return unfrackpath(path, follow=False)
|
|
252
245
|
|
|
@@ -294,10 +287,9 @@ class DataLoader:
|
|
|
294
287
|
"""
|
|
295
288
|
|
|
296
289
|
search = []
|
|
297
|
-
source = to_text(source, errors='surrogate_or_strict')
|
|
298
290
|
|
|
299
291
|
# I have full path, nothing else needs to be looked at
|
|
300
|
-
if source.startswith(
|
|
292
|
+
if source.startswith(os.path.sep) or source.startswith('~'):
|
|
301
293
|
search.append(unfrackpath(source, follow=False))
|
|
302
294
|
else:
|
|
303
295
|
# base role/play path + templates/files/vars + relative filename
|
|
@@ -364,7 +356,7 @@ class DataLoader:
|
|
|
364
356
|
if os.path.exists(to_bytes(test_path, errors='surrogate_or_strict')):
|
|
365
357
|
result = test_path
|
|
366
358
|
else:
|
|
367
|
-
display.debug(
|
|
359
|
+
display.debug('evaluation_path:\n\t%s' % '\n\t'.join(paths))
|
|
368
360
|
for path in paths:
|
|
369
361
|
upath = unfrackpath(path, follow=False)
|
|
370
362
|
b_upath = to_bytes(upath, errors='surrogate_or_strict')
|
|
@@ -385,9 +377,9 @@ class DataLoader:
|
|
|
385
377
|
search.append(os.path.join(to_bytes(self.get_basedir(), errors='surrogate_or_strict'), b_dirname, b_source))
|
|
386
378
|
search.append(os.path.join(to_bytes(self.get_basedir(), errors='surrogate_or_strict'), b_source))
|
|
387
379
|
|
|
388
|
-
display.debug(
|
|
380
|
+
display.debug('search_path:\n\t%s' % to_text(b'\n\t'.join(search)))
|
|
389
381
|
for b_candidate in search:
|
|
390
|
-
display.vvvvv(
|
|
382
|
+
display.vvvvv('looking for "%s" at "%s"' % (source, to_text(b_candidate)))
|
|
391
383
|
if os.path.exists(b_candidate):
|
|
392
384
|
result = to_text(b_candidate)
|
|
393
385
|
break
|
|
@@ -418,11 +410,10 @@ class DataLoader:
|
|
|
418
410
|
Temporary files are cleanup in the destructor
|
|
419
411
|
"""
|
|
420
412
|
|
|
421
|
-
if not file_path or not isinstance(file_path, (
|
|
413
|
+
if not file_path or not isinstance(file_path, (bytes, str)):
|
|
422
414
|
raise AnsibleParserError("Invalid filename: '%s'" % to_native(file_path))
|
|
423
415
|
|
|
424
|
-
|
|
425
|
-
if not self.path_exists(b_file_path) or not self.is_file(b_file_path):
|
|
416
|
+
if not self.path_exists(file_path) or not self.is_file(file_path):
|
|
426
417
|
raise AnsibleFileNotFound(file_name=file_path)
|
|
427
418
|
|
|
428
419
|
real_path = self.path_dwim(file_path)
|
|
@@ -480,7 +471,7 @@ class DataLoader:
|
|
|
480
471
|
"""
|
|
481
472
|
|
|
482
473
|
b_path = to_bytes(os.path.join(path, name))
|
|
483
|
-
found = []
|
|
474
|
+
found: list[str] = []
|
|
484
475
|
|
|
485
476
|
if extensions is None:
|
|
486
477
|
# Look for file with no extension first to find dir before file
|
|
@@ -489,27 +480,29 @@ class DataLoader:
|
|
|
489
480
|
for ext in extensions:
|
|
490
481
|
|
|
491
482
|
if '.' in ext:
|
|
492
|
-
|
|
483
|
+
b_full_path = b_path + to_bytes(ext)
|
|
493
484
|
elif ext:
|
|
494
|
-
|
|
485
|
+
b_full_path = b'.'.join([b_path, to_bytes(ext)])
|
|
495
486
|
else:
|
|
496
|
-
|
|
487
|
+
b_full_path = b_path
|
|
488
|
+
|
|
489
|
+
full_path = to_text(b_full_path)
|
|
497
490
|
|
|
498
491
|
if self.path_exists(full_path):
|
|
499
492
|
if self.is_directory(full_path):
|
|
500
493
|
if allow_dir:
|
|
501
|
-
found.extend(self._get_dir_vars_files(
|
|
494
|
+
found.extend(self._get_dir_vars_files(full_path, extensions))
|
|
502
495
|
else:
|
|
503
496
|
continue
|
|
504
497
|
else:
|
|
505
|
-
found.append(
|
|
498
|
+
found.append(full_path)
|
|
506
499
|
break
|
|
507
500
|
return found
|
|
508
501
|
|
|
509
502
|
def _get_dir_vars_files(self, path: str, extensions: list[str]) -> list[str]:
|
|
510
503
|
found = []
|
|
511
504
|
for spath in sorted(self.list_directory(path)):
|
|
512
|
-
if not spath.startswith(
|
|
505
|
+
if not spath.startswith('.') and not spath.endswith('~'): # skip hidden and backups
|
|
513
506
|
|
|
514
507
|
ext = os.path.splitext(spath)[-1]
|
|
515
508
|
full_spath = os.path.join(path, spath)
|
ansible/parsing/mod_args.py
CHANGED
|
@@ -130,6 +130,7 @@ class ModuleArgsParser:
|
|
|
130
130
|
# HACK: why are these not FieldAttributes on task with a post-validate to check usage?
|
|
131
131
|
self._task_attrs.update(['local_action', 'static'])
|
|
132
132
|
self._task_attrs = frozenset(self._task_attrs)
|
|
133
|
+
self._resolved_action = None
|
|
133
134
|
|
|
134
135
|
def _split_module_string(self, module_string: str) -> tuple[str, str]:
|
|
135
136
|
"""
|
|
@@ -344,6 +345,8 @@ class ModuleArgsParser:
|
|
|
344
345
|
raise e
|
|
345
346
|
|
|
346
347
|
is_action_candidate = context.resolved and bool(context.redirect_list)
|
|
348
|
+
if is_action_candidate:
|
|
349
|
+
self._resolved_action = context.resolved_fqcn
|
|
347
350
|
|
|
348
351
|
if is_action_candidate:
|
|
349
352
|
# finding more than one module name is a problem
|
|
@@ -59,7 +59,6 @@ except ImportError:
|
|
|
59
59
|
|
|
60
60
|
from ansible.errors import AnsibleError, AnsibleAssertionError
|
|
61
61
|
from ansible import constants as C
|
|
62
|
-
from ansible.module_utils.six import binary_type
|
|
63
62
|
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
|
|
64
63
|
from ansible.utils.display import Display
|
|
65
64
|
from ansible.utils.path import makedirs_safe, unfrackpath
|
|
@@ -1237,7 +1236,7 @@ class VaultAES256:
|
|
|
1237
1236
|
|
|
1238
1237
|
It would be nice if there were a library for this but hey.
|
|
1239
1238
|
"""
|
|
1240
|
-
if not (isinstance(b_a,
|
|
1239
|
+
if not (isinstance(b_a, bytes) and isinstance(b_b, bytes)):
|
|
1241
1240
|
raise TypeError('_is_equal can only be used to compare two byte strings')
|
|
1242
1241
|
|
|
1243
1242
|
# http://codahale.com/a-lesson-in-timing-attacks/
|
ansible/playbook/base.py
CHANGED
|
@@ -19,7 +19,6 @@ from ansible import context
|
|
|
19
19
|
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleAssertionError, AnsibleValueOmittedError, AnsibleFieldAttributeError
|
|
20
20
|
from ansible.module_utils.datatag import native_type_name
|
|
21
21
|
from ansible._internal._datatag._tags import Origin
|
|
22
|
-
from ansible.module_utils.six import string_types
|
|
23
22
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
24
23
|
from ansible.module_utils.common.sentinel import Sentinel
|
|
25
24
|
from ansible.module_utils.common.text.converters import to_text
|
|
@@ -37,7 +36,7 @@ display = Display()
|
|
|
37
36
|
def _validate_action_group_metadata(action, found_group_metadata, fq_group_name):
|
|
38
37
|
valid_metadata = {
|
|
39
38
|
'extend_group': {
|
|
40
|
-
'types': (list,
|
|
39
|
+
'types': (list, str,),
|
|
41
40
|
'errortype': 'list',
|
|
42
41
|
},
|
|
43
42
|
}
|
|
@@ -204,7 +203,7 @@ class FieldAttributeBase:
|
|
|
204
203
|
value = self.set_to_context(attr.name)
|
|
205
204
|
|
|
206
205
|
valid_values = frozenset(('always', 'on_failed', 'on_unreachable', 'on_skipped', 'never'))
|
|
207
|
-
if value and isinstance(value,
|
|
206
|
+
if value and isinstance(value, str) and value not in valid_values:
|
|
208
207
|
raise AnsibleParserError("'%s' is not a valid value for debugger. Must be one of %s" % (value, ', '.join(valid_values)), obj=self.get_ds())
|
|
209
208
|
return value
|
|
210
209
|
|
|
@@ -350,14 +349,14 @@ class FieldAttributeBase:
|
|
|
350
349
|
found_group_metadata = False
|
|
351
350
|
for action in action_group:
|
|
352
351
|
# Everything should be a string except the metadata entry
|
|
353
|
-
if not isinstance(action,
|
|
352
|
+
if not isinstance(action, str):
|
|
354
353
|
_validate_action_group_metadata(action, found_group_metadata, fq_group_name)
|
|
355
354
|
|
|
356
355
|
if isinstance(action['metadata'], dict):
|
|
357
356
|
found_group_metadata = True
|
|
358
357
|
|
|
359
358
|
include_groups = action['metadata'].get('extend_group', [])
|
|
360
|
-
if isinstance(include_groups,
|
|
359
|
+
if isinstance(include_groups, str):
|
|
361
360
|
include_groups = [include_groups]
|
|
362
361
|
if not isinstance(include_groups, list):
|
|
363
362
|
# Bad entries may be a warning above, but prevent tracebacks by setting it back to the acceptable type.
|
|
@@ -472,7 +471,7 @@ class FieldAttributeBase:
|
|
|
472
471
|
elif attribute.isa == 'percent':
|
|
473
472
|
# special value, which may be an integer or float
|
|
474
473
|
# with an optional '%' at the end
|
|
475
|
-
if isinstance(value,
|
|
474
|
+
if isinstance(value, str) and '%' in value:
|
|
476
475
|
value = value.replace('%', '')
|
|
477
476
|
value = float(value)
|
|
478
477
|
elif attribute.isa == 'list':
|
|
@@ -660,8 +659,8 @@ class FieldAttributeBase:
|
|
|
660
659
|
attrs = {}
|
|
661
660
|
for (name, attribute) in self.fattributes.items():
|
|
662
661
|
attr = getattr(self, name)
|
|
663
|
-
if attribute.isa == 'class'
|
|
664
|
-
attrs[name] = attr.
|
|
662
|
+
if attribute.isa == 'class':
|
|
663
|
+
attrs[name] = attr.dump_attrs()
|
|
665
664
|
else:
|
|
666
665
|
attrs[name] = attr
|
|
667
666
|
return attrs
|
|
@@ -675,60 +674,13 @@ class FieldAttributeBase:
|
|
|
675
674
|
attribute = self.fattributes[attr]
|
|
676
675
|
if attribute.isa == 'class' and isinstance(value, dict):
|
|
677
676
|
obj = attribute.class_type()
|
|
678
|
-
obj.
|
|
677
|
+
obj.from_attrs(value)
|
|
679
678
|
setattr(self, attr, obj)
|
|
680
679
|
else:
|
|
681
680
|
setattr(self, attr, value)
|
|
682
681
|
else:
|
|
683
682
|
setattr(self, attr, value) # overridden dump_attrs in derived types may dump attributes which are not field attributes
|
|
684
683
|
|
|
685
|
-
# from_attrs is only used to create a finalized task
|
|
686
|
-
# from attrs from the Worker/TaskExecutor
|
|
687
|
-
# Those attrs are finalized and squashed in the TE
|
|
688
|
-
# and controller side use needs to reflect that
|
|
689
|
-
self._finalized = True
|
|
690
|
-
self._squashed = True
|
|
691
|
-
|
|
692
|
-
def serialize(self):
|
|
693
|
-
"""
|
|
694
|
-
Serializes the object derived from the base object into
|
|
695
|
-
a dictionary of values. This only serializes the field
|
|
696
|
-
attributes for the object, so this may need to be overridden
|
|
697
|
-
for any classes which wish to add additional items not stored
|
|
698
|
-
as field attributes.
|
|
699
|
-
"""
|
|
700
|
-
|
|
701
|
-
repr = self.dump_attrs()
|
|
702
|
-
|
|
703
|
-
# serialize the uuid field
|
|
704
|
-
repr['uuid'] = self._uuid
|
|
705
|
-
repr['finalized'] = self._finalized
|
|
706
|
-
repr['squashed'] = self._squashed
|
|
707
|
-
|
|
708
|
-
return repr
|
|
709
|
-
|
|
710
|
-
def deserialize(self, data):
|
|
711
|
-
"""
|
|
712
|
-
Given a dictionary of values, load up the field attributes for
|
|
713
|
-
this object. As with serialize(), if there are any non-field
|
|
714
|
-
attribute data members, this method will need to be overridden
|
|
715
|
-
and extended.
|
|
716
|
-
"""
|
|
717
|
-
|
|
718
|
-
if not isinstance(data, dict):
|
|
719
|
-
raise AnsibleAssertionError('data (%s) should be a dict but is a %s' % (data, type(data)))
|
|
720
|
-
|
|
721
|
-
for (name, attribute) in self.fattributes.items():
|
|
722
|
-
if name in data:
|
|
723
|
-
setattr(self, name, data[name])
|
|
724
|
-
else:
|
|
725
|
-
self.set_to_context(name)
|
|
726
|
-
|
|
727
|
-
# restore the UUID field
|
|
728
|
-
setattr(self, '_uuid', data.get('uuid'))
|
|
729
|
-
self._finalized = data.get('finalized', False)
|
|
730
|
-
self._squashed = data.get('squashed', False)
|
|
731
|
-
|
|
732
684
|
|
|
733
685
|
class Base(FieldAttributeBase):
|
|
734
686
|
|
ansible/playbook/block.py
CHANGED
|
@@ -27,7 +27,6 @@ from ansible.playbook.collectionsearch import CollectionSearch
|
|
|
27
27
|
from ansible.playbook.delegatable import Delegatable
|
|
28
28
|
from ansible.playbook.helpers import load_list_of_tasks
|
|
29
29
|
from ansible.playbook.notifiable import Notifiable
|
|
30
|
-
from ansible.playbook.role import Role
|
|
31
30
|
from ansible.playbook.taggable import Taggable
|
|
32
31
|
|
|
33
32
|
|
|
@@ -220,65 +219,6 @@ class Block(Base, Conditional, CollectionSearch, Taggable, Notifiable, Delegatab
|
|
|
220
219
|
new_me.validate()
|
|
221
220
|
return new_me
|
|
222
221
|
|
|
223
|
-
def serialize(self):
|
|
224
|
-
"""
|
|
225
|
-
Override of the default serialize method, since when we're serializing
|
|
226
|
-
a task we don't want to include the attribute list of tasks.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
data = dict()
|
|
230
|
-
for attr in self.fattributes:
|
|
231
|
-
if attr not in ('block', 'rescue', 'always'):
|
|
232
|
-
data[attr] = getattr(self, attr)
|
|
233
|
-
|
|
234
|
-
data['dep_chain'] = self.get_dep_chain()
|
|
235
|
-
|
|
236
|
-
if self._role is not None:
|
|
237
|
-
data['role'] = self._role.serialize()
|
|
238
|
-
if self._parent is not None:
|
|
239
|
-
data['parent'] = self._parent.copy(exclude_tasks=True).serialize()
|
|
240
|
-
data['parent_type'] = self._parent.__class__.__name__
|
|
241
|
-
|
|
242
|
-
return data
|
|
243
|
-
|
|
244
|
-
def deserialize(self, data):
|
|
245
|
-
"""
|
|
246
|
-
Override of the default deserialize method, to match the above overridden
|
|
247
|
-
serialize method
|
|
248
|
-
"""
|
|
249
|
-
|
|
250
|
-
# import is here to avoid import loops
|
|
251
|
-
from ansible.playbook.task_include import TaskInclude
|
|
252
|
-
from ansible.playbook.handler_task_include import HandlerTaskInclude
|
|
253
|
-
|
|
254
|
-
# we don't want the full set of attributes (the task lists), as that
|
|
255
|
-
# would lead to a serialize/deserialize loop
|
|
256
|
-
for attr in self.fattributes:
|
|
257
|
-
if attr in data and attr not in ('block', 'rescue', 'always'):
|
|
258
|
-
setattr(self, attr, data.get(attr))
|
|
259
|
-
|
|
260
|
-
self._dep_chain = data.get('dep_chain', None)
|
|
261
|
-
|
|
262
|
-
# if there was a serialized role, unpack it too
|
|
263
|
-
role_data = data.get('role')
|
|
264
|
-
if role_data:
|
|
265
|
-
r = Role()
|
|
266
|
-
r.deserialize(role_data)
|
|
267
|
-
self._role = r
|
|
268
|
-
|
|
269
|
-
parent_data = data.get('parent')
|
|
270
|
-
if parent_data:
|
|
271
|
-
parent_type = data.get('parent_type')
|
|
272
|
-
if parent_type == 'Block':
|
|
273
|
-
p = Block()
|
|
274
|
-
elif parent_type == 'TaskInclude':
|
|
275
|
-
p = TaskInclude()
|
|
276
|
-
elif parent_type == 'HandlerTaskInclude':
|
|
277
|
-
p = HandlerTaskInclude()
|
|
278
|
-
p.deserialize(parent_data)
|
|
279
|
-
self._parent = p
|
|
280
|
-
self._dep_chain = self._parent.get_dep_chain()
|
|
281
|
-
|
|
282
222
|
def set_loader(self, loader):
|
|
283
223
|
self._loader = loader
|
|
284
224
|
if self._parent:
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from ansible.module_utils.six import string_types
|
|
7
6
|
from ansible.playbook.attribute import FieldAttribute
|
|
8
7
|
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
|
9
8
|
from ansible.utils.display import Display
|
|
@@ -32,7 +31,7 @@ def _ensure_default_collection(collection_list=None):
|
|
|
32
31
|
class CollectionSearch:
|
|
33
32
|
|
|
34
33
|
# this needs to be populated before we can resolve tasks/roles/etc
|
|
35
|
-
collections = FieldAttribute(isa='list', listof=
|
|
34
|
+
collections = FieldAttribute(isa='list', listof=(str,), priority=100, default=_ensure_default_collection, always_post_validate=True, static=True)
|
|
36
35
|
|
|
37
36
|
def _load_collections(self, attr, ds):
|
|
38
37
|
# We are always a mixin with Base, so we can validate this untemplated
|
ansible/playbook/handler.py
CHANGED
|
@@ -20,12 +20,11 @@ from __future__ import annotations
|
|
|
20
20
|
from ansible.errors import AnsibleAssertionError
|
|
21
21
|
from ansible.playbook.attribute import NonInheritableFieldAttribute
|
|
22
22
|
from ansible.playbook.task import Task
|
|
23
|
-
from ansible.module_utils.six import string_types
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
class Handler(Task):
|
|
27
26
|
|
|
28
|
-
listen = NonInheritableFieldAttribute(isa='list', default=list, listof=
|
|
27
|
+
listen = NonInheritableFieldAttribute(isa='list', default=list, listof=(str,), static=True)
|
|
29
28
|
|
|
30
29
|
def __init__(self, block=None, role=None, task_include=None):
|
|
31
30
|
self.notified_hosts = []
|
|
@@ -72,8 +71,3 @@ class Handler(Task):
|
|
|
72
71
|
|
|
73
72
|
def is_host_notified(self, host):
|
|
74
73
|
return host in self.notified_hosts
|
|
75
|
-
|
|
76
|
-
def serialize(self):
|
|
77
|
-
result = super(Handler, self).serialize()
|
|
78
|
-
result['is_handler'] = True
|
|
79
|
-
return result
|
ansible/playbook/helpers.py
CHANGED
|
@@ -230,13 +230,6 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|
|
230
230
|
variable_manager=variable_manager,
|
|
231
231
|
)
|
|
232
232
|
|
|
233
|
-
tags = ti_copy.tags[:]
|
|
234
|
-
|
|
235
|
-
# now we extend the tags on each of the included blocks
|
|
236
|
-
for b in included_blocks:
|
|
237
|
-
b.tags = list(set(b.tags).union(tags))
|
|
238
|
-
# FIXME - END
|
|
239
|
-
|
|
240
233
|
# FIXME: handlers shouldn't need this special handling, but do
|
|
241
234
|
# right now because they don't iterate blocks correctly
|
|
242
235
|
if use_handlers:
|
|
@@ -203,7 +203,7 @@ class IncludedFile:
|
|
|
203
203
|
for from_arg in new_task.FROM_ARGS:
|
|
204
204
|
if from_arg in include_args:
|
|
205
205
|
from_key = from_arg.removesuffix('_from')
|
|
206
|
-
new_task._from_files[from_key] = include_args.
|
|
206
|
+
new_task._from_files[from_key] = include_args.get(from_arg)
|
|
207
207
|
|
|
208
208
|
inc_file = IncludedFile(role_name, include_args, special_vars, new_task, is_role=True)
|
|
209
209
|
|