ansible-core 2.19.0b5__py3-none-any.whl → 2.19.0b7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_templating/_jinja_bits.py +22 -4
- ansible/_internal/_templating/_jinja_common.py +1 -1
- ansible/_internal/_templating/_jinja_plugins.py +5 -2
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +6 -0
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/cli/__init__.py +9 -14
- ansible/cli/adhoc.py +6 -3
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/console.py +2 -2
- ansible/cli/doc.py +4 -4
- ansible/cli/inventory.py +5 -7
- ansible/config/base.yml +33 -6
- ansible/errors/__init__.py +2 -1
- ansible/executor/module_common.py +75 -44
- ansible/executor/powershell/psrp_put_file.ps1 +1 -1
- ansible/executor/process/worker.py +2 -2
- ansible/executor/task_executor.py +2 -2
- ansible/executor/task_queue_manager.py +34 -70
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +3 -6
- ansible/galaxy/collection/__init__.py +1 -6
- ansible/galaxy/collection/concrete_artifact_manager.py +4 -10
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/galaxy/role.py +2 -2
- ansible/inventory/group.py +6 -1
- ansible/inventory/host.py +6 -1
- ansible/module_utils/_internal/__init__.py +7 -4
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +10 -38
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +23 -1
- ansible/module_utils/_internal/_deprecator.py +39 -34
- ansible/module_utils/_internal/_json/_profiles/__init__.py +1 -0
- ansible/module_utils/_internal/_messages.py +26 -2
- ansible/module_utils/_internal/_plugin_info.py +14 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +58 -70
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
- ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
- ansible/module_utils/facts/hardware/base.py +1 -1
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/other/facter.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/distribution.py +2 -2
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
- ansible/module_utils/service.py +1 -1
- ansible/module_utils/urls.py +5 -5
- ansible/modules/apt.py +9 -3
- ansible/modules/apt_repository.py +10 -10
- ansible/modules/assemble.py +7 -5
- ansible/modules/async_wrapper.py +7 -17
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/expect.py +5 -5
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/hostname.py +2 -2
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/pip.py +9 -11
- ansible/modules/raw.py +2 -2
- ansible/modules/replace.py +2 -2
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +6 -8
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +38 -33
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/mod_args.py +38 -20
- ansible/parsing/vault/__init__.py +9 -13
- ansible/playbook/base.py +7 -4
- ansible/playbook/helpers.py +1 -1
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/playbook_include.py +23 -56
- ansible/playbook/role/__init__.py +38 -21
- ansible/playbook/taggable.py +19 -5
- ansible/playbook/task.py +2 -0
- ansible/plugins/action/__init__.py +2 -2
- ansible/plugins/action/assemble.py +2 -1
- ansible/plugins/action/assert.py +2 -2
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/script.py +5 -4
- ansible/plugins/action/template.py +9 -3
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/__init__.py +77 -87
- ansible/plugins/callback/default.py +0 -3
- ansible/plugins/callback/junit.py +0 -6
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +4 -4
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +9 -7
- ansible/plugins/connection/winrm.py +1 -1
- ansible/plugins/filter/core.py +19 -21
- ansible/plugins/filter/encryption.py +10 -2
- ansible/plugins/filter/pow.yml +1 -1
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/strftime.yml +3 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +5 -4
- ansible/plugins/loader.py +5 -0
- ansible/plugins/lookup/password.py +4 -6
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/powershell.py +3 -2
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +2 -2
- ansible/release.py +1 -1
- ansible/template/__init__.py +9 -53
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +38 -37
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -7
- ansible/utils/path.py +6 -8
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/utils/vars.py +4 -1
- ansible/vars/manager.py +6 -3
- ansible/vars/plugins.py +3 -3
- ansible/vars/reserved.py +6 -4
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +184 -173
- ansible_test/_internal/__init__.py +5 -0
- ansible_test/_internal/ansible_util.py +1 -1
- ansible_test/_internal/classification/python.py +6 -0
- ansible_test/_internal/cli/commands/__init__.py +0 -5
- ansible_test/_internal/cli/environments.py +51 -5
- ansible_test/_internal/commands/coverage/__init__.py +1 -1
- ansible_test/_internal/commands/integration/__init__.py +18 -5
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/commands/sanity/__init__.py +3 -1
- ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
- ansible_test/_internal/commands/shell/__init__.py +43 -4
- ansible_test/_internal/commands/units/__init__.py +4 -1
- ansible_test/_internal/config.py +21 -13
- ansible_test/_internal/debugging.py +166 -0
- ansible_test/_internal/delegation.py +21 -13
- ansible_test/_internal/host_profiles.py +259 -16
- ansible_test/_internal/inventory.py +4 -0
- ansible_test/_internal/metadata.py +94 -4
- ansible_test/_internal/processes.py +80 -0
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/python_requirements.py +27 -0
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/target.py +8 -0
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +20 -12
- ansible_test/_internal/util_common.py +13 -3
- ansible_test/_util/target/injector/python.py +8 -0
- ansible_test/_util/target/setup/requirements.py +3 -9
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ A future release will remove the provisional status.
|
|
8
8
|
from __future__ import annotations as _annotations
|
9
9
|
|
10
10
|
import dataclasses as _dataclasses
|
11
|
+
import enum as _enum
|
11
12
|
import sys as _sys
|
12
13
|
import typing as _t
|
13
14
|
|
@@ -21,14 +22,37 @@ else:
|
|
21
22
|
_dataclass_kwargs = dict(frozen=True)
|
22
23
|
|
23
24
|
|
25
|
+
class PluginType(_datatag.AnsibleSerializableEnum):
|
26
|
+
"""Enum of Ansible plugin types."""
|
27
|
+
|
28
|
+
ACTION = _enum.auto()
|
29
|
+
BECOME = _enum.auto()
|
30
|
+
CACHE = _enum.auto()
|
31
|
+
CALLBACK = _enum.auto()
|
32
|
+
CLICONF = _enum.auto()
|
33
|
+
CONNECTION = _enum.auto()
|
34
|
+
DOC_FRAGMENTS = _enum.auto()
|
35
|
+
FILTER = _enum.auto()
|
36
|
+
HTTPAPI = _enum.auto()
|
37
|
+
INVENTORY = _enum.auto()
|
38
|
+
LOOKUP = _enum.auto()
|
39
|
+
MODULE = _enum.auto()
|
40
|
+
NETCONF = _enum.auto()
|
41
|
+
SHELL = _enum.auto()
|
42
|
+
STRATEGY = _enum.auto()
|
43
|
+
TERMINAL = _enum.auto()
|
44
|
+
TEST = _enum.auto()
|
45
|
+
VARS = _enum.auto()
|
46
|
+
|
47
|
+
|
24
48
|
@_dataclasses.dataclass(**_dataclass_kwargs)
|
25
49
|
class PluginInfo(_datatag.AnsibleSerializableDataclass):
|
26
50
|
"""Information about a loaded plugin."""
|
27
51
|
|
28
|
-
resolved_name: str
|
52
|
+
resolved_name: _t.Optional[str]
|
29
53
|
"""The resolved canonical plugin name; always fully-qualified for collection plugins."""
|
30
54
|
|
31
|
-
type:
|
55
|
+
type: _t.Optional[PluginType]
|
32
56
|
"""The plugin type."""
|
33
57
|
|
34
58
|
|
@@ -21,5 +21,18 @@ def get_plugin_info(value: HasPluginInfo) -> _messages.PluginInfo:
|
|
21
21
|
"""Utility method that returns a `PluginInfo` from an object implementing the `HasPluginInfo` protocol."""
|
22
22
|
return _messages.PluginInfo(
|
23
23
|
resolved_name=value.ansible_name,
|
24
|
-
type=value.plugin_type,
|
24
|
+
type=normalize_plugin_type(value.plugin_type),
|
25
25
|
)
|
26
|
+
|
27
|
+
|
28
|
+
def normalize_plugin_type(value: str) -> _messages.PluginType | None:
|
29
|
+
"""Normalize value and return it as a PluginType, or None if the value does match any known plugin type."""
|
30
|
+
value = value.lower()
|
31
|
+
|
32
|
+
if value == 'modules':
|
33
|
+
value = 'module'
|
34
|
+
|
35
|
+
try:
|
36
|
+
return _messages.PluginType(value)
|
37
|
+
except ValueError:
|
38
|
+
return None
|
ansible/module_utils/basic.py
CHANGED
@@ -480,9 +480,11 @@ class AnsibleModule(object):
|
|
480
480
|
if basedir is not None and not os.path.exists(basedir):
|
481
481
|
try:
|
482
482
|
os.makedirs(basedir, mode=0o700)
|
483
|
-
except
|
484
|
-
self.
|
485
|
-
|
483
|
+
except OSError as ex:
|
484
|
+
self.error_as_warning(
|
485
|
+
msg=f"Unable to use {basedir!r} as temporary directory, falling back to system default.",
|
486
|
+
exception=ex,
|
487
|
+
)
|
486
488
|
basedir = None
|
487
489
|
else:
|
488
490
|
self.warn("Module remote_tmp %s did not exist and was "
|
@@ -494,11 +496,11 @@ class AnsibleModule(object):
|
|
494
496
|
basefile = "ansible-moduletmp-%s-" % time.time()
|
495
497
|
try:
|
496
498
|
tmpdir = tempfile.mkdtemp(prefix=basefile, dir=basedir)
|
497
|
-
except
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
)
|
499
|
+
except OSError as ex:
|
500
|
+
raise Exception(
|
501
|
+
f"Failed to create remote module tmp path at dir {basedir!r} "
|
502
|
+
f"with prefix {basefile!r}.",
|
503
|
+
) from ex
|
502
504
|
if not self._keep_remote_files:
|
503
505
|
atexit.register(shutil.rmtree, tmpdir)
|
504
506
|
self._tmpdir = tmpdir
|
@@ -658,11 +660,8 @@ class AnsibleModule(object):
|
|
658
660
|
return context
|
659
661
|
try:
|
660
662
|
ret = selinux.lgetfilecon_raw(to_native(path, errors='surrogate_or_strict'))
|
661
|
-
except OSError as
|
662
|
-
|
663
|
-
self.fail_json(path=path, msg='path %s does not exist' % path)
|
664
|
-
else:
|
665
|
-
self.fail_json(path=path, msg='failed to retrieve selinux context')
|
663
|
+
except OSError as ex:
|
664
|
+
self.fail_json(path=path, msg='Failed to retrieve selinux context.', exception=ex)
|
666
665
|
if ret[0] == -1:
|
667
666
|
return context
|
668
667
|
# Limit split to 4 because the selevel, the last in the list,
|
@@ -802,9 +801,9 @@ class AnsibleModule(object):
|
|
802
801
|
return True
|
803
802
|
try:
|
804
803
|
os.lchown(b_path, uid, -1)
|
805
|
-
except
|
804
|
+
except OSError as ex:
|
806
805
|
path = to_text(b_path)
|
807
|
-
self.fail_json(path=path, msg='chown failed
|
806
|
+
self.fail_json(path=path, msg='chown failed', exception=ex)
|
808
807
|
changed = True
|
809
808
|
return changed
|
810
809
|
|
@@ -1330,7 +1329,7 @@ class AnsibleModule(object):
|
|
1330
1329
|
else:
|
1331
1330
|
journal.send(MESSAGE=u"%s %s" % (module, journal_msg),
|
1332
1331
|
**dict(journal_args))
|
1333
|
-
except
|
1332
|
+
except OSError:
|
1334
1333
|
# fall back to syslog since logging to journal failed
|
1335
1334
|
self._log_to_syslog(journal_msg)
|
1336
1335
|
else:
|
@@ -1658,10 +1657,11 @@ class AnsibleModule(object):
|
|
1658
1657
|
ext = time.strftime("%Y-%m-%d@%H:%M:%S~", time.localtime(time.time()))
|
1659
1658
|
backupdest = '%s.%s.%s' % (fn, os.getpid(), ext)
|
1660
1659
|
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1660
|
+
if not self.check_mode:
|
1661
|
+
try:
|
1662
|
+
self.preserved_copy(fn, backupdest)
|
1663
|
+
except (shutil.Error, IOError) as ex:
|
1664
|
+
raise Exception(f'Could not make backup of {fn!r} to {backupdest!r}.') from ex
|
1665
1665
|
|
1666
1666
|
return backupdest
|
1667
1667
|
|
@@ -1735,28 +1735,25 @@ class AnsibleModule(object):
|
|
1735
1735
|
try:
|
1736
1736
|
# Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
|
1737
1737
|
os.rename(b_src, b_dest)
|
1738
|
-
except
|
1739
|
-
if
|
1738
|
+
except OSError as ex:
|
1739
|
+
if ex.errno in (errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY):
|
1740
1740
|
# only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied)
|
1741
1741
|
# and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
|
1742
|
-
self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)))
|
1743
|
-
else:
|
1744
1742
|
# Use bytes here. In the shippable CI, this fails with
|
1745
1743
|
# a UnicodeError with surrogateescape'd strings for an unknown
|
1746
1744
|
# reason (doesn't happen in a local Ubuntu16.04 VM)
|
1747
1745
|
b_dest_dir = os.path.dirname(b_dest)
|
1748
1746
|
b_suffix = os.path.basename(b_dest)
|
1749
|
-
error_msg = None
|
1750
1747
|
tmp_dest_name = None
|
1751
1748
|
try:
|
1752
1749
|
tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
|
1753
|
-
except
|
1754
|
-
error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), to_native(e))
|
1755
|
-
|
1750
|
+
except OSError as ex:
|
1756
1751
|
if unsafe_writes:
|
1757
1752
|
self._unsafe_writes(b_src, b_dest)
|
1758
1753
|
else:
|
1759
|
-
|
1754
|
+
raise Exception(
|
1755
|
+
f'The destination directory {os.path.dirname(dest)!r} is not writable by the current user.'
|
1756
|
+
) from ex
|
1760
1757
|
|
1761
1758
|
if tmp_dest_name:
|
1762
1759
|
b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict')
|
@@ -1785,24 +1782,27 @@ class AnsibleModule(object):
|
|
1785
1782
|
if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid):
|
1786
1783
|
os.chown(b_tmp_dest_name, dest_stat.st_uid, dest_stat.st_gid)
|
1787
1784
|
os.utime(b_tmp_dest_name, times=(time.time(), time.time()))
|
1788
|
-
except OSError as
|
1789
|
-
if
|
1785
|
+
except OSError as ex:
|
1786
|
+
if ex.errno != errno.EPERM:
|
1790
1787
|
raise
|
1791
1788
|
try:
|
1792
1789
|
os.rename(b_tmp_dest_name, b_dest)
|
1793
|
-
except (shutil.Error, OSError
|
1794
|
-
if unsafe_writes and
|
1790
|
+
except (shutil.Error, OSError) as ex:
|
1791
|
+
if unsafe_writes and ex.errno == errno.EBUSY:
|
1795
1792
|
self._unsafe_writes(b_tmp_dest_name, b_dest)
|
1796
1793
|
else:
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1794
|
+
raise Exception(
|
1795
|
+
f'Unable to make {src!r} into to {dest!r}, failed final rename from {to_text(b_tmp_dest_name)!r}.'
|
1796
|
+
) from ex
|
1797
|
+
except (shutil.Error, OSError) as ex:
|
1800
1798
|
if unsafe_writes:
|
1801
1799
|
self._unsafe_writes(b_src, b_dest)
|
1802
1800
|
else:
|
1803
|
-
|
1801
|
+
raise Exception(f'Failed to replace {dest!r} with {src!r}.') from ex
|
1804
1802
|
finally:
|
1805
1803
|
self.cleanup(b_tmp_dest_name)
|
1804
|
+
else:
|
1805
|
+
raise Exception(f'Could not replace {dest!r} with {src!r}.') from ex
|
1806
1806
|
|
1807
1807
|
if creating:
|
1808
1808
|
# make sure the file has the correct permissions
|
@@ -1829,18 +1829,11 @@ class AnsibleModule(object):
|
|
1829
1829
|
# sadly there are some situations where we cannot ensure atomicity, but only if
|
1830
1830
|
# the user insists and we get the appropriate error we update the file unsafely
|
1831
1831
|
try:
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
finally: # assuring closed files in 2.4 compatible way
|
1838
|
-
if out_dest:
|
1839
|
-
out_dest.close()
|
1840
|
-
if in_src:
|
1841
|
-
in_src.close()
|
1842
|
-
except (shutil.Error, OSError, IOError) as e:
|
1843
|
-
self.fail_json(msg='Could not write data to file (%s) from (%s): %s' % (dest, src, to_native(e)))
|
1832
|
+
with open(dest, 'wb') as out_dest:
|
1833
|
+
with open(src, 'rb') as in_src:
|
1834
|
+
shutil.copyfileobj(in_src, out_dest)
|
1835
|
+
except (shutil.Error, OSError) as ex:
|
1836
|
+
raise Exception(f'Could not write data to file {dest!r} from {src!r}.') from ex
|
1844
1837
|
|
1845
1838
|
def _clean_args(self, args):
|
1846
1839
|
|
@@ -1907,18 +1900,18 @@ class AnsibleModule(object):
|
|
1907
1900
|
the execution to hang (especially if no input data is specified)
|
1908
1901
|
:kw environ_update: dictionary to *update* environ variables with
|
1909
1902
|
:kw umask: Umask to be used when running the command. Default None
|
1910
|
-
:kw encoding: Since we return
|
1903
|
+
:kw encoding: Since we return strings, we need to
|
1911
1904
|
know the encoding to use to transform from bytes to text. If you
|
1912
1905
|
want to always get bytes back, use encoding=None. The default is
|
1913
1906
|
"utf-8". This does not affect transformation of strings given as
|
1914
1907
|
args.
|
1915
|
-
:kw errors: Since we return
|
1908
|
+
:kw errors: Since we return strings, we need to
|
1916
1909
|
transform stdout and stderr from bytes to text. If the bytes are
|
1917
1910
|
undecodable in the ``encoding`` specified, then use this error
|
1918
1911
|
handler to deal with them. The default is ``surrogate_or_strict``
|
1919
1912
|
which means that the bytes will be decoded using the
|
1920
1913
|
surrogateescape error handler if available (available on all
|
1921
|
-
|
1914
|
+
Python versions we support) otherwise a UnicodeError traceback
|
1922
1915
|
will be raised. This does not affect transformations of strings
|
1923
1916
|
given as args.
|
1924
1917
|
:kw expand_user_and_vars: When ``use_unsafe_shell=False`` this argument
|
@@ -1926,10 +1919,8 @@ class AnsibleModule(object):
|
|
1926
1919
|
are expanded before running the command. When ``True`` a string such as
|
1927
1920
|
``$SHELL`` will be expanded regardless of escaping. When ``False`` and
|
1928
1921
|
``use_unsafe_shell=False`` no path or variable expansion will be done.
|
1929
|
-
:kw pass_fds:
|
1930
|
-
|
1931
|
-
to an underlying ``Popen`` constructor. On Python 2, this will
|
1932
|
-
set ``close_fds`` to False.
|
1922
|
+
:kw pass_fds: This argument dictates which file descriptors should be passed
|
1923
|
+
to an underlying ``Popen`` constructor.
|
1933
1924
|
:kw before_communicate_callback: This function will be called
|
1934
1925
|
after ``Popen`` object will be created
|
1935
1926
|
but before communicating to the process.
|
@@ -1940,11 +1931,10 @@ class AnsibleModule(object):
|
|
1940
1931
|
:kw handle_exceptions: This flag indicates whether an exception will
|
1941
1932
|
be handled inline and issue a failed_json or if the caller should
|
1942
1933
|
handle it.
|
1943
|
-
:returns: A 3-tuple of return code (
|
1944
|
-
and stderr
|
1945
|
-
|
1946
|
-
|
1947
|
-
strings on python3, use encoding=None to turn decoding to text off.
|
1934
|
+
:returns: A 3-tuple of return code (int), stdout (str), and stderr (str).
|
1935
|
+
stdout and stderr are text strings converted according to the encoding
|
1936
|
+
and errors parameters. If you want byte strings, use encoding=None
|
1937
|
+
to turn decoding to text off.
|
1948
1938
|
"""
|
1949
1939
|
# used by clean args later on
|
1950
1940
|
self._clean = None
|
@@ -2126,18 +2116,16 @@ class AnsibleModule(object):
|
|
2126
2116
|
selector.close()
|
2127
2117
|
|
2128
2118
|
rc = cmd.returncode
|
2129
|
-
except
|
2130
|
-
self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(e)))
|
2119
|
+
except OSError as ex:
|
2131
2120
|
if handle_exceptions:
|
2132
|
-
self.fail_json(rc=
|
2121
|
+
self.fail_json(rc=ex.errno, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
|
2133
2122
|
else:
|
2134
|
-
raise
|
2135
|
-
except Exception as
|
2136
|
-
self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(traceback.format_exc())))
|
2123
|
+
raise
|
2124
|
+
except Exception as ex:
|
2137
2125
|
if handle_exceptions:
|
2138
|
-
self.fail_json(rc=257, stdout=
|
2126
|
+
self.fail_json(rc=257, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
|
2139
2127
|
else:
|
2140
|
-
raise
|
2128
|
+
raise
|
2141
2129
|
|
2142
2130
|
if rc != 0 and check_rc:
|
2143
2131
|
msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values)
|
@@ -2208,7 +2196,7 @@ def __getattr__(importable_name):
|
|
2208
2196
|
importable = repeat
|
2209
2197
|
elif importable_name in {
|
2210
2198
|
'PY2', 'PY3', 'b', 'binary_type', 'integer_types',
|
2211
|
-
'iteritems', 'string_types', '
|
2199
|
+
'iteritems', 'string_types', 'text_type',
|
2212
2200
|
}:
|
2213
2201
|
import importlib
|
2214
2202
|
importable = getattr(
|
@@ -10,6 +10,7 @@ import sys
|
|
10
10
|
import typing as t
|
11
11
|
|
12
12
|
from ansible.module_utils.common.text.converters import to_bytes
|
13
|
+
from ansible.module_utils._internal._ansiballz import _respawn
|
13
14
|
|
14
15
|
_ANSIBLE_PARENT_PATH = pathlib.Path(__file__).parents[3]
|
15
16
|
|
@@ -39,7 +40,7 @@ def respawn_module(interpreter_path) -> t.NoReturn:
|
|
39
40
|
raise Exception('module has already been respawned')
|
40
41
|
|
41
42
|
# FUTURE: we need a safe way to log that a respawn has occurred for forensic/debug purposes
|
42
|
-
payload =
|
43
|
+
payload = _respawn.create_payload()
|
43
44
|
stdin_read, stdin_write = os.pipe()
|
44
45
|
os.write(stdin_write, to_bytes(payload))
|
45
46
|
os.close(stdin_write)
|
@@ -59,10 +60,12 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
|
|
59
60
|
:arg module_name: fully-qualified Python module name to probe for (for example, ``selinux``)
|
60
61
|
"""
|
61
62
|
PYTHONPATH = os.getenv('PYTHONPATH', '')
|
63
|
+
|
62
64
|
env = os.environ.copy()
|
63
65
|
env.update({
|
64
66
|
'PYTHONPATH': f'{_ANSIBLE_PARENT_PATH}:{PYTHONPATH}'.rstrip(': ')
|
65
67
|
})
|
68
|
+
|
66
69
|
for interpreter_path in interpreter_paths:
|
67
70
|
if not os.path.exists(interpreter_path):
|
68
71
|
continue
|
@@ -81,43 +84,3 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
|
|
81
84
|
continue
|
82
85
|
|
83
86
|
return None
|
84
|
-
|
85
|
-
|
86
|
-
def _create_payload():
|
87
|
-
# FIXME: move this into _ansiballz and skip the template
|
88
|
-
from ansible.module_utils import basic
|
89
|
-
|
90
|
-
module_fqn = sys.modules['__main__']._module_fqn
|
91
|
-
modlib_path = sys.modules['__main__']._modlib_path
|
92
|
-
|
93
|
-
respawn_code_template = """
|
94
|
-
if __name__ == '__main__':
|
95
|
-
import runpy
|
96
|
-
import sys
|
97
|
-
|
98
|
-
json_params = {json_params!r}
|
99
|
-
profile = {profile!r}
|
100
|
-
module_fqn = {module_fqn!r}
|
101
|
-
modlib_path = {modlib_path!r}
|
102
|
-
|
103
|
-
sys.path.insert(0, modlib_path)
|
104
|
-
|
105
|
-
from ansible.module_utils._internal import _ansiballz
|
106
|
-
|
107
|
-
_ansiballz.run_module(
|
108
|
-
json_params=json_params,
|
109
|
-
profile=profile,
|
110
|
-
module_fqn=module_fqn,
|
111
|
-
modlib_path=modlib_path,
|
112
|
-
init_globals=dict(_respawned=True),
|
113
|
-
)
|
114
|
-
"""
|
115
|
-
|
116
|
-
respawn_code = respawn_code_template.format(
|
117
|
-
json_params=basic._ANSIBLE_ARGS,
|
118
|
-
profile=basic._ANSIBLE_PROFILE,
|
119
|
-
module_fqn=module_fqn,
|
120
|
-
modlib_path=modlib_path,
|
121
|
-
)
|
122
|
-
|
123
|
-
return respawn_code
|
@@ -26,7 +26,7 @@ else:
|
|
26
26
|
|
27
27
|
# DTFIX-FUTURE: refactor this to share the implementation with the controller version
|
28
28
|
# use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
|
29
|
-
# then tests can be consolidated
|
29
|
+
# then tests can be consolidated instead of having two nearly identical copies
|
30
30
|
|
31
31
|
if HAS_YAML:
|
32
32
|
try:
|
@@ -33,7 +33,6 @@ import json
|
|
33
33
|
import pickle
|
34
34
|
import socket
|
35
35
|
import struct
|
36
|
-
import traceback
|
37
36
|
import uuid
|
38
37
|
|
39
38
|
from functools import partial
|
@@ -136,12 +135,11 @@ class Connection(object):
|
|
136
135
|
|
137
136
|
try:
|
138
137
|
out = self.send(data)
|
139
|
-
except
|
138
|
+
except OSError as ex:
|
140
139
|
raise ConnectionError(
|
141
|
-
'
|
142
|
-
'in the Network Debug and Troubleshooting Guide'
|
143
|
-
|
144
|
-
)
|
140
|
+
f'Unable to connect to socket {self.socket_path!r}. See Troubleshooting socket path issues '
|
141
|
+
'in the Network Debug and Troubleshooting Guide.'
|
142
|
+
) from ex
|
145
143
|
|
146
144
|
try:
|
147
145
|
response = json.loads(out)
|
@@ -192,13 +190,12 @@ class Connection(object):
|
|
192
190
|
send_data(sf, to_bytes(data))
|
193
191
|
response = recv_data(sf)
|
194
192
|
|
195
|
-
except
|
193
|
+
except OSError as ex:
|
196
194
|
sf.close()
|
197
195
|
raise ConnectionError(
|
198
|
-
'
|
199
|
-
'Network Debug and Troubleshooting Guide'
|
200
|
-
|
201
|
-
)
|
196
|
+
f'Unable to connect to socket {self.socket_path!r}. See the socket path issue category in '
|
197
|
+
'Network Debug and Troubleshooting Guide.',
|
198
|
+
) from ex
|
202
199
|
|
203
200
|
sf.close()
|
204
201
|
|
@@ -1696,7 +1696,7 @@ namespace Ansible.Basic
|
|
1696
1696
|
if ((attr & FileAttributes.ReadOnly) != 0)
|
1697
1697
|
{
|
1698
1698
|
// Windows does not allow files set with ReadOnly to be
|
1699
|
-
// deleted.
|
1699
|
+
// deleted. Preemptively unset the attribute.
|
1700
1700
|
// FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE is quite new,
|
1701
1701
|
// look at using that flag with POSIX delete once Server 2019
|
1702
1702
|
// is the baseline.
|
@@ -228,7 +228,7 @@ namespace Ansible.Privilege
|
|
228
228
|
}
|
229
229
|
|
230
230
|
/// <summary>
|
231
|
-
///
|
231
|
+
/// Gets the status of all the privileges on the token specified
|
232
232
|
/// </summary>
|
233
233
|
/// <param name="token">The process token to get the privilege status on</param>
|
234
234
|
/// <returns>Dictionary where the key is the privilege constant and the value is the PrivilegeAttributes flags</returns>
|
@@ -342,7 +342,7 @@ namespace Ansible.Privilege
|
|
342
342
|
// Need to manually marshal the bytes requires for newState as the constant size
|
343
343
|
// of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
|
344
344
|
// always contains at least 1 entry so we need to calculate the extra size if there are
|
345
|
-
//
|
345
|
+
// more than 1 LUID_AND_ATTRIBUTES entry
|
346
346
|
int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
|
347
347
|
int luidAttrSize = 0;
|
348
348
|
if (newState.Length > 1)
|
@@ -49,7 +49,7 @@ class HardwareCollector(BaseFactCollector):
|
|
49
49
|
_fact_ids = set(['processor',
|
50
50
|
'processor_cores',
|
51
51
|
'processor_count',
|
52
|
-
# TODO: mounts
|
52
|
+
# TODO: mounts isn't exactly hardware
|
53
53
|
'mounts',
|
54
54
|
'devices']) # type: t.Set[str]
|
55
55
|
_fact_class = Hardware
|
@@ -61,7 +61,7 @@ class FacterFactCollector(BaseFactCollector):
|
|
61
61
|
return out
|
62
62
|
|
63
63
|
def collect(self, module=None, collected_facts=None):
|
64
|
-
# Note that this mirrors previous facter behavior, where there
|
64
|
+
# Note that this mirrors previous facter behavior, where there isn't
|
65
65
|
# a 'ansible_facter' key in the main fact dict, but instead, 'facter_whatever'
|
66
66
|
# items are added to the main dict.
|
67
67
|
facter_dict = {}
|
@@ -17,8 +17,6 @@ from __future__ import annotations
|
|
17
17
|
|
18
18
|
import re
|
19
19
|
|
20
|
-
from ansible.module_utils.common.text.converters import to_text
|
21
|
-
|
22
20
|
|
23
21
|
def get_sysctl(module, prefixes):
|
24
22
|
|
@@ -31,8 +29,8 @@ def get_sysctl(module, prefixes):
|
|
31
29
|
|
32
30
|
try:
|
33
31
|
rc, out, err = module.run_command(cmd)
|
34
|
-
except
|
35
|
-
module.
|
32
|
+
except OSError as ex:
|
33
|
+
module.error_as_warning('Unable to read sysctl.', exception=ex)
|
36
34
|
rc = 1
|
37
35
|
|
38
36
|
if rc == 0:
|
@@ -54,8 +52,8 @@ def get_sysctl(module, prefixes):
|
|
54
52
|
|
55
53
|
try:
|
56
54
|
(key, value) = re.split(r'\s?=\s?|: ', line, maxsplit=1)
|
57
|
-
except Exception as
|
58
|
-
module.
|
55
|
+
except Exception as ex:
|
56
|
+
module.error_as_warning(f'Unable to split sysctl line {line!r}.', exception=ex)
|
59
57
|
|
60
58
|
if key:
|
61
59
|
sysctl[key] = value.strip()
|
@@ -38,8 +38,8 @@ class SystemCapabilitiesFactCollector(BaseFactCollector):
|
|
38
38
|
# NOTE: -> get_caps_data()/parse_caps_data() for easier mocking -akl
|
39
39
|
try:
|
40
40
|
rc, out, err = module.run_command([capsh_path, "--print"], errors='surrogate_then_replace', handle_exceptions=False)
|
41
|
-
except
|
42
|
-
module.
|
41
|
+
except OSError as ex:
|
42
|
+
module.error_as_warning('Could not query system capabilities.', exception=ex)
|
43
43
|
|
44
44
|
if rc == 0:
|
45
45
|
enforced_caps = []
|
@@ -100,7 +100,7 @@ class DistributionFiles:
|
|
100
100
|
return get_file_content(path)
|
101
101
|
|
102
102
|
def _get_dist_file_content(self, path, allow_empty=False):
|
103
|
-
#
|
103
|
+
# can't find that dist file, or it is incorrectly empty
|
104
104
|
if not _file_exists(path, allow_empty=allow_empty):
|
105
105
|
return False, None
|
106
106
|
|
@@ -585,7 +585,7 @@ class Distribution(object):
|
|
585
585
|
distribution_facts.update(dist_file_facts)
|
586
586
|
|
587
587
|
distro = distribution_facts['distribution']
|
588
|
-
# look for
|
588
|
+
# look for an os family alias for the 'distribution', if there isn't one, use 'distribution'
|
589
589
|
distribution_facts['os_family'] = self.OS_FAMILY.get(distro, None) or distro
|
590
590
|
|
591
591
|
return distribution_facts
|
@@ -50,7 +50,7 @@ class LocalFactCollector(BaseFactCollector):
|
|
50
50
|
rc, out, err = module.run_command(fn)
|
51
51
|
if rc != 0:
|
52
52
|
failed = 'Failure executing fact script (%s), rc: %s, err: %s' % (fn, rc, err)
|
53
|
-
except
|
53
|
+
except OSError as e:
|
54
54
|
failed = 'Could not execute fact script (%s): %s' % (fn, to_text(e))
|
55
55
|
|
56
56
|
if failed is not None:
|
@@ -5,7 +5,7 @@ Function Add-CSharpType {
|
|
5
5
|
<#
|
6
6
|
.SYNOPSIS
|
7
7
|
Compiles one or more C# scripts similar to Add-Type. This exposes
|
8
|
-
more configuration options that are
|
8
|
+
more configuration options that are usable within Ansible and it
|
9
9
|
also allows multiple C# sources to be compiled together.
|
10
10
|
|
11
11
|
.PARAMETER References
|
@@ -21,7 +21,7 @@ Function Convert-StringToSnakeCase($string) {
|
|
21
21
|
return $string
|
22
22
|
}
|
23
23
|
|
24
|
-
# used by Convert-DictToSnakeCase to
|
24
|
+
# used by Convert-DictToSnakeCase to convert list entries from camelCase
|
25
25
|
# to snake_case
|
26
26
|
Function Convert-ListToSnakeCase($list) {
|
27
27
|
$snake_list = [System.Collections.ArrayList]@()
|
@@ -6,7 +6,7 @@
|
|
6
6
|
Function Get-ExecutablePath {
|
7
7
|
<#
|
8
8
|
.SYNOPSIS
|
9
|
-
|
9
|
+
Gets the full path to an executable, will search the directory specified or ones in the PATH env var.
|
10
10
|
|
11
11
|
.PARAMETER executable
|
12
12
|
[String]The executable to search for.
|
@@ -17,7 +17,7 @@ Function Get-AnsibleWebRequest {
|
|
17
17
|
The protocol method to use, if omitted, will use the default value for the URI protocol specified.
|
18
18
|
|
19
19
|
.PARAMETER FollowRedirects
|
20
|
-
Whether to follow redirect
|
20
|
+
Whether to follow redirect responses. This is only valid when using a HTTP URI.
|
21
21
|
all - Will follow all redirects
|
22
22
|
none - Will follow no redirects
|
23
23
|
safe - Will only follow redirects when GET or HEAD is used as the Method
|