ansible-core 2.18.5rc1__py3-none-any.whl → 2.19.0b2__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/__init__.py +53 -0
- ansible/_internal/_ansiballz.py +265 -0
- ansible/_internal/_collection_proxy.py +47 -0
- ansible/_internal/_datatag/__init__.py +0 -0
- ansible/_internal/_datatag/_tags.py +130 -0
- ansible/_internal/_datatag/_utils.py +19 -0
- ansible/_internal/_datatag/_wrappers.py +33 -0
- ansible/_internal/_errors/__init__.py +0 -0
- ansible/_internal/_errors/_captured.py +128 -0
- ansible/_internal/_errors/_handler.py +91 -0
- ansible/_internal/_errors/_utils.py +310 -0
- ansible/_internal/_json/__init__.py +203 -0
- ansible/_internal/_json/_legacy_encoder.py +34 -0
- ansible/_internal/_json/_profiles/__init__.py +0 -0
- ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
- ansible/_internal/_json/_profiles/_legacy.py +197 -0
- ansible/_internal/_locking.py +21 -0
- ansible/_internal/_plugins/__init__.py +0 -0
- ansible/_internal/_plugins/_cache.py +57 -0
- ansible/_internal/_task.py +78 -0
- ansible/_internal/_templating/__init__.py +10 -0
- ansible/_internal/_templating/_access.py +86 -0
- ansible/_internal/_templating/_chain_templar.py +63 -0
- ansible/_internal/_templating/_datatag.py +95 -0
- ansible/_internal/_templating/_engine.py +588 -0
- ansible/_internal/_templating/_errors.py +28 -0
- ansible/_internal/_templating/_jinja_bits.py +1066 -0
- ansible/_internal/_templating/_jinja_common.py +332 -0
- ansible/_internal/_templating/_jinja_patches.py +44 -0
- ansible/_internal/_templating/_jinja_plugins.py +345 -0
- ansible/_internal/_templating/_lazy_containers.py +633 -0
- ansible/_internal/_templating/_marker_behaviors.py +103 -0
- ansible/_internal/_templating/_transform.py +63 -0
- ansible/_internal/_templating/_utils.py +107 -0
- ansible/_internal/_wrapt.py +1052 -0
- ansible/_internal/_yaml/__init__.py +0 -0
- ansible/_internal/_yaml/_constructor.py +240 -0
- ansible/_internal/_yaml/_dumper.py +62 -0
- ansible/_internal/_yaml/_errors.py +166 -0
- ansible/_internal/_yaml/_loader.py +66 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
- ansible/cli/__init__.py +159 -89
- ansible/cli/_ssh_askpass.py +47 -0
- ansible/cli/adhoc.py +14 -7
- ansible/cli/arguments/option_helpers.py +154 -7
- ansible/cli/config.py +43 -68
- ansible/cli/console.py +10 -8
- ansible/cli/doc.py +62 -53
- ansible/cli/galaxy.py +27 -20
- ansible/cli/inventory.py +28 -26
- ansible/cli/playbook.py +4 -12
- ansible/cli/pull.py +51 -11
- ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
- ansible/cli/vault.py +12 -11
- ansible/compat/__init__.py +2 -2
- ansible/config/base.yml +166 -112
- ansible/config/manager.py +52 -49
- ansible/constants.py +3 -4
- ansible/errors/__init__.py +277 -235
- ansible/executor/interpreter_discovery.py +28 -149
- ansible/executor/module_common.py +426 -493
- ansible/executor/play_iterator.py +22 -27
- ansible/executor/playbook_executor.py +11 -11
- ansible/executor/powershell/async_watchdog.ps1 +97 -102
- ansible/executor/powershell/async_wrapper.ps1 +202 -151
- ansible/executor/powershell/become_wrapper.ps1 +89 -144
- ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
- ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
- ansible/executor/powershell/exec_wrapper.ps1 +462 -196
- ansible/executor/powershell/module_manifest.py +417 -265
- ansible/executor/powershell/module_wrapper.ps1 +169 -186
- ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
- ansible/executor/powershell/psrp_put_file.ps1 +122 -0
- ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
- ansible/executor/powershell/winrm_put_file.ps1 +36 -0
- ansible/executor/process/worker.py +161 -96
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +268 -258
- ansible/executor/task_queue_manager.py +124 -90
- ansible/executor/task_result.py +183 -78
- ansible/galaxy/__init__.py +2 -2
- ansible/galaxy/api.py +22 -18
- ansible/galaxy/collection/__init__.py +1 -1
- ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
- ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
- ansible/galaxy/dependency_resolution/providers.py +1 -1
- ansible/galaxy/dependency_resolution/reporters.py +81 -0
- ansible/galaxy/role.py +4 -8
- ansible/galaxy/token.py +28 -21
- ansible/inventory/data.py +47 -57
- ansible/inventory/group.py +44 -72
- ansible/inventory/helpers.py +9 -0
- ansible/inventory/host.py +32 -54
- ansible/inventory/manager.py +78 -34
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +55 -0
- ansible/module_utils/_internal/_ambient_context.py +58 -0
- ansible/module_utils/_internal/_ansiballz.py +133 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
- ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
- ansible/module_utils/_internal/_dataclass_validation.py +217 -0
- ansible/module_utils/_internal/_datatag/__init__.py +928 -0
- ansible/module_utils/_internal/_datatag/_tags.py +38 -0
- ansible/module_utils/_internal/_debugging.py +31 -0
- ansible/module_utils/_internal/_errors.py +30 -0
- ansible/module_utils/_internal/_json/__init__.py +63 -0
- ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +410 -0
- ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +50 -0
- ansible/module_utils/_internal/_patches/__init__.py +66 -0
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -0
- ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
- ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
- ansible/module_utils/_internal/_plugin_exec_context.py +49 -0
- ansible/module_utils/_internal/_testing.py +0 -0
- ansible/module_utils/_internal/_traceback.py +89 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +1 -2
- ansible/module_utils/basic.py +152 -120
- ansible/module_utils/common/_utils.py +24 -28
- ansible/module_utils/common/collections.py +1 -2
- ansible/module_utils/common/dict_transformations.py +2 -2
- ansible/module_utils/common/file.py +2 -2
- ansible/module_utils/common/json.py +90 -84
- ansible/module_utils/common/locale.py +2 -2
- ansible/module_utils/common/messages.py +108 -0
- ansible/module_utils/common/parameters.py +27 -24
- ansible/module_utils/common/process.py +2 -2
- ansible/module_utils/common/respawn.py +41 -19
- ansible/module_utils/common/sentinel.py +66 -0
- ansible/module_utils/common/sys_info.py +8 -8
- ansible/module_utils/common/text/converters.py +16 -37
- ansible/module_utils/common/validation.py +35 -24
- ansible/module_utils/common/warnings.py +86 -25
- ansible/module_utils/common/yaml.py +29 -3
- ansible/module_utils/compat/datetime.py +33 -21
- ansible/module_utils/compat/paramiko.py +21 -10
- ansible/module_utils/compat/typing.py +6 -5
- ansible/module_utils/connection.py +2 -2
- ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
- ansible/module_utils/csharp/Ansible.Become.cs +1 -0
- ansible/module_utils/csharp/Ansible._Async.cs +517 -0
- ansible/module_utils/datatag.py +46 -0
- ansible/module_utils/distro/__init__.py +2 -2
- ansible/module_utils/facts/ansible_collector.py +4 -5
- ansible/module_utils/facts/collector.py +13 -14
- ansible/module_utils/facts/compat.py +4 -4
- ansible/module_utils/facts/default_collectors.py +1 -1
- ansible/module_utils/facts/hardware/aix.py +34 -0
- ansible/module_utils/facts/hardware/base.py +1 -1
- ansible/module_utils/facts/hardware/darwin.py +1 -3
- ansible/module_utils/facts/hardware/freebsd.py +2 -2
- ansible/module_utils/facts/hardware/linux.py +4 -4
- ansible/module_utils/facts/namespace.py +1 -1
- ansible/module_utils/facts/network/base.py +1 -1
- ansible/module_utils/facts/network/fc_wwn.py +1 -2
- ansible/module_utils/facts/network/iscsi.py +1 -2
- ansible/module_utils/facts/network/nvme.py +1 -2
- ansible/module_utils/facts/other/facter.py +1 -2
- ansible/module_utils/facts/other/ohai.py +2 -3
- ansible/module_utils/facts/system/apparmor.py +1 -2
- ansible/module_utils/facts/system/caps.py +1 -1
- ansible/module_utils/facts/system/chroot.py +1 -2
- ansible/module_utils/facts/system/cmdline.py +1 -2
- ansible/module_utils/facts/system/date_time.py +5 -3
- ansible/module_utils/facts/system/distribution.py +9 -8
- ansible/module_utils/facts/system/dns.py +1 -1
- ansible/module_utils/facts/system/env.py +1 -2
- ansible/module_utils/facts/system/fips.py +7 -20
- ansible/module_utils/facts/system/loadavg.py +1 -2
- ansible/module_utils/facts/system/local.py +1 -2
- ansible/module_utils/facts/system/lsb.py +1 -2
- ansible/module_utils/facts/system/pkg_mgr.py +1 -2
- ansible/module_utils/facts/system/platform.py +1 -2
- ansible/module_utils/facts/system/python.py +1 -2
- ansible/module_utils/facts/system/selinux.py +1 -1
- ansible/module_utils/facts/system/service_mgr.py +1 -2
- ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
- ansible/module_utils/facts/system/systemd.py +1 -1
- ansible/module_utils/facts/system/user.py +1 -2
- ansible/module_utils/facts/utils.py +3 -3
- ansible/module_utils/facts/virtual/base.py +1 -1
- ansible/module_utils/facts/virtual/sunos.py +3 -15
- ansible/module_utils/facts/virtual/sysctl.py +3 -16
- ansible/module_utils/json_utils.py +2 -2
- ansible/module_utils/parsing/convert_bool.py +1 -1
- ansible/module_utils/service.py +18 -21
- ansible/module_utils/splitter.py +7 -7
- ansible/module_utils/testing.py +31 -0
- ansible/module_utils/urls.py +60 -31
- ansible/modules/add_host.py +4 -4
- ansible/modules/apt.py +60 -46
- ansible/modules/apt_key.py +19 -12
- ansible/modules/apt_repository.py +19 -16
- ansible/modules/assemble.py +6 -6
- ansible/modules/assert.py +4 -4
- ansible/modules/async_status.py +10 -12
- ansible/modules/async_wrapper.py +8 -3
- ansible/modules/blockinfile.py +6 -7
- ansible/modules/command.py +10 -17
- ansible/modules/copy.py +57 -144
- ansible/modules/cron.py +20 -15
- ansible/modules/deb822_repository.py +8 -9
- ansible/modules/debconf.py +5 -5
- ansible/modules/debug.py +4 -4
- ansible/modules/dnf.py +8 -8
- ansible/modules/dnf5.py +39 -13
- ansible/modules/dpkg_selections.py +4 -4
- ansible/modules/expect.py +8 -10
- ansible/modules/fail.py +4 -4
- ansible/modules/fetch.py +4 -4
- ansible/modules/file.py +174 -133
- ansible/modules/find.py +19 -17
- ansible/modules/gather_facts.py +3 -3
- ansible/modules/get_url.py +59 -53
- ansible/modules/getent.py +7 -9
- ansible/modules/git.py +28 -25
- ansible/modules/group.py +6 -6
- ansible/modules/group_by.py +4 -4
- ansible/modules/hostname.py +13 -29
- ansible/modules/import_playbook.py +6 -6
- ansible/modules/import_role.py +6 -6
- ansible/modules/import_tasks.py +6 -6
- ansible/modules/include_role.py +6 -6
- ansible/modules/include_tasks.py +6 -6
- ansible/modules/include_vars.py +6 -6
- ansible/modules/iptables.py +86 -73
- ansible/modules/known_hosts.py +10 -10
- ansible/modules/lineinfile.py +5 -5
- ansible/modules/meta.py +4 -4
- ansible/modules/mount_facts.py +2 -2
- ansible/modules/package.py +4 -4
- ansible/modules/package_facts.py +22 -10
- ansible/modules/pause.py +6 -6
- ansible/modules/ping.py +6 -6
- ansible/modules/pip.py +10 -11
- ansible/modules/raw.py +4 -4
- ansible/modules/reboot.py +6 -6
- ansible/modules/replace.py +9 -13
- ansible/modules/rpm_key.py +7 -8
- ansible/modules/script.py +4 -4
- ansible/modules/service.py +7 -8
- ansible/modules/service_facts.py +87 -10
- ansible/modules/set_fact.py +5 -5
- ansible/modules/set_stats.py +4 -4
- ansible/modules/setup.py +2 -2
- ansible/modules/shell.py +6 -6
- ansible/modules/slurp.py +6 -6
- ansible/modules/stat.py +9 -23
- ansible/modules/subversion.py +15 -15
- ansible/modules/systemd.py +6 -6
- ansible/modules/systemd_service.py +6 -6
- ansible/modules/sysvinit.py +6 -6
- ansible/modules/tempfile.py +5 -6
- ansible/modules/template.py +6 -6
- ansible/modules/unarchive.py +32 -11
- ansible/modules/uri.py +33 -26
- ansible/modules/user.py +53 -34
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +32 -27
- ansible/modules/wait_for_connection.py +6 -6
- ansible/modules/yum_repository.py +6 -6
- ansible/parsing/ajson.py +14 -32
- ansible/parsing/dataloader.py +99 -54
- ansible/parsing/mod_args.py +28 -44
- ansible/parsing/plugin_docs.py +21 -86
- ansible/parsing/quoting.py +1 -1
- ansible/parsing/splitter.py +27 -12
- ansible/parsing/utils/addresses.py +24 -24
- ansible/parsing/utils/jsonify.py +5 -1
- ansible/parsing/utils/yaml.py +32 -61
- ansible/parsing/vault/__init__.py +319 -87
- ansible/parsing/yaml/__init__.py +0 -18
- ansible/parsing/yaml/dumper.py +6 -120
- ansible/parsing/yaml/loader.py +6 -39
- ansible/parsing/yaml/objects.py +43 -335
- ansible/playbook/__init__.py +1 -1
- ansible/playbook/attribute.py +8 -3
- ansible/playbook/base.py +182 -132
- ansible/playbook/block.py +26 -24
- ansible/playbook/collectionsearch.py +1 -15
- ansible/playbook/conditional.py +3 -77
- ansible/playbook/handler.py +8 -2
- ansible/playbook/helpers.py +41 -53
- ansible/playbook/included_file.py +31 -27
- ansible/playbook/loop_control.py +2 -2
- ansible/playbook/play.py +85 -44
- ansible/playbook/play_context.py +12 -17
- ansible/playbook/playbook_include.py +14 -15
- ansible/playbook/role/__init__.py +24 -26
- ansible/playbook/role/definition.py +15 -17
- ansible/playbook/role/include.py +2 -4
- ansible/playbook/role/metadata.py +10 -11
- ansible/playbook/role_include.py +3 -3
- ansible/playbook/taggable.py +13 -8
- ansible/playbook/task.py +188 -118
- ansible/playbook/task_include.py +5 -5
- ansible/plugins/__init__.py +68 -21
- ansible/plugins/action/__init__.py +209 -176
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +1 -1
- ansible/plugins/action/assert.py +54 -66
- ansible/plugins/action/copy.py +7 -11
- ansible/plugins/action/debug.py +37 -31
- ansible/plugins/action/dnf.py +3 -4
- ansible/plugins/action/fail.py +1 -1
- ansible/plugins/action/fetch.py +4 -5
- ansible/plugins/action/gather_facts.py +7 -6
- ansible/plugins/action/group_by.py +1 -1
- ansible/plugins/action/include_vars.py +10 -11
- ansible/plugins/action/package.py +3 -6
- ansible/plugins/action/pause.py +2 -2
- ansible/plugins/action/script.py +15 -8
- ansible/plugins/action/service.py +6 -11
- ansible/plugins/action/set_fact.py +3 -12
- ansible/plugins/action/set_stats.py +3 -8
- ansible/plugins/action/template.py +35 -59
- ansible/plugins/action/unarchive.py +1 -1
- ansible/plugins/action/validate_argument_spec.py +5 -5
- ansible/plugins/action/wait_for_connection.py +1 -1
- ansible/plugins/become/__init__.py +31 -8
- ansible/plugins/become/runas.py +71 -0
- ansible/plugins/become/su.py +13 -8
- ansible/plugins/become/sudo.py +19 -0
- ansible/plugins/cache/__init__.py +35 -44
- ansible/plugins/cache/base.py +8 -0
- ansible/plugins/cache/jsonfile.py +10 -16
- ansible/plugins/cache/memory.py +6 -12
- ansible/plugins/callback/__init__.py +284 -179
- ansible/plugins/callback/default.py +99 -92
- ansible/plugins/callback/junit.py +44 -39
- ansible/plugins/callback/minimal.py +28 -25
- ansible/plugins/callback/oneline.py +28 -21
- ansible/plugins/callback/tree.py +16 -11
- ansible/plugins/connection/__init__.py +47 -34
- ansible/plugins/connection/local.py +150 -54
- ansible/plugins/connection/paramiko_ssh.py +21 -18
- ansible/plugins/connection/psrp.py +76 -165
- ansible/plugins/connection/ssh.py +301 -78
- ansible/plugins/connection/winrm.py +58 -140
- ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
- ansible/plugins/doc_fragments/action_core.py +6 -6
- ansible/plugins/doc_fragments/backup.py +2 -2
- ansible/plugins/doc_fragments/checksum_common.py +27 -0
- ansible/plugins/doc_fragments/constructed.py +6 -2
- ansible/plugins/doc_fragments/decrypt.py +2 -2
- ansible/plugins/doc_fragments/default_callback.py +2 -2
- ansible/plugins/doc_fragments/files.py +2 -2
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +2 -2
- ansible/plugins/doc_fragments/return_common.py +2 -2
- ansible/plugins/doc_fragments/template_common.py +4 -4
- ansible/plugins/doc_fragments/url.py +17 -1
- ansible/plugins/doc_fragments/url_windows.py +2 -2
- ansible/plugins/doc_fragments/validate.py +2 -2
- ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
- ansible/plugins/filter/__init__.py +6 -2
- ansible/plugins/filter/b64decode.yml +22 -0
- ansible/plugins/filter/b64encode.yml +22 -0
- ansible/plugins/filter/bool.yml +11 -4
- ansible/plugins/filter/core.py +225 -108
- ansible/plugins/filter/encryption.py +32 -32
- ansible/plugins/filter/flatten.yml +3 -2
- ansible/plugins/filter/human_to_bytes.yml +1 -1
- ansible/plugins/filter/mathstuff.py +30 -37
- ansible/plugins/filter/password_hash.yml +8 -0
- ansible/plugins/filter/regex_search.yml +1 -4
- ansible/plugins/filter/split.yml +1 -1
- ansible/plugins/filter/to_nice_yaml.yml +0 -4
- ansible/plugins/filter/to_yaml.yml +0 -4
- ansible/plugins/filter/unvault.yml +1 -1
- ansible/plugins/filter/urls.py +1 -1
- ansible/plugins/filter/urlsplit.py +8 -9
- ansible/plugins/filter/vault.yml +14 -9
- ansible/plugins/filter/win_basename.yml +6 -1
- ansible/plugins/filter/win_dirname.yml +5 -0
- ansible/plugins/inventory/__init__.py +97 -77
- ansible/plugins/inventory/advanced_host_list.py +7 -5
- ansible/plugins/inventory/auto.py +11 -4
- ansible/plugins/inventory/constructed.py +21 -24
- ansible/plugins/inventory/generator.py +16 -11
- ansible/plugins/inventory/host_list.py +7 -5
- ansible/plugins/inventory/ini.py +78 -44
- ansible/plugins/inventory/script.py +189 -119
- ansible/plugins/inventory/toml.py +16 -126
- ansible/plugins/inventory/yaml.py +10 -8
- ansible/plugins/list.py +3 -3
- ansible/plugins/loader.py +197 -82
- ansible/plugins/lookup/__init__.py +21 -4
- ansible/plugins/lookup/config.py +21 -35
- ansible/plugins/lookup/csvfile.py +3 -2
- ansible/plugins/lookup/dict.py +1 -6
- ansible/plugins/lookup/env.py +12 -9
- ansible/plugins/lookup/file.py +5 -8
- ansible/plugins/lookup/first_found.py +86 -55
- ansible/plugins/lookup/indexed_items.py +1 -10
- ansible/plugins/lookup/ini.py +14 -13
- ansible/plugins/lookup/items.py +1 -1
- ansible/plugins/lookup/lines.py +8 -1
- ansible/plugins/lookup/list.py +1 -1
- ansible/plugins/lookup/nested.py +2 -18
- ansible/plugins/lookup/password.py +5 -5
- ansible/plugins/lookup/pipe.py +5 -7
- ansible/plugins/lookup/sequence.py +18 -8
- ansible/plugins/lookup/subelements.py +1 -4
- ansible/plugins/lookup/template.py +42 -36
- ansible/plugins/lookup/together.py +0 -12
- ansible/plugins/lookup/unvault.py +1 -5
- ansible/plugins/lookup/url.py +2 -8
- ansible/plugins/lookup/vars.py +16 -24
- ansible/plugins/shell/__init__.py +2 -2
- ansible/plugins/shell/cmd.py +2 -2
- ansible/plugins/shell/powershell.py +39 -22
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +159 -184
- ansible/plugins/strategy/debug.py +2 -2
- ansible/plugins/strategy/free.py +16 -31
- ansible/plugins/strategy/host_pinned.py +2 -2
- ansible/plugins/strategy/linear.py +41 -41
- ansible/plugins/terminal/__init__.py +4 -4
- ansible/plugins/test/__init__.py +7 -2
- ansible/plugins/test/core.py +55 -21
- ansible/plugins/test/files.py +1 -1
- ansible/plugins/test/mathstuff.py +3 -3
- ansible/plugins/test/uri.py +3 -3
- ansible/plugins/vars/host_group_vars.py +7 -14
- ansible/release.py +2 -2
- ansible/template/__init__.py +370 -944
- ansible/utils/__init__.py +0 -18
- ansible/utils/_ssh_agent.py +657 -0
- ansible/utils/collection_loader/__init__.py +52 -5
- ansible/utils/collection_loader/_collection_config.py +5 -6
- ansible/utils/collection_loader/_collection_finder.py +79 -93
- ansible/utils/collection_loader/_collection_meta.py +13 -8
- ansible/utils/display.py +433 -63
- ansible/utils/encrypt.py +27 -19
- ansible/utils/fqcn.py +2 -2
- ansible/utils/hashing.py +2 -2
- ansible/utils/helpers.py +2 -2
- ansible/utils/listify.py +8 -8
- ansible/utils/lock.py +2 -2
- ansible/utils/path.py +4 -4
- ansible/utils/plugin_docs.py +14 -13
- ansible/utils/sentinel.py +4 -62
- ansible/utils/singleton.py +2 -0
- ansible/utils/ssh_functions.py +1 -1
- ansible/utils/unsafe_proxy.py +23 -332
- ansible/utils/vars.py +51 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +5 -5
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +206 -282
- ansible/vars/reserved.py +8 -9
- ansible_core-2.19.0b2.dist-info/BSD-3-Clause.txt +28 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/METADATA +5 -4
- ansible_core-2.19.0b2.dist-info/RECORD +1072 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/WHEEL +1 -1
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +6 -6
- ansible_test/_data/completion/windows.txt +1 -0
- ansible_test/_data/requirements/ansible.txt +2 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.txt +1 -1
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_data/requirements/units.txt +1 -0
- ansible_test/_internal/__init__.py +1 -0
- ansible_test/_internal/ansible_util.py +2 -0
- ansible_test/_internal/become.py +1 -0
- ansible_test/_internal/bootstrap.py +1 -0
- ansible_test/_internal/cache.py +1 -0
- ansible_test/_internal/cgroup.py +1 -0
- ansible_test/_internal/ci/__init__.py +1 -0
- ansible_test/_internal/ci/azp.py +1 -0
- ansible_test/_internal/ci/local.py +1 -0
- ansible_test/_internal/classification/__init__.py +1 -0
- ansible_test/_internal/classification/common.py +1 -0
- ansible_test/_internal/classification/csharp.py +1 -0
- ansible_test/_internal/classification/powershell.py +1 -0
- ansible_test/_internal/classification/python.py +1 -0
- ansible_test/_internal/cli/__init__.py +1 -0
- ansible_test/_internal/cli/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/__init__.py +1 -0
- ansible_test/_internal/cli/argparsing/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
- ansible_test/_internal/cli/argparsing/parsers.py +1 -0
- ansible_test/_internal/cli/commands/__init__.py +11 -0
- ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
- ansible_test/_internal/cli/commands/coverage/html.py +1 -0
- ansible_test/_internal/cli/commands/coverage/report.py +1 -0
- ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
- ansible_test/_internal/cli/commands/env.py +1 -0
- ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
- ansible_test/_internal/cli/commands/integration/network.py +1 -0
- ansible_test/_internal/cli/commands/integration/posix.py +1 -0
- ansible_test/_internal/cli/commands/integration/windows.py +1 -0
- ansible_test/_internal/cli/commands/sanity.py +9 -0
- ansible_test/_internal/cli/commands/shell.py +1 -0
- ansible_test/_internal/cli/commands/units.py +1 -0
- ansible_test/_internal/cli/compat.py +1 -0
- ansible_test/_internal/cli/completers.py +1 -0
- ansible_test/_internal/cli/converters.py +1 -0
- ansible_test/_internal/cli/environments.py +1 -0
- ansible_test/_internal/cli/epilog.py +1 -0
- ansible_test/_internal/cli/parsers/__init__.py +1 -0
- ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/helpers.py +1 -0
- ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
- ansible_test/_internal/commands/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/__init__.py +2 -1
- ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/commands/coverage/combine.py +2 -1
- ansible_test/_internal/commands/coverage/erase.py +1 -0
- ansible_test/_internal/commands/coverage/html.py +1 -0
- ansible_test/_internal/commands/coverage/report.py +1 -0
- ansible_test/_internal/commands/coverage/xml.py +1 -0
- ansible_test/_internal/commands/env/__init__.py +2 -0
- ansible_test/_internal/commands/integration/__init__.py +4 -0
- ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
- ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
- ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
- ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
- ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
- ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
- ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
- ansible_test/_internal/commands/integration/cloud/httptester.py +2 -1
- ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
- ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
- ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
- ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
- ansible_test/_internal/commands/integration/coverage.py +1 -0
- ansible_test/_internal/commands/integration/filters.py +1 -0
- ansible_test/_internal/commands/integration/network.py +1 -0
- ansible_test/_internal/commands/integration/posix.py +1 -0
- ansible_test/_internal/commands/integration/windows.py +1 -0
- ansible_test/_internal/commands/sanity/__init__.py +16 -1
- ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
- ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
- ansible_test/_internal/commands/sanity/compile.py +1 -0
- ansible_test/_internal/commands/sanity/ignores.py +1 -0
- ansible_test/_internal/commands/sanity/import.py +1 -0
- ansible_test/_internal/commands/sanity/integration_aliases.py +1 -0
- ansible_test/_internal/commands/sanity/pep8.py +1 -0
- ansible_test/_internal/commands/sanity/pslint.py +1 -0
- ansible_test/_internal/commands/sanity/pylint.py +24 -26
- ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
- ansible_test/_internal/commands/sanity/yamllint.py +1 -0
- ansible_test/_internal/commands/shell/__init__.py +1 -0
- ansible_test/_internal/commands/units/__init__.py +1 -0
- ansible_test/_internal/compat/__init__.py +1 -0
- ansible_test/_internal/compat/packaging.py +1 -0
- ansible_test/_internal/compat/yaml.py +1 -0
- ansible_test/_internal/completion.py +1 -0
- ansible_test/_internal/config.py +2 -0
- ansible_test/_internal/connections.py +1 -0
- ansible_test/_internal/constants.py +1 -0
- ansible_test/_internal/containers.py +1 -0
- ansible_test/_internal/content_config.py +1 -0
- ansible_test/_internal/core_ci.py +1 -0
- ansible_test/_internal/coverage_util.py +11 -10
- ansible_test/_internal/data.py +1 -0
- ansible_test/_internal/delegation.py +1 -0
- ansible_test/_internal/dev/__init__.py +1 -0
- ansible_test/_internal/dev/container_probe.py +1 -0
- ansible_test/_internal/diff.py +3 -2
- ansible_test/_internal/docker_util.py +2 -1
- ansible_test/_internal/encoding.py +1 -0
- ansible_test/_internal/executor.py +1 -0
- ansible_test/_internal/git.py +1 -0
- ansible_test/_internal/host_configs.py +1 -0
- ansible_test/_internal/host_profiles.py +1 -0
- ansible_test/_internal/http.py +1 -0
- ansible_test/_internal/init.py +1 -0
- ansible_test/_internal/inventory.py +35 -3
- ansible_test/_internal/io.py +1 -0
- ansible_test/_internal/metadata.py +1 -0
- ansible_test/_internal/payload.py +1 -0
- ansible_test/_internal/provider/__init__.py +1 -0
- ansible_test/_internal/provider/layout/__init__.py +1 -0
- ansible_test/_internal/provider/layout/ansible.py +1 -0
- ansible_test/_internal/provider/layout/collection.py +1 -0
- ansible_test/_internal/provider/layout/unsupported.py +1 -0
- ansible_test/_internal/provider/source/__init__.py +1 -0
- ansible_test/_internal/provider/source/git.py +1 -0
- ansible_test/_internal/provider/source/installed.py +1 -0
- ansible_test/_internal/provider/source/unsupported.py +1 -0
- ansible_test/_internal/provider/source/unversioned.py +1 -0
- ansible_test/_internal/provisioning.py +1 -0
- ansible_test/_internal/pypi_proxy.py +6 -5
- ansible_test/_internal/python_requirements.py +1 -0
- ansible_test/_internal/ssh.py +1 -0
- ansible_test/_internal/target.py +1 -0
- ansible_test/_internal/test.py +3 -2
- ansible_test/_internal/thread.py +1 -0
- ansible_test/_internal/timeout.py +1 -0
- ansible_test/_internal/util.py +1 -0
- ansible_test/_internal/util_common.py +5 -2
- ansible_test/_internal/venv.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
- ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
- ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
- ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
- ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
- ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
- ansible_test/_util/controller/tools/collection_detail.py +1 -0
- ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
- ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
- ansible_test/_util/target/sanity/compile/compile.py +1 -0
- ansible_test/_util/target/sanity/import/importer.py +15 -16
- ansible_test/_util/target/setup/bootstrap.sh +9 -20
- ansible_test/_util/target/setup/probe_cgroups.py +1 -0
- ansible_test/_util/target/setup/quiet_pip.py +1 -0
- ansible_test/_util/target/setup/requirements.py +35 -27
- ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
- ansible_test/_util/target/tools/yamlcheck.py +2 -1
- ansible/compat/selectors.py +0 -32
- ansible/errors/yaml_strings.py +0 -138
- ansible/executor/action_write_locks.py +0 -44
- ansible/executor/discovery/python_target.py +0 -47
- ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
- ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
- ansible/module_utils/compat/importlib.py +0 -26
- ansible/module_utils/compat/selectors.py +0 -32
- ansible/module_utils/pycompat24.py +0 -73
- ansible/parsing/yaml/constructor.py +0 -178
- ansible/template/native_helpers.py +0 -251
- ansible/template/template.py +0 -43
- ansible/template/vars.py +0 -77
- ansible/utils/native_jinja.py +0 -11
- ansible/vars/fact_cache.py +0 -71
- ansible_core-2.18.5rc1.dist-info/RECORD +0 -992
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/COPYING +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import base64
|
9
|
+
import contextlib
|
9
10
|
import json
|
10
11
|
import os
|
11
12
|
import re
|
@@ -13,29 +14,44 @@ import secrets
|
|
13
14
|
import shlex
|
14
15
|
import stat
|
15
16
|
import tempfile
|
17
|
+
import typing as t
|
16
18
|
|
17
19
|
from abc import ABC, abstractmethod
|
18
20
|
from collections.abc import Sequence
|
19
21
|
|
20
22
|
from ansible import constants as C
|
23
|
+
from ansible._internal._errors import _captured
|
21
24
|
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleActionSkip, AnsibleActionFail, AnsibleAuthenticationFailure
|
22
|
-
from ansible.
|
25
|
+
from ansible._internal._errors import _utils
|
26
|
+
from ansible.executor.module_common import modify_module, _BuiltModule
|
23
27
|
from ansible.executor.interpreter_discovery import discover_interpreter, InterpreterDiscoveryRequiredError
|
28
|
+
from ansible.module_utils._internal import _traceback
|
24
29
|
from ansible.module_utils.common.arg_spec import ArgumentSpecValidator
|
25
30
|
from ansible.module_utils.errors import UnsupportedError
|
26
31
|
from ansible.module_utils.json_utils import _filter_non_json_lines
|
32
|
+
from ansible.module_utils.common.json import Direction, get_module_encoder, get_module_decoder
|
27
33
|
from ansible.module_utils.six import binary_type, string_types, text_type
|
28
34
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
29
|
-
from ansible.parsing.utils.jsonify import jsonify
|
30
35
|
from ansible.release import __version__
|
31
36
|
from ansible.utils.collection_loader import resource_from_fqcr
|
32
37
|
from ansible.utils.display import Display
|
33
|
-
from ansible.utils.unsafe_proxy import wrap_var, AnsibleUnsafeText
|
34
38
|
from ansible.vars.clean import remove_internal_keys
|
35
39
|
from ansible.utils.plugin_docs import get_versioned_doclink
|
40
|
+
from ansible import _internal
|
41
|
+
from ansible._internal._templating import _engine
|
42
|
+
|
43
|
+
from .. import _AnsiblePluginInfoMixin
|
44
|
+
from ...module_utils.common.messages import PluginInfo
|
36
45
|
|
37
46
|
display = Display()
|
38
47
|
|
48
|
+
if t.TYPE_CHECKING:
|
49
|
+
from ansible.parsing.dataloader import DataLoader
|
50
|
+
from ansible.playbook.play_context import PlayContext
|
51
|
+
from ansible.playbook.task import Task
|
52
|
+
from ansible.plugins.connection import ConnectionBase
|
53
|
+
from ansible.template import Templar
|
54
|
+
|
39
55
|
|
40
56
|
def _validate_utf8_json(d):
|
41
57
|
if isinstance(d, text_type):
|
@@ -49,14 +65,13 @@ def _validate_utf8_json(d):
|
|
49
65
|
_validate_utf8_json(o)
|
50
66
|
|
51
67
|
|
52
|
-
class ActionBase(ABC):
|
53
|
-
|
54
|
-
'''
|
68
|
+
class ActionBase(ABC, _AnsiblePluginInfoMixin):
|
69
|
+
"""
|
55
70
|
This class is the base class for all action plugins, and defines
|
56
71
|
code common to all actions. The base class handles the connection
|
57
72
|
by putting/getting files and executing commands based on the current
|
58
73
|
action in use.
|
59
|
-
|
74
|
+
"""
|
60
75
|
|
61
76
|
# A set of valid arguments
|
62
77
|
_VALID_ARGS = frozenset([]) # type: frozenset[str]
|
@@ -67,22 +82,24 @@ class ActionBase(ABC):
|
|
67
82
|
_requires_connection = True
|
68
83
|
_supports_check_mode = True
|
69
84
|
_supports_async = False
|
85
|
+
supports_raw_params = False
|
70
86
|
|
71
|
-
def __init__(self, task, connection, play_context, loader, templar, shared_loader_obj):
|
87
|
+
def __init__(self, task: Task, connection: ConnectionBase, play_context: PlayContext, loader: DataLoader, templar: Templar, shared_loader_obj=None):
|
72
88
|
self._task = task
|
73
89
|
self._connection = connection
|
74
90
|
self._play_context = play_context
|
75
91
|
self._loader = loader
|
76
92
|
self._templar = templar
|
77
|
-
|
93
|
+
|
94
|
+
from ansible.plugins import loader as plugin_loaders # avoid circular global import since PluginLoader needs ActionBase
|
95
|
+
|
96
|
+
self._shared_loader_obj = plugin_loaders # shared_loader_obj was just a ref to `ansible.plugins.loader` anyway; this lets us inherit its type
|
78
97
|
self._cleanup_remote_tmp = False
|
79
98
|
|
80
99
|
# interpreter discovery state
|
81
|
-
self._discovered_interpreter_key = None
|
100
|
+
self._discovered_interpreter_key: str | None = None
|
82
101
|
self._discovered_interpreter = False
|
83
|
-
self.
|
84
|
-
self._discovery_warnings = []
|
85
|
-
self._used_interpreter = None
|
102
|
+
self._used_interpreter: str | None = None
|
86
103
|
|
87
104
|
# Backwards compat: self._display isn't really needed, just import the global display and use that.
|
88
105
|
self._display = display
|
@@ -109,9 +126,9 @@ class ActionBase(ABC):
|
|
109
126
|
result = {}
|
110
127
|
|
111
128
|
if tmp is not None:
|
112
|
-
|
113
|
-
|
114
|
-
|
129
|
+
display.warning('ActionModule.run() no longer honors the tmp parameter. Action'
|
130
|
+
' plugins should set self._connection._shell.tmpdir to share'
|
131
|
+
' the tmpdir.')
|
115
132
|
del tmp
|
116
133
|
|
117
134
|
if self._task.async_val and not self._supports_async:
|
@@ -177,7 +194,7 @@ class ActionBase(ABC):
|
|
177
194
|
if isinstance(error, UnsupportedError):
|
178
195
|
msg = f"Unsupported parameters for ({self._load_name}) module: {msg}"
|
179
196
|
|
180
|
-
raise AnsibleActionFail(msg)
|
197
|
+
raise AnsibleActionFail(msg, obj=self._task.args)
|
181
198
|
|
182
199
|
return validation_result, new_module_args
|
183
200
|
|
@@ -193,6 +210,28 @@ class ActionBase(ABC):
|
|
193
210
|
if force or not self._task.async_val:
|
194
211
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
195
212
|
|
213
|
+
@classmethod
|
214
|
+
@contextlib.contextmanager
|
215
|
+
@_internal.experimental
|
216
|
+
def get_finalize_task_args_context(cls) -> t.Any:
|
217
|
+
"""
|
218
|
+
EXPERIMENTAL: Unstable API subject to change at any time without notice.
|
219
|
+
Wraps task arg finalization with (optional) stateful context.
|
220
|
+
The context manager is entered during `Task.post_validate_args, and may yield a single value to be passed
|
221
|
+
as `context` to Task.finalize_task_arg for each task arg.
|
222
|
+
"""
|
223
|
+
yield None
|
224
|
+
|
225
|
+
@classmethod
|
226
|
+
@_internal.experimental
|
227
|
+
def finalize_task_arg(cls, name: str, value: t.Any, templar: _engine.TemplateEngine, context: t.Any) -> t.Any:
|
228
|
+
"""
|
229
|
+
EXPERIMENTAL: Unstable API subject to change at any time without notice.
|
230
|
+
Called for each task arg to allow for custom templating.
|
231
|
+
The optional `context` value is sourced from `Task.get_finalize_task_args_context`.
|
232
|
+
"""
|
233
|
+
return templar.template(value)
|
234
|
+
|
196
235
|
def get_plugin_option(self, plugin, option, default=None):
|
197
236
|
"""Helper to get an option from a plugin without having to use
|
198
237
|
the try/except dance everywhere to set a default
|
@@ -218,11 +257,11 @@ class ActionBase(ABC):
|
|
218
257
|
return True
|
219
258
|
return False
|
220
259
|
|
221
|
-
def _configure_module(self, module_name, module_args, task_vars):
|
222
|
-
|
260
|
+
def _configure_module(self, module_name, module_args, task_vars) -> tuple[_BuiltModule, str]:
|
261
|
+
"""
|
223
262
|
Handles the loading and templating of the module code through the
|
224
263
|
modify_module() function.
|
225
|
-
|
264
|
+
"""
|
226
265
|
if self._task.delegate_to:
|
227
266
|
use_vars = task_vars.get('ansible_delegated_vars')[self._task.delegate_to]
|
228
267
|
else:
|
@@ -276,38 +315,37 @@ class ActionBase(ABC):
|
|
276
315
|
raise AnsibleError("The module %s was not found in configured module paths" % (module_name))
|
277
316
|
|
278
317
|
# insert shared code and arguments into the module
|
279
|
-
final_environment =
|
318
|
+
final_environment: dict[str, t.Any] = {}
|
280
319
|
self._compute_environment_string(final_environment)
|
281
320
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
become_kwargs['become_password'] = self._connection.become.get_option('become_pass',
|
289
|
-
playcontext=self._play_context)
|
290
|
-
become_kwargs['become_flags'] = self._connection.become.get_option('become_flags',
|
291
|
-
playcontext=self._play_context)
|
321
|
+
# `modify_module` adapts PluginInfo to allow target-side use of `PluginExecContext` since modules aren't plugins
|
322
|
+
plugin = PluginInfo(
|
323
|
+
requested_name=module_name,
|
324
|
+
resolved_name=result.resolved_fqcn,
|
325
|
+
type='module',
|
326
|
+
)
|
292
327
|
|
293
328
|
# modify_module will exit early if interpreter discovery is required; re-run after if necessary
|
294
|
-
for
|
329
|
+
for _dummy in (1, 2):
|
295
330
|
try:
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
331
|
+
module_bits = modify_module(
|
332
|
+
module_name=module_name,
|
333
|
+
module_path=module_path,
|
334
|
+
module_args=module_args,
|
335
|
+
templar=self._templar,
|
336
|
+
task_vars=use_vars,
|
337
|
+
module_compression=C.config.get_config_value('DEFAULT_MODULE_COMPRESSION', variables=task_vars),
|
338
|
+
async_timeout=self._task.async_val,
|
339
|
+
environment=final_environment,
|
340
|
+
remote_is_local=bool(getattr(self._connection, '_remote_is_local', False)),
|
341
|
+
plugin=plugin,
|
342
|
+
become_plugin=self._connection.become,
|
343
|
+
)
|
344
|
+
|
304
345
|
break
|
305
346
|
except InterpreterDiscoveryRequiredError as idre:
|
306
|
-
self._discovered_interpreter =
|
307
|
-
|
308
|
-
interpreter_name=idre.interpreter_name,
|
309
|
-
discovery_mode=idre.discovery_mode,
|
310
|
-
task_vars=use_vars))
|
347
|
+
self._discovered_interpreter = discover_interpreter(action=self, interpreter_name=idre.interpreter_name,
|
348
|
+
discovery_mode=idre.discovery_mode, task_vars=use_vars)
|
311
349
|
|
312
350
|
# update the local task_vars with the discovered interpreter (which might be None);
|
313
351
|
# we'll propagate back to the controller in the task result
|
@@ -327,12 +365,12 @@ class ActionBase(ABC):
|
|
327
365
|
else:
|
328
366
|
task_vars['ansible_delegated_vars'][self._task.delegate_to]['ansible_facts'][discovered_key] = self._discovered_interpreter
|
329
367
|
|
330
|
-
return
|
368
|
+
return module_bits, module_path
|
331
369
|
|
332
370
|
def _compute_environment_string(self, raw_environment_out=None):
|
333
|
-
|
371
|
+
"""
|
334
372
|
Builds the environment string to be used when executing the remote task.
|
335
|
-
|
373
|
+
"""
|
336
374
|
|
337
375
|
final_environment = dict()
|
338
376
|
if self._task.environment is not None:
|
@@ -363,52 +401,28 @@ class ActionBase(ABC):
|
|
363
401
|
return self._connection._shell.env_prefix(**final_environment)
|
364
402
|
|
365
403
|
def _early_needs_tmp_path(self):
|
366
|
-
|
404
|
+
"""
|
367
405
|
Determines if a tmp path should be created before the action is executed.
|
368
|
-
|
406
|
+
"""
|
369
407
|
|
370
408
|
return getattr(self, 'TRANSFERS_FILES', False)
|
371
409
|
|
372
|
-
def _is_pipelining_enabled(self, module_style, wrap_async=False):
|
373
|
-
|
374
|
-
Determines if we are required and can do pipelining
|
375
|
-
|
376
|
-
|
377
|
-
try:
|
378
|
-
is_enabled = self._connection.get_option('pipelining')
|
379
|
-
except (KeyError, AttributeError, ValueError):
|
380
|
-
is_enabled = self._play_context.pipelining
|
381
|
-
|
382
|
-
# winrm supports async pipeline
|
383
|
-
# TODO: make other class property 'has_async_pipelining' to separate cases
|
384
|
-
always_pipeline = self._connection.always_pipeline_modules
|
385
|
-
|
386
|
-
# su does not work with pipelining
|
387
|
-
# TODO: add has_pipelining class prop to become plugins
|
388
|
-
become_exception = (self._connection.become.name if self._connection.become else '') != 'su'
|
389
|
-
|
390
|
-
# any of these require a true
|
391
|
-
conditions = [
|
392
|
-
self._connection.has_pipelining, # connection class supports it
|
393
|
-
is_enabled or always_pipeline, # enabled via config or forced via connection (eg winrm)
|
394
|
-
module_style == "new", # old style modules do not support pipelining
|
395
|
-
not C.DEFAULT_KEEP_REMOTE_FILES, # user wants remote files
|
396
|
-
not wrap_async or always_pipeline, # async does not normally support pipelining unless it does (eg winrm)
|
397
|
-
become_exception,
|
398
|
-
]
|
399
|
-
|
400
|
-
return all(conditions)
|
410
|
+
def _is_pipelining_enabled(self, module_style: str, wrap_async: bool = False) -> bool:
|
411
|
+
"""
|
412
|
+
Determines if we are required and can do pipelining, only 'new' style modules can support pipelining
|
413
|
+
"""
|
414
|
+
return bool(module_style == 'new' and self._connection.is_pipelining_enabled(wrap_async))
|
401
415
|
|
402
416
|
def _get_admin_users(self):
|
403
|
-
|
417
|
+
"""
|
404
418
|
Returns a list of admin users that are configured for the current shell
|
405
419
|
plugin
|
406
|
-
|
420
|
+
"""
|
407
421
|
|
408
422
|
return self.get_shell_option('admin_users', ['root'])
|
409
423
|
|
410
424
|
def _get_remote_addr(self, tvars):
|
411
|
-
|
425
|
+
""" consistently get the 'remote_address' for the action plugin """
|
412
426
|
remote_addr = tvars.get('delegated_vars', {}).get('ansible_host', tvars.get('ansible_host', tvars.get('inventory_hostname', None)))
|
413
427
|
for variation in ('remote_addr', 'host'):
|
414
428
|
try:
|
@@ -422,7 +436,7 @@ class ActionBase(ABC):
|
|
422
436
|
return remote_addr
|
423
437
|
|
424
438
|
def _get_remote_user(self):
|
425
|
-
|
439
|
+
""" consistently get the 'remote_user' for the action plugin """
|
426
440
|
# TODO: use 'current user running ansible' as fallback when moving away from play_context
|
427
441
|
# pwd.getpwuid(os.getuid()).pw_name
|
428
442
|
remote_user = None
|
@@ -437,10 +451,10 @@ class ActionBase(ABC):
|
|
437
451
|
return remote_user
|
438
452
|
|
439
453
|
def _is_become_unprivileged(self):
|
440
|
-
|
454
|
+
"""
|
441
455
|
The user is not the same as the connection user and is not part of the
|
442
456
|
shell configured admin users
|
443
|
-
|
457
|
+
"""
|
444
458
|
# if we don't use become then we know we aren't switching to a
|
445
459
|
# different unprivileged user
|
446
460
|
if not self._connection.become:
|
@@ -454,9 +468,9 @@ class ActionBase(ABC):
|
|
454
468
|
return bool(become_user and become_user not in admin_users + [remote_user])
|
455
469
|
|
456
470
|
def _make_tmp_path(self, remote_user=None):
|
457
|
-
|
471
|
+
"""
|
458
472
|
Create and return a temporary path on a remote box.
|
459
|
-
|
473
|
+
"""
|
460
474
|
|
461
475
|
# Network connection plugins (network_cli, netconf, etc.) execute on the controller, rather than the remote host.
|
462
476
|
# As such, we want to avoid using remote_user for paths as remote_user may not line up with the local user
|
@@ -517,11 +531,11 @@ class ActionBase(ABC):
|
|
517
531
|
return rc
|
518
532
|
|
519
533
|
def _should_remove_tmp_path(self, tmp_path):
|
520
|
-
|
534
|
+
"""Determine if temporary path should be deleted or kept by user request/config"""
|
521
535
|
return tmp_path and self._cleanup_remote_tmp and not C.DEFAULT_KEEP_REMOTE_FILES and "-tmp-" in tmp_path
|
522
536
|
|
523
537
|
def _remove_tmp_path(self, tmp_path, force=False):
|
524
|
-
|
538
|
+
"""Remove a temporary path we created. """
|
525
539
|
|
526
540
|
if tmp_path is None and self._connection._shell.tmpdir:
|
527
541
|
tmp_path = self._connection._shell.tmpdir
|
@@ -556,18 +570,19 @@ class ActionBase(ABC):
|
|
556
570
|
self._connection.put_file(local_path, remote_path)
|
557
571
|
return remote_path
|
558
572
|
|
559
|
-
def _transfer_data(self, remote_path, data):
|
560
|
-
|
573
|
+
def _transfer_data(self, remote_path: str | bytes, data: str | bytes) -> str | bytes:
|
574
|
+
"""
|
561
575
|
Copies the module data out to the temporary module path.
|
562
|
-
|
576
|
+
"""
|
563
577
|
|
564
|
-
if isinstance(data,
|
565
|
-
data =
|
578
|
+
if isinstance(data, str):
|
579
|
+
data = data.encode(errors='surrogateescape')
|
580
|
+
elif not isinstance(data, bytes):
|
581
|
+
raise TypeError('data must be either a string or bytes')
|
566
582
|
|
567
583
|
afd, afile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP)
|
568
584
|
afo = os.fdopen(afd, 'wb')
|
569
585
|
try:
|
570
|
-
data = to_bytes(data, errors='surrogate_or_strict')
|
571
586
|
afo.write(data)
|
572
587
|
except Exception as e:
|
573
588
|
raise AnsibleError("failure writing module data to temporary file for transfer: %s" % to_native(e))
|
@@ -802,41 +817,41 @@ class ActionBase(ABC):
|
|
802
817
|
to_native(res['stderr']), become_link))
|
803
818
|
|
804
819
|
def _remote_chmod(self, paths, mode, sudoable=False):
|
805
|
-
|
820
|
+
"""
|
806
821
|
Issue a remote chmod command
|
807
|
-
|
822
|
+
"""
|
808
823
|
cmd = self._connection._shell.chmod(paths, mode)
|
809
824
|
res = self._low_level_execute_command(cmd, sudoable=sudoable)
|
810
825
|
return res
|
811
826
|
|
812
827
|
def _remote_chown(self, paths, user, sudoable=False):
|
813
|
-
|
828
|
+
"""
|
814
829
|
Issue a remote chown command
|
815
|
-
|
830
|
+
"""
|
816
831
|
cmd = self._connection._shell.chown(paths, user)
|
817
832
|
res = self._low_level_execute_command(cmd, sudoable=sudoable)
|
818
833
|
return res
|
819
834
|
|
820
835
|
def _remote_chgrp(self, paths, group, sudoable=False):
|
821
|
-
|
836
|
+
"""
|
822
837
|
Issue a remote chgrp command
|
823
|
-
|
838
|
+
"""
|
824
839
|
cmd = self._connection._shell.chgrp(paths, group)
|
825
840
|
res = self._low_level_execute_command(cmd, sudoable=sudoable)
|
826
841
|
return res
|
827
842
|
|
828
843
|
def _remote_set_user_facl(self, paths, user, mode, sudoable=False):
|
829
|
-
|
844
|
+
"""
|
830
845
|
Issue a remote call to setfacl
|
831
|
-
|
846
|
+
"""
|
832
847
|
cmd = self._connection._shell.set_user_facl(paths, user, mode)
|
833
848
|
res = self._low_level_execute_command(cmd, sudoable=sudoable)
|
834
849
|
return res
|
835
850
|
|
836
851
|
def _execute_remote_stat(self, path, all_vars, follow, tmp=None, checksum=True):
|
837
|
-
|
852
|
+
"""
|
838
853
|
Get information from remote file.
|
839
|
-
|
854
|
+
"""
|
840
855
|
if tmp is not None:
|
841
856
|
display.warning('_execute_remote_stat no longer honors the tmp parameter. Action'
|
842
857
|
' plugins should set self._connection._shell.tmpdir to share'
|
@@ -876,7 +891,7 @@ class ActionBase(ABC):
|
|
876
891
|
return mystat['stat']
|
877
892
|
|
878
893
|
def _remote_expand_user(self, path, sudoable=True, pathsep=None):
|
879
|
-
|
894
|
+
""" takes a remote path and performs tilde/$HOME expansion on the remote host """
|
880
895
|
|
881
896
|
# We only expand ~/path and ~username/path
|
882
897
|
if not path.startswith('~'):
|
@@ -930,9 +945,9 @@ class ActionBase(ABC):
|
|
930
945
|
return expanded
|
931
946
|
|
932
947
|
def _strip_success_message(self, data):
|
933
|
-
|
948
|
+
"""
|
934
949
|
Removes the BECOME-SUCCESS message from the data.
|
935
|
-
|
950
|
+
"""
|
936
951
|
if data.strip().startswith('BECOME-SUCCESS-'):
|
937
952
|
data = re.sub(r'^((\r)?\n)?BECOME-SUCCESS.*(\r)?\n', '', data)
|
938
953
|
return data
|
@@ -972,9 +987,6 @@ class ActionBase(ABC):
|
|
972
987
|
# let module know about filesystems that selinux treats specially
|
973
988
|
module_args['_ansible_selinux_special_fs'] = C.DEFAULT_SELINUX_SPECIAL_FS
|
974
989
|
|
975
|
-
# what to do when parameter values are converted to strings
|
976
|
-
module_args['_ansible_string_conversion_action'] = C.STRING_CONVERSION_ACTION
|
977
|
-
|
978
990
|
# give the module the socket for persistent connections
|
979
991
|
module_args['_ansible_socket'] = getattr(self._connection, 'socket_path')
|
980
992
|
if not module_args['_ansible_socket']:
|
@@ -1001,11 +1013,13 @@ class ActionBase(ABC):
|
|
1001
1013
|
# allow user to insert string to add context to remote loggging
|
1002
1014
|
module_args['_ansible_target_log_info'] = C.config.get_config_value('TARGET_LOG_INFO', variables=task_vars)
|
1003
1015
|
|
1016
|
+
module_args['_ansible_tracebacks_for'] = _traceback.traceback_for()
|
1017
|
+
|
1004
1018
|
def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False,
|
1005
1019
|
ignore_unknown_opts: bool = False):
|
1006
|
-
|
1020
|
+
"""
|
1007
1021
|
Transfer and run a module along with its arguments.
|
1008
|
-
|
1022
|
+
"""
|
1009
1023
|
if tmp is not None:
|
1010
1024
|
display.warning('_execute_module no longer honors the tmp parameter. Action plugins'
|
1011
1025
|
' should set self._connection._shell.tmpdir to share the tmpdir')
|
@@ -1047,7 +1061,8 @@ class ActionBase(ABC):
|
|
1047
1061
|
self._task.environment.append({"ANSIBLE_ASYNC_DIR": async_dir})
|
1048
1062
|
|
1049
1063
|
# FUTURE: refactor this along with module build process to better encapsulate "smart wrapper" functionality
|
1050
|
-
|
1064
|
+
module_bits, module_path = self._configure_module(module_name=module_name, module_args=module_args, task_vars=task_vars)
|
1065
|
+
(module_style, shebang, module_data) = (module_bits.module_style, module_bits.shebang, module_bits.b_module_data)
|
1051
1066
|
display.vvv("Using module file %s" % module_path)
|
1052
1067
|
if not shebang and module_style != 'binary':
|
1053
1068
|
raise AnsibleError("module (%s) is missing interpreter line" % module_name)
|
@@ -1083,7 +1098,8 @@ class ActionBase(ABC):
|
|
1083
1098
|
args_data += '%s=%s ' % (k, shlex.quote(text_type(v)))
|
1084
1099
|
self._transfer_data(args_file_path, args_data)
|
1085
1100
|
elif module_style in ('non_native_want_json', 'binary'):
|
1086
|
-
|
1101
|
+
profile_encoder = get_module_encoder(module_bits.serialization_profile, Direction.CONTROLLER_TO_MODULE)
|
1102
|
+
self._transfer_data(args_file_path, json.dumps(module_args, cls=profile_encoder))
|
1087
1103
|
display.debug("done transferring module to remote")
|
1088
1104
|
|
1089
1105
|
environment_string = self._compute_environment_string()
|
@@ -1106,8 +1122,8 @@ class ActionBase(ABC):
|
|
1106
1122
|
|
1107
1123
|
if wrap_async and not self._connection.always_pipeline_modules:
|
1108
1124
|
# configure, upload, and chmod the async_wrapper module
|
1109
|
-
(
|
1110
|
-
|
1125
|
+
(async_module_bits, async_module_path) = self._configure_module(module_name='ansible.legacy.async_wrapper', module_args=dict(), task_vars=task_vars)
|
1126
|
+
(async_module_style, shebang, async_module_data) = (async_module_bits.module_style, async_module_bits.shebang, async_module_bits.b_module_data)
|
1111
1127
|
async_module_remote_filename = self._connection._shell.get_remote_filename(async_module_path)
|
1112
1128
|
remote_async_module_path = self._connection._shell.join_path(tmpdir, async_module_remote_filename)
|
1113
1129
|
self._transfer_data(remote_async_module_path, async_module_data)
|
@@ -1156,7 +1172,7 @@ class ActionBase(ABC):
|
|
1156
1172
|
res = self._low_level_execute_command(cmd, sudoable=sudoable, in_data=in_data)
|
1157
1173
|
|
1158
1174
|
# parse the main result
|
1159
|
-
data = self._parse_returned_data(res)
|
1175
|
+
data = self._parse_returned_data(res, module_bits.serialization_profile)
|
1160
1176
|
|
1161
1177
|
# NOTE: INTERNAL KEYS ONLY ACCESSIBLE HERE
|
1162
1178
|
# get internal info before cleaning
|
@@ -1197,74 +1213,71 @@ class ActionBase(ABC):
|
|
1197
1213
|
|
1198
1214
|
data['ansible_facts'][self._discovered_interpreter_key] = self._discovered_interpreter
|
1199
1215
|
|
1200
|
-
if self._discovery_warnings:
|
1201
|
-
if data.get('warnings') is None:
|
1202
|
-
data['warnings'] = []
|
1203
|
-
data['warnings'].extend(self._discovery_warnings)
|
1204
|
-
|
1205
|
-
if self._discovery_deprecation_warnings:
|
1206
|
-
if data.get('deprecations') is None:
|
1207
|
-
data['deprecations'] = []
|
1208
|
-
data['deprecations'].extend(self._discovery_deprecation_warnings)
|
1209
|
-
|
1210
|
-
# mark the entire module results untrusted as a template right here, since the current action could
|
1211
|
-
# possibly template one of these values.
|
1212
|
-
data = wrap_var(data)
|
1213
|
-
|
1214
1216
|
display.debug("done with _execute_module (%s, %s)" % (module_name, module_args))
|
1215
1217
|
return data
|
1216
1218
|
|
1217
|
-
def _parse_returned_data(self, res):
|
1219
|
+
def _parse_returned_data(self, res: dict[str, t.Any], profile: str) -> dict[str, t.Any]:
|
1218
1220
|
try:
|
1219
|
-
filtered_output, warnings = _filter_non_json_lines(res.get('stdout',
|
1221
|
+
filtered_output, warnings = _filter_non_json_lines(res.get('stdout', ''), objects_only=True)
|
1222
|
+
|
1220
1223
|
for w in warnings:
|
1221
1224
|
display.warning(w)
|
1222
1225
|
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
except ValueError as e:
|
1236
|
-
# not valid json, lets try to capture error
|
1237
|
-
data = dict(failed=True, _ansible_parsed=False)
|
1238
|
-
data['module_stdout'] = res.get('stdout', u'')
|
1239
|
-
if 'stderr' in res:
|
1240
|
-
data['module_stderr'] = res['stderr']
|
1241
|
-
if res['stderr'].startswith(u'Traceback'):
|
1242
|
-
data['exception'] = res['stderr']
|
1243
|
-
|
1244
|
-
# in some cases a traceback will arrive on stdout instead of stderr, such as when using ssh with -tt
|
1245
|
-
if 'exception' not in data and data['module_stdout'].startswith(u'Traceback'):
|
1246
|
-
data['exception'] = data['module_stdout']
|
1247
|
-
|
1248
|
-
# The default
|
1249
|
-
data['msg'] = f"MODULE FAILURE: {e}"
|
1250
|
-
|
1251
|
-
# try to figure out if we are missing interpreter
|
1226
|
+
decoder = get_module_decoder(profile, Direction.MODULE_TO_CONTROLLER)
|
1227
|
+
|
1228
|
+
data = json.loads(filtered_output, cls=decoder)
|
1229
|
+
|
1230
|
+
_captured.AnsibleModuleCapturedError.normalize_result_exception(data)
|
1231
|
+
|
1232
|
+
data.update(_ansible_parsed=True) # this must occur after normalize_result_exception, since it checks the type of data to ensure it's a dict
|
1233
|
+
except ValueError as ex:
|
1234
|
+
message = "Module result deserialization failed."
|
1235
|
+
help_text = ""
|
1236
|
+
include_cause_message = True
|
1237
|
+
|
1252
1238
|
if self._used_interpreter is not None:
|
1253
|
-
interpreter =
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1239
|
+
interpreter = self._used_interpreter.lstrip('!#')
|
1240
|
+
# "not found" case is currently not tested; it was once reproducible
|
1241
|
+
# see: https://github.com/ansible/ansible/pull/53534
|
1242
|
+
not_found_err_re = re.compile(rf'{re.escape(interpreter)}: (?:No such file or directory|not found|command not found)')
|
1257
1243
|
|
1258
|
-
|
1259
|
-
|
1244
|
+
if not_found_err_re.search(res.get('stderr', '')) or not_found_err_re.search(res.get('stdout', '')):
|
1245
|
+
message = f"The module interpreter {interpreter!r} was not found."
|
1246
|
+
help_text = 'Consider overriding the configured interpreter path for this host. '
|
1247
|
+
include_cause_message = False # cause context *might* be useful in the traceback, but the JSON deserialization failure message is not
|
1248
|
+
|
1249
|
+
try:
|
1250
|
+
# Because the underlying action API is built on result dicts instead of exceptions (for all but the most catastrophic failures),
|
1251
|
+
# we're using a tweaked version of the module exception handler to get new ErrorDetail-backed errors from this part of the code.
|
1252
|
+
# Ideally this would raise immediately on failure, but this would likely break actions that assume `ActionBase._execute_module()`
|
1253
|
+
# does not raise on module failure.
|
1254
|
+
|
1255
|
+
error = AnsibleError(
|
1256
|
+
message=message,
|
1257
|
+
help_text=help_text + "See stdout/stderr for the returned output.",
|
1258
|
+
)
|
1259
|
+
|
1260
|
+
error._include_cause_message = include_cause_message
|
1261
|
+
|
1262
|
+
raise error from ex
|
1263
|
+
except AnsibleError as ansible_ex:
|
1264
|
+
sentinel = object()
|
1265
|
+
|
1266
|
+
data = self.result_dict_from_exception(ansible_ex)
|
1267
|
+
data.update(
|
1268
|
+
_ansible_parsed=False,
|
1269
|
+
module_stdout=res.get('stdout', ''),
|
1270
|
+
module_stderr=res.get('stderr', sentinel),
|
1271
|
+
rc=res.get('rc', sentinel),
|
1272
|
+
)
|
1273
|
+
|
1274
|
+
data = {k: v for k, v in data.items() if v is not sentinel}
|
1260
1275
|
|
1261
|
-
if 'rc' in res:
|
1262
|
-
data['rc'] = res['rc']
|
1263
1276
|
return data
|
1264
1277
|
|
1265
1278
|
# FIXME: move to connection base
|
1266
1279
|
def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, executable=None, encoding_errors='surrogate_then_replace', chdir=None):
|
1267
|
-
|
1280
|
+
"""
|
1268
1281
|
This is the function which executes the low level shell command, which
|
1269
1282
|
may be commands to create/remove directories for temporary files, or to
|
1270
1283
|
run the module code or python directly when pipelining.
|
@@ -1277,7 +1290,7 @@ class ActionBase(ABC):
|
|
1277
1290
|
verbatim, then this won't work. May have to use some sort of
|
1278
1291
|
replacement strategy (python3 could use surrogateescape)
|
1279
1292
|
:kwarg chdir: cd into this directory before executing the command.
|
1280
|
-
|
1293
|
+
"""
|
1281
1294
|
|
1282
1295
|
display.debug("_low_level_execute_command(): starting")
|
1283
1296
|
# if not cmd:
|
@@ -1373,7 +1386,7 @@ class ActionBase(ABC):
|
|
1373
1386
|
elif peek_result.get('size') and C.MAX_FILE_SIZE_FOR_DIFF > 0 and peek_result['size'] > C.MAX_FILE_SIZE_FOR_DIFF:
|
1374
1387
|
diff['dst_larger'] = C.MAX_FILE_SIZE_FOR_DIFF
|
1375
1388
|
else:
|
1376
|
-
display.debug(u"Slurping the file %s" %
|
1389
|
+
display.debug(u"Slurping the file %s" % destination)
|
1377
1390
|
dest_result = self._execute_module(
|
1378
1391
|
module_name='ansible.legacy.slurp', module_args=dict(path=destination),
|
1379
1392
|
task_vars=task_vars, persist_files=True)
|
@@ -1420,14 +1433,34 @@ class ActionBase(ABC):
|
|
1420
1433
|
return diff
|
1421
1434
|
|
1422
1435
|
def _find_needle(self, dirname, needle):
|
1423
|
-
|
1436
|
+
"""
|
1424
1437
|
find a needle in haystack of paths, optionally using 'dirname' as a subdir.
|
1425
1438
|
This will build the ordered list of paths to search and pass them to dwim
|
1426
1439
|
to get back the first existing file found.
|
1427
|
-
|
1440
|
+
"""
|
1428
1441
|
|
1429
1442
|
# dwim already deals with playbook basedirs
|
1430
1443
|
path_stack = self._task.get_search_path()
|
1431
1444
|
|
1432
1445
|
# if missing it will return a file not found exception
|
1433
1446
|
return self._loader.path_dwim_relative_stack(path_stack, dirname, needle)
|
1447
|
+
|
1448
|
+
@staticmethod
|
1449
|
+
def result_dict_from_exception(exception: BaseException) -> dict[str, t.Any]:
|
1450
|
+
"""Return a failed task result dict from the given exception."""
|
1451
|
+
if ansible_remoted_error := _captured.AnsibleResultCapturedError.find_first_remoted_error(exception):
|
1452
|
+
result = ansible_remoted_error._result.copy()
|
1453
|
+
else:
|
1454
|
+
result = {}
|
1455
|
+
|
1456
|
+
error_summary = _utils._create_error_summary(exception, _traceback.TracebackEvent.ERROR)
|
1457
|
+
|
1458
|
+
result.update(
|
1459
|
+
failed=True,
|
1460
|
+
exception=error_summary,
|
1461
|
+
)
|
1462
|
+
|
1463
|
+
if 'msg' not in result:
|
1464
|
+
result.update(msg=_utils._dedupe_and_concat_message_chain([md.msg for md in error_summary.details]))
|
1465
|
+
|
1466
|
+
return result
|