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
@@ -10,29 +10,42 @@ import pathlib
|
|
10
10
|
import signal
|
11
11
|
import subprocess
|
12
12
|
import sys
|
13
|
+
|
13
14
|
import traceback
|
15
|
+
import typing as t
|
14
16
|
|
15
17
|
from ansible import constants as C
|
16
18
|
from ansible.cli import scripts
|
17
|
-
from ansible.errors import
|
18
|
-
|
19
|
-
|
19
|
+
from ansible.errors import (
|
20
|
+
AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip, AnsibleTaskError,
|
21
|
+
AnsibleValueOmittedError,
|
22
|
+
)
|
23
|
+
from ansible.executor.task_result import _RawTaskResult
|
24
|
+
from ansible._internal._datatag import _utils
|
25
|
+
from ansible.module_utils._internal._plugin_exec_context import PluginExecContext
|
26
|
+
from ansible.module_utils.common.messages import Detail, WarningSummary, DeprecationSummary
|
27
|
+
from ansible.module_utils.datatag import native_type_name
|
28
|
+
from ansible._internal._datatag._tags import TrustedAsTemplate
|
20
29
|
from ansible.module_utils.parsing.convert_bool import boolean
|
21
|
-
from ansible.module_utils.six import binary_type
|
22
30
|
from ansible.module_utils.common.text.converters import to_text, to_native
|
23
31
|
from ansible.module_utils.connection import write_to_stream
|
24
32
|
from ansible.module_utils.six import string_types
|
25
|
-
from ansible.playbook.conditional import Conditional
|
26
33
|
from ansible.playbook.task import Task
|
27
34
|
from ansible.plugins import get_plugin_class
|
35
|
+
from ansible.plugins.action import ActionBase
|
28
36
|
from ansible.plugins.loader import become_loader, cliconf_loader, connection_loader, httpapi_loader, netconf_loader, terminal_loader
|
37
|
+
from ansible._internal._templating._jinja_plugins import _invoke_lookup, _DirectCall
|
38
|
+
from ansible._internal._templating._engine import TemplateEngine
|
29
39
|
from ansible.template import Templar
|
30
40
|
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
31
|
-
from ansible.utils.
|
32
|
-
from ansible.utils.unsafe_proxy import to_unsafe_text, wrap_var
|
33
|
-
from ansible.vars.clean import namespace_facts, clean_facts
|
34
|
-
from ansible.utils.display import Display
|
41
|
+
from ansible.utils.display import Display, _DeferredWarningContext
|
35
42
|
from ansible.utils.vars import combine_vars
|
43
|
+
from ansible.vars.clean import namespace_facts, clean_facts
|
44
|
+
from ansible.vars.manager import _deprecate_top_level_fact
|
45
|
+
from ansible._internal._errors import _captured
|
46
|
+
|
47
|
+
if t.TYPE_CHECKING:
|
48
|
+
from ansible.executor.task_queue_manager import FinalQueue
|
36
49
|
|
37
50
|
display = Display()
|
38
51
|
|
@@ -60,60 +73,37 @@ def task_timeout(signum, frame):
|
|
60
73
|
raise TaskTimeoutError(frame=frame)
|
61
74
|
|
62
75
|
|
63
|
-
def remove_omit(task_args, omit_token):
|
64
|
-
'''
|
65
|
-
Remove args with a value equal to the ``omit_token`` recursively
|
66
|
-
to align with now having suboptions in the argument_spec
|
67
|
-
'''
|
68
|
-
|
69
|
-
if not isinstance(task_args, dict):
|
70
|
-
return task_args
|
71
|
-
|
72
|
-
new_args = {}
|
73
|
-
for i in task_args.items():
|
74
|
-
if i[1] == omit_token:
|
75
|
-
continue
|
76
|
-
elif isinstance(i[1], dict):
|
77
|
-
new_args[i[0]] = remove_omit(i[1], omit_token)
|
78
|
-
elif isinstance(i[1], list):
|
79
|
-
new_args[i[0]] = [remove_omit(v, omit_token) for v in i[1]]
|
80
|
-
else:
|
81
|
-
new_args[i[0]] = i[1]
|
82
|
-
|
83
|
-
return new_args
|
84
|
-
|
85
|
-
|
86
76
|
class TaskExecutor:
|
87
77
|
|
88
|
-
|
78
|
+
"""
|
89
79
|
This is the main worker class for the executor pipeline, which
|
90
80
|
handles loading an action plugin to actually dispatch the task to
|
91
81
|
a given host. This class roughly corresponds to the old Runner()
|
92
82
|
class.
|
93
|
-
|
83
|
+
"""
|
94
84
|
|
95
|
-
def __init__(self, host, task, job_vars, play_context,
|
85
|
+
def __init__(self, host, task: Task, job_vars, play_context, loader, shared_loader_obj, final_q: FinalQueue, variable_manager):
|
96
86
|
self._host = host
|
97
87
|
self._task = task
|
98
88
|
self._job_vars = job_vars
|
99
89
|
self._play_context = play_context
|
100
|
-
self._new_stdin = new_stdin
|
101
90
|
self._loader = loader
|
102
91
|
self._shared_loader_obj = shared_loader_obj
|
103
92
|
self._connection = None
|
104
93
|
self._final_q = final_q
|
105
94
|
self._variable_manager = variable_manager
|
106
95
|
self._loop_eval_error = None
|
96
|
+
self._task_templar = TemplateEngine(loader=self._loader, variables=self._job_vars)
|
107
97
|
|
108
98
|
self._task.squash()
|
109
99
|
|
110
100
|
def run(self):
|
111
|
-
|
101
|
+
"""
|
112
102
|
The main executor entrypoint, where we determine if the specified
|
113
103
|
task requires looping and either runs the task with self._run_loop()
|
114
104
|
or self._execute(). After that, the returned results are parsed and
|
115
105
|
returned as a dict.
|
116
|
-
|
106
|
+
"""
|
117
107
|
|
118
108
|
display.debug("in run() - task %s" % self._task._uuid)
|
119
109
|
|
@@ -135,10 +125,14 @@ class TaskExecutor:
|
|
135
125
|
# loop through the item results and set the global changed/failed/skipped result flags based on any item.
|
136
126
|
res['skipped'] = True
|
137
127
|
for item in item_results:
|
128
|
+
if item.get('_ansible_no_log'):
|
129
|
+
res.update(_ansible_no_log=True) # ensure no_log processing recognizes at least one item needs to be censored
|
130
|
+
|
138
131
|
if 'changed' in item and item['changed'] and not res.get('changed'):
|
139
132
|
res['changed'] = True
|
140
133
|
if res['skipped'] and ('skipped' not in item or ('skipped' in item and not item['skipped'])):
|
141
134
|
res['skipped'] = False
|
135
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
142
136
|
if 'failed' in item and item['failed']:
|
143
137
|
item_ignore = item.pop('_ansible_ignore_errors')
|
144
138
|
if not res.get('failed'):
|
@@ -165,6 +159,7 @@ class TaskExecutor:
|
|
165
159
|
res[array] = res[array] + item[array]
|
166
160
|
del item[array]
|
167
161
|
|
162
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
168
163
|
if not res.get('failed', False):
|
169
164
|
res['msg'] = 'All items completed'
|
170
165
|
if res['skipped']:
|
@@ -173,43 +168,23 @@ class TaskExecutor:
|
|
173
168
|
res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
|
174
169
|
else:
|
175
170
|
display.debug("calling self._execute()")
|
176
|
-
res = self._execute()
|
171
|
+
res = self._execute(self._task_templar, self._job_vars)
|
177
172
|
display.debug("_execute() done")
|
178
173
|
|
179
174
|
# make sure changed is set in the result, if it's not present
|
180
175
|
if 'changed' not in res:
|
181
176
|
res['changed'] = False
|
182
177
|
|
183
|
-
def _clean_res(res, errors='surrogate_or_strict'):
|
184
|
-
if isinstance(res, binary_type):
|
185
|
-
return to_unsafe_text(res, errors=errors)
|
186
|
-
elif isinstance(res, dict):
|
187
|
-
for k in res:
|
188
|
-
try:
|
189
|
-
res[k] = _clean_res(res[k], errors=errors)
|
190
|
-
except UnicodeError:
|
191
|
-
if k == 'diff':
|
192
|
-
# If this is a diff, substitute a replacement character if the value
|
193
|
-
# is undecodable as utf8. (Fix #21804)
|
194
|
-
display.warning("We were unable to decode all characters in the module return data."
|
195
|
-
" Replaced some in an effort to return as much as possible")
|
196
|
-
res[k] = _clean_res(res[k], errors='surrogate_then_replace')
|
197
|
-
else:
|
198
|
-
raise
|
199
|
-
elif isinstance(res, list):
|
200
|
-
for idx, item in enumerate(res):
|
201
|
-
res[idx] = _clean_res(item, errors=errors)
|
202
|
-
return res
|
203
|
-
|
204
|
-
display.debug("dumping result to json")
|
205
|
-
res = _clean_res(res)
|
206
|
-
display.debug("done dumping result, returning")
|
207
178
|
return res
|
208
|
-
except
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
179
|
+
except Exception as ex:
|
180
|
+
result = ActionBase.result_dict_from_exception(ex)
|
181
|
+
|
182
|
+
self._task.update_result_no_log(self._task_templar, result)
|
183
|
+
|
184
|
+
if not isinstance(ex, AnsibleError):
|
185
|
+
result.update(msg=f'Unexpected failure during task execution: {result["msg"]}')
|
186
|
+
|
187
|
+
return result
|
213
188
|
finally:
|
214
189
|
try:
|
215
190
|
self._connection.close()
|
@@ -218,11 +193,11 @@ class TaskExecutor:
|
|
218
193
|
except Exception as e:
|
219
194
|
display.debug(u"error closing connection: %s" % to_text(e))
|
220
195
|
|
221
|
-
def _get_loop_items(self):
|
222
|
-
|
196
|
+
def _get_loop_items(self) -> list[t.Any] | None:
|
197
|
+
"""
|
223
198
|
Loads a lookup plugin to handle the with_* portion of a task (if specified),
|
224
199
|
and returns the items result.
|
225
|
-
|
200
|
+
"""
|
226
201
|
|
227
202
|
# get search path for this task to pass to lookup plugins
|
228
203
|
self._job_vars['ansible_search_path'] = self._task.get_search_path()
|
@@ -231,49 +206,51 @@ class TaskExecutor:
|
|
231
206
|
if self._loader.get_basedir() not in self._job_vars['ansible_search_path']:
|
232
207
|
self._job_vars['ansible_search_path'].append(self._loader.get_basedir())
|
233
208
|
|
234
|
-
templar = Templar(loader=self._loader, variables=self._job_vars)
|
235
209
|
items = None
|
236
210
|
if self._task.loop_with:
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
211
|
+
templar = self._task_templar
|
212
|
+
terms = self._task.loop
|
213
|
+
|
214
|
+
if isinstance(terms, str):
|
215
|
+
terms = templar.resolve_to_container(_utils.str_problematic_strip(terms))
|
216
|
+
|
217
|
+
if not isinstance(terms, list):
|
218
|
+
terms = [terms]
|
219
|
+
|
220
|
+
@_DirectCall.mark
|
221
|
+
def invoke_lookup() -> t.Any:
|
222
|
+
"""Scope-capturing wrapper around _invoke_lookup to avoid functools.partial obscuring its usage from type-checking tools."""
|
223
|
+
return _invoke_lookup(
|
224
|
+
plugin_name=self._task.loop_with,
|
225
|
+
lookup_terms=terms,
|
226
|
+
lookup_kwargs=dict(wantlist=True),
|
227
|
+
invoked_as_with=True,
|
228
|
+
)
|
252
229
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop_with)
|
230
|
+
# Smuggle a special wrapped lookup invocation in as a local variable for its exclusive use when being evaluated as `with_(lookup)`.
|
231
|
+
# This value will not be visible to other users of this templar or its `available_variables`.
|
232
|
+
items = templar.evaluate_expression(expression=TrustedAsTemplate().tag("invoke_lookup()"), local_variables=dict(invoke_lookup=invoke_lookup))
|
257
233
|
|
258
234
|
elif self._task.loop is not None:
|
259
|
-
items =
|
235
|
+
items = self._task_templar.template(self._task.loop)
|
236
|
+
|
260
237
|
if not isinstance(items, list):
|
261
238
|
raise AnsibleError(
|
262
|
-
"
|
263
|
-
"
|
264
|
-
|
239
|
+
f"The `loop` value must resolve to a 'list', not {native_type_name(items)!r}.",
|
240
|
+
help_text="Provide a list of items/templates, or a template resolving to a list.",
|
241
|
+
obj=self._task.loop,
|
265
242
|
)
|
266
243
|
|
267
244
|
return items
|
268
245
|
|
269
|
-
def _run_loop(self, items):
|
270
|
-
|
246
|
+
def _run_loop(self, items: list[t.Any]) -> list[dict[str, t.Any]]:
|
247
|
+
"""
|
271
248
|
Runs the task with the loop items specified and collates the result
|
272
249
|
into an array named 'results' which is inserted into the final result
|
273
250
|
along with the item for which the loop ran.
|
274
|
-
|
251
|
+
"""
|
275
252
|
task_vars = self._job_vars
|
276
|
-
templar =
|
253
|
+
templar = TemplateEngine(loader=self._loader, variables=task_vars)
|
277
254
|
|
278
255
|
self._task.loop_control.post_validate(templar=templar)
|
279
256
|
|
@@ -282,17 +259,20 @@ class TaskExecutor:
|
|
282
259
|
loop_pause = self._task.loop_control.pause
|
283
260
|
extended = self._task.loop_control.extended
|
284
261
|
extended_allitems = self._task.loop_control.extended_allitems
|
262
|
+
|
285
263
|
# ensure we always have a label
|
286
|
-
label = self._task.loop_control.label or
|
264
|
+
label = self._task.loop_control.label or templar.variable_name_as_template(loop_var)
|
287
265
|
|
288
266
|
if loop_var in task_vars:
|
289
|
-
display.warning(
|
290
|
-
|
291
|
-
|
267
|
+
display.warning(
|
268
|
+
msg=f"The loop variable {loop_var!r} is already in use.",
|
269
|
+
help_text="You should set the `loop_var` value in the `loop_control` option for the task "
|
270
|
+
"to something else to avoid variable collisions and unexpected behavior.",
|
271
|
+
obj=loop_var,
|
272
|
+
)
|
292
273
|
|
293
274
|
ran_once = False
|
294
275
|
task_fields = None
|
295
|
-
no_log = False
|
296
276
|
items_len = len(items)
|
297
277
|
results = []
|
298
278
|
for item_index, item in enumerate(items):
|
@@ -332,7 +312,7 @@ class TaskExecutor:
|
|
332
312
|
ran_once = True
|
333
313
|
|
334
314
|
try:
|
335
|
-
tmp_task = self._task.copy(exclude_parent=True, exclude_tasks=True)
|
315
|
+
tmp_task: Task = self._task.copy(exclude_parent=True, exclude_tasks=True)
|
336
316
|
tmp_task._parent = self._task._parent
|
337
317
|
tmp_play_context = self._play_context.copy()
|
338
318
|
except AnsibleParserError as e:
|
@@ -341,9 +321,11 @@ class TaskExecutor:
|
|
341
321
|
|
342
322
|
# now we swap the internal task and play context with their copies,
|
343
323
|
# execute, and swap them back so we can do the next iteration cleanly
|
324
|
+
# NB: this swap-a-dee-doo confuses some type checkers about the type of tmp_task/self._task
|
344
325
|
(self._task, tmp_task) = (tmp_task, self._task)
|
345
326
|
(self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
|
346
|
-
|
327
|
+
|
328
|
+
res = self._execute(templar=templar, variables=task_vars)
|
347
329
|
|
348
330
|
if self._task.register:
|
349
331
|
# Ensure per loop iteration results are registered in case `_execute()`
|
@@ -355,9 +337,6 @@ class TaskExecutor:
|
|
355
337
|
(self._task, tmp_task) = (tmp_task, self._task)
|
356
338
|
(self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
|
357
339
|
|
358
|
-
# update 'general no_log' based on specific no_log
|
359
|
-
no_log = no_log or tmp_task.no_log
|
360
|
-
|
361
340
|
# now update the result with the item info, and append the result
|
362
341
|
# to the list of results
|
363
342
|
res[loop_var] = item
|
@@ -385,13 +364,14 @@ class TaskExecutor:
|
|
385
364
|
if self._connection and not isinstance(self._connection, string_types):
|
386
365
|
task_fields['connection'] = getattr(self._connection, 'ansible_name')
|
387
366
|
|
388
|
-
tr =
|
389
|
-
self._host
|
390
|
-
self._task
|
391
|
-
res,
|
367
|
+
tr = _RawTaskResult(
|
368
|
+
host=self._host,
|
369
|
+
task=self._task,
|
370
|
+
return_data=res,
|
392
371
|
task_fields=task_fields,
|
393
372
|
)
|
394
373
|
|
374
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
395
375
|
if tr.is_failed() or tr.is_unreachable():
|
396
376
|
self._final_q.send_callback('v2_runner_item_on_failed', tr)
|
397
377
|
elif tr.is_skipped():
|
@@ -406,11 +386,14 @@ class TaskExecutor:
|
|
406
386
|
|
407
387
|
# break loop if break_when conditions are met
|
408
388
|
if self._task.loop_control and self._task.loop_control.break_when:
|
409
|
-
|
410
|
-
|
411
|
-
|
389
|
+
break_when = self._task.loop_control.get_validated_value(
|
390
|
+
'break_when',
|
391
|
+
self._task.loop_control.fattributes.get('break_when'),
|
392
|
+
self._task.loop_control.break_when,
|
393
|
+
templar,
|
412
394
|
)
|
413
|
-
|
395
|
+
|
396
|
+
if self._task._resolve_conditional(break_when, task_vars):
|
414
397
|
# delete loop vars before exiting loop
|
415
398
|
del task_vars[loop_var]
|
416
399
|
break
|
@@ -432,7 +415,6 @@ class TaskExecutor:
|
|
432
415
|
if var in task_vars and var not in self._job_vars:
|
433
416
|
del task_vars[var]
|
434
417
|
|
435
|
-
self._task.no_log = no_log
|
436
418
|
# NOTE: run_once cannot contain loop vars because it's templated earlier also
|
437
419
|
# This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
|
438
420
|
self._task.run_once = task_fields.get('run_once')
|
@@ -448,21 +430,49 @@ class TaskExecutor:
|
|
448
430
|
# At the point this is executed it is safe to mutate self._task,
|
449
431
|
# since `self._task` is either a copy referred to by `tmp_task` in `_run_loop`
|
450
432
|
# or just a singular non-looped task
|
451
|
-
if delegated_host_name:
|
452
|
-
self._task.delegate_to = delegated_host_name
|
453
|
-
variables.update(delegated_vars)
|
454
433
|
|
455
|
-
|
456
|
-
|
434
|
+
self._task.delegate_to = delegated_host_name # always override, since a templated result could be an omit (-> None)
|
435
|
+
variables.update(delegated_vars)
|
436
|
+
|
437
|
+
def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
|
438
|
+
result: dict[str, t.Any]
|
439
|
+
|
440
|
+
with _DeferredWarningContext(variables=variables) as warning_ctx:
|
441
|
+
try:
|
442
|
+
# DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
|
443
|
+
result = self._execute_internal(templar, variables)
|
444
|
+
self._apply_task_result_compat(result, warning_ctx)
|
445
|
+
_captured.AnsibleActionCapturedError.maybe_raise_on_result(result)
|
446
|
+
except Exception as ex:
|
447
|
+
try:
|
448
|
+
raise AnsibleTaskError(obj=self._task.get_ds()) from ex
|
449
|
+
except AnsibleTaskError as atex:
|
450
|
+
result = ActionBase.result_dict_from_exception(atex)
|
451
|
+
result.setdefault('changed', False)
|
452
|
+
|
453
|
+
self._task.update_result_no_log(templar, result)
|
454
|
+
|
455
|
+
# The warnings/deprecations in the result have already been captured in the _DeferredWarningContext by _apply_task_result_compat.
|
456
|
+
# The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
|
457
|
+
# These are then used to supersede the entries in the result.
|
458
|
+
|
459
|
+
result.pop('warnings', None)
|
460
|
+
result.pop('deprecations', None)
|
461
|
+
|
462
|
+
if warnings := warning_ctx.get_warnings():
|
463
|
+
result.update(warnings=warnings)
|
464
|
+
|
465
|
+
if deprecation_warnings := warning_ctx.get_deprecation_warnings():
|
466
|
+
result.update(deprecations=deprecation_warnings)
|
467
|
+
|
468
|
+
return result
|
469
|
+
|
470
|
+
def _execute_internal(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
|
471
|
+
"""
|
457
472
|
The primary workhorse of the executor system, this runs the task
|
458
473
|
on the specified host (which may be the delegated_to host) and handles
|
459
474
|
the retry/until and block rescue/always execution
|
460
|
-
|
461
|
-
|
462
|
-
if variables is None:
|
463
|
-
variables = self._job_vars
|
464
|
-
|
465
|
-
templar = Templar(loader=self._loader, variables=variables)
|
475
|
+
"""
|
466
476
|
|
467
477
|
self._calculate_delegate_to(templar, variables)
|
468
478
|
|
@@ -498,18 +508,13 @@ class TaskExecutor:
|
|
498
508
|
# skipping this task during the conditional evaluation step
|
499
509
|
context_validation_error = e
|
500
510
|
|
501
|
-
no_log = self._play_context.no_log
|
502
|
-
|
503
511
|
# Evaluate the conditional (if any) for this task, which we do before running
|
504
512
|
# the final task post-validation. We do this before the post validation due to
|
505
513
|
# the fact that the conditional may specify that the task be skipped due to a
|
506
514
|
# variable not being present which would otherwise cause validation to fail
|
507
515
|
try:
|
508
|
-
|
509
|
-
|
510
|
-
display.debug("when evaluation is False, skipping this task")
|
511
|
-
return dict(changed=False, skipped=True, skip_reason='Conditional result was False',
|
512
|
-
false_condition=false_condition, _ansible_no_log=no_log)
|
516
|
+
if not self._task._resolve_conditional(self._task.when, tempvars, result_context=(rc := t.cast(dict[str, t.Any], {}))):
|
517
|
+
return dict(changed=False, skipped=True, skip_reason='Conditional result was False') | rc
|
513
518
|
except AnsibleError as e:
|
514
519
|
# loop error takes precedence
|
515
520
|
if self._loop_eval_error is not None:
|
@@ -525,22 +530,27 @@ class TaskExecutor:
|
|
525
530
|
|
526
531
|
# if we ran into an error while setting up the PlayContext, raise it now, unless is known issue with delegation
|
527
532
|
# and undefined vars (correct values are in cvars later on and connection plugins, if still error, blows up there)
|
533
|
+
|
534
|
+
# DTFIX-RELEASE: this should probably be declaratively handled in post_validate (or better, get rid of play_context)
|
528
535
|
if context_validation_error is not None:
|
529
536
|
raiseit = True
|
530
537
|
if self._task.delegate_to:
|
531
|
-
if isinstance(context_validation_error,
|
532
|
-
raiseit = False
|
533
|
-
elif isinstance(context_validation_error, AnsibleParserError):
|
538
|
+
if isinstance(context_validation_error, AnsibleParserError):
|
534
539
|
# parser error, might be cause by undef too
|
535
|
-
|
536
|
-
if isinstance(orig_exc, AnsibleUndefinedVariable):
|
540
|
+
if isinstance(context_validation_error.__cause__, AnsibleUndefinedVariable):
|
537
541
|
raiseit = False
|
542
|
+
elif isinstance(context_validation_error, AnsibleUndefinedVariable):
|
543
|
+
# DTFIX-RELEASE: should not be possible to hit this now (all are AnsibleFieldAttributeError)?
|
544
|
+
raiseit = False
|
538
545
|
if raiseit:
|
539
546
|
raise context_validation_error # pylint: disable=raising-bad-type
|
540
547
|
|
541
548
|
# set templar to use temp variables until loop is evaluated
|
542
549
|
templar.available_variables = tempvars
|
543
550
|
|
551
|
+
# Now we do final validation on the task, which sets all fields to their final values.
|
552
|
+
self._task.post_validate(templar=templar)
|
553
|
+
|
544
554
|
# if this task is a TaskInclude, we just return now with a success code so the
|
545
555
|
# main thread can expand the task list for the given host
|
546
556
|
if self._task.action in C._ACTION_INCLUDE_TASKS:
|
@@ -549,7 +559,6 @@ class TaskExecutor:
|
|
549
559
|
if not include_file:
|
550
560
|
return dict(failed=True, msg="No include file was specified to the include")
|
551
561
|
|
552
|
-
include_file = templar.template(include_file)
|
553
562
|
return dict(include=include_file, include_args=include_args)
|
554
563
|
|
555
564
|
# if this task is a IncludeRole, we just return now with a success code so the main thread can expand the task list for the given host
|
@@ -557,32 +566,9 @@ class TaskExecutor:
|
|
557
566
|
include_args = self._task.args.copy()
|
558
567
|
return dict(include_args=include_args)
|
559
568
|
|
560
|
-
# Now we do final validation on the task, which sets all fields to their final values.
|
561
|
-
try:
|
562
|
-
self._task.post_validate(templar=templar)
|
563
|
-
except AnsibleError:
|
564
|
-
raise
|
565
|
-
except Exception:
|
566
|
-
return dict(changed=False, failed=True, _ansible_no_log=no_log, exception=to_text(traceback.format_exc()))
|
567
|
-
if '_variable_params' in self._task.args:
|
568
|
-
variable_params = self._task.args.pop('_variable_params')
|
569
|
-
if isinstance(variable_params, dict):
|
570
|
-
if C.INJECT_FACTS_AS_VARS:
|
571
|
-
display.warning("Using a variable for a task's 'args' is unsafe in some situations "
|
572
|
-
"(see https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#argsplat-unsafe)")
|
573
|
-
variable_params.update(self._task.args)
|
574
|
-
self._task.args = variable_params
|
575
|
-
else:
|
576
|
-
# if we didn't get a dict, it means there's garbage remaining after k=v parsing, just give up
|
577
|
-
# see https://github.com/ansible/ansible/issues/79862
|
578
|
-
raise AnsibleError(f"invalid or malformed argument: '{variable_params}'")
|
579
|
-
|
580
|
-
# update no_log to task value, now that we have it templated
|
581
|
-
no_log = self._task.no_log
|
582
|
-
|
583
569
|
# free tempvars up, not used anymore, cvars and vars_copy should be mainly used after this point
|
584
570
|
# updating the original 'variables' at the end
|
585
|
-
tempvars
|
571
|
+
del tempvars
|
586
572
|
|
587
573
|
# setup cvars copy, used for all connection related templating
|
588
574
|
if self._task.delegate_to:
|
@@ -634,23 +620,7 @@ class TaskExecutor:
|
|
634
620
|
cvars['ansible_python_interpreter'] = sys.executable
|
635
621
|
|
636
622
|
# get handler
|
637
|
-
self._handler,
|
638
|
-
|
639
|
-
if module_context is not None:
|
640
|
-
module_defaults_fqcn = module_context.resolved_fqcn
|
641
|
-
else:
|
642
|
-
module_defaults_fqcn = self._task.resolved_action
|
643
|
-
|
644
|
-
# Apply default params for action/module, if present
|
645
|
-
self._task.args = get_action_args_with_defaults(
|
646
|
-
module_defaults_fqcn, self._task.args, self._task.module_defaults, templar,
|
647
|
-
action_groups=self._task._parent._play._action_groups
|
648
|
-
)
|
649
|
-
|
650
|
-
# And filter out any fields which were set to default(omit), and got the omit token value
|
651
|
-
omit_token = variables.get('omit')
|
652
|
-
if omit_token is not None:
|
653
|
-
self._task.args = remove_omit(self._task.args, omit_token)
|
623
|
+
self._handler, _module_context = self._get_action_handler_with_module_context(templar=templar)
|
654
624
|
|
655
625
|
retries = 1 # includes the default actual run + retries set by user/default
|
656
626
|
if self._task.retries is not None:
|
@@ -670,7 +640,10 @@ class TaskExecutor:
|
|
670
640
|
if self._task.timeout:
|
671
641
|
old_sig = signal.signal(signal.SIGALRM, task_timeout)
|
672
642
|
signal.alarm(self._task.timeout)
|
673
|
-
|
643
|
+
with PluginExecContext(self._handler):
|
644
|
+
result = self._handler.run(task_vars=vars_copy)
|
645
|
+
|
646
|
+
# DTFIX-RELEASE: nuke this, it hides a lot of error detail- remove the active exception propagation hack from AnsibleActionFail at the same time
|
674
647
|
except (AnsibleActionFail, AnsibleActionSkip) as e:
|
675
648
|
return e.result
|
676
649
|
except AnsibleConnectionFailure as e:
|
@@ -685,12 +658,6 @@ class TaskExecutor:
|
|
685
658
|
self._handler.cleanup()
|
686
659
|
display.debug("handler run complete")
|
687
660
|
|
688
|
-
# propagate no log to result- the action can set this, so only overwrite it with the task's value if missing or falsey
|
689
|
-
result["_ansible_no_log"] = bool(no_log or result.get('_ansible_no_log', False))
|
690
|
-
|
691
|
-
if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
|
692
|
-
result = wrap_var(result)
|
693
|
-
|
694
661
|
# update the local copy of vars with the registered value, if specified,
|
695
662
|
# or any facts which may have been generated by the module execution
|
696
663
|
if self._task.register:
|
@@ -702,37 +669,23 @@ class TaskExecutor:
|
|
702
669
|
if result.get('failed'):
|
703
670
|
self._final_q.send_callback(
|
704
671
|
'v2_runner_on_async_failed',
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
672
|
+
_RawTaskResult(
|
673
|
+
host=self._host,
|
674
|
+
task=self._task,
|
675
|
+
return_data=result,
|
676
|
+
task_fields=self._task.dump_attrs(),
|
677
|
+
),
|
678
|
+
)
|
709
679
|
else:
|
710
680
|
self._final_q.send_callback(
|
711
681
|
'v2_runner_on_async_ok',
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
# helper methods for use below in evaluating changed/failed_when
|
721
|
-
def _evaluate_changed_when_result(result):
|
722
|
-
if self._task.changed_when is not None and self._task.changed_when:
|
723
|
-
cond = Conditional(loader=self._loader)
|
724
|
-
cond.when = self._task.changed_when
|
725
|
-
result['changed'] = cond.evaluate_conditional(templar, vars_copy)
|
726
|
-
|
727
|
-
def _evaluate_failed_when_result(result):
|
728
|
-
if self._task.failed_when:
|
729
|
-
cond = Conditional(loader=self._loader)
|
730
|
-
cond.when = self._task.failed_when
|
731
|
-
failed_when_result = cond.evaluate_conditional(templar, vars_copy)
|
732
|
-
result['failed_when_result'] = result['failed'] = failed_when_result
|
733
|
-
else:
|
734
|
-
failed_when_result = False
|
735
|
-
return failed_when_result
|
682
|
+
_RawTaskResult(
|
683
|
+
host=self._host,
|
684
|
+
task=self._task,
|
685
|
+
return_data=result,
|
686
|
+
task_fields=self._task.dump_attrs(),
|
687
|
+
),
|
688
|
+
)
|
736
689
|
|
737
690
|
if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
|
738
691
|
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
|
@@ -745,10 +698,11 @@ class TaskExecutor:
|
|
745
698
|
vars_copy.update(result['ansible_facts'])
|
746
699
|
else:
|
747
700
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
748
|
-
af =
|
701
|
+
af = result['ansible_facts']
|
749
702
|
vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
|
750
703
|
if C.INJECT_FACTS_AS_VARS:
|
751
|
-
|
704
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
705
|
+
vars_copy.update(cleaned_toplevel)
|
752
706
|
|
753
707
|
# set the failed property if it was missing.
|
754
708
|
if 'failed' not in result:
|
@@ -766,9 +720,6 @@ class TaskExecutor:
|
|
766
720
|
if 'changed' not in result:
|
767
721
|
result['changed'] = False
|
768
722
|
|
769
|
-
if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
|
770
|
-
result = wrap_var(result)
|
771
|
-
|
772
723
|
# re-update the local copy of vars with the registered value, if specified,
|
773
724
|
# or any facts which may have been generated by the module execution
|
774
725
|
# This gives changed/failed_when access to additional recently modified
|
@@ -781,18 +732,30 @@ class TaskExecutor:
|
|
781
732
|
if 'skipped' not in result:
|
782
733
|
condname = 'changed'
|
783
734
|
|
735
|
+
# DTFIX-RELEASE: error normalization has not yet occurred; this means that the expressions used for until/failed_when/changed_when/break_when
|
736
|
+
# and when (for loops on the second and later iterations) cannot see the normalized error shapes. This, and the current impl of the expression
|
737
|
+
# handling here causes a number of problems:
|
738
|
+
# * any error in one of the post-task exec expressions is silently ignored and detail lost (eg: `failed_when: syntax ERROR @$123`)
|
739
|
+
# * they cannot reliably access error/warning details, since many of those details are inaccessible until the error normalization occurs
|
740
|
+
# * error normalization includes `msg` if present, and supplies `unknown error` if not; this leads to screwy results on True failed_when if
|
741
|
+
# `msg` is present, eg: `{debug: {}, failed_when: True` -> "Task failed: Action failed: Hello world!"
|
742
|
+
# * detail about failed_when is lost; any error details from the task could potentially be grafted in/preserved if error normalization was done
|
743
|
+
|
784
744
|
try:
|
785
|
-
|
745
|
+
if self._task.changed_when is not None and self._task.changed_when:
|
746
|
+
result['changed'] = self._task._resolve_conditional(self._task.changed_when, vars_copy)
|
747
|
+
|
786
748
|
condname = 'failed'
|
787
|
-
|
749
|
+
|
750
|
+
if self._task.failed_when:
|
751
|
+
result['failed_when_result'] = result['failed'] = self._task._resolve_conditional(self._task.failed_when, vars_copy)
|
752
|
+
|
788
753
|
except AnsibleError as e:
|
789
754
|
result['failed'] = True
|
790
755
|
result['%s_when_result' % condname] = to_text(e)
|
791
756
|
|
792
757
|
if retries > 1:
|
793
|
-
|
794
|
-
cond.when = self._task.until or [not result['failed']]
|
795
|
-
if cond.evaluate_conditional(templar, vars_copy):
|
758
|
+
if self._task._resolve_conditional(self._task.until or [not result['failed']], vars_copy):
|
796
759
|
break
|
797
760
|
else:
|
798
761
|
# no conditional check, or it failed, so sleep for the specified time
|
@@ -802,12 +765,12 @@ class TaskExecutor:
|
|
802
765
|
display.debug('Retrying task, attempt %d of %d' % (attempt, retries))
|
803
766
|
self._final_q.send_callback(
|
804
767
|
'v2_runner_retry',
|
805
|
-
|
806
|
-
self._host
|
807
|
-
self._task
|
808
|
-
result,
|
768
|
+
_RawTaskResult(
|
769
|
+
host=self._host,
|
770
|
+
task=self._task,
|
771
|
+
return_data=result,
|
809
772
|
task_fields=self._task.dump_attrs()
|
810
|
-
)
|
773
|
+
),
|
811
774
|
)
|
812
775
|
time.sleep(delay)
|
813
776
|
self._handler = self._get_action_handler(templar=templar)
|
@@ -817,9 +780,6 @@ class TaskExecutor:
|
|
817
780
|
result['attempts'] = retries - 1
|
818
781
|
result['failed'] = True
|
819
782
|
|
820
|
-
if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
|
821
|
-
result = wrap_var(result)
|
822
|
-
|
823
783
|
# do the final update of the local variables here, for both registered
|
824
784
|
# values and any facts which may have been created
|
825
785
|
if self._task.register:
|
@@ -830,10 +790,12 @@ class TaskExecutor:
|
|
830
790
|
variables.update(result['ansible_facts'])
|
831
791
|
else:
|
832
792
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
833
|
-
af =
|
793
|
+
af = result['ansible_facts']
|
834
794
|
variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
|
835
795
|
if C.INJECT_FACTS_AS_VARS:
|
836
|
-
|
796
|
+
# DTFIX-FUTURE: why is this happening twice, esp since we're post-fork and these will be discarded?
|
797
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
798
|
+
variables.update(cleaned_toplevel)
|
837
799
|
|
838
800
|
# save the notification target in the result, if it was specified, as
|
839
801
|
# this task may be running in a loop in which case the notification
|
@@ -858,10 +820,54 @@ class TaskExecutor:
|
|
858
820
|
display.debug("attempt loop complete, returning result")
|
859
821
|
return result
|
860
822
|
|
823
|
+
@staticmethod
|
824
|
+
def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _DeferredWarningContext) -> None:
|
825
|
+
"""Apply backward-compatibility mutations to the supplied task result."""
|
826
|
+
if warnings := result.get('warnings'):
|
827
|
+
if isinstance(warnings, list):
|
828
|
+
for warning in warnings:
|
829
|
+
if not isinstance(warning, WarningSummary):
|
830
|
+
# translate non-WarningMessageDetail messages
|
831
|
+
warning = WarningSummary(
|
832
|
+
details=(
|
833
|
+
Detail(msg=str(warning)),
|
834
|
+
),
|
835
|
+
)
|
836
|
+
|
837
|
+
warning_ctx.capture(warning)
|
838
|
+
else:
|
839
|
+
display.warning(f"Task result `warnings` was {type(warnings)} instead of {list}.")
|
840
|
+
|
841
|
+
if deprecations := result.get('deprecations'):
|
842
|
+
if isinstance(deprecations, list):
|
843
|
+
for deprecation in deprecations:
|
844
|
+
if not isinstance(deprecation, DeprecationSummary):
|
845
|
+
# translate non-DeprecationMessageDetail message dicts
|
846
|
+
try:
|
847
|
+
if deprecation.pop('collection_name', ...) is not ...:
|
848
|
+
# deprecated: description='enable the deprecation message for collection_name' core_version='2.23'
|
849
|
+
# self.deprecated('The `collection_name` key in the `deprecations` dictionary is deprecated.', version='2.27')
|
850
|
+
pass
|
851
|
+
|
852
|
+
# DTFIX-RELEASE: when plugin isn't set, do it at the boundary where we receive the module/action results
|
853
|
+
# that may even allow us to never set it in modules/actions directly and to populate it at the boundary
|
854
|
+
deprecation = DeprecationSummary(
|
855
|
+
details=(
|
856
|
+
Detail(msg=deprecation.pop('msg')),
|
857
|
+
),
|
858
|
+
**deprecation,
|
859
|
+
)
|
860
|
+
except Exception as ex:
|
861
|
+
display.error_as_warning("Task result `deprecations` contained an invalid item.", exception=ex)
|
862
|
+
|
863
|
+
warning_ctx.capture(deprecation)
|
864
|
+
else:
|
865
|
+
display.warning(f"Task result `deprecations` was {type(deprecations)} instead of {list}.")
|
866
|
+
|
861
867
|
def _poll_async_result(self, result, templar, task_vars=None):
|
862
|
-
|
868
|
+
"""
|
863
869
|
Polls for the specified JID to be complete
|
864
|
-
|
870
|
+
"""
|
865
871
|
|
866
872
|
if task_vars is None:
|
867
873
|
task_vars = self._job_vars
|
@@ -891,7 +897,7 @@ class TaskExecutor:
|
|
891
897
|
connection=self._connection,
|
892
898
|
play_context=self._play_context,
|
893
899
|
loader=self._loader,
|
894
|
-
templar=templar,
|
900
|
+
templar=Templar._from_template_engine(templar),
|
895
901
|
shared_loader_obj=self._shared_loader_obj,
|
896
902
|
)
|
897
903
|
|
@@ -929,10 +935,10 @@ class TaskExecutor:
|
|
929
935
|
time_left -= self._task.poll
|
930
936
|
self._final_q.send_callback(
|
931
937
|
'v2_runner_on_async_poll',
|
932
|
-
|
933
|
-
self._host
|
934
|
-
async_task
|
935
|
-
async_result,
|
938
|
+
_RawTaskResult(
|
939
|
+
host=self._host,
|
940
|
+
task=async_task,
|
941
|
+
return_data=async_result,
|
936
942
|
task_fields=async_task.dump_attrs(),
|
937
943
|
),
|
938
944
|
)
|
@@ -961,7 +967,7 @@ class TaskExecutor:
|
|
961
967
|
connection=self._connection,
|
962
968
|
play_context=self._play_context,
|
963
969
|
loader=self._loader,
|
964
|
-
templar=templar,
|
970
|
+
templar=Templar._from_template_engine(templar),
|
965
971
|
shared_loader_obj=self._shared_loader_obj,
|
966
972
|
)
|
967
973
|
cleanup_handler.run(task_vars=task_vars)
|
@@ -977,10 +983,10 @@ class TaskExecutor:
|
|
977
983
|
return become
|
978
984
|
|
979
985
|
def _get_connection(self, cvars, templar, current_connection):
|
980
|
-
|
986
|
+
"""
|
981
987
|
Reads the connection property for the host, and returns the
|
982
988
|
correct connection object from the list of connection plugins
|
983
|
-
|
989
|
+
"""
|
984
990
|
|
985
991
|
self._play_context.connection = current_connection
|
986
992
|
|
@@ -992,7 +998,7 @@ class TaskExecutor:
|
|
992
998
|
connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
|
993
999
|
conn_type,
|
994
1000
|
self._play_context,
|
995
|
-
|
1001
|
+
new_stdin=None, # No longer used, kept for backwards compat for plugins that explicitly accept this as an arg
|
996
1002
|
task_uuid=self._task._uuid,
|
997
1003
|
ansible_playbook_pid=to_text(os.getppid())
|
998
1004
|
)
|
@@ -1058,7 +1064,11 @@ class TaskExecutor:
|
|
1058
1064
|
options = {}
|
1059
1065
|
for k in option_vars:
|
1060
1066
|
if k in variables:
|
1061
|
-
|
1067
|
+
try:
|
1068
|
+
options[k] = templar.template(variables[k])
|
1069
|
+
except AnsibleValueOmittedError:
|
1070
|
+
pass
|
1071
|
+
|
1062
1072
|
# TODO move to task method?
|
1063
1073
|
plugin.set_options(task_keys=task_keys, var_options=options)
|
1064
1074
|
|
@@ -1124,15 +1134,15 @@ class TaskExecutor:
|
|
1124
1134
|
return varnames
|
1125
1135
|
|
1126
1136
|
def _get_action_handler(self, templar):
|
1127
|
-
|
1137
|
+
"""
|
1128
1138
|
Returns the correct action plugin to handle the requestion task action
|
1129
|
-
|
1139
|
+
"""
|
1130
1140
|
return self._get_action_handler_with_module_context(templar)[0]
|
1131
1141
|
|
1132
|
-
def _get_action_handler_with_module_context(self, templar):
|
1133
|
-
|
1142
|
+
def _get_action_handler_with_module_context(self, templar: TemplateEngine):
|
1143
|
+
"""
|
1134
1144
|
Returns the correct action plugin to handle the requestion task action and the module context
|
1135
|
-
|
1145
|
+
"""
|
1136
1146
|
module_collection, separator, module_name = self._task.action.rpartition(".")
|
1137
1147
|
module_prefix = module_name.split('_')[0]
|
1138
1148
|
if module_collection:
|
@@ -1191,7 +1201,7 @@ class TaskExecutor:
|
|
1191
1201
|
connection=self._connection,
|
1192
1202
|
play_context=self._play_context,
|
1193
1203
|
loader=self._loader,
|
1194
|
-
templar=templar,
|
1204
|
+
templar=Templar._from_template_engine(templar),
|
1195
1205
|
shared_loader_obj=self._shared_loader_obj,
|
1196
1206
|
collection_list=collections
|
1197
1207
|
)
|
@@ -1206,9 +1216,9 @@ CLI_STUB_NAME = 'ansible_connection_cli_stub.py'
|
|
1206
1216
|
|
1207
1217
|
|
1208
1218
|
def start_connection(play_context, options, task_uuid):
|
1209
|
-
|
1219
|
+
"""
|
1210
1220
|
Starts the persistent connection
|
1211
|
-
|
1221
|
+
"""
|
1212
1222
|
|
1213
1223
|
env = os.environ.copy()
|
1214
1224
|
env.update({
|