ansible-core 2.18.4rc1__py3-none-any.whl → 2.19.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansible/_internal/__init__.py +53 -0
- ansible/_internal/_ansiballz.py +265 -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 +160 -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 +198 -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 +351 -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 +153 -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 +48 -46
- 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 +165 -108
- 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 +136 -76
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +237 -236
- ansible/executor/task_queue_manager.py +62 -38
- ansible/executor/task_result.py +21 -12
- 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 +77 -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 +154 -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 +52 -17
- 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 +20 -18
- 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 +7 -7
- 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 +35 -49
- ansible/modules/user.py +53 -34
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +39 -32
- 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/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 +36 -339
- 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 +6 -15
- 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 +8 -7
- 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 +141 -123
- ansible/plugins/callback/default.py +30 -23
- ansible/plugins/callback/junit.py +28 -24
- ansible/plugins/callback/minimal.py +17 -14
- ansible/plugins/callback/oneline.py +13 -7
- ansible/plugins/callback/tree.py +10 -6
- 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 +218 -108
- ansible/plugins/filter/encryption.py +32 -32
- ansible/plugins/filter/flatten.yml +3 -2
- ansible/plugins/filter/human_to_bytes.yml +2 -2
- 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 +94 -113
- ansible/plugins/strategy/debug.py +2 -2
- ansible/plugins/strategy/free.py +13 -28
- ansible/plugins/strategy/host_pinned.py +2 -2
- ansible/plugins/strategy/linear.py +31 -33
- ansible/plugins/terminal/__init__.py +4 -4
- ansible/plugins/test/__init__.py +7 -2
- ansible/plugins/test/core.py +54 -20
- 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 +368 -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 +428 -58
- 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 +28 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +4 -4
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +205 -264
- ansible/vars/reserved.py +8 -9
- ansible_core-2.19.0b1.dist-info/BSD-3-Clause.txt +28 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/METADATA +5 -4
- ansible_core-2.19.0b1.dist-info/RECORD +1070 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.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/utils/jsonify.py +0 -36
- 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.4rc1.dist-info/RECORD +0 -992
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/top_level.txt +0 -0
@@ -10,29 +10,39 @@ 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
|
19
|
+
from ansible.errors import (
|
20
|
+
AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip, AnsibleTaskError,
|
21
|
+
AnsibleValueOmittedError,
|
22
|
+
)
|
18
23
|
from ansible.executor.task_result import TaskResult
|
19
|
-
from ansible.
|
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
|
36
46
|
|
37
47
|
display = Display()
|
38
48
|
|
@@ -60,60 +70,37 @@ def task_timeout(signum, frame):
|
|
60
70
|
raise TaskTimeoutError(frame=frame)
|
61
71
|
|
62
72
|
|
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
73
|
class TaskExecutor:
|
87
74
|
|
88
|
-
|
75
|
+
"""
|
89
76
|
This is the main worker class for the executor pipeline, which
|
90
77
|
handles loading an action plugin to actually dispatch the task to
|
91
78
|
a given host. This class roughly corresponds to the old Runner()
|
92
79
|
class.
|
93
|
-
|
80
|
+
"""
|
94
81
|
|
95
|
-
def __init__(self, host, task, job_vars, play_context,
|
82
|
+
def __init__(self, host, task: Task, job_vars, play_context, loader, shared_loader_obj, final_q, variable_manager):
|
96
83
|
self._host = host
|
97
84
|
self._task = task
|
98
85
|
self._job_vars = job_vars
|
99
86
|
self._play_context = play_context
|
100
|
-
self._new_stdin = new_stdin
|
101
87
|
self._loader = loader
|
102
88
|
self._shared_loader_obj = shared_loader_obj
|
103
89
|
self._connection = None
|
104
90
|
self._final_q = final_q
|
105
91
|
self._variable_manager = variable_manager
|
106
92
|
self._loop_eval_error = None
|
93
|
+
self._task_templar = TemplateEngine(loader=self._loader, variables=self._job_vars)
|
107
94
|
|
108
95
|
self._task.squash()
|
109
96
|
|
110
97
|
def run(self):
|
111
|
-
|
98
|
+
"""
|
112
99
|
The main executor entrypoint, where we determine if the specified
|
113
100
|
task requires looping and either runs the task with self._run_loop()
|
114
101
|
or self._execute(). After that, the returned results are parsed and
|
115
102
|
returned as a dict.
|
116
|
-
|
103
|
+
"""
|
117
104
|
|
118
105
|
display.debug("in run() - task %s" % self._task._uuid)
|
119
106
|
|
@@ -135,10 +122,14 @@ class TaskExecutor:
|
|
135
122
|
# loop through the item results and set the global changed/failed/skipped result flags based on any item.
|
136
123
|
res['skipped'] = True
|
137
124
|
for item in item_results:
|
125
|
+
if item.get('_ansible_no_log'):
|
126
|
+
res.update(_ansible_no_log=True) # ensure no_log processing recognizes at least one item needs to be censored
|
127
|
+
|
138
128
|
if 'changed' in item and item['changed'] and not res.get('changed'):
|
139
129
|
res['changed'] = True
|
140
130
|
if res['skipped'] and ('skipped' not in item or ('skipped' in item and not item['skipped'])):
|
141
131
|
res['skipped'] = False
|
132
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
142
133
|
if 'failed' in item and item['failed']:
|
143
134
|
item_ignore = item.pop('_ansible_ignore_errors')
|
144
135
|
if not res.get('failed'):
|
@@ -165,6 +156,7 @@ class TaskExecutor:
|
|
165
156
|
res[array] = res[array] + item[array]
|
166
157
|
del item[array]
|
167
158
|
|
159
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
168
160
|
if not res.get('failed', False):
|
169
161
|
res['msg'] = 'All items completed'
|
170
162
|
if res['skipped']:
|
@@ -173,43 +165,23 @@ class TaskExecutor:
|
|
173
165
|
res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
|
174
166
|
else:
|
175
167
|
display.debug("calling self._execute()")
|
176
|
-
res = self._execute()
|
168
|
+
res = self._execute(self._task_templar, self._job_vars)
|
177
169
|
display.debug("_execute() done")
|
178
170
|
|
179
171
|
# make sure changed is set in the result, if it's not present
|
180
172
|
if 'changed' not in res:
|
181
173
|
res['changed'] = False
|
182
174
|
|
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
175
|
return res
|
208
|
-
except
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
176
|
+
except Exception as ex:
|
177
|
+
result = ActionBase.result_dict_from_exception(ex)
|
178
|
+
|
179
|
+
self._task.update_result_no_log(self._task_templar, result)
|
180
|
+
|
181
|
+
if not isinstance(ex, AnsibleError):
|
182
|
+
result.update(msg=f'Unexpected failure during task execution: {result["msg"]}')
|
183
|
+
|
184
|
+
return result
|
213
185
|
finally:
|
214
186
|
try:
|
215
187
|
self._connection.close()
|
@@ -218,11 +190,11 @@ class TaskExecutor:
|
|
218
190
|
except Exception as e:
|
219
191
|
display.debug(u"error closing connection: %s" % to_text(e))
|
220
192
|
|
221
|
-
def _get_loop_items(self):
|
222
|
-
|
193
|
+
def _get_loop_items(self) -> list[t.Any] | None:
|
194
|
+
"""
|
223
195
|
Loads a lookup plugin to handle the with_* portion of a task (if specified),
|
224
196
|
and returns the items result.
|
225
|
-
|
197
|
+
"""
|
226
198
|
|
227
199
|
# get search path for this task to pass to lookup plugins
|
228
200
|
self._job_vars['ansible_search_path'] = self._task.get_search_path()
|
@@ -231,49 +203,51 @@ class TaskExecutor:
|
|
231
203
|
if self._loader.get_basedir() not in self._job_vars['ansible_search_path']:
|
232
204
|
self._job_vars['ansible_search_path'].append(self._loader.get_basedir())
|
233
205
|
|
234
|
-
templar = Templar(loader=self._loader, variables=self._job_vars)
|
235
206
|
items = None
|
236
207
|
if self._task.loop_with:
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
208
|
+
templar = self._task_templar
|
209
|
+
terms = self._task.loop
|
210
|
+
|
211
|
+
if isinstance(terms, str):
|
212
|
+
terms = templar.resolve_to_container(_utils.str_problematic_strip(terms))
|
213
|
+
|
214
|
+
if not isinstance(terms, list):
|
215
|
+
terms = [terms]
|
216
|
+
|
217
|
+
@_DirectCall.mark
|
218
|
+
def invoke_lookup() -> t.Any:
|
219
|
+
"""Scope-capturing wrapper around _invoke_lookup to avoid functools.partial obscuring its usage from type-checking tools."""
|
220
|
+
return _invoke_lookup(
|
221
|
+
plugin_name=self._task.loop_with,
|
222
|
+
lookup_terms=terms,
|
223
|
+
lookup_kwargs=dict(wantlist=True),
|
224
|
+
invoked_as_with=True,
|
225
|
+
)
|
252
226
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop_with)
|
227
|
+
# Smuggle a special wrapped lookup invocation in as a local variable for its exclusive use when being evaluated as `with_(lookup)`.
|
228
|
+
# This value will not be visible to other users of this templar or its `available_variables`.
|
229
|
+
items = templar.evaluate_expression(expression=TrustedAsTemplate().tag("invoke_lookup()"), local_variables=dict(invoke_lookup=invoke_lookup))
|
257
230
|
|
258
231
|
elif self._task.loop is not None:
|
259
|
-
items =
|
232
|
+
items = self._task_templar.template(self._task.loop)
|
233
|
+
|
260
234
|
if not isinstance(items, list):
|
261
235
|
raise AnsibleError(
|
262
|
-
"
|
263
|
-
"
|
264
|
-
|
236
|
+
f"The `loop` value must resolve to a 'list', not {native_type_name(items)!r}.",
|
237
|
+
help_text="Provide a list of items/templates, or a template resolving to a list.",
|
238
|
+
obj=self._task.loop,
|
265
239
|
)
|
266
240
|
|
267
241
|
return items
|
268
242
|
|
269
|
-
def _run_loop(self, items):
|
270
|
-
|
243
|
+
def _run_loop(self, items: list[t.Any]) -> list[dict[str, t.Any]]:
|
244
|
+
"""
|
271
245
|
Runs the task with the loop items specified and collates the result
|
272
246
|
into an array named 'results' which is inserted into the final result
|
273
247
|
along with the item for which the loop ran.
|
274
|
-
|
248
|
+
"""
|
275
249
|
task_vars = self._job_vars
|
276
|
-
templar =
|
250
|
+
templar = TemplateEngine(loader=self._loader, variables=task_vars)
|
277
251
|
|
278
252
|
self._task.loop_control.post_validate(templar=templar)
|
279
253
|
|
@@ -282,17 +256,20 @@ class TaskExecutor:
|
|
282
256
|
loop_pause = self._task.loop_control.pause
|
283
257
|
extended = self._task.loop_control.extended
|
284
258
|
extended_allitems = self._task.loop_control.extended_allitems
|
259
|
+
|
285
260
|
# ensure we always have a label
|
286
|
-
label = self._task.loop_control.label or
|
261
|
+
label = self._task.loop_control.label or templar.variable_name_as_template(loop_var)
|
287
262
|
|
288
263
|
if loop_var in task_vars:
|
289
|
-
display.warning(
|
290
|
-
|
291
|
-
|
264
|
+
display.warning(
|
265
|
+
msg=f"The loop variable {loop_var!r} is already in use.",
|
266
|
+
help_text="You should set the `loop_var` value in the `loop_control` option for the task "
|
267
|
+
"to something else to avoid variable collisions and unexpected behavior.",
|
268
|
+
obj=loop_var,
|
269
|
+
)
|
292
270
|
|
293
271
|
ran_once = False
|
294
272
|
task_fields = None
|
295
|
-
no_log = False
|
296
273
|
items_len = len(items)
|
297
274
|
results = []
|
298
275
|
for item_index, item in enumerate(items):
|
@@ -332,7 +309,7 @@ class TaskExecutor:
|
|
332
309
|
ran_once = True
|
333
310
|
|
334
311
|
try:
|
335
|
-
tmp_task = self._task.copy(exclude_parent=True, exclude_tasks=True)
|
312
|
+
tmp_task: Task = self._task.copy(exclude_parent=True, exclude_tasks=True)
|
336
313
|
tmp_task._parent = self._task._parent
|
337
314
|
tmp_play_context = self._play_context.copy()
|
338
315
|
except AnsibleParserError as e:
|
@@ -341,9 +318,11 @@ class TaskExecutor:
|
|
341
318
|
|
342
319
|
# now we swap the internal task and play context with their copies,
|
343
320
|
# execute, and swap them back so we can do the next iteration cleanly
|
321
|
+
# NB: this swap-a-dee-doo confuses some type checkers about the type of tmp_task/self._task
|
344
322
|
(self._task, tmp_task) = (tmp_task, self._task)
|
345
323
|
(self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
|
346
|
-
|
324
|
+
|
325
|
+
res = self._execute(templar=templar, variables=task_vars)
|
347
326
|
|
348
327
|
if self._task.register:
|
349
328
|
# Ensure per loop iteration results are registered in case `_execute()`
|
@@ -355,9 +334,6 @@ class TaskExecutor:
|
|
355
334
|
(self._task, tmp_task) = (tmp_task, self._task)
|
356
335
|
(self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
|
357
336
|
|
358
|
-
# update 'general no_log' based on specific no_log
|
359
|
-
no_log = no_log or tmp_task.no_log
|
360
|
-
|
361
337
|
# now update the result with the item info, and append the result
|
362
338
|
# to the list of results
|
363
339
|
res[loop_var] = item
|
@@ -392,6 +368,7 @@ class TaskExecutor:
|
|
392
368
|
task_fields=task_fields,
|
393
369
|
)
|
394
370
|
|
371
|
+
# FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
|
395
372
|
if tr.is_failed() or tr.is_unreachable():
|
396
373
|
self._final_q.send_callback('v2_runner_item_on_failed', tr)
|
397
374
|
elif tr.is_skipped():
|
@@ -406,11 +383,14 @@ class TaskExecutor:
|
|
406
383
|
|
407
384
|
# break loop if break_when conditions are met
|
408
385
|
if self._task.loop_control and self._task.loop_control.break_when:
|
409
|
-
|
410
|
-
|
411
|
-
|
386
|
+
break_when = self._task.loop_control.get_validated_value(
|
387
|
+
'break_when',
|
388
|
+
self._task.loop_control.fattributes.get('break_when'),
|
389
|
+
self._task.loop_control.break_when,
|
390
|
+
templar,
|
412
391
|
)
|
413
|
-
|
392
|
+
|
393
|
+
if self._task._resolve_conditional(break_when, task_vars):
|
414
394
|
# delete loop vars before exiting loop
|
415
395
|
del task_vars[loop_var]
|
416
396
|
break
|
@@ -432,7 +412,6 @@ class TaskExecutor:
|
|
432
412
|
if var in task_vars and var not in self._job_vars:
|
433
413
|
del task_vars[var]
|
434
414
|
|
435
|
-
self._task.no_log = no_log
|
436
415
|
# NOTE: run_once cannot contain loop vars because it's templated earlier also
|
437
416
|
# This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
|
438
417
|
self._task.run_once = task_fields.get('run_once')
|
@@ -448,21 +427,49 @@ class TaskExecutor:
|
|
448
427
|
# At the point this is executed it is safe to mutate self._task,
|
449
428
|
# since `self._task` is either a copy referred to by `tmp_task` in `_run_loop`
|
450
429
|
# 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
430
|
|
455
|
-
|
456
|
-
|
431
|
+
self._task.delegate_to = delegated_host_name # always override, since a templated result could be an omit (-> None)
|
432
|
+
variables.update(delegated_vars)
|
433
|
+
|
434
|
+
def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
|
435
|
+
result: dict[str, t.Any]
|
436
|
+
|
437
|
+
with _DeferredWarningContext(variables=variables) as warning_ctx:
|
438
|
+
try:
|
439
|
+
# DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
|
440
|
+
result = self._execute_internal(templar, variables)
|
441
|
+
self._apply_task_result_compat(result, warning_ctx)
|
442
|
+
_captured.AnsibleActionCapturedError.maybe_raise_on_result(result)
|
443
|
+
except Exception as ex:
|
444
|
+
try:
|
445
|
+
raise AnsibleTaskError(obj=self._task.get_ds()) from ex
|
446
|
+
except AnsibleTaskError as atex:
|
447
|
+
result = ActionBase.result_dict_from_exception(atex)
|
448
|
+
result.setdefault('changed', False)
|
449
|
+
|
450
|
+
self._task.update_result_no_log(templar, result)
|
451
|
+
|
452
|
+
# The warnings/deprecations in the result have already been captured in the _DeferredWarningContext by _apply_task_result_compat.
|
453
|
+
# The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
|
454
|
+
# These are then used to supersede the entries in the result.
|
455
|
+
|
456
|
+
result.pop('warnings', None)
|
457
|
+
result.pop('deprecations', None)
|
458
|
+
|
459
|
+
if warnings := warning_ctx.get_warnings():
|
460
|
+
result.update(warnings=warnings)
|
461
|
+
|
462
|
+
if deprecation_warnings := warning_ctx.get_deprecation_warnings():
|
463
|
+
result.update(deprecations=deprecation_warnings)
|
464
|
+
|
465
|
+
return result
|
466
|
+
|
467
|
+
def _execute_internal(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
|
468
|
+
"""
|
457
469
|
The primary workhorse of the executor system, this runs the task
|
458
470
|
on the specified host (which may be the delegated_to host) and handles
|
459
471
|
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)
|
472
|
+
"""
|
466
473
|
|
467
474
|
self._calculate_delegate_to(templar, variables)
|
468
475
|
|
@@ -498,18 +505,13 @@ class TaskExecutor:
|
|
498
505
|
# skipping this task during the conditional evaluation step
|
499
506
|
context_validation_error = e
|
500
507
|
|
501
|
-
no_log = self._play_context.no_log
|
502
|
-
|
503
508
|
# Evaluate the conditional (if any) for this task, which we do before running
|
504
509
|
# the final task post-validation. We do this before the post validation due to
|
505
510
|
# the fact that the conditional may specify that the task be skipped due to a
|
506
511
|
# variable not being present which would otherwise cause validation to fail
|
507
512
|
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)
|
513
|
+
if not self._task._resolve_conditional(self._task.when, tempvars, result_context=(rc := t.cast(dict[str, t.Any], {}))):
|
514
|
+
return dict(changed=False, skipped=True, skip_reason='Conditional result was False') | rc
|
513
515
|
except AnsibleError as e:
|
514
516
|
# loop error takes precedence
|
515
517
|
if self._loop_eval_error is not None:
|
@@ -525,22 +527,27 @@ class TaskExecutor:
|
|
525
527
|
|
526
528
|
# if we ran into an error while setting up the PlayContext, raise it now, unless is known issue with delegation
|
527
529
|
# and undefined vars (correct values are in cvars later on and connection plugins, if still error, blows up there)
|
530
|
+
|
531
|
+
# DTFIX-RELEASE: this should probably be declaratively handled in post_validate (or better, get rid of play_context)
|
528
532
|
if context_validation_error is not None:
|
529
533
|
raiseit = True
|
530
534
|
if self._task.delegate_to:
|
531
|
-
if isinstance(context_validation_error,
|
532
|
-
raiseit = False
|
533
|
-
elif isinstance(context_validation_error, AnsibleParserError):
|
535
|
+
if isinstance(context_validation_error, AnsibleParserError):
|
534
536
|
# parser error, might be cause by undef too
|
535
|
-
|
536
|
-
if isinstance(orig_exc, AnsibleUndefinedVariable):
|
537
|
+
if isinstance(context_validation_error.__cause__, AnsibleUndefinedVariable):
|
537
538
|
raiseit = False
|
539
|
+
elif isinstance(context_validation_error, AnsibleUndefinedVariable):
|
540
|
+
# DTFIX-RELEASE: should not be possible to hit this now (all are AnsibleFieldAttributeError)?
|
541
|
+
raiseit = False
|
538
542
|
if raiseit:
|
539
543
|
raise context_validation_error # pylint: disable=raising-bad-type
|
540
544
|
|
541
545
|
# set templar to use temp variables until loop is evaluated
|
542
546
|
templar.available_variables = tempvars
|
543
547
|
|
548
|
+
# Now we do final validation on the task, which sets all fields to their final values.
|
549
|
+
self._task.post_validate(templar=templar)
|
550
|
+
|
544
551
|
# if this task is a TaskInclude, we just return now with a success code so the
|
545
552
|
# main thread can expand the task list for the given host
|
546
553
|
if self._task.action in C._ACTION_INCLUDE_TASKS:
|
@@ -549,7 +556,6 @@ class TaskExecutor:
|
|
549
556
|
if not include_file:
|
550
557
|
return dict(failed=True, msg="No include file was specified to the include")
|
551
558
|
|
552
|
-
include_file = templar.template(include_file)
|
553
559
|
return dict(include=include_file, include_args=include_args)
|
554
560
|
|
555
561
|
# 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 +563,9 @@ class TaskExecutor:
|
|
557
563
|
include_args = self._task.args.copy()
|
558
564
|
return dict(include_args=include_args)
|
559
565
|
|
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
566
|
# free tempvars up, not used anymore, cvars and vars_copy should be mainly used after this point
|
584
567
|
# updating the original 'variables' at the end
|
585
|
-
tempvars
|
568
|
+
del tempvars
|
586
569
|
|
587
570
|
# setup cvars copy, used for all connection related templating
|
588
571
|
if self._task.delegate_to:
|
@@ -634,23 +617,7 @@ class TaskExecutor:
|
|
634
617
|
cvars['ansible_python_interpreter'] = sys.executable
|
635
618
|
|
636
619
|
# 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)
|
620
|
+
self._handler, _module_context = self._get_action_handler_with_module_context(templar=templar)
|
654
621
|
|
655
622
|
retries = 1 # includes the default actual run + retries set by user/default
|
656
623
|
if self._task.retries is not None:
|
@@ -670,7 +637,10 @@ class TaskExecutor:
|
|
670
637
|
if self._task.timeout:
|
671
638
|
old_sig = signal.signal(signal.SIGALRM, task_timeout)
|
672
639
|
signal.alarm(self._task.timeout)
|
673
|
-
|
640
|
+
with PluginExecContext(self._handler):
|
641
|
+
result = self._handler.run(task_vars=vars_copy)
|
642
|
+
|
643
|
+
# DTFIX-RELEASE: nuke this, it hides a lot of error detail- remove the active exception propagation hack from AnsibleActionFail at the same time
|
674
644
|
except (AnsibleActionFail, AnsibleActionSkip) as e:
|
675
645
|
return e.result
|
676
646
|
except AnsibleConnectionFailure as e:
|
@@ -685,12 +655,6 @@ class TaskExecutor:
|
|
685
655
|
self._handler.cleanup()
|
686
656
|
display.debug("handler run complete")
|
687
657
|
|
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
658
|
# update the local copy of vars with the registered value, if specified,
|
695
659
|
# or any facts which may have been generated by the module execution
|
696
660
|
if self._task.register:
|
@@ -714,26 +678,6 @@ class TaskExecutor:
|
|
714
678
|
result,
|
715
679
|
task_fields=self._task.dump_attrs()))
|
716
680
|
|
717
|
-
# ensure no log is preserved
|
718
|
-
result["_ansible_no_log"] = no_log
|
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
|
736
|
-
|
737
681
|
if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
|
738
682
|
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
|
739
683
|
if self._task.delegate_to and self._task.delegate_facts:
|
@@ -745,10 +689,11 @@ class TaskExecutor:
|
|
745
689
|
vars_copy.update(result['ansible_facts'])
|
746
690
|
else:
|
747
691
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
748
|
-
af =
|
692
|
+
af = result['ansible_facts']
|
749
693
|
vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
|
750
694
|
if C.INJECT_FACTS_AS_VARS:
|
751
|
-
|
695
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
696
|
+
vars_copy.update(cleaned_toplevel)
|
752
697
|
|
753
698
|
# set the failed property if it was missing.
|
754
699
|
if 'failed' not in result:
|
@@ -766,9 +711,6 @@ class TaskExecutor:
|
|
766
711
|
if 'changed' not in result:
|
767
712
|
result['changed'] = False
|
768
713
|
|
769
|
-
if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
|
770
|
-
result = wrap_var(result)
|
771
|
-
|
772
714
|
# re-update the local copy of vars with the registered value, if specified,
|
773
715
|
# or any facts which may have been generated by the module execution
|
774
716
|
# This gives changed/failed_when access to additional recently modified
|
@@ -781,18 +723,30 @@ class TaskExecutor:
|
|
781
723
|
if 'skipped' not in result:
|
782
724
|
condname = 'changed'
|
783
725
|
|
726
|
+
# DTFIX-RELEASE: error normalization has not yet occurred; this means that the expressions used for until/failed_when/changed_when/break_when
|
727
|
+
# and when (for loops on the second and later iterations) cannot see the normalized error shapes. This, and the current impl of the expression
|
728
|
+
# handling here causes a number of problems:
|
729
|
+
# * any error in one of the post-task exec expressions is silently ignored and detail lost (eg: `failed_when: syntax ERROR @$123`)
|
730
|
+
# * they cannot reliably access error/warning details, since many of those details are inaccessible until the error normalization occurs
|
731
|
+
# * error normalization includes `msg` if present, and supplies `unknown error` if not; this leads to screwy results on True failed_when if
|
732
|
+
# `msg` is present, eg: `{debug: {}, failed_when: True` -> "Task failed: Action failed: Hello world!"
|
733
|
+
# * detail about failed_when is lost; any error details from the task could potentially be grafted in/preserved if error normalization was done
|
734
|
+
|
784
735
|
try:
|
785
|
-
|
736
|
+
if self._task.changed_when is not None and self._task.changed_when:
|
737
|
+
result['changed'] = self._task._resolve_conditional(self._task.changed_when, vars_copy)
|
738
|
+
|
786
739
|
condname = 'failed'
|
787
|
-
|
740
|
+
|
741
|
+
if self._task.failed_when:
|
742
|
+
result['failed_when_result'] = result['failed'] = self._task._resolve_conditional(self._task.failed_when, vars_copy)
|
743
|
+
|
788
744
|
except AnsibleError as e:
|
789
745
|
result['failed'] = True
|
790
746
|
result['%s_when_result' % condname] = to_text(e)
|
791
747
|
|
792
748
|
if retries > 1:
|
793
|
-
|
794
|
-
cond.when = self._task.until or [not result['failed']]
|
795
|
-
if cond.evaluate_conditional(templar, vars_copy):
|
749
|
+
if self._task._resolve_conditional(self._task.until or [not result['failed']], vars_copy):
|
796
750
|
break
|
797
751
|
else:
|
798
752
|
# no conditional check, or it failed, so sleep for the specified time
|
@@ -817,9 +771,6 @@ class TaskExecutor:
|
|
817
771
|
result['attempts'] = retries - 1
|
818
772
|
result['failed'] = True
|
819
773
|
|
820
|
-
if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
|
821
|
-
result = wrap_var(result)
|
822
|
-
|
823
774
|
# do the final update of the local variables here, for both registered
|
824
775
|
# values and any facts which may have been created
|
825
776
|
if self._task.register:
|
@@ -830,10 +781,12 @@ class TaskExecutor:
|
|
830
781
|
variables.update(result['ansible_facts'])
|
831
782
|
else:
|
832
783
|
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
|
833
|
-
af =
|
784
|
+
af = result['ansible_facts']
|
834
785
|
variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
|
835
786
|
if C.INJECT_FACTS_AS_VARS:
|
836
|
-
|
787
|
+
# DTFIX-FUTURE: why is this happening twice, esp since we're post-fork and these will be discarded?
|
788
|
+
cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
|
789
|
+
variables.update(cleaned_toplevel)
|
837
790
|
|
838
791
|
# save the notification target in the result, if it was specified, as
|
839
792
|
# this task may be running in a loop in which case the notification
|
@@ -858,10 +811,54 @@ class TaskExecutor:
|
|
858
811
|
display.debug("attempt loop complete, returning result")
|
859
812
|
return result
|
860
813
|
|
814
|
+
@staticmethod
|
815
|
+
def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _DeferredWarningContext) -> None:
|
816
|
+
"""Apply backward-compatibility mutations to the supplied task result."""
|
817
|
+
if warnings := result.get('warnings'):
|
818
|
+
if isinstance(warnings, list):
|
819
|
+
for warning in warnings:
|
820
|
+
if not isinstance(warning, WarningSummary):
|
821
|
+
# translate non-WarningMessageDetail messages
|
822
|
+
warning = WarningSummary(
|
823
|
+
details=(
|
824
|
+
Detail(msg=str(warning)),
|
825
|
+
),
|
826
|
+
)
|
827
|
+
|
828
|
+
warning_ctx.capture(warning)
|
829
|
+
else:
|
830
|
+
display.warning(f"Task result `warnings` was {type(warnings)} instead of {list}.")
|
831
|
+
|
832
|
+
if deprecations := result.get('deprecations'):
|
833
|
+
if isinstance(deprecations, list):
|
834
|
+
for deprecation in deprecations:
|
835
|
+
if not isinstance(deprecation, DeprecationSummary):
|
836
|
+
# translate non-DeprecationMessageDetail message dicts
|
837
|
+
try:
|
838
|
+
if deprecation.pop('collection_name', ...) is not ...:
|
839
|
+
# deprecated: description='enable the deprecation message for collection_name' core_version='2.23'
|
840
|
+
# self.deprecated('The `collection_name` key in the `deprecations` dictionary is deprecated.', version='2.27')
|
841
|
+
pass
|
842
|
+
|
843
|
+
# DTFIX-RELEASE: when plugin isn't set, do it at the boundary where we receive the module/action results
|
844
|
+
# that may even allow us to never set it in modules/actions directly and to populate it at the boundary
|
845
|
+
deprecation = DeprecationSummary(
|
846
|
+
details=(
|
847
|
+
Detail(msg=deprecation.pop('msg')),
|
848
|
+
),
|
849
|
+
**deprecation,
|
850
|
+
)
|
851
|
+
except Exception as ex:
|
852
|
+
display.error_as_warning("Task result `deprecations` contained an invalid item.", exception=ex)
|
853
|
+
|
854
|
+
warning_ctx.capture(deprecation)
|
855
|
+
else:
|
856
|
+
display.warning(f"Task result `deprecations` was {type(deprecations)} instead of {list}.")
|
857
|
+
|
861
858
|
def _poll_async_result(self, result, templar, task_vars=None):
|
862
|
-
|
859
|
+
"""
|
863
860
|
Polls for the specified JID to be complete
|
864
|
-
|
861
|
+
"""
|
865
862
|
|
866
863
|
if task_vars is None:
|
867
864
|
task_vars = self._job_vars
|
@@ -891,7 +888,7 @@ class TaskExecutor:
|
|
891
888
|
connection=self._connection,
|
892
889
|
play_context=self._play_context,
|
893
890
|
loader=self._loader,
|
894
|
-
templar=templar,
|
891
|
+
templar=Templar._from_template_engine(templar),
|
895
892
|
shared_loader_obj=self._shared_loader_obj,
|
896
893
|
)
|
897
894
|
|
@@ -961,7 +958,7 @@ class TaskExecutor:
|
|
961
958
|
connection=self._connection,
|
962
959
|
play_context=self._play_context,
|
963
960
|
loader=self._loader,
|
964
|
-
templar=templar,
|
961
|
+
templar=Templar._from_template_engine(templar),
|
965
962
|
shared_loader_obj=self._shared_loader_obj,
|
966
963
|
)
|
967
964
|
cleanup_handler.run(task_vars=task_vars)
|
@@ -977,10 +974,10 @@ class TaskExecutor:
|
|
977
974
|
return become
|
978
975
|
|
979
976
|
def _get_connection(self, cvars, templar, current_connection):
|
980
|
-
|
977
|
+
"""
|
981
978
|
Reads the connection property for the host, and returns the
|
982
979
|
correct connection object from the list of connection plugins
|
983
|
-
|
980
|
+
"""
|
984
981
|
|
985
982
|
self._play_context.connection = current_connection
|
986
983
|
|
@@ -992,7 +989,7 @@ class TaskExecutor:
|
|
992
989
|
connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
|
993
990
|
conn_type,
|
994
991
|
self._play_context,
|
995
|
-
|
992
|
+
new_stdin=None, # No longer used, kept for backwards compat for plugins that explicitly accept this as an arg
|
996
993
|
task_uuid=self._task._uuid,
|
997
994
|
ansible_playbook_pid=to_text(os.getppid())
|
998
995
|
)
|
@@ -1058,7 +1055,11 @@ class TaskExecutor:
|
|
1058
1055
|
options = {}
|
1059
1056
|
for k in option_vars:
|
1060
1057
|
if k in variables:
|
1061
|
-
|
1058
|
+
try:
|
1059
|
+
options[k] = templar.template(variables[k])
|
1060
|
+
except AnsibleValueOmittedError:
|
1061
|
+
pass
|
1062
|
+
|
1062
1063
|
# TODO move to task method?
|
1063
1064
|
plugin.set_options(task_keys=task_keys, var_options=options)
|
1064
1065
|
|
@@ -1124,15 +1125,15 @@ class TaskExecutor:
|
|
1124
1125
|
return varnames
|
1125
1126
|
|
1126
1127
|
def _get_action_handler(self, templar):
|
1127
|
-
|
1128
|
+
"""
|
1128
1129
|
Returns the correct action plugin to handle the requestion task action
|
1129
|
-
|
1130
|
+
"""
|
1130
1131
|
return self._get_action_handler_with_module_context(templar)[0]
|
1131
1132
|
|
1132
|
-
def _get_action_handler_with_module_context(self, templar):
|
1133
|
-
|
1133
|
+
def _get_action_handler_with_module_context(self, templar: TemplateEngine):
|
1134
|
+
"""
|
1134
1135
|
Returns the correct action plugin to handle the requestion task action and the module context
|
1135
|
-
|
1136
|
+
"""
|
1136
1137
|
module_collection, separator, module_name = self._task.action.rpartition(".")
|
1137
1138
|
module_prefix = module_name.split('_')[0]
|
1138
1139
|
if module_collection:
|
@@ -1191,7 +1192,7 @@ class TaskExecutor:
|
|
1191
1192
|
connection=self._connection,
|
1192
1193
|
play_context=self._play_context,
|
1193
1194
|
loader=self._loader,
|
1194
|
-
templar=templar,
|
1195
|
+
templar=Templar._from_template_engine(templar),
|
1195
1196
|
shared_loader_obj=self._shared_loader_obj,
|
1196
1197
|
collection_list=collections
|
1197
1198
|
)
|
@@ -1206,9 +1207,9 @@ CLI_STUB_NAME = 'ansible_connection_cli_stub.py'
|
|
1206
1207
|
|
1207
1208
|
|
1208
1209
|
def start_connection(play_context, options, task_uuid):
|
1209
|
-
|
1210
|
+
"""
|
1210
1211
|
Starts the persistent connection
|
1211
|
-
|
1212
|
+
"""
|
1212
1213
|
|
1213
1214
|
env = os.environ.copy()
|
1214
1215
|
env.update({
|