ansible-core 2.15.4rc1__py3-none-any.whl → 2.16.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/cli/__init__.py +3 -3
- ansible/cli/adhoc.py +1 -1
- ansible/cli/arguments/option_helpers.py +15 -5
- ansible/cli/config.py +2 -2
- ansible/cli/console.py +21 -17
- ansible/cli/doc.py +8 -9
- ansible/cli/galaxy.py +60 -27
- ansible/cli/inventory.py +1 -1
- ansible/cli/playbook.py +1 -1
- ansible/cli/pull.py +2 -2
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
- ansible/cli/vault.py +11 -6
- ansible/collections/__init__.py +0 -29
- ansible/collections/list.py +23 -44
- ansible/config/ansible_builtin_runtime.yml +8 -4
- ansible/config/base.yml +34 -22
- ansible/config/manager.py +1 -1
- ansible/constants.py +3 -5
- ansible/errors/__init__.py +1 -1
- ansible/executor/interpreter_discovery.py +1 -1
- ansible/executor/module_common.py +39 -32
- ansible/executor/play_iterator.py +0 -15
- ansible/executor/playbook_executor.py +3 -3
- ansible/executor/powershell/module_manifest.py +1 -1
- ansible/executor/powershell/module_wrapper.ps1 +4 -1
- ansible/executor/process/worker.py +22 -7
- ansible/executor/task_executor.py +39 -40
- ansible/executor/task_queue_manager.py +8 -11
- ansible/galaxy/__init__.py +1 -1
- ansible/galaxy/api.py +8 -11
- ansible/galaxy/collection/__init__.py +17 -4
- ansible/galaxy/collection/concrete_artifact_manager.py +7 -2
- ansible/galaxy/collection/galaxy_api_proxy.py +1 -1
- ansible/galaxy/data/container/README.md +3 -5
- ansible/galaxy/dependency_resolution/__init__.py +1 -6
- ansible/galaxy/dependency_resolution/dataclasses.py +22 -1
- ansible/galaxy/dependency_resolution/providers.py +61 -69
- ansible/galaxy/role.py +31 -13
- ansible/galaxy/token.py +2 -2
- ansible/inventory/group.py +1 -1
- ansible/inventory/manager.py +1 -1
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/basic.py +11 -41
- ansible/module_utils/common/file.py +0 -100
- ansible/module_utils/common/json.py +1 -1
- ansible/module_utils/common/locale.py +1 -1
- ansible/module_utils/common/text/converters.py +2 -2
- ansible/module_utils/common/validation.py +1 -1
- ansible/module_utils/compat/_selectors2.py +4 -4
- ansible/module_utils/compat/datetime.py +40 -0
- ansible/module_utils/compat/selinux.py +1 -1
- ansible/module_utils/compat/typing.py +1 -1
- ansible/module_utils/connection.py +1 -1
- ansible/module_utils/facts/hardware/linux.py +2 -2
- ansible/module_utils/facts/hardware/openbsd.py +1 -1
- ansible/module_utils/facts/network/linux.py +3 -3
- ansible/module_utils/facts/other/facter.py +8 -15
- ansible/module_utils/facts/sysctl.py +1 -1
- ansible/module_utils/facts/system/date_time.py +2 -2
- ansible/module_utils/facts/system/distribution.py +1 -1
- ansible/module_utils/facts/system/local.py +6 -2
- ansible/module_utils/facts/system/pkg_mgr.py +6 -1
- ansible/module_utils/facts/system/service_mgr.py +4 -2
- ansible/module_utils/parsing/convert_bool.py +1 -1
- ansible/module_utils/service.py +9 -6
- ansible/module_utils/urls.py +40 -22
- ansible/modules/add_host.py +2 -2
- ansible/modules/apt.py +48 -31
- ansible/modules/apt_key.py +4 -4
- ansible/modules/apt_repository.py +5 -5
- ansible/modules/assemble.py +7 -7
- ansible/modules/assert.py +1 -1
- ansible/modules/async_status.py +11 -7
- ansible/modules/async_wrapper.py +1 -1
- ansible/modules/blockinfile.py +60 -17
- ansible/modules/command.py +37 -15
- ansible/modules/copy.py +35 -30
- ansible/modules/cron.py +14 -14
- ansible/modules/deb822_repository.py +4 -3
- ansible/modules/debconf.py +35 -14
- ansible/modules/debug.py +1 -1
- ansible/modules/dnf.py +29 -27
- ansible/modules/dnf5.py +22 -22
- ansible/modules/dpkg_selections.py +9 -2
- ansible/modules/expect.py +4 -4
- ansible/modules/fetch.py +7 -7
- ansible/modules/file.py +30 -30
- ansible/modules/find.py +82 -22
- ansible/modules/gather_facts.py +6 -2
- ansible/modules/get_url.py +29 -29
- ansible/modules/getent.py +4 -4
- ansible/modules/git.py +27 -27
- ansible/modules/group.py +5 -12
- ansible/modules/hostname.py +21 -2
- ansible/modules/include_role.py +5 -5
- ansible/modules/include_tasks.py +2 -2
- ansible/modules/include_vars.py +5 -5
- ansible/modules/iptables.py +70 -65
- ansible/modules/known_hosts.py +7 -7
- ansible/modules/lineinfile.py +33 -33
- ansible/modules/meta.py +13 -13
- ansible/modules/package.py +8 -8
- ansible/modules/package_facts.py +3 -3
- ansible/modules/pause.py +2 -2
- ansible/modules/ping.py +5 -5
- ansible/modules/pip.py +80 -46
- ansible/modules/reboot.py +8 -4
- ansible/modules/replace.py +20 -15
- ansible/modules/rpm_key.py +2 -2
- ansible/modules/script.py +16 -10
- ansible/modules/service.py +26 -98
- ansible/modules/service_facts.py +36 -12
- ansible/modules/set_fact.py +2 -2
- ansible/modules/set_stats.py +2 -2
- ansible/modules/setup.py +18 -18
- ansible/modules/shell.py +3 -3
- ansible/modules/stat.py +9 -30
- ansible/modules/subversion.py +9 -9
- ansible/modules/systemd.py +20 -19
- ansible/modules/systemd_service.py +20 -19
- ansible/modules/sysvinit.py +26 -21
- ansible/modules/tempfile.py +5 -4
- ansible/modules/template.py +60 -6
- ansible/modules/unarchive.py +21 -18
- ansible/modules/uri.py +39 -39
- ansible/modules/user.py +81 -53
- ansible/modules/wait_for.py +22 -21
- ansible/modules/wait_for_connection.py +4 -4
- ansible/modules/yum.py +38 -38
- ansible/modules/yum_repository.py +58 -80
- ansible/parsing/dataloader.py +27 -27
- ansible/parsing/mod_args.py +1 -1
- ansible/parsing/plugin_docs.py +3 -3
- ansible/parsing/splitter.py +14 -16
- ansible/parsing/utils/yaml.py +1 -1
- ansible/parsing/vault/__init__.py +8 -6
- ansible/parsing/yaml/constructor.py +1 -1
- ansible/parsing/yaml/objects.py +1 -1
- ansible/playbook/__init__.py +1 -1
- ansible/playbook/base.py +2 -2
- ansible/playbook/block.py +0 -1
- ansible/playbook/conditional.py +40 -114
- ansible/playbook/helpers.py +5 -28
- ansible/playbook/included_file.py +8 -7
- ansible/playbook/play.py +1 -1
- ansible/playbook/play_context.py +2 -2
- ansible/playbook/playbook_include.py +2 -2
- ansible/playbook/role/__init__.py +1 -1
- ansible/playbook/role/include.py +1 -1
- ansible/playbook/role/metadata.py +1 -1
- ansible/playbook/role_include.py +1 -1
- ansible/playbook/task.py +2 -2
- ansible/playbook/task_include.py +1 -24
- ansible/plugins/__init__.py +13 -5
- ansible/plugins/action/__init__.py +17 -43
- ansible/plugins/action/add_host.py +2 -3
- ansible/plugins/action/assemble.py +1 -1
- ansible/plugins/action/assert.py +2 -1
- ansible/plugins/action/copy.py +2 -2
- ansible/plugins/action/debug.py +2 -1
- ansible/plugins/action/fail.py +1 -0
- ansible/plugins/action/fetch.py +3 -1
- ansible/plugins/action/gather_facts.py +37 -13
- ansible/plugins/action/group_by.py +1 -0
- ansible/plugins/action/include_vars.py +3 -2
- ansible/plugins/action/normal.py +3 -3
- ansible/plugins/action/pause.py +1 -1
- ansible/plugins/action/reboot.py +21 -16
- ansible/plugins/action/script.py +23 -8
- ansible/plugins/action/set_fact.py +1 -0
- ansible/plugins/action/set_stats.py +1 -0
- ansible/plugins/action/shell.py +6 -0
- ansible/plugins/action/template.py +1 -1
- ansible/plugins/action/unarchive.py +1 -1
- ansible/plugins/action/uri.py +1 -1
- ansible/plugins/action/validate_argument_spec.py +1 -0
- ansible/plugins/action/wait_for_connection.py +4 -4
- ansible/plugins/become/__init__.py +1 -1
- ansible/plugins/become/su.py +1 -1
- ansible/plugins/cache/__init__.py +1 -1
- ansible/plugins/callback/junit.py +1 -1
- ansible/plugins/callback/oneline.py +1 -1
- ansible/plugins/callback/tree.py +1 -1
- ansible/plugins/cliconf/__init__.py +2 -2
- ansible/plugins/connection/__init__.py +65 -37
- ansible/plugins/connection/local.py +9 -8
- ansible/plugins/connection/paramiko_ssh.py +34 -28
- ansible/plugins/connection/psrp.py +56 -43
- ansible/plugins/connection/ssh.py +67 -43
- ansible/plugins/connection/winrm.py +77 -30
- ansible/plugins/doc_fragments/constructed.py +4 -4
- ansible/plugins/doc_fragments/files.py +12 -12
- ansible/plugins/doc_fragments/inventory_cache.py +0 -6
- ansible/plugins/doc_fragments/result_format_callback.py +5 -5
- ansible/plugins/doc_fragments/shell_common.py +2 -2
- ansible/plugins/doc_fragments/shell_windows.py +1 -1
- ansible/plugins/doc_fragments/template_common.py +6 -6
- ansible/plugins/doc_fragments/url.py +10 -10
- ansible/plugins/doc_fragments/url_windows.py +15 -15
- ansible/plugins/doc_fragments/vars_plugin_staging.py +4 -4
- ansible/plugins/filter/b64decode.yml +1 -1
- ansible/plugins/filter/b64encode.yml +2 -2
- ansible/plugins/filter/bool.yml +5 -5
- ansible/plugins/filter/combine.yml +1 -1
- ansible/plugins/filter/commonpath.yml +2 -1
- ansible/plugins/filter/core.py +6 -8
- ansible/plugins/filter/dict2items.yml +11 -1
- ansible/plugins/filter/difference.yml +1 -0
- ansible/plugins/filter/encryption.py +1 -1
- ansible/plugins/filter/extract.yml +1 -1
- ansible/plugins/filter/flatten.yml +1 -1
- ansible/plugins/filter/from_yaml.yml +1 -1
- ansible/plugins/filter/from_yaml_all.yml +2 -2
- ansible/plugins/filter/hash.yml +1 -1
- ansible/plugins/filter/human_readable.yml +1 -1
- ansible/plugins/filter/human_to_bytes.yml +2 -2
- ansible/plugins/filter/intersect.yml +1 -0
- ansible/plugins/filter/mandatory.yml +7 -0
- ansible/plugins/filter/mathstuff.py +15 -17
- ansible/plugins/filter/normpath.yml +1 -1
- ansible/plugins/filter/path_join.yml +8 -1
- ansible/plugins/filter/realpath.yml +3 -2
- ansible/plugins/filter/regex_findall.yml +8 -2
- ansible/plugins/filter/regex_replace.yml +9 -3
- ansible/plugins/filter/regex_search.yml +8 -2
- ansible/plugins/filter/relpath.yml +2 -2
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/splitext.yml +1 -1
- ansible/plugins/filter/subelements.yml +2 -2
- ansible/plugins/filter/symmetric_difference.yml +1 -0
- ansible/plugins/filter/ternary.yml +5 -5
- ansible/plugins/filter/to_json.yml +7 -7
- ansible/plugins/filter/to_nice_json.yml +5 -5
- ansible/plugins/filter/to_yaml.yml +2 -2
- ansible/plugins/filter/type_debug.yml +1 -1
- ansible/plugins/filter/union.yml +1 -0
- ansible/plugins/filter/unvault.yml +2 -2
- ansible/plugins/filter/urldecode.yml +13 -32
- ansible/plugins/filter/urlsplit.py +1 -1
- ansible/plugins/filter/vault.yml +1 -1
- ansible/plugins/filter/zip.yml +1 -1
- ansible/plugins/filter/zip_longest.yml +1 -1
- ansible/plugins/inventory/__init__.py +1 -1
- ansible/plugins/inventory/advanced_host_list.py +1 -1
- ansible/plugins/inventory/constructed.py +2 -2
- ansible/plugins/inventory/host_list.py +1 -1
- ansible/plugins/inventory/ini.py +6 -3
- ansible/plugins/inventory/script.py +8 -2
- ansible/plugins/inventory/toml.py +1 -1
- ansible/plugins/inventory/yaml.py +1 -1
- ansible/plugins/list.py +21 -17
- ansible/plugins/loader.py +66 -88
- ansible/plugins/lookup/__init__.py +1 -1
- ansible/plugins/lookup/config.py +16 -6
- ansible/plugins/lookup/csvfile.py +7 -4
- ansible/plugins/lookup/env.py +1 -1
- ansible/plugins/lookup/file.py +5 -2
- ansible/plugins/lookup/fileglob.py +5 -2
- ansible/plugins/lookup/first_found.py +20 -14
- ansible/plugins/lookup/ini.py +6 -3
- ansible/plugins/lookup/lines.py +2 -1
- ansible/plugins/lookup/password.py +7 -7
- ansible/plugins/lookup/pipe.py +1 -0
- ansible/plugins/lookup/random_choice.py +2 -2
- ansible/plugins/lookup/sequence.py +1 -1
- ansible/plugins/lookup/subelements.py +2 -2
- ansible/plugins/lookup/template.py +4 -1
- ansible/plugins/lookup/unvault.py +4 -1
- ansible/plugins/lookup/url.py +6 -6
- ansible/plugins/lookup/varnames.py +1 -1
- ansible/plugins/netconf/__init__.py +3 -3
- ansible/plugins/shell/__init__.py +1 -1
- ansible/plugins/shell/cmd.py +7 -7
- ansible/plugins/shell/powershell.py +1 -1
- ansible/plugins/strategy/__init__.py +8 -10
- ansible/plugins/strategy/free.py +1 -1
- ansible/plugins/strategy/linear.py +3 -3
- ansible/plugins/terminal/__init__.py +2 -2
- ansible/plugins/test/abs.yml +1 -1
- ansible/plugins/test/all.yml +1 -1
- ansible/plugins/test/any.yml +1 -1
- ansible/plugins/test/change.yml +2 -2
- ansible/plugins/test/changed.yml +2 -2
- ansible/plugins/test/contains.yml +1 -1
- ansible/plugins/test/core.py +1 -1
- ansible/plugins/test/directory.yml +1 -1
- ansible/plugins/test/exists.yml +3 -2
- ansible/plugins/test/failed.yml +2 -2
- ansible/plugins/test/failure.yml +2 -2
- ansible/plugins/test/falsy.yml +2 -2
- ansible/plugins/test/file.yml +1 -1
- ansible/plugins/test/finished.yml +2 -2
- ansible/plugins/test/is_abs.yml +1 -1
- ansible/plugins/test/is_dir.yml +1 -1
- ansible/plugins/test/is_file.yml +1 -1
- ansible/plugins/test/is_link.yml +1 -1
- ansible/plugins/test/is_mount.yml +1 -1
- ansible/plugins/test/is_same_file.yml +1 -1
- ansible/plugins/test/isnan.yml +1 -1
- ansible/plugins/test/issubset.yml +1 -2
- ansible/plugins/test/issuperset.yml +1 -2
- ansible/plugins/test/link.yml +1 -1
- ansible/plugins/test/link_exists.yml +1 -1
- ansible/plugins/test/match.yml +2 -2
- ansible/plugins/test/mount.yml +1 -1
- ansible/plugins/test/nan.yml +1 -1
- ansible/plugins/test/reachable.yml +2 -2
- ansible/plugins/test/regex.yml +1 -1
- ansible/plugins/test/same_file.yml +1 -1
- ansible/plugins/test/search.yml +2 -2
- ansible/plugins/test/skip.yml +3 -3
- ansible/plugins/test/skipped.yml +3 -3
- ansible/plugins/test/started.yml +2 -2
- ansible/plugins/test/subset.yml +1 -2
- ansible/plugins/test/succeeded.yml +2 -2
- ansible/plugins/test/success.yml +2 -2
- ansible/plugins/test/successful.yml +2 -2
- ansible/plugins/test/superset.yml +1 -2
- ansible/plugins/test/truthy.yml +3 -3
- ansible/plugins/test/unreachable.yml +2 -2
- ansible/plugins/test/uri.yml +1 -1
- ansible/plugins/test/url.yml +1 -1
- ansible/plugins/test/urn.yml +1 -1
- ansible/plugins/test/vault_encrypted.yml +1 -1
- ansible/plugins/test/version.yml +7 -7
- ansible/plugins/test/version_compare.yml +7 -7
- ansible/plugins/vars/host_group_vars.py +1 -1
- ansible/release.py +2 -2
- ansible/template/__init__.py +24 -26
- ansible/template/native_helpers.py +1 -1
- ansible/template/vars.py +1 -1
- ansible/utils/_junit_xml.py +1 -1
- ansible/utils/cmd_functions.py +1 -1
- ansible/utils/collection_loader/_collection_finder.py +12 -1
- ansible/utils/display.py +113 -62
- ansible/utils/encrypt.py +11 -14
- ansible/utils/hashing.py +1 -1
- ansible/utils/jsonrpc.py +1 -1
- ansible/utils/path.py +1 -1
- ansible/utils/plugin_docs.py +1 -1
- ansible/utils/py3compat.py +1 -1
- ansible/utils/shlex.py +2 -10
- ansible/utils/ssh_functions.py +5 -4
- ansible/utils/unicode.py +1 -1
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/utils/vars.py +4 -29
- ansible/vars/hostvars.py +1 -2
- ansible/vars/manager.py +13 -9
- ansible/vars/plugins.py +2 -2
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/COPYING +4 -5
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/METADATA +2 -4
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/RECORD +424 -425
- ansible_test/_data/completion/docker.txt +9 -9
- ansible_test/_data/completion/remote.txt +4 -7
- ansible_test/_data/completion/windows.txt +0 -2
- ansible_test/_data/requirements/ansible-test.txt +2 -1
- ansible_test/_data/requirements/ansible.txt +0 -3
- ansible_test/_data/requirements/constraints.txt +0 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -5
- ansible_test/_data/requirements/sanity.changelog.in +1 -2
- ansible_test/_data/requirements/sanity.changelog.txt +4 -6
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -4
- ansible_test/_data/requirements/sanity.import.txt +1 -3
- ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -3
- ansible_test/_data/requirements/sanity.mypy.txt +12 -12
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +6 -12
- ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -3
- ansible_test/_data/requirements/sanity.validate-modules.in +1 -1
- ansible_test/_data/requirements/sanity.validate-modules.txt +3 -5
- ansible_test/_data/requirements/sanity.yamllint.txt +3 -5
- ansible_test/_data/requirements/units.txt +0 -1
- ansible_test/_internal/ci/azp.py +4 -4
- ansible_test/_internal/cli/environments.py +0 -13
- ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +4 -4
- ansible_test/_internal/commands/coverage/combine.py +1 -1
- ansible_test/_internal/commands/integration/cloud/acme.py +6 -8
- ansible_test/_internal/commands/integration/cloud/cs.py +4 -9
- ansible_test/_internal/commands/integration/cloud/galaxy.py +103 -96
- ansible_test/_internal/commands/integration/cloud/httptester.py +0 -3
- ansible_test/_internal/commands/integration/cloud/nios.py +7 -9
- ansible_test/_internal/commands/integration/cloud/openshift.py +2 -7
- ansible_test/_internal/commands/integration/cloud/vcenter.py +11 -95
- ansible_test/_internal/commands/sanity/__init__.py +10 -0
- ansible_test/_internal/commands/sanity/import.py +8 -2
- ansible_test/_internal/commands/sanity/pylint.py +27 -1
- ansible_test/_internal/commands/units/__init__.py +2 -1
- ansible_test/_internal/config.py +0 -7
- ansible_test/_internal/containers.py +11 -56
- ansible_test/_internal/core_ci.py +0 -7
- ansible_test/_internal/coverage_util.py +8 -3
- ansible_test/_internal/delegation.py +0 -1
- ansible_test/_internal/diff.py +1 -1
- ansible_test/_internal/docker_util.py +9 -2
- ansible_test/_internal/host_profiles.py +6 -6
- ansible_test/_internal/http.py +1 -1
- ansible_test/_internal/junit_xml.py +1 -1
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/python_requirements.py +3 -8
- ansible_test/_internal/util.py +1 -6
- ansible_test/_util/controller/sanity/code-smell/no-get-exception.json +4 -0
- ansible_test/_util/controller/sanity/code-smell/replace-urlopen.json +4 -0
- ansible_test/_util/controller/sanity/code-smell/use-compat-six.json +4 -0
- ansible_test/_util/controller/sanity/mypy/ansible-core.ini +3 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +2 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +0 -1
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +172 -10
- ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +13 -2
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +7 -1
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +6 -6
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -1
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -1
- ansible_test/_util/controller/sanity/yamllint/yamllinter.py +3 -3
- ansible_test/_util/controller/tools/collection_detail.py +2 -2
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/pytest/plugins/ansible_forked.py +103 -0
- ansible_test/_util/target/sanity/import/importer.py +0 -8
- ansible_test/_util/target/setup/bootstrap.sh +36 -16
- ansible_test/_util/target/setup/quiet_pip.py +0 -4
- ansible/modules/_include.py +0 -80
- ansible_test/_internal/commands/integration/cloud/foreman.py +0 -102
- ansible_test/_util/target/setup/ConfigureRemotingForAnsible.ps1 +0 -435
- {ansible_core-2.15.4rc1.data → ansible_core-2.16.0b2.data}/scripts/ansible-test +0 -0
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/WHEEL +0 -0
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.15.4rc1.dist-info → ansible_core-2.16.0b2.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Copyright (c) 2017 Ansible Project
|
|
5
5
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
6
6
|
|
|
7
|
-
from __future__ import (absolute_import, division, print_function)
|
|
7
|
+
from __future__ import (annotations, absolute_import, division, print_function)
|
|
8
8
|
__metaclass__ = type
|
|
9
9
|
|
|
10
10
|
DOCUMENTATION = '''
|
|
@@ -20,7 +20,7 @@ DOCUMENTATION = '''
|
|
|
20
20
|
- connection_pipelining
|
|
21
21
|
version_added: historical
|
|
22
22
|
notes:
|
|
23
|
-
- Many options default to
|
|
23
|
+
- Many options default to V(None) here but that only means we do not override the SSH tool's defaults and/or configuration.
|
|
24
24
|
For example, if you specify the port in this plugin it will override any C(Port) entry in your C(.ssh/config).
|
|
25
25
|
- The ssh CLI tool uses return code 255 as a 'connection error', this can conflict with commands/tools that
|
|
26
26
|
also return 255 as an error code and will look like an 'unreachable' condition or 'connection error' to this plugin.
|
|
@@ -55,7 +55,7 @@ DOCUMENTATION = '''
|
|
|
55
55
|
- name: ansible_ssh_host_key_checking
|
|
56
56
|
version_added: '2.5'
|
|
57
57
|
password:
|
|
58
|
-
description: Authentication password for the
|
|
58
|
+
description: Authentication password for the O(remote_user). Can be supplied as CLI option.
|
|
59
59
|
type: string
|
|
60
60
|
vars:
|
|
61
61
|
- name: ansible_password
|
|
@@ -105,7 +105,7 @@ DOCUMENTATION = '''
|
|
|
105
105
|
ssh_executable:
|
|
106
106
|
default: ssh
|
|
107
107
|
description:
|
|
108
|
-
- This defines the location of the SSH binary. It defaults to
|
|
108
|
+
- This defines the location of the SSH binary. It defaults to V(ssh) which will use the first SSH binary available in $PATH.
|
|
109
109
|
- This option is usually not required, it might be useful when access to system SSH is restricted,
|
|
110
110
|
or when using SSH wrappers to connect to remote hosts.
|
|
111
111
|
type: string
|
|
@@ -120,7 +120,7 @@ DOCUMENTATION = '''
|
|
|
120
120
|
sftp_executable:
|
|
121
121
|
default: sftp
|
|
122
122
|
description:
|
|
123
|
-
- This defines the location of the sftp binary. It defaults to
|
|
123
|
+
- This defines the location of the sftp binary. It defaults to V(sftp) which will use the first binary available in $PATH.
|
|
124
124
|
type: string
|
|
125
125
|
env: [{name: ANSIBLE_SFTP_EXECUTABLE}]
|
|
126
126
|
ini:
|
|
@@ -132,7 +132,7 @@ DOCUMENTATION = '''
|
|
|
132
132
|
scp_executable:
|
|
133
133
|
default: scp
|
|
134
134
|
description:
|
|
135
|
-
- This defines the location of the scp binary. It defaults to
|
|
135
|
+
- This defines the location of the scp binary. It defaults to V(scp) which will use the first binary available in $PATH.
|
|
136
136
|
type: string
|
|
137
137
|
env: [{name: ANSIBLE_SCP_EXECUTABLE}]
|
|
138
138
|
ini:
|
|
@@ -319,16 +319,16 @@ DOCUMENTATION = '''
|
|
|
319
319
|
version_added: '2.12'
|
|
320
320
|
scp_if_ssh:
|
|
321
321
|
deprecated:
|
|
322
|
-
why: In favor of the
|
|
322
|
+
why: In favor of the O(ssh_transfer_method) option.
|
|
323
323
|
version: "2.17"
|
|
324
|
-
alternatives: ssh_transfer_method
|
|
324
|
+
alternatives: O(ssh_transfer_method)
|
|
325
325
|
default: smart
|
|
326
326
|
description:
|
|
327
327
|
- "Preferred method to use when transferring files over SSH."
|
|
328
|
-
- When set to
|
|
329
|
-
- If set to
|
|
330
|
-
- For OpenSSH >=9.0 you must add an additional option to enable scp (scp_extra_args="-O")
|
|
331
|
-
- This setting will overridden by ssh_transfer_method if set.
|
|
328
|
+
- When set to V(smart), Ansible will try them until one succeeds or they all fail.
|
|
329
|
+
- If set to V(True), it will force 'scp', if V(False) it will use 'sftp'.
|
|
330
|
+
- For OpenSSH >=9.0 you must add an additional option to enable scp (C(scp_extra_args="-O"))
|
|
331
|
+
- This setting will overridden by O(ssh_transfer_method) if set.
|
|
332
332
|
env: [{name: ANSIBLE_SCP_IF_SSH}]
|
|
333
333
|
ini:
|
|
334
334
|
- {key: scp_if_ssh, section: ssh_connection}
|
|
@@ -381,15 +381,18 @@ DOCUMENTATION = '''
|
|
|
381
381
|
- name: ansible_ssh_pkcs11_provider
|
|
382
382
|
'''
|
|
383
383
|
|
|
384
|
+
import collections.abc as c
|
|
384
385
|
import errno
|
|
385
386
|
import fcntl
|
|
386
387
|
import hashlib
|
|
388
|
+
import io
|
|
387
389
|
import os
|
|
388
390
|
import pty
|
|
389
391
|
import re
|
|
390
392
|
import shlex
|
|
391
393
|
import subprocess
|
|
392
394
|
import time
|
|
395
|
+
import typing as t
|
|
393
396
|
|
|
394
397
|
from functools import wraps
|
|
395
398
|
from ansible.errors import (
|
|
@@ -401,7 +404,7 @@ from ansible.errors import (
|
|
|
401
404
|
from ansible.errors import AnsibleOptionsError
|
|
402
405
|
from ansible.module_utils.compat import selectors
|
|
403
406
|
from ansible.module_utils.six import PY3, text_type, binary_type
|
|
404
|
-
from ansible.module_utils.
|
|
407
|
+
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
405
408
|
from ansible.module_utils.parsing.convert_bool import BOOLEANS, boolean
|
|
406
409
|
from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
|
407
410
|
from ansible.plugins.shell.powershell import _parse_clixml
|
|
@@ -410,6 +413,8 @@ from ansible.utils.path import unfrackpath, makedirs_safe
|
|
|
410
413
|
|
|
411
414
|
display = Display()
|
|
412
415
|
|
|
416
|
+
P = t.ParamSpec('P')
|
|
417
|
+
|
|
413
418
|
# error messages that indicate 255 return code is not from ssh itself.
|
|
414
419
|
b_NOT_SSH_ERRORS = (b'Traceback (most recent call last):', # Python-2.6 when there's an exception
|
|
415
420
|
# while invoking a script via -m
|
|
@@ -427,7 +432,14 @@ class AnsibleControlPersistBrokenPipeError(AnsibleError):
|
|
|
427
432
|
pass
|
|
428
433
|
|
|
429
434
|
|
|
430
|
-
def _handle_error(
|
|
435
|
+
def _handle_error(
|
|
436
|
+
remaining_retries: int,
|
|
437
|
+
command: bytes,
|
|
438
|
+
return_tuple: tuple[int, bytes, bytes],
|
|
439
|
+
no_log: bool,
|
|
440
|
+
host: str,
|
|
441
|
+
display: Display = display,
|
|
442
|
+
) -> None:
|
|
431
443
|
|
|
432
444
|
# sshpass errors
|
|
433
445
|
if command == b'sshpass':
|
|
@@ -483,7 +495,9 @@ def _handle_error(remaining_retries, command, return_tuple, no_log, host, displa
|
|
|
483
495
|
display.vvv(msg, host=host)
|
|
484
496
|
|
|
485
497
|
|
|
486
|
-
def _ssh_retry(
|
|
498
|
+
def _ssh_retry(
|
|
499
|
+
func: c.Callable[t.Concatenate[Connection, P], tuple[int, bytes, bytes]],
|
|
500
|
+
) -> c.Callable[t.Concatenate[Connection, P], tuple[int, bytes, bytes]]:
|
|
487
501
|
"""
|
|
488
502
|
Decorator to retry ssh/scp/sftp in the case of a connection failure
|
|
489
503
|
|
|
@@ -496,12 +510,12 @@ def _ssh_retry(func):
|
|
|
496
510
|
* retries limit reached
|
|
497
511
|
"""
|
|
498
512
|
@wraps(func)
|
|
499
|
-
def wrapped(self, *args, **kwargs):
|
|
513
|
+
def wrapped(self: Connection, *args: P.args, **kwargs: P.kwargs) -> tuple[int, bytes, bytes]:
|
|
500
514
|
remaining_tries = int(self.get_option('reconnection_retries')) + 1
|
|
501
515
|
cmd_summary = u"%s..." % to_text(args[0])
|
|
502
516
|
conn_password = self.get_option('password') or self._play_context.password
|
|
503
517
|
for attempt in range(remaining_tries):
|
|
504
|
-
cmd = args[0]
|
|
518
|
+
cmd = t.cast(list[bytes], args[0])
|
|
505
519
|
if attempt != 0 and conn_password and isinstance(cmd, list):
|
|
506
520
|
# If this is a retry, the fd/pipe for sshpass is closed, and we need a new one
|
|
507
521
|
self.sshpass_pipe = os.pipe()
|
|
@@ -514,13 +528,13 @@ def _ssh_retry(func):
|
|
|
514
528
|
if self._play_context.no_log:
|
|
515
529
|
display.vvv(u'rc=%s, stdout and stderr censored due to no log' % return_tuple[0], host=self.host)
|
|
516
530
|
else:
|
|
517
|
-
display.vvv(return_tuple, host=self.host)
|
|
531
|
+
display.vvv(str(return_tuple), host=self.host)
|
|
518
532
|
# 0 = success
|
|
519
533
|
# 1-254 = remote command return code
|
|
520
534
|
# 255 could be a failure from the ssh command itself
|
|
521
535
|
except (AnsibleControlPersistBrokenPipeError):
|
|
522
536
|
# Retry one more time because of the ControlPersist broken pipe (see #16731)
|
|
523
|
-
cmd = args[0]
|
|
537
|
+
cmd = t.cast(list[bytes], args[0])
|
|
524
538
|
if conn_password and isinstance(cmd, list):
|
|
525
539
|
# This is a retry, so the fd/pipe for sshpass is closed, and we need a new one
|
|
526
540
|
self.sshpass_pipe = os.pipe()
|
|
@@ -568,15 +582,15 @@ class Connection(ConnectionBase):
|
|
|
568
582
|
transport = 'ssh'
|
|
569
583
|
has_pipelining = True
|
|
570
584
|
|
|
571
|
-
def __init__(self, *args, **kwargs):
|
|
585
|
+
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
|
|
572
586
|
super(Connection, self).__init__(*args, **kwargs)
|
|
573
587
|
|
|
574
588
|
# TODO: all should come from get_option(), but not might be set at this point yet
|
|
575
589
|
self.host = self._play_context.remote_addr
|
|
576
590
|
self.port = self._play_context.port
|
|
577
591
|
self.user = self._play_context.remote_user
|
|
578
|
-
self.control_path = None
|
|
579
|
-
self.control_path_dir = None
|
|
592
|
+
self.control_path: str | None = None
|
|
593
|
+
self.control_path_dir: str | None = None
|
|
580
594
|
|
|
581
595
|
# Windows operates differently from a POSIX connection/shell plugin,
|
|
582
596
|
# we need to set various properties to ensure SSH on Windows continues
|
|
@@ -591,11 +605,17 @@ class Connection(ConnectionBase):
|
|
|
591
605
|
# put_file, and fetch_file methods, so we don't need to do any connection
|
|
592
606
|
# management here.
|
|
593
607
|
|
|
594
|
-
def _connect(self):
|
|
608
|
+
def _connect(self) -> Connection:
|
|
595
609
|
return self
|
|
596
610
|
|
|
597
611
|
@staticmethod
|
|
598
|
-
def _create_control_path(
|
|
612
|
+
def _create_control_path(
|
|
613
|
+
host: str | None,
|
|
614
|
+
port: int | None,
|
|
615
|
+
user: str | None,
|
|
616
|
+
connection: ConnectionBase | None = None,
|
|
617
|
+
pid: int | None = None,
|
|
618
|
+
) -> str:
|
|
599
619
|
'''Make a hash for the controlpath based on con attributes'''
|
|
600
620
|
pstring = '%s-%s-%s' % (host, port, user)
|
|
601
621
|
if connection:
|
|
@@ -609,7 +629,7 @@ class Connection(ConnectionBase):
|
|
|
609
629
|
return cpath
|
|
610
630
|
|
|
611
631
|
@staticmethod
|
|
612
|
-
def _sshpass_available():
|
|
632
|
+
def _sshpass_available() -> bool:
|
|
613
633
|
global SSHPASS_AVAILABLE
|
|
614
634
|
|
|
615
635
|
# We test once if sshpass is available, and remember the result. It
|
|
@@ -627,7 +647,7 @@ class Connection(ConnectionBase):
|
|
|
627
647
|
return SSHPASS_AVAILABLE
|
|
628
648
|
|
|
629
649
|
@staticmethod
|
|
630
|
-
def _persistence_controls(b_command):
|
|
650
|
+
def _persistence_controls(b_command: list[bytes]) -> tuple[bool, bool]:
|
|
631
651
|
'''
|
|
632
652
|
Takes a command array and scans it for ControlPersist and ControlPath
|
|
633
653
|
settings and returns two booleans indicating whether either was found.
|
|
@@ -646,7 +666,7 @@ class Connection(ConnectionBase):
|
|
|
646
666
|
|
|
647
667
|
return controlpersist, controlpath
|
|
648
668
|
|
|
649
|
-
def _add_args(self, b_command, b_args, explanation):
|
|
669
|
+
def _add_args(self, b_command: list[bytes], b_args: t.Iterable[bytes], explanation: str) -> None:
|
|
650
670
|
"""
|
|
651
671
|
Adds arguments to the ssh command and displays a caller-supplied explanation of why.
|
|
652
672
|
|
|
@@ -662,7 +682,7 @@ class Connection(ConnectionBase):
|
|
|
662
682
|
display.vvvvv(u'SSH: %s: (%s)' % (explanation, ')('.join(to_text(a) for a in b_args)), host=self.host)
|
|
663
683
|
b_command += b_args
|
|
664
684
|
|
|
665
|
-
def _build_command(self, binary, subsystem, *other_args):
|
|
685
|
+
def _build_command(self, binary: str, subsystem: str, *other_args: bytes | str) -> list[bytes]:
|
|
666
686
|
'''
|
|
667
687
|
Takes a executable (ssh, scp, sftp or wrapper) and optional extra arguments and returns the remote command
|
|
668
688
|
wrapped in local ssh shell commands and ready for execution.
|
|
@@ -719,6 +739,7 @@ class Connection(ConnectionBase):
|
|
|
719
739
|
# be disabled if the client side doesn't support the option. However,
|
|
720
740
|
# sftp batch mode does not prompt for passwords so it must be disabled
|
|
721
741
|
# if not using controlpersist and using sshpass
|
|
742
|
+
b_args: t.Iterable[bytes]
|
|
722
743
|
if subsystem == 'sftp' and self.get_option('sftp_batch_mode'):
|
|
723
744
|
if conn_password:
|
|
724
745
|
b_args = [b'-o', b'BatchMode=no']
|
|
@@ -818,7 +839,7 @@ class Connection(ConnectionBase):
|
|
|
818
839
|
|
|
819
840
|
return b_command
|
|
820
841
|
|
|
821
|
-
def _send_initial_data(self, fh, in_data, ssh_process):
|
|
842
|
+
def _send_initial_data(self, fh: io.IOBase, in_data: bytes, ssh_process: subprocess.Popen) -> None:
|
|
822
843
|
'''
|
|
823
844
|
Writes initial data to the stdin filehandle of the subprocess and closes
|
|
824
845
|
it. (The handle must be closed; otherwise, for example, "sftp -b -" will
|
|
@@ -845,7 +866,7 @@ class Connection(ConnectionBase):
|
|
|
845
866
|
|
|
846
867
|
# Used by _run() to kill processes on failures
|
|
847
868
|
@staticmethod
|
|
848
|
-
def _terminate_process(p):
|
|
869
|
+
def _terminate_process(p: subprocess.Popen) -> None:
|
|
849
870
|
""" Terminate a process, ignoring errors """
|
|
850
871
|
try:
|
|
851
872
|
p.terminate()
|
|
@@ -854,7 +875,7 @@ class Connection(ConnectionBase):
|
|
|
854
875
|
|
|
855
876
|
# This is separate from _run() because we need to do the same thing for stdout
|
|
856
877
|
# and stderr.
|
|
857
|
-
def _examine_output(self, source, state, b_chunk, sudoable):
|
|
878
|
+
def _examine_output(self, source: str, state: str, b_chunk: bytes, sudoable: bool) -> tuple[bytes, bytes]:
|
|
858
879
|
'''
|
|
859
880
|
Takes a string, extracts complete lines from it, tests to see if they
|
|
860
881
|
are a prompt, error message, etc., and sets appropriate flags in self.
|
|
@@ -903,7 +924,7 @@ class Connection(ConnectionBase):
|
|
|
903
924
|
|
|
904
925
|
return b''.join(output), remainder
|
|
905
926
|
|
|
906
|
-
def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):
|
|
927
|
+
def _bare_run(self, cmd: list[bytes], in_data: bytes | None, sudoable: bool = True, checkrc: bool = True) -> tuple[int, bytes, bytes]:
|
|
907
928
|
'''
|
|
908
929
|
Starts the command and communicates with it until it ends.
|
|
909
930
|
'''
|
|
@@ -949,7 +970,7 @@ class Connection(ConnectionBase):
|
|
|
949
970
|
else:
|
|
950
971
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
951
972
|
stderr=subprocess.PIPE)
|
|
952
|
-
stdin = p.stdin
|
|
973
|
+
stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
|
|
953
974
|
except (OSError, IOError) as e:
|
|
954
975
|
raise AnsibleError('Unable to execute ssh command line on a controller due to: %s' % to_native(e))
|
|
955
976
|
|
|
@@ -1199,13 +1220,13 @@ class Connection(ConnectionBase):
|
|
|
1199
1220
|
return (p.returncode, b_stdout, b_stderr)
|
|
1200
1221
|
|
|
1201
1222
|
@_ssh_retry
|
|
1202
|
-
def _run(self, cmd, in_data, sudoable=True, checkrc=True):
|
|
1223
|
+
def _run(self, cmd: list[bytes], in_data: bytes | None, sudoable: bool = True, checkrc: bool = True) -> tuple[int, bytes, bytes]:
|
|
1203
1224
|
"""Wrapper around _bare_run that retries the connection
|
|
1204
1225
|
"""
|
|
1205
1226
|
return self._bare_run(cmd, in_data, sudoable=sudoable, checkrc=checkrc)
|
|
1206
1227
|
|
|
1207
1228
|
@_ssh_retry
|
|
1208
|
-
def _file_transport_command(self, in_path, out_path, sftp_action):
|
|
1229
|
+
def _file_transport_command(self, in_path: str, out_path: str, sftp_action: str) -> tuple[int, bytes, bytes]:
|
|
1209
1230
|
# scp and sftp require square brackets for IPv6 addresses, but
|
|
1210
1231
|
# accept them for hostnames and IPv4 addresses too.
|
|
1211
1232
|
host = '[%s]' % self.host
|
|
@@ -1293,7 +1314,7 @@ class Connection(ConnectionBase):
|
|
|
1293
1314
|
raise AnsibleError("failed to transfer file to %s %s:\n%s\n%s" %
|
|
1294
1315
|
(to_native(in_path), to_native(out_path), to_native(stdout), to_native(stderr)))
|
|
1295
1316
|
|
|
1296
|
-
def _escape_win_path(self, path):
|
|
1317
|
+
def _escape_win_path(self, path: str) -> str:
|
|
1297
1318
|
""" converts a Windows path to one that's supported by SFTP and SCP """
|
|
1298
1319
|
# If using a root path then we need to start with /
|
|
1299
1320
|
prefix = ""
|
|
@@ -1306,7 +1327,7 @@ class Connection(ConnectionBase):
|
|
|
1306
1327
|
#
|
|
1307
1328
|
# Main public methods
|
|
1308
1329
|
#
|
|
1309
|
-
def exec_command(self, cmd, in_data=None, sudoable=True):
|
|
1330
|
+
def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: bool = True) -> tuple[int, bytes, bytes]:
|
|
1310
1331
|
''' run a command on the remote host '''
|
|
1311
1332
|
|
|
1312
1333
|
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
|
@@ -1323,8 +1344,10 @@ class Connection(ConnectionBase):
|
|
|
1323
1344
|
|
|
1324
1345
|
# Make sure our first command is to set the console encoding to
|
|
1325
1346
|
# utf-8, this must be done via chcp to get utf-8 (65001)
|
|
1326
|
-
|
|
1327
|
-
|
|
1347
|
+
# union-attr ignores rely on internal powershell shell plugin details,
|
|
1348
|
+
# this should be fixed at a future point in time.
|
|
1349
|
+
cmd_parts = ["chcp.com", "65001", self._shell._SHELL_REDIRECT_ALLNULL, self._shell._SHELL_AND] # type: ignore[union-attr]
|
|
1350
|
+
cmd_parts.extend(self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)) # type: ignore[union-attr]
|
|
1328
1351
|
cmd = ' '.join(cmd_parts)
|
|
1329
1352
|
|
|
1330
1353
|
# we can only use tty when we are not pipelining the modules. piping
|
|
@@ -1338,6 +1361,7 @@ class Connection(ConnectionBase):
|
|
|
1338
1361
|
# to disable it as a troubleshooting method.
|
|
1339
1362
|
use_tty = self.get_option('use_tty')
|
|
1340
1363
|
|
|
1364
|
+
args: tuple[str, ...]
|
|
1341
1365
|
if not in_data and sudoable and use_tty:
|
|
1342
1366
|
args = ('-tt', self.host, cmd)
|
|
1343
1367
|
else:
|
|
@@ -1352,7 +1376,7 @@ class Connection(ConnectionBase):
|
|
|
1352
1376
|
|
|
1353
1377
|
return (returncode, stdout, stderr)
|
|
1354
1378
|
|
|
1355
|
-
def put_file(self, in_path, out_path):
|
|
1379
|
+
def put_file(self, in_path: str, out_path: str) -> tuple[int, bytes, bytes]: # type: ignore[override] # Used by tests and would break API
|
|
1356
1380
|
''' transfer a file from local to remote '''
|
|
1357
1381
|
|
|
1358
1382
|
super(Connection, self).put_file(in_path, out_path)
|
|
@@ -1368,7 +1392,7 @@ class Connection(ConnectionBase):
|
|
|
1368
1392
|
|
|
1369
1393
|
return self._file_transport_command(in_path, out_path, 'put')
|
|
1370
1394
|
|
|
1371
|
-
def fetch_file(self, in_path, out_path):
|
|
1395
|
+
def fetch_file(self, in_path: str, out_path: str) -> tuple[int, bytes, bytes]: # type: ignore[override] # Used by tests and would break API
|
|
1372
1396
|
''' fetch a file from remote to local '''
|
|
1373
1397
|
|
|
1374
1398
|
super(Connection, self).fetch_file(in_path, out_path)
|
|
@@ -1383,7 +1407,7 @@ class Connection(ConnectionBase):
|
|
|
1383
1407
|
|
|
1384
1408
|
return self._file_transport_command(in_path, out_path, 'get')
|
|
1385
1409
|
|
|
1386
|
-
def reset(self):
|
|
1410
|
+
def reset(self) -> None:
|
|
1387
1411
|
|
|
1388
1412
|
run_reset = False
|
|
1389
1413
|
self.host = self.get_option('host') or self._play_context.remote_addr
|
|
@@ -1412,5 +1436,5 @@ class Connection(ConnectionBase):
|
|
|
1412
1436
|
|
|
1413
1437
|
self.close()
|
|
1414
1438
|
|
|
1415
|
-
def close(self):
|
|
1439
|
+
def close(self) -> None:
|
|
1416
1440
|
self._connected = False
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Copyright (c) 2017 Ansible Project
|
|
3
3
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
4
4
|
|
|
5
|
-
from __future__ import (absolute_import, division, print_function)
|
|
5
|
+
from __future__ import (annotations, absolute_import, division, print_function)
|
|
6
6
|
__metaclass__ = type
|
|
7
7
|
|
|
8
8
|
DOCUMENTATION = """
|
|
@@ -39,7 +39,7 @@ DOCUMENTATION = """
|
|
|
39
39
|
- name: remote_user
|
|
40
40
|
type: str
|
|
41
41
|
remote_password:
|
|
42
|
-
description: Authentication password for the
|
|
42
|
+
description: Authentication password for the O(remote_user). Can be supplied as CLI option.
|
|
43
43
|
vars:
|
|
44
44
|
- name: ansible_password
|
|
45
45
|
- name: ansible_winrm_pass
|
|
@@ -61,8 +61,8 @@ DOCUMENTATION = """
|
|
|
61
61
|
scheme:
|
|
62
62
|
description:
|
|
63
63
|
- URI scheme to use
|
|
64
|
-
- If not set, then will default to
|
|
65
|
-
|
|
64
|
+
- If not set, then will default to V(https) or V(http) if O(port) is
|
|
65
|
+
V(5985).
|
|
66
66
|
choices: [http, https]
|
|
67
67
|
vars:
|
|
68
68
|
- name: ansible_winrm_scheme
|
|
@@ -119,7 +119,7 @@ DOCUMENTATION = """
|
|
|
119
119
|
- The managed option means Ansible will obtain kerberos ticket.
|
|
120
120
|
- While the manual one means a ticket must already have been obtained by the user.
|
|
121
121
|
- If having issues with Ansible freezing when trying to obtain the
|
|
122
|
-
Kerberos ticket, you can either set this to
|
|
122
|
+
Kerberos ticket, you can either set this to V(manual) and obtain
|
|
123
123
|
it outside Ansible or install C(pexpect) through pip and try
|
|
124
124
|
again.
|
|
125
125
|
choices: [managed, manual]
|
|
@@ -170,6 +170,8 @@ import json
|
|
|
170
170
|
import tempfile
|
|
171
171
|
import shlex
|
|
172
172
|
import subprocess
|
|
173
|
+
import time
|
|
174
|
+
import typing as t
|
|
173
175
|
|
|
174
176
|
from inspect import getfullargspec
|
|
175
177
|
from urllib.parse import urlunsplit
|
|
@@ -186,10 +188,11 @@ from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
|
|
186
188
|
from ansible.errors import AnsibleFileNotFound
|
|
187
189
|
from ansible.module_utils.json_utils import _filter_non_json_lines
|
|
188
190
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
189
|
-
from ansible.module_utils.
|
|
191
|
+
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
|
190
192
|
from ansible.module_utils.six import binary_type
|
|
191
193
|
from ansible.plugins.connection import ConnectionBase
|
|
192
194
|
from ansible.plugins.shell.powershell import _parse_clixml
|
|
195
|
+
from ansible.plugins.shell.powershell import ShellBase as PowerShellBase
|
|
193
196
|
from ansible.utils.hashing import secure_hash
|
|
194
197
|
from ansible.utils.display import Display
|
|
195
198
|
|
|
@@ -197,6 +200,7 @@ from ansible.utils.display import Display
|
|
|
197
200
|
try:
|
|
198
201
|
import winrm
|
|
199
202
|
from winrm import Response
|
|
203
|
+
from winrm.exceptions import WinRMError, WinRMOperationTimeoutError
|
|
200
204
|
from winrm.protocol import Protocol
|
|
201
205
|
import requests.exceptions
|
|
202
206
|
HAS_WINRM = True
|
|
@@ -245,14 +249,15 @@ class Connection(ConnectionBase):
|
|
|
245
249
|
has_pipelining = True
|
|
246
250
|
allow_extras = True
|
|
247
251
|
|
|
248
|
-
def __init__(self, *args, **kwargs):
|
|
252
|
+
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
|
|
249
253
|
|
|
250
254
|
self.always_pipeline_modules = True
|
|
251
255
|
self.has_native_async = True
|
|
252
256
|
|
|
253
|
-
self.protocol = None
|
|
254
|
-
self.shell_id = None
|
|
257
|
+
self.protocol: winrm.Protocol | None = None
|
|
258
|
+
self.shell_id: str | None = None
|
|
255
259
|
self.delegate = None
|
|
260
|
+
self._shell: PowerShellBase
|
|
256
261
|
self._shell_type = 'powershell'
|
|
257
262
|
|
|
258
263
|
super(Connection, self).__init__(*args, **kwargs)
|
|
@@ -262,7 +267,7 @@ class Connection(ConnectionBase):
|
|
|
262
267
|
logging.getLogger('requests_kerberos').setLevel(logging.INFO)
|
|
263
268
|
logging.getLogger('urllib3').setLevel(logging.INFO)
|
|
264
269
|
|
|
265
|
-
def _build_winrm_kwargs(self):
|
|
270
|
+
def _build_winrm_kwargs(self) -> None:
|
|
266
271
|
# this used to be in set_options, as win_reboot needs to be able to
|
|
267
272
|
# override the conn timeout, we need to be able to build the args
|
|
268
273
|
# after setting individual options. This is called by _connect before
|
|
@@ -336,7 +341,7 @@ class Connection(ConnectionBase):
|
|
|
336
341
|
|
|
337
342
|
# Until pykerberos has enough goodies to implement a rudimentary kinit/klist, simplest way is to let each connection
|
|
338
343
|
# auth itself with a private CCACHE.
|
|
339
|
-
def _kerb_auth(self, principal, password):
|
|
344
|
+
def _kerb_auth(self, principal: str, password: str) -> None:
|
|
340
345
|
if password is None:
|
|
341
346
|
password = ""
|
|
342
347
|
|
|
@@ -401,8 +406,8 @@ class Connection(ConnectionBase):
|
|
|
401
406
|
rc = child.exitstatus
|
|
402
407
|
else:
|
|
403
408
|
proc_mechanism = "subprocess"
|
|
404
|
-
|
|
405
|
-
|
|
409
|
+
b_password = to_bytes(password, encoding='utf-8',
|
|
410
|
+
errors='surrogate_or_strict')
|
|
406
411
|
|
|
407
412
|
display.vvvv("calling kinit with subprocess for principal %s"
|
|
408
413
|
% principal)
|
|
@@ -417,7 +422,7 @@ class Connection(ConnectionBase):
|
|
|
417
422
|
"'%s': %s" % (self._kinit_cmd, to_native(err))
|
|
418
423
|
raise AnsibleConnectionFailure(err_msg)
|
|
419
424
|
|
|
420
|
-
stdout, stderr = p.communicate(
|
|
425
|
+
stdout, stderr = p.communicate(b_password + b'\n')
|
|
421
426
|
rc = p.returncode != 0
|
|
422
427
|
|
|
423
428
|
if rc != 0:
|
|
@@ -432,7 +437,7 @@ class Connection(ConnectionBase):
|
|
|
432
437
|
|
|
433
438
|
display.vvvvv("kinit succeeded for principal %s" % principal)
|
|
434
439
|
|
|
435
|
-
def _winrm_connect(self):
|
|
440
|
+
def _winrm_connect(self) -> winrm.Protocol:
|
|
436
441
|
'''
|
|
437
442
|
Establish a WinRM connection over HTTP/HTTPS.
|
|
438
443
|
'''
|
|
@@ -491,7 +496,44 @@ class Connection(ConnectionBase):
|
|
|
491
496
|
else:
|
|
492
497
|
raise AnsibleError('No transport found for WinRM connection')
|
|
493
498
|
|
|
494
|
-
def
|
|
499
|
+
def _winrm_write_stdin(self, command_id: str, stdin_iterator: t.Iterable[tuple[bytes, bool]]) -> None:
|
|
500
|
+
for (data, is_last) in stdin_iterator:
|
|
501
|
+
for attempt in range(1, 4):
|
|
502
|
+
try:
|
|
503
|
+
self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last)
|
|
504
|
+
|
|
505
|
+
except WinRMOperationTimeoutError:
|
|
506
|
+
# A WSMan OperationTimeout can be received for a Send
|
|
507
|
+
# operation when the server is under severe load. On manual
|
|
508
|
+
# testing the input is still processed and it's safe to
|
|
509
|
+
# continue. As the calling method still tries to wait for
|
|
510
|
+
# the proc to end if this failed it shouldn't hurt to just
|
|
511
|
+
# treat this as a warning.
|
|
512
|
+
display.warning(
|
|
513
|
+
"WSMan OperationTimeout during send input, attempting to continue. "
|
|
514
|
+
"If this continues to occur, try increasing the connection_timeout "
|
|
515
|
+
"value for this host."
|
|
516
|
+
)
|
|
517
|
+
if not is_last:
|
|
518
|
+
time.sleep(5)
|
|
519
|
+
|
|
520
|
+
except WinRMError as e:
|
|
521
|
+
# Error 170 == ERROR_BUSY. This could be the result of a
|
|
522
|
+
# timed out Send from above still being processed on the
|
|
523
|
+
# server. Add a 5 second delay and try up to 3 times before
|
|
524
|
+
# fully giving up.
|
|
525
|
+
# pywinrm does not expose the internal WSMan fault details
|
|
526
|
+
# through an actual object but embeds it as a repr.
|
|
527
|
+
if attempt == 3 or "'wsmanfault_code': '170'" not in str(e):
|
|
528
|
+
raise
|
|
529
|
+
|
|
530
|
+
display.warning(f"WSMan send failed on attempt {attempt} as the command is busy, trying to send data again")
|
|
531
|
+
time.sleep(5)
|
|
532
|
+
continue
|
|
533
|
+
|
|
534
|
+
break
|
|
535
|
+
|
|
536
|
+
def _winrm_send_input(self, protocol: winrm.Protocol, shell_id: str, command_id: str, stdin: bytes, eof: bool = False) -> None:
|
|
495
537
|
rq = {'env:Envelope': protocol._get_soap_header(
|
|
496
538
|
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
|
|
497
539
|
action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send',
|
|
@@ -505,7 +547,13 @@ class Connection(ConnectionBase):
|
|
|
505
547
|
stream['@End'] = 'true'
|
|
506
548
|
protocol.send_message(xmltodict.unparse(rq))
|
|
507
549
|
|
|
508
|
-
def _winrm_exec(
|
|
550
|
+
def _winrm_exec(
|
|
551
|
+
self,
|
|
552
|
+
command: str,
|
|
553
|
+
args: t.Iterable[bytes] = (),
|
|
554
|
+
from_exec: bool = False,
|
|
555
|
+
stdin_iterator: t.Iterable[tuple[bytes, bool]] = None,
|
|
556
|
+
) -> winrm.Response:
|
|
509
557
|
if not self.protocol:
|
|
510
558
|
self.protocol = self._winrm_connect()
|
|
511
559
|
self._connected = True
|
|
@@ -520,8 +568,7 @@ class Connection(ConnectionBase):
|
|
|
520
568
|
|
|
521
569
|
try:
|
|
522
570
|
if stdin_iterator:
|
|
523
|
-
|
|
524
|
-
self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last)
|
|
571
|
+
self._winrm_write_stdin(command_id, stdin_iterator)
|
|
525
572
|
|
|
526
573
|
except Exception as ex:
|
|
527
574
|
display.warning("ERROR DURING WINRM SEND INPUT - attempting to recover: %s %s"
|
|
@@ -567,7 +614,7 @@ class Connection(ConnectionBase):
|
|
|
567
614
|
if command_id:
|
|
568
615
|
self.protocol.cleanup_command(self.shell_id, command_id)
|
|
569
616
|
|
|
570
|
-
def _connect(self):
|
|
617
|
+
def _connect(self) -> Connection:
|
|
571
618
|
|
|
572
619
|
if not HAS_WINRM:
|
|
573
620
|
raise AnsibleError("winrm or requests is not installed: %s" % to_native(WINRM_IMPORT_ERR))
|
|
@@ -581,20 +628,20 @@ class Connection(ConnectionBase):
|
|
|
581
628
|
self._connected = True
|
|
582
629
|
return self
|
|
583
630
|
|
|
584
|
-
def reset(self):
|
|
631
|
+
def reset(self) -> None:
|
|
585
632
|
if not self._connected:
|
|
586
633
|
return
|
|
587
634
|
self.protocol = None
|
|
588
635
|
self.shell_id = None
|
|
589
636
|
self._connect()
|
|
590
637
|
|
|
591
|
-
def _wrapper_payload_stream(self, payload, buffer_size=200000):
|
|
638
|
+
def _wrapper_payload_stream(self, payload: bytes, buffer_size: int = 200000) -> t.Iterable[tuple[bytes, bool]]:
|
|
592
639
|
payload_bytes = to_bytes(payload)
|
|
593
640
|
byte_count = len(payload_bytes)
|
|
594
641
|
for i in range(0, byte_count, buffer_size):
|
|
595
642
|
yield payload_bytes[i:i + buffer_size], i + buffer_size >= byte_count
|
|
596
643
|
|
|
597
|
-
def exec_command(self, cmd, in_data=None, sudoable=True):
|
|
644
|
+
def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: bool = True) -> tuple[int, bytes, bytes]:
|
|
598
645
|
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
|
599
646
|
cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)
|
|
600
647
|
|
|
@@ -622,7 +669,7 @@ class Connection(ConnectionBase):
|
|
|
622
669
|
return (result.status_code, result.std_out, result.std_err)
|
|
623
670
|
|
|
624
671
|
# FUTURE: determine buffer size at runtime via remote winrm config?
|
|
625
|
-
def _put_file_stdin_iterator(self, in_path, out_path, buffer_size=250000):
|
|
672
|
+
def _put_file_stdin_iterator(self, in_path: str, out_path: str, buffer_size: int = 250000) -> t.Iterable[tuple[bytes, bool]]:
|
|
626
673
|
in_size = os.path.getsize(to_bytes(in_path, errors='surrogate_or_strict'))
|
|
627
674
|
offset = 0
|
|
628
675
|
with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file:
|
|
@@ -635,9 +682,9 @@ class Connection(ConnectionBase):
|
|
|
635
682
|
yield b64_data, (in_file.tell() == in_size)
|
|
636
683
|
|
|
637
684
|
if offset == 0: # empty file, return an empty buffer + eof to close it
|
|
638
|
-
yield "", True
|
|
685
|
+
yield b"", True
|
|
639
686
|
|
|
640
|
-
def put_file(self, in_path, out_path):
|
|
687
|
+
def put_file(self, in_path: str, out_path: str) -> None:
|
|
641
688
|
super(Connection, self).put_file(in_path, out_path)
|
|
642
689
|
out_path = self._shell._unquote(out_path)
|
|
643
690
|
display.vvv('PUT "%s" TO "%s"' % (in_path, out_path), host=self._winrm_host)
|
|
@@ -700,7 +747,7 @@ class Connection(ConnectionBase):
|
|
|
700
747
|
if not remote_sha1 == local_sha1:
|
|
701
748
|
raise AnsibleError("Remote sha1 hash {0} does not match local hash {1}".format(to_native(remote_sha1), to_native(local_sha1)))
|
|
702
749
|
|
|
703
|
-
def fetch_file(self, in_path, out_path):
|
|
750
|
+
def fetch_file(self, in_path: str, out_path: str) -> None:
|
|
704
751
|
super(Connection, self).fetch_file(in_path, out_path)
|
|
705
752
|
in_path = self._shell._unquote(in_path)
|
|
706
753
|
out_path = out_path.replace('\\', '/')
|
|
@@ -714,7 +761,7 @@ class Connection(ConnectionBase):
|
|
|
714
761
|
try:
|
|
715
762
|
script = '''
|
|
716
763
|
$path = '%(path)s'
|
|
717
|
-
If (Test-Path -
|
|
764
|
+
If (Test-Path -LiteralPath $path -PathType Leaf)
|
|
718
765
|
{
|
|
719
766
|
$buffer_size = %(buffer_size)d
|
|
720
767
|
$offset = %(offset)d
|
|
@@ -729,7 +776,7 @@ class Connection(ConnectionBase):
|
|
|
729
776
|
}
|
|
730
777
|
$stream.Close() > $null
|
|
731
778
|
}
|
|
732
|
-
ElseIf (Test-Path -
|
|
779
|
+
ElseIf (Test-Path -LiteralPath $path -PathType Container)
|
|
733
780
|
{
|
|
734
781
|
Write-Host "[DIR]";
|
|
735
782
|
}
|
|
@@ -767,7 +814,7 @@ class Connection(ConnectionBase):
|
|
|
767
814
|
if out_file:
|
|
768
815
|
out_file.close()
|
|
769
816
|
|
|
770
|
-
def close(self):
|
|
817
|
+
def close(self) -> None:
|
|
771
818
|
if self.protocol and self.shell_id:
|
|
772
819
|
display.vvvvv('WINRM CLOSE SHELL: %s' % self.shell_id, host=self._winrm_host)
|
|
773
820
|
self.protocol.close_shell(self.shell_id)
|