ansible-core 2.18.5rc1__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 +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/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 +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 +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 +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 +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.5rc1.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.5rc1.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.5rc1.dist-info/RECORD +0 -992
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/top_level.txt +0 -0
ansible/inventory/host.py
CHANGED
@@ -17,28 +17,26 @@
|
|
17
17
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
|
+
import collections.abc as c
|
21
|
+
import typing as t
|
22
|
+
|
20
23
|
from collections.abc import Mapping, MutableMapping
|
21
24
|
|
22
25
|
from ansible.inventory.group import Group, InventoryObjectType
|
23
26
|
from ansible.parsing.utils.addresses import patterns
|
24
|
-
from ansible.utils.vars import combine_vars, get_unique_id
|
27
|
+
from ansible.utils.vars import combine_vars, get_unique_id, validate_variable_name
|
25
28
|
|
29
|
+
from . import helpers # this is left as a module import to facilitate easier unit test patching
|
26
30
|
|
27
31
|
__all__ = ['Host']
|
28
32
|
|
29
33
|
|
30
34
|
class Host:
|
31
|
-
|
35
|
+
"""A single ansible host."""
|
32
36
|
base_type = InventoryObjectType.HOST
|
33
37
|
|
34
38
|
# __slots__ = [ 'name', 'vars', 'groups' ]
|
35
39
|
|
36
|
-
def __getstate__(self):
|
37
|
-
return self.serialize()
|
38
|
-
|
39
|
-
def __setstate__(self, data):
|
40
|
-
return self.deserialize(data)
|
41
|
-
|
42
40
|
def __eq__(self, other):
|
43
41
|
if not isinstance(other, Host):
|
44
42
|
return False
|
@@ -56,55 +54,28 @@ class Host:
|
|
56
54
|
def __repr__(self):
|
57
55
|
return self.get_name()
|
58
56
|
|
59
|
-
def
|
60
|
-
|
61
|
-
for group in self.groups:
|
62
|
-
groups.append(group.serialize())
|
63
|
-
|
64
|
-
return dict(
|
65
|
-
name=self.name,
|
66
|
-
vars=self.vars.copy(),
|
67
|
-
address=self.address,
|
68
|
-
uuid=self._uuid,
|
69
|
-
groups=groups,
|
70
|
-
implicit=self.implicit,
|
71
|
-
)
|
72
|
-
|
73
|
-
def deserialize(self, data):
|
74
|
-
self.__init__(gen_uuid=False) # used by __setstate__ to deserialize in place # pylint: disable=unnecessary-dunder-call
|
75
|
-
|
76
|
-
self.name = data.get('name')
|
77
|
-
self.vars = data.get('vars', dict())
|
78
|
-
self.address = data.get('address', '')
|
79
|
-
self._uuid = data.get('uuid', None)
|
80
|
-
self.implicit = data.get('implicit', False)
|
81
|
-
|
82
|
-
groups = data.get('groups', [])
|
83
|
-
for group_data in groups:
|
84
|
-
g = Group()
|
85
|
-
g.deserialize(group_data)
|
86
|
-
self.groups.append(g)
|
57
|
+
def __init__(self, name: str, port: int | str | None = None, gen_uuid: bool = True) -> None:
|
58
|
+
name = helpers.remove_trust(name)
|
87
59
|
|
88
|
-
|
60
|
+
self.vars: dict[str, t.Any] = {}
|
61
|
+
self.groups: list[Group] = []
|
62
|
+
self._uuid: str | None = None
|
89
63
|
|
90
|
-
self.
|
91
|
-
self.
|
92
|
-
self._uuid = None
|
93
|
-
|
94
|
-
self.name = name
|
95
|
-
self.address = name
|
64
|
+
self.name: str = name
|
65
|
+
self.address: str = name
|
96
66
|
|
97
67
|
if port:
|
98
68
|
self.set_variable('ansible_port', int(port))
|
99
69
|
|
100
70
|
if gen_uuid:
|
101
71
|
self._uuid = get_unique_id()
|
102
|
-
self.implicit = False
|
103
72
|
|
104
|
-
|
73
|
+
self.implicit: bool = False
|
74
|
+
|
75
|
+
def get_name(self) -> str:
|
105
76
|
return self.name
|
106
77
|
|
107
|
-
def populate_ancestors(self, additions=None):
|
78
|
+
def populate_ancestors(self, additions: c.Iterable[Group] | None = None) -> None:
|
108
79
|
# populate ancestors
|
109
80
|
if additions is None:
|
110
81
|
for group in self.groups:
|
@@ -114,7 +85,7 @@ class Host:
|
|
114
85
|
if group not in self.groups:
|
115
86
|
self.groups.append(group)
|
116
87
|
|
117
|
-
def add_group(self, group):
|
88
|
+
def add_group(self, group: Group) -> bool:
|
118
89
|
added = False
|
119
90
|
# populate ancestors first
|
120
91
|
for oldg in group.get_ancestors():
|
@@ -127,7 +98,7 @@ class Host:
|
|
127
98
|
added = True
|
128
99
|
return added
|
129
100
|
|
130
|
-
def remove_group(self, group):
|
101
|
+
def remove_group(self, group: Group) -> bool:
|
131
102
|
removed = False
|
132
103
|
if group in self.groups:
|
133
104
|
self.groups.remove(group)
|
@@ -143,18 +114,25 @@ class Host:
|
|
143
114
|
self.remove_group(oldg)
|
144
115
|
return removed
|
145
116
|
|
146
|
-
def set_variable(self, key, value):
|
117
|
+
def set_variable(self, key: str, value: t.Any) -> None:
|
118
|
+
key = helpers.remove_trust(key)
|
119
|
+
|
120
|
+
validate_variable_name(key)
|
121
|
+
|
147
122
|
if key in self.vars and isinstance(self.vars[key], MutableMapping) and isinstance(value, Mapping):
|
148
123
|
self.vars = combine_vars(self.vars, {key: value})
|
149
124
|
else:
|
150
125
|
self.vars[key] = value
|
151
126
|
|
152
|
-
def get_groups(self):
|
127
|
+
def get_groups(self) -> list[Group]:
|
153
128
|
return self.groups
|
154
129
|
|
155
|
-
def get_magic_vars(self):
|
156
|
-
results =
|
157
|
-
|
130
|
+
def get_magic_vars(self) -> dict[str, t.Any]:
|
131
|
+
results: dict[str, t.Any] = dict(
|
132
|
+
inventory_hostname=self.name,
|
133
|
+
)
|
134
|
+
|
135
|
+
# FUTURE: these values should be dynamically calculated on access ala the rest of magic vars
|
158
136
|
if patterns['ipv4'].match(self.name) or patterns['ipv6'].match(self.name):
|
159
137
|
results['inventory_hostname_short'] = self.name
|
160
138
|
else:
|
@@ -164,5 +142,5 @@ class Host:
|
|
164
142
|
|
165
143
|
return results
|
166
144
|
|
167
|
-
def get_vars(self):
|
145
|
+
def get_vars(self) -> dict[str, t.Any]:
|
168
146
|
return combine_vars(self.vars, self.get_magic_vars())
|
ansible/inventory/manager.py
CHANGED
@@ -19,28 +19,33 @@
|
|
19
19
|
from __future__ import annotations
|
20
20
|
|
21
21
|
import fnmatch
|
22
|
+
import functools
|
22
23
|
import os
|
23
|
-
import sys
|
24
24
|
import re
|
25
25
|
import itertools
|
26
|
-
import
|
26
|
+
import typing as t
|
27
27
|
|
28
28
|
from operator import attrgetter
|
29
29
|
from random import shuffle
|
30
30
|
|
31
31
|
from ansible import constants as C
|
32
|
-
from ansible.
|
32
|
+
from ansible._internal import _json, _wrapt
|
33
|
+
from ansible.errors import AnsibleError, AnsibleOptionsError
|
33
34
|
from ansible.inventory.data import InventoryData
|
34
35
|
from ansible.module_utils.six import string_types
|
35
36
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
36
37
|
from ansible.parsing.utils.addresses import parse_address
|
37
38
|
from ansible.plugins.loader import inventory_loader
|
39
|
+
from ansible._internal._datatag._tags import Origin
|
38
40
|
from ansible.utils.helpers import deduplicate_list
|
39
41
|
from ansible.utils.path import unfrackpath
|
40
42
|
from ansible.utils.display import Display
|
41
43
|
from ansible.utils.vars import combine_vars
|
42
44
|
from ansible.vars.plugins import get_vars_from_inventory_sources
|
43
45
|
|
46
|
+
if t.TYPE_CHECKING:
|
47
|
+
from ansible.plugins.inventory import BaseInventoryPlugin
|
48
|
+
|
44
49
|
display = Display()
|
45
50
|
|
46
51
|
IGNORED_ALWAYS = [br"^\.", b"^host_vars$", b"^group_vars$", b"^vars_plugins$"]
|
@@ -50,7 +55,7 @@ IGNORED_EXTS = [b'%s$' % to_bytes(re.escape(x)) for x in C.INVENTORY_IGNORE_EXTS
|
|
50
55
|
IGNORED = re.compile(b'|'.join(IGNORED_ALWAYS + IGNORED_PATTERNS + IGNORED_EXTS))
|
51
56
|
|
52
57
|
PATTERN_WITH_SUBSCRIPT = re.compile(
|
53
|
-
r
|
58
|
+
r"""^
|
54
59
|
(.+) # A pattern expression ending with...
|
55
60
|
\[(?: # A [subscript] expression comprising:
|
56
61
|
(-?[0-9]+)| # A single positive or negative number
|
@@ -58,12 +63,12 @@ PATTERN_WITH_SUBSCRIPT = re.compile(
|
|
58
63
|
([0-9]*)
|
59
64
|
)\]
|
60
65
|
$
|
61
|
-
|
66
|
+
""", re.X
|
62
67
|
)
|
63
68
|
|
64
69
|
|
65
70
|
def order_patterns(patterns):
|
66
|
-
|
71
|
+
""" takes a list of patterns and reorders them by modifier to apply them consistently """
|
67
72
|
|
68
73
|
# FIXME: this goes away if we apply patterns incrementally or by groups
|
69
74
|
pattern_regular = []
|
@@ -125,19 +130,19 @@ def split_host_pattern(pattern):
|
|
125
130
|
# This mishandles IPv6 addresses, and is retained only for backwards
|
126
131
|
# compatibility.
|
127
132
|
patterns = re.findall(
|
128
|
-
to_text(r
|
133
|
+
to_text(r"""(?: # We want to match something comprising:
|
129
134
|
[^\s:\[\]] # (anything other than whitespace or ':[]'
|
130
135
|
| # ...or...
|
131
136
|
\[[^\]]*\] # a single complete bracketed expression)
|
132
137
|
)+ # occurring once or more
|
133
|
-
|
138
|
+
"""), pattern, re.X
|
134
139
|
)
|
135
140
|
|
136
141
|
return [p.strip() for p in patterns if p.strip()]
|
137
142
|
|
138
143
|
|
139
144
|
class InventoryManager(object):
|
140
|
-
|
145
|
+
""" Creates and manages inventory """
|
141
146
|
|
142
147
|
def __init__(self, loader, sources=None, parse=True, cache=True):
|
143
148
|
|
@@ -196,12 +201,12 @@ class InventoryManager(object):
|
|
196
201
|
def get_host(self, hostname):
|
197
202
|
return self._inventory.get_host(hostname)
|
198
203
|
|
199
|
-
def _fetch_inventory_plugins(self):
|
200
|
-
|
204
|
+
def _fetch_inventory_plugins(self) -> list[BaseInventoryPlugin]:
|
205
|
+
""" sets up loaded inventory plugins for usage """
|
201
206
|
|
202
207
|
display.vvvv('setting up inventory plugins')
|
203
208
|
|
204
|
-
plugins = []
|
209
|
+
plugins: list[BaseInventoryPlugin] = []
|
205
210
|
for name in C.INVENTORY_ENABLED:
|
206
211
|
plugin = inventory_loader.get(name)
|
207
212
|
if plugin:
|
@@ -215,7 +220,7 @@ class InventoryManager(object):
|
|
215
220
|
return plugins
|
216
221
|
|
217
222
|
def parse_sources(self, cache=False):
|
218
|
-
|
223
|
+
""" iterate over inventory sources and parse each one to populate it"""
|
219
224
|
|
220
225
|
parsed = False
|
221
226
|
# allow for multiple inventory parsing
|
@@ -243,7 +248,7 @@ class InventoryManager(object):
|
|
243
248
|
host.vars = combine_vars(host.vars, get_vars_from_inventory_sources(self._loader, self._sources, [host], 'inventory'))
|
244
249
|
|
245
250
|
def parse_source(self, source, cache=False):
|
246
|
-
|
251
|
+
""" Generate or update inventory for the source provided """
|
247
252
|
|
248
253
|
parsed = False
|
249
254
|
failures = []
|
@@ -276,7 +281,6 @@ class InventoryManager(object):
|
|
276
281
|
|
277
282
|
# try source with each plugin
|
278
283
|
for plugin in self._fetch_inventory_plugins():
|
279
|
-
|
280
284
|
plugin_name = to_text(getattr(plugin, '_load_name', getattr(plugin, '_original_path', '')))
|
281
285
|
display.debug(u'Attempting to use plugin %s (%s)' % (plugin_name, plugin._original_path))
|
282
286
|
|
@@ -287,9 +291,14 @@ class InventoryManager(object):
|
|
287
291
|
plugin_wants = False
|
288
292
|
|
289
293
|
if plugin_wants:
|
294
|
+
# have this tag ready to apply to errors or output; str-ify source since it is often tagged by the CLI
|
295
|
+
origin = Origin(description=f'<inventory plugin {plugin_name!r} with source {str(source)!r}>')
|
290
296
|
try:
|
291
|
-
|
292
|
-
|
297
|
+
inventory_wrapper = _InventoryDataWrapper(self._inventory, target_plugin=plugin, origin=origin)
|
298
|
+
|
299
|
+
# FUTURE: now that we have a wrapper around inventory, we can have it use ChainMaps to preview the in-progress inventory,
|
300
|
+
# but be able to roll back partial inventory failures by discarding the outermost layer
|
301
|
+
plugin.parse(inventory_wrapper, self._loader, source, cache=cache)
|
293
302
|
try:
|
294
303
|
plugin.update_cache_if_changed()
|
295
304
|
except AttributeError:
|
@@ -298,14 +307,16 @@ class InventoryManager(object):
|
|
298
307
|
parsed = True
|
299
308
|
display.vvv('Parsed %s inventory source with %s plugin' % (source, plugin_name))
|
300
309
|
break
|
301
|
-
except
|
302
|
-
|
303
|
-
|
304
|
-
failures.append({'src': source, 'plugin': plugin_name, 'exc':
|
305
|
-
except Exception as
|
306
|
-
|
307
|
-
|
308
|
-
|
310
|
+
except AnsibleError as ex:
|
311
|
+
if not ex.obj:
|
312
|
+
ex.obj = origin
|
313
|
+
failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
|
314
|
+
except Exception as ex:
|
315
|
+
try:
|
316
|
+
# omit line number to prevent contextual display of script or possibly sensitive info
|
317
|
+
raise AnsibleError(str(ex), obj=origin) from ex
|
318
|
+
except AnsibleError as ex:
|
319
|
+
failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
|
309
320
|
else:
|
310
321
|
display.vvv("%s declined parsing %s as it did not pass its verify_file() method" % (plugin_name, source))
|
311
322
|
|
@@ -319,9 +330,8 @@ class InventoryManager(object):
|
|
319
330
|
if failures:
|
320
331
|
# only if no plugin processed files should we show errors.
|
321
332
|
for fail in failures:
|
322
|
-
|
323
|
-
|
324
|
-
display.vvv(to_text(fail['tb']))
|
333
|
+
# `obj` should always be set
|
334
|
+
display.error_as_warning(msg=f'Failed to parse inventory with {fail["plugin"]!r} plugin.', exception=fail['exc'])
|
325
335
|
|
326
336
|
# final error/warning on inventory source failure
|
327
337
|
if C.INVENTORY_ANY_UNPARSED_IS_FAILED:
|
@@ -335,12 +345,12 @@ class InventoryManager(object):
|
|
335
345
|
return parsed
|
336
346
|
|
337
347
|
def clear_caches(self):
|
338
|
-
|
348
|
+
""" clear all caches """
|
339
349
|
self._hosts_patterns_cache = {}
|
340
350
|
self._pattern_cache = {}
|
341
351
|
|
342
352
|
def refresh_inventory(self):
|
343
|
-
|
353
|
+
""" recalculate inventory """
|
344
354
|
|
345
355
|
self.clear_caches()
|
346
356
|
self._inventory = InventoryData()
|
@@ -657,9 +667,9 @@ class InventoryManager(object):
|
|
657
667
|
self._pattern_cache = {}
|
658
668
|
|
659
669
|
def add_dynamic_host(self, host_info, result_item):
|
660
|
-
|
670
|
+
"""
|
661
671
|
Helper function to add a new host to inventory based on a task result.
|
662
|
-
|
672
|
+
"""
|
663
673
|
|
664
674
|
changed = False
|
665
675
|
if not result_item.get('refresh'):
|
@@ -697,10 +707,10 @@ class InventoryManager(object):
|
|
697
707
|
result_item['changed'] = changed
|
698
708
|
|
699
709
|
def add_dynamic_group(self, host, result_item):
|
700
|
-
|
710
|
+
"""
|
701
711
|
Helper function to add a group (if it does not exist), and to assign the
|
702
712
|
specified host to that group.
|
703
|
-
|
713
|
+
"""
|
704
714
|
|
705
715
|
changed = False
|
706
716
|
|
@@ -749,3 +759,36 @@ class InventoryManager(object):
|
|
749
759
|
self.reconcile_inventory()
|
750
760
|
|
751
761
|
result_item['changed'] = changed
|
762
|
+
|
763
|
+
|
764
|
+
class _InventoryDataWrapper(_wrapt.ObjectProxy):
|
765
|
+
"""
|
766
|
+
Proxy wrapper around InventoryData.
|
767
|
+
Allows `set_variable` calls to automatically apply template trust for plugins that don't know how.
|
768
|
+
"""
|
769
|
+
|
770
|
+
# declared as class attrs to signal to ObjectProxy that we want them stored on the proxy, not the wrapped value
|
771
|
+
_target_plugin = None
|
772
|
+
_default_origin = None
|
773
|
+
|
774
|
+
def __init__(self, referent: InventoryData, target_plugin: BaseInventoryPlugin, origin: Origin) -> None:
|
775
|
+
super().__init__(referent)
|
776
|
+
self._target_plugin = target_plugin
|
777
|
+
# fallback origin to ensure that vars are tagged with at least the file they came from
|
778
|
+
self._default_origin = origin
|
779
|
+
|
780
|
+
@functools.cached_property
|
781
|
+
def _inspector(self) -> _json.AnsibleVariableVisitor:
|
782
|
+
"""
|
783
|
+
Inventory plugins can delegate to other plugins (e.g. `auto`).
|
784
|
+
This hack defers sampling the target plugin's `trusted_by_default` attr until `set_variable` is called, typically inside `parse`.
|
785
|
+
Trust is then optionally applied based on the plugin's declared intent via `trusted_by_default`.
|
786
|
+
"""
|
787
|
+
return _json.AnsibleVariableVisitor(
|
788
|
+
trusted_as_template=self._target_plugin.trusted_by_default,
|
789
|
+
origin=self._default_origin,
|
790
|
+
allow_encrypted_string=True,
|
791
|
+
)
|
792
|
+
|
793
|
+
def set_variable(self, entity: str, varname: str, value: t.Any) -> None:
|
794
|
+
self.__wrapped__.set_variable(entity, varname, self._inspector.visit(value))
|
ansible/keyword_desc.yml
CHANGED
@@ -13,7 +13,7 @@ become_method: Which method of privilege escalation to use (such as sudo or su).
|
|
13
13
|
become_user: "User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user."
|
14
14
|
block: List of tasks in a block.
|
15
15
|
changed_when: "Conditional expression that overrides the task's normal 'changed' status."
|
16
|
-
check_mode: A boolean that controls if a task is
|
16
|
+
check_mode: A boolean that controls if a task is run normally or avoids changes to the target and tries to report what it would have done (check mode/dry run). See :ref:`check_mode_dry`.
|
17
17
|
collections: |
|
18
18
|
List of collection namespaces to search for modules, plugins, and roles. See :ref:`collections_using_playbook`
|
19
19
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import collections.abc as c
|
4
|
+
|
5
|
+
import typing as t
|
6
|
+
|
7
|
+
|
8
|
+
# DTFIX-RELEASE: bikeshed "intermediate"
|
9
|
+
INTERMEDIATE_MAPPING_TYPES = (c.Mapping,)
|
10
|
+
"""
|
11
|
+
Mapping types which are supported for recursion and runtime usage, such as in serialization and templating.
|
12
|
+
These will be converted to a simple Python `dict` before serialization or storage as a variable.
|
13
|
+
"""
|
14
|
+
|
15
|
+
INTERMEDIATE_ITERABLE_TYPES = (tuple, set, frozenset, c.Sequence)
|
16
|
+
"""
|
17
|
+
Iterable types which are supported for recursion and runtime usage, such as in serialization and templating.
|
18
|
+
These will be converted to a simple Python `list` before serialization or storage as a variable.
|
19
|
+
CAUTION: Scalar types which are sequences should be excluded when using this.
|
20
|
+
"""
|
21
|
+
|
22
|
+
ITERABLE_SCALARS_NOT_TO_ITERATE_FIXME = (str, bytes)
|
23
|
+
"""Scalars which are also iterable, and should thus be excluded from iterable checks."""
|
24
|
+
|
25
|
+
|
26
|
+
def is_intermediate_mapping(value: object) -> bool:
|
27
|
+
"""Returns `True` if `value` is a type supported for projection to a Python `dict`, otherwise returns `False`."""
|
28
|
+
# DTFIX-RELEASE: bikeshed name
|
29
|
+
return isinstance(value, INTERMEDIATE_MAPPING_TYPES)
|
30
|
+
|
31
|
+
|
32
|
+
def is_intermediate_iterable(value: object) -> bool:
|
33
|
+
"""Returns `True` if `value` is a type supported for projection to a Python `list`, otherwise returns `False`."""
|
34
|
+
# DTFIX-RELEASE: bikeshed name
|
35
|
+
return isinstance(value, INTERMEDIATE_ITERABLE_TYPES) and not isinstance(value, ITERABLE_SCALARS_NOT_TO_ITERATE_FIXME)
|
36
|
+
|
37
|
+
|
38
|
+
is_controller: bool = False
|
39
|
+
"""Set to True automatically when this module is imported into an Ansible controller context."""
|
40
|
+
|
41
|
+
|
42
|
+
def get_controller_serialize_map() -> dict[type, t.Callable]:
|
43
|
+
"""
|
44
|
+
Called to augment serialization maps.
|
45
|
+
This implementation is replaced with the one from ansible._internal in controller contexts.
|
46
|
+
"""
|
47
|
+
return {}
|
48
|
+
|
49
|
+
|
50
|
+
def import_controller_module(_module_name: str, /) -> t.Any:
|
51
|
+
"""
|
52
|
+
Called to conditionally import the named module in a controller context, otherwise returns `None`.
|
53
|
+
This implementation is replaced with the one from ansible._internal in controller contexts.
|
54
|
+
"""
|
55
|
+
return None
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (c) 2024 Ansible Project
|
2
|
+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
import contextlib
|
7
|
+
import contextvars
|
8
|
+
|
9
|
+
# deprecated: description='typing.Self exists in Python 3.11+' python_version='3.10'
|
10
|
+
from ..compat import typing as t
|
11
|
+
|
12
|
+
|
13
|
+
class AmbientContextBase:
|
14
|
+
"""
|
15
|
+
An abstract base context manager that, once entered, will be accessible via its `current` classmethod to any code in the same
|
16
|
+
`contextvars` context (e.g. same thread/coroutine), until it is exited.
|
17
|
+
"""
|
18
|
+
|
19
|
+
__slots__ = ('_contextvar_token',)
|
20
|
+
|
21
|
+
# DTFIX-FUTURE: subclasses need to be able to opt-in to blocking nested contexts of the same type (basically optional per-callstack singleton behavior)
|
22
|
+
# DTFIX-RELEASE: this class should enforce strict nesting of contexts; overlapping context lifetimes leads to incredibly difficult to
|
23
|
+
# debug situations with undefined behavior, so it should fail fast.
|
24
|
+
# DTFIX-RELEASE: make frozen=True dataclass subclasses work (fix the mutability of the contextvar instance)
|
25
|
+
|
26
|
+
_contextvar: t.ClassVar[contextvars.ContextVar] # pylint: disable=declare-non-slot # pylint bug, see https://github.com/pylint-dev/pylint/issues/9950
|
27
|
+
_contextvar_token: contextvars.Token
|
28
|
+
|
29
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
30
|
+
cls._contextvar = contextvars.ContextVar(cls.__name__)
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def when(cls, condition: bool, /, *args, **kwargs) -> t.Self | contextlib.nullcontext:
|
34
|
+
"""Return an instance of the context if `condition` is `True`, otherwise return a `nullcontext` instance."""
|
35
|
+
return cls(*args, **kwargs) if condition else contextlib.nullcontext()
|
36
|
+
|
37
|
+
@classmethod
|
38
|
+
def current(cls, optional: bool = False) -> t.Self | None:
|
39
|
+
"""
|
40
|
+
Return the currently active context value for the current thread or coroutine.
|
41
|
+
Raises ReferenceError if a context is not active, unless `optional` is `True`.
|
42
|
+
"""
|
43
|
+
try:
|
44
|
+
return cls._contextvar.get()
|
45
|
+
except LookupError:
|
46
|
+
if optional:
|
47
|
+
return None
|
48
|
+
|
49
|
+
raise ReferenceError(f"A required {cls.__name__} context is not active.") from None
|
50
|
+
|
51
|
+
def __enter__(self) -> t.Self:
|
52
|
+
# DTFIX-RELEASE: actively block multiple entry
|
53
|
+
self._contextvar_token = self.__class__._contextvar.set(self)
|
54
|
+
return self
|
55
|
+
|
56
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
57
|
+
self.__class__._contextvar.reset(self._contextvar_token)
|
58
|
+
del self._contextvar_token
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Copyright (c) 2024 Ansible Project
|
2
|
+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
3
|
+
|
4
|
+
"""Support code for exclusive use by the AnsiballZ wrapper."""
|
5
|
+
|
6
|
+
from __future__ import annotations
|
7
|
+
|
8
|
+
import atexit
|
9
|
+
import dataclasses
|
10
|
+
import importlib.util
|
11
|
+
import json
|
12
|
+
import os
|
13
|
+
import runpy
|
14
|
+
import sys
|
15
|
+
import typing as t
|
16
|
+
|
17
|
+
from . import _errors
|
18
|
+
from ._plugin_exec_context import PluginExecContext, HasPluginInfo
|
19
|
+
from .. import basic
|
20
|
+
from ..common.json import get_module_encoder, Direction
|
21
|
+
from ..common.messages import PluginInfo
|
22
|
+
|
23
|
+
|
24
|
+
def run_module(
|
25
|
+
*,
|
26
|
+
json_params: bytes,
|
27
|
+
profile: str,
|
28
|
+
plugin_info_dict: dict[str, object],
|
29
|
+
module_fqn: str,
|
30
|
+
modlib_path: str,
|
31
|
+
init_globals: dict[str, t.Any] | None = None,
|
32
|
+
coverage_config: str | None = None,
|
33
|
+
coverage_output: str | None = None,
|
34
|
+
) -> None: # pragma: nocover
|
35
|
+
"""Used internally by the AnsiballZ wrapper to run an Ansible module."""
|
36
|
+
try:
|
37
|
+
_enable_coverage(coverage_config, coverage_output)
|
38
|
+
_run_module(
|
39
|
+
json_params=json_params,
|
40
|
+
profile=profile,
|
41
|
+
plugin_info_dict=plugin_info_dict,
|
42
|
+
module_fqn=module_fqn,
|
43
|
+
modlib_path=modlib_path,
|
44
|
+
init_globals=init_globals,
|
45
|
+
)
|
46
|
+
except Exception as ex: # not BaseException, since modules are expected to raise SystemExit
|
47
|
+
_handle_exception(ex, profile)
|
48
|
+
|
49
|
+
|
50
|
+
def _enable_coverage(coverage_config: str | None, coverage_output: str | None) -> None: # pragma: nocover
|
51
|
+
"""Bootstrap `coverage` for the current Ansible module invocation."""
|
52
|
+
if not coverage_config:
|
53
|
+
return
|
54
|
+
|
55
|
+
if coverage_output:
|
56
|
+
# Enable code coverage analysis of the module.
|
57
|
+
# This feature is for internal testing and may change without notice.
|
58
|
+
python_version_string = '.'.join(str(v) for v in sys.version_info[:2])
|
59
|
+
os.environ['COVERAGE_FILE'] = f'{coverage_output}=python-{python_version_string}=coverage'
|
60
|
+
|
61
|
+
import coverage
|
62
|
+
|
63
|
+
cov = coverage.Coverage(config_file=coverage_config)
|
64
|
+
|
65
|
+
def atexit_coverage():
|
66
|
+
cov.stop()
|
67
|
+
cov.save()
|
68
|
+
|
69
|
+
atexit.register(atexit_coverage)
|
70
|
+
|
71
|
+
cov.start()
|
72
|
+
else:
|
73
|
+
# Verify coverage is available without importing it.
|
74
|
+
# This will detect when a module would fail with coverage enabled with minimal overhead.
|
75
|
+
if importlib.util.find_spec('coverage') is None:
|
76
|
+
raise RuntimeError('Could not find the `coverage` Python module.')
|
77
|
+
|
78
|
+
|
79
|
+
def _run_module(
|
80
|
+
*,
|
81
|
+
json_params: bytes,
|
82
|
+
profile: str,
|
83
|
+
plugin_info_dict: dict[str, object],
|
84
|
+
module_fqn: str,
|
85
|
+
modlib_path: str,
|
86
|
+
init_globals: dict[str, t.Any] | None = None,
|
87
|
+
) -> None:
|
88
|
+
"""Used internally by `_run_module` to run an Ansible module after coverage has been enabled (if applicable)."""
|
89
|
+
basic._ANSIBLE_ARGS = json_params
|
90
|
+
basic._ANSIBLE_PROFILE = profile
|
91
|
+
|
92
|
+
init_globals = init_globals or {}
|
93
|
+
init_globals.update(_module_fqn=module_fqn, _modlib_path=modlib_path)
|
94
|
+
|
95
|
+
with PluginExecContext(_ModulePluginWrapper(PluginInfo._from_dict(plugin_info_dict))):
|
96
|
+
# Run the module. By importing it as '__main__', it executes as a script.
|
97
|
+
runpy.run_module(mod_name=module_fqn, init_globals=init_globals, run_name='__main__', alter_sys=True)
|
98
|
+
|
99
|
+
# An Ansible module must print its own results and exit. If execution reaches this point, that did not happen.
|
100
|
+
raise RuntimeError('New-style module did not handle its own exit.')
|
101
|
+
|
102
|
+
|
103
|
+
def _handle_exception(exception: BaseException, profile: str) -> t.NoReturn:
|
104
|
+
"""Handle the given exception."""
|
105
|
+
result = dict(
|
106
|
+
failed=True,
|
107
|
+
exception=_errors.create_error_summary(exception),
|
108
|
+
)
|
109
|
+
|
110
|
+
encoder = get_module_encoder(profile, Direction.MODULE_TO_CONTROLLER)
|
111
|
+
|
112
|
+
print(json.dumps(result, cls=encoder)) # pylint: disable=ansible-bad-function
|
113
|
+
|
114
|
+
sys.exit(1) # pylint: disable=ansible-bad-function
|
115
|
+
|
116
|
+
|
117
|
+
@dataclasses.dataclass(frozen=True)
|
118
|
+
class _ModulePluginWrapper(HasPluginInfo):
|
119
|
+
"""Modules aren't plugin instances; this adapter implements the `HasPluginInfo` protocol to allow `PluginExecContext` infra to work with modules."""
|
120
|
+
|
121
|
+
plugin: PluginInfo
|
122
|
+
|
123
|
+
@property
|
124
|
+
def _load_name(self) -> str:
|
125
|
+
return self.plugin.requested_name
|
126
|
+
|
127
|
+
@property
|
128
|
+
def ansible_name(self) -> str:
|
129
|
+
return self.plugin.resolved_name
|
130
|
+
|
131
|
+
@property
|
132
|
+
def plugin_type(self) -> str:
|
133
|
+
return self.plugin.type
|
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Proxy stdlib threading module that only supports non-joinable daemon threads."""
|
2
|
+
|
2
3
|
# NB: all new local module attrs are _ prefixed to ensure an identical public attribute surface area to the module we're proxying
|
3
4
|
|
4
5
|
from __future__ import annotations as _annotations
|