ansible-core 2.17.6__py3-none-any.whl → 2.18.0__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/__main__.py +2 -17
- ansible/cli/__init__.py +3 -15
- ansible/cli/config.py +187 -24
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +38 -16
- ansible/cli/galaxy.py +3 -49
- ansible/cli/inventory.py +2 -2
- ansible/cli/pull.py +2 -2
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -10
- ansible/config/base.yml +127 -57
- ansible/config/manager.py +89 -11
- ansible/constants.py +32 -9
- ansible/errors/__init__.py +5 -0
- ansible/executor/interpreter_discovery.py +1 -1
- ansible/executor/play_iterator.py +16 -0
- ansible/executor/playbook_executor.py +1 -4
- ansible/executor/powershell/become_wrapper.ps1 +4 -5
- ansible/executor/powershell/bootstrap_wrapper.ps1 +2 -3
- ansible/executor/powershell/exec_wrapper.ps1 +1 -1
- ansible/executor/powershell/module_manifest.py +2 -2
- ansible/executor/task_executor.py +50 -39
- ansible/executor/task_queue_manager.py +1 -1
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +3 -4
- ansible/galaxy/collection/__init__.py +21 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
- ansible/galaxy/collection/galaxy_api_proxy.py +10 -16
- ansible/galaxy/collection/gpg.py +17 -23
- ansible/galaxy/data/COPYING +7 -0
- ansible/galaxy/data/apb/Dockerfile.j2 +1 -0
- ansible/galaxy/data/apb/Makefile.j2 +1 -0
- ansible/galaxy/data/apb/README.md +7 -3
- ansible/galaxy/data/apb/apb.yml.j2 +1 -0
- ansible/galaxy/data/apb/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/deprovision.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/provision.yml.j2 +1 -0
- ansible/galaxy/data/apb/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/tests/ansible.cfg +1 -0
- ansible/galaxy/data/apb/tests/inventory +1 -0
- ansible/galaxy/data/apb/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/apb/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/collections_galaxy_meta.yml +1 -0
- ansible/galaxy/data/container/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/container/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/container.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tests/ansible.cfg +1 -0
- ansible/galaxy/data/container/tests/inventory +1 -0
- ansible/galaxy/data/container/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/container/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/README.md.j2 +1 -0
- ansible/galaxy/data/default/collection/galaxy.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/meta/runtime.yml +1 -0
- ansible/galaxy/data/default/collection/plugins/README.md.j2 +1 -0
- ansible/galaxy/data/default/role/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tests/inventory +1 -0
- ansible/galaxy/data/default/role/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/default/role/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/network/library/example_command.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_config.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_facts.py.j2 +1 -0
- ansible/galaxy/data/network/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/network/module_utils/example.py.j2 +1 -0
- ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tests/inventory +1 -0
- ansible/galaxy/data/network/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/network/vars/main.yml.j2 +1 -0
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/galaxy/role.py +1 -1
- ansible/galaxy/token.py +20 -8
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +28 -0
- ansible/module_utils/_internal/_concurrent/_futures.py +21 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +2 -2
- ansible/module_utils/basic.py +8 -8
- ansible/module_utils/common/collections.py +1 -1
- ansible/module_utils/common/file.py +0 -6
- ansible/module_utils/common/process.py +22 -9
- ansible/module_utils/common/text/converters.py +5 -8
- ansible/module_utils/common/text/formatters.py +20 -4
- ansible/module_utils/common/validation.py +33 -25
- ansible/module_utils/compat/paramiko.py +6 -1
- ansible/module_utils/compat/selinux.py +2 -2
- ansible/module_utils/connection.py +8 -24
- ansible/module_utils/csharp/Ansible.Become.cs +14 -25
- ansible/module_utils/csharp/Ansible.Process.cs +1 -1
- ansible/module_utils/distro/__init__.py +1 -1
- ansible/module_utils/distro/_distro.py +8 -4
- ansible/module_utils/facts/collector.py +2 -0
- ansible/module_utils/facts/default_collectors.py +3 -1
- ansible/module_utils/facts/hardware/aix.py +54 -52
- ansible/module_utils/facts/hardware/darwin.py +37 -34
- ansible/module_utils/facts/hardware/freebsd.py +55 -15
- ansible/module_utils/facts/hardware/hpux.py +3 -0
- ansible/module_utils/facts/hardware/linux.py +101 -57
- ansible/module_utils/facts/hardware/netbsd.py +3 -0
- ansible/module_utils/facts/hardware/openbsd.py +4 -1
- ansible/module_utils/facts/hardware/sunos.py +7 -1
- ansible/module_utils/facts/network/aix.py +16 -17
- ansible/module_utils/facts/network/fc_wwn.py +4 -1
- ansible/module_utils/facts/network/hpux.py +21 -4
- ansible/module_utils/facts/network/iscsi.py +7 -8
- ansible/module_utils/facts/network/linux.py +0 -2
- ansible/module_utils/facts/other/facter.py +9 -4
- ansible/module_utils/facts/other/ohai.py +5 -5
- ansible/module_utils/facts/packages.py +49 -7
- ansible/module_utils/facts/sysctl.py +33 -31
- ansible/module_utils/facts/system/distribution.py +1 -1
- ansible/module_utils/facts/system/local.py +12 -22
- ansible/module_utils/facts/system/service_mgr.py +3 -1
- ansible/module_utils/facts/system/systemd.py +47 -0
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/splitter.py +1 -1
- ansible/modules/add_host.py +1 -1
- ansible/modules/apt.py +43 -32
- ansible/modules/apt_key.py +6 -6
- ansible/modules/apt_repository.py +23 -14
- ansible/modules/assemble.py +7 -2
- ansible/modules/assert.py +4 -4
- ansible/modules/blockinfile.py +3 -6
- ansible/modules/command.py +1 -1
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +13 -10
- ansible/modules/deb822_repository.py +16 -17
- ansible/modules/debconf.py +9 -9
- ansible/modules/debug.py +1 -1
- ansible/modules/dnf.py +79 -164
- ansible/modules/dnf5.py +54 -29
- ansible/modules/dpkg_selections.py +2 -2
- ansible/modules/expect.py +2 -2
- ansible/modules/fetch.py +2 -2
- ansible/modules/file.py +5 -3
- ansible/modules/find.py +40 -12
- ansible/modules/gather_facts.py +4 -2
- ansible/modules/get_url.py +29 -24
- ansible/modules/git.py +35 -35
- ansible/modules/group.py +71 -1
- ansible/modules/hostname.py +2 -4
- ansible/modules/include_vars.py +5 -5
- ansible/modules/iptables.py +13 -16
- ansible/modules/known_hosts.py +16 -13
- ansible/modules/lineinfile.py +1 -4
- ansible/modules/meta.py +6 -1
- ansible/modules/mount_facts.py +651 -0
- ansible/modules/package_facts.py +63 -80
- ansible/modules/pause.py +4 -3
- ansible/modules/pip.py +14 -14
- ansible/modules/replace.py +1 -4
- ansible/modules/rpm_key.py +31 -11
- ansible/modules/service.py +8 -8
- ansible/modules/service_facts.py +20 -5
- ansible/modules/set_stats.py +1 -1
- ansible/modules/setup.py +3 -3
- ansible/modules/stat.py +3 -3
- ansible/modules/subversion.py +1 -1
- ansible/modules/systemd.py +16 -10
- ansible/modules/systemd_service.py +16 -10
- ansible/modules/sysvinit.py +4 -4
- ansible/modules/unarchive.py +35 -22
- ansible/modules/uri.py +24 -18
- ansible/modules/user.py +145 -12
- ansible/modules/validate_argument_spec.py +3 -3
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +136 -179
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/mod_args.py +11 -10
- ansible/parsing/vault/__init__.py +8 -3
- ansible/parsing/yaml/constructor.py +10 -8
- ansible/parsing/yaml/objects.py +1 -1
- ansible/playbook/base.py +12 -23
- ansible/playbook/helpers.py +4 -0
- ansible/playbook/loop_control.py +8 -0
- ansible/playbook/play.py +4 -22
- ansible/playbook/play_context.py +0 -16
- ansible/playbook/playbook_include.py +2 -2
- ansible/playbook/role/__init__.py +2 -2
- ansible/plugins/__init__.py +2 -0
- ansible/plugins/action/__init__.py +7 -9
- ansible/plugins/action/dnf.py +7 -5
- ansible/plugins/action/package.py +5 -4
- ansible/plugins/action/reboot.py +2 -2
- ansible/plugins/become/__init__.py +1 -1
- ansible/plugins/callback/__init__.py +44 -3
- ansible/plugins/callback/default.py +1 -1
- ansible/plugins/cliconf/__init__.py +1 -1
- ansible/plugins/connection/paramiko_ssh.py +2 -80
- ansible/plugins/connection/psrp.py +33 -82
- ansible/plugins/connection/ssh.py +0 -8
- ansible/plugins/connection/winrm.py +46 -1
- ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
- ansible/plugins/doc_fragments/constructed.py +10 -10
- ansible/plugins/doc_fragments/default_callback.py +8 -8
- ansible/plugins/doc_fragments/files.py +5 -5
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +6 -6
- ansible/plugins/doc_fragments/return_common.py +1 -1
- ansible/plugins/doc_fragments/shell_common.py +2 -10
- ansible/plugins/doc_fragments/shell_windows.py +0 -9
- ansible/plugins/doc_fragments/url.py +2 -2
- ansible/plugins/doc_fragments/url_windows.py +4 -5
- ansible/plugins/doc_fragments/validate.py +1 -1
- ansible/plugins/filter/core.py +2 -0
- ansible/plugins/filter/human_to_bytes.yml +9 -0
- ansible/plugins/filter/password_hash.yml +1 -1
- ansible/plugins/filter/strftime.yml +1 -1
- ansible/plugins/filter/to_nice_json.yml +7 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +1 -1
- ansible/plugins/loader.py +0 -11
- ansible/plugins/lookup/config.py +1 -1
- ansible/plugins/lookup/csvfile.py +21 -9
- ansible/plugins/lookup/env.py +8 -9
- ansible/plugins/lookup/ini.py +10 -1
- ansible/plugins/lookup/random_choice.py +2 -2
- ansible/plugins/lookup/url.py +7 -2
- ansible/plugins/shell/__init__.py +15 -20
- ansible/plugins/shell/powershell.py +9 -6
- ansible/plugins/strategy/__init__.py +16 -7
- ansible/plugins/test/core.py +23 -1
- ansible/plugins/test/issubset.yml +1 -1
- ansible/plugins/test/subset.yml +1 -1
- ansible/plugins/test/timedout.yml +20 -0
- ansible/plugins/test/vault_encrypted.yml +6 -6
- ansible/plugins/test/vaulted_file.yml +19 -0
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -8
- ansible/utils/collection_loader/_collection_finder.py +23 -55
- ansible/utils/display.py +44 -31
- ansible/utils/jsonrpc.py +1 -1
- ansible/utils/listify.py +1 -5
- ansible/utils/path.py +3 -0
- ansible/utils/vars.py +18 -27
- ansible/vars/manager.py +7 -150
- ansible/vars/plugins.py +1 -1
- ansible_core-2.18.0.dist-info/Apache-License.txt +202 -0
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/METADATA +36 -23
- ansible_core-2.18.0.dist-info/MIT-license.txt +14 -0
- ansible_core-2.18.0.dist-info/PSF-license.txt +48 -0
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/RECORD +316 -311
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/entry_points.txt +1 -1
- ansible_core-2.18.0.dist-info/simplified_bsd.txt +8 -0
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +5 -4
- ansible_test/_data/completion/windows.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -2
- ansible_test/_data/requirements/constraints.txt +1 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.in +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +4 -4
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.import.txt +1 -1
- ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +6 -8
- ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -2
- ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
- ansible_test/_data/requirements/sanity.yamllint.in +1 -0
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/ansible_util.py +8 -35
- ansible_test/_internal/ci/azp.py +1 -1
- ansible_test/_internal/classification/__init__.py +0 -2
- ansible_test/_internal/cli/parsers/key_value_parsers.py +3 -0
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -1
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
- ansible_test/_internal/commands/sanity/__init__.py +96 -19
- ansible_test/_internal/commands/sanity/pylint.py +20 -24
- ansible_test/_internal/completion.py +2 -0
- ansible_test/_internal/constants.py +0 -1
- ansible_test/_internal/coverage_util.py +1 -2
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/encoding.py +4 -4
- ansible_test/_internal/host_configs.py +10 -0
- ansible_test/_internal/host_profiles.py +9 -13
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/python_requirements.py +5 -14
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -0
- ansible_test/_internal/util_common.py +5 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
- ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +5 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +5 -0
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +5 -0
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +6 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +6 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
- ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/injector/python.py +5 -0
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
- ansible_test/_util/target/sanity/import/importer.py +1 -1
- ansible_test/_util/target/setup/bootstrap.sh +6 -17
- ansible_test/_util/target/setup/requirements.py +18 -24
- ansible_test/config/config.yml +1 -1
- ansible_core-2.17.6.data/scripts/ansible-test +0 -44
- ansible_test/_data/requirements/sanity.mypy.in +0 -10
- ansible_test/_data/requirements/sanity.mypy.txt +0 -18
- ansible_test/_internal/commands/sanity/mypy.py +0 -274
- ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
- ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
- ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/COPYING +0 -0
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/WHEEL +0 -0
- {ansible_core-2.17.6.dist-info → ansible_core-2.18.0.dist-info}/top_level.txt +0 -0
|
@@ -838,6 +838,46 @@ class ModuleValidator(Validator):
|
|
|
838
838
|
msg='%s: %s' % (combined_path, error_message)
|
|
839
839
|
)
|
|
840
840
|
|
|
841
|
+
def _validate_option_docs(self, options, context=None):
|
|
842
|
+
if not isinstance(options, dict):
|
|
843
|
+
return
|
|
844
|
+
if context is None:
|
|
845
|
+
context = []
|
|
846
|
+
|
|
847
|
+
normalized_option_alias_names = dict()
|
|
848
|
+
|
|
849
|
+
def add_option_alias_name(name, option_name):
|
|
850
|
+
normalized_name = str(name).lower()
|
|
851
|
+
normalized_option_alias_names.setdefault(normalized_name, {}).setdefault(option_name, set()).add(name)
|
|
852
|
+
|
|
853
|
+
for option, data in options.items():
|
|
854
|
+
if 'suboptions' in data:
|
|
855
|
+
self._validate_option_docs(data.get('suboptions'), context + [option])
|
|
856
|
+
add_option_alias_name(option, option)
|
|
857
|
+
if 'aliases' in data and isinstance(data['aliases'], list):
|
|
858
|
+
for alias in data['aliases']:
|
|
859
|
+
add_option_alias_name(alias, option)
|
|
860
|
+
|
|
861
|
+
for normalized_name, options in normalized_option_alias_names.items():
|
|
862
|
+
if len(options) < 2:
|
|
863
|
+
continue
|
|
864
|
+
|
|
865
|
+
what = []
|
|
866
|
+
for option_name, names in sorted(options.items()):
|
|
867
|
+
if option_name in names:
|
|
868
|
+
what.append("option '%s'" % option_name)
|
|
869
|
+
else:
|
|
870
|
+
what.append("alias '%s' of option '%s'" % (sorted(names)[0], option_name))
|
|
871
|
+
msg = "Multiple options/aliases"
|
|
872
|
+
if context:
|
|
873
|
+
msg += " found in %s" % " -> ".join(context)
|
|
874
|
+
msg += " are equal up to casing: %s" % ", ".join(what)
|
|
875
|
+
self.reporter.error(
|
|
876
|
+
path=self.object_path,
|
|
877
|
+
code='option-equal-up-to-casing',
|
|
878
|
+
msg=msg,
|
|
879
|
+
)
|
|
880
|
+
|
|
841
881
|
def _validate_docs(self):
|
|
842
882
|
doc = None
|
|
843
883
|
# We have three ways of marking deprecated/removed files. Have to check each one
|
|
@@ -1015,6 +1055,9 @@ class ModuleValidator(Validator):
|
|
|
1015
1055
|
'invalid-documentation',
|
|
1016
1056
|
)
|
|
1017
1057
|
|
|
1058
|
+
if doc:
|
|
1059
|
+
self._validate_option_docs(doc.get('options'))
|
|
1060
|
+
|
|
1018
1061
|
self._validate_all_semantic_markup(doc, returns)
|
|
1019
1062
|
|
|
1020
1063
|
if not self.collection:
|
|
@@ -1235,7 +1278,7 @@ class ModuleValidator(Validator):
|
|
|
1235
1278
|
self._validate_semantic_markup(entry.get(key))
|
|
1236
1279
|
|
|
1237
1280
|
if isinstance(docs.get('deprecated'), dict):
|
|
1238
|
-
for key in ('why', 'alternative'):
|
|
1281
|
+
for key in ('why', 'alternative', 'alternatives'):
|
|
1239
1282
|
self._validate_semantic_markup(docs.get('deprecated').get(key))
|
|
1240
1283
|
|
|
1241
1284
|
self._validate_semantic_markup_options(docs.get('options'))
|
|
@@ -1876,8 +1919,10 @@ class ModuleValidator(Validator):
|
|
|
1876
1919
|
if len(doc_options_args) == 0:
|
|
1877
1920
|
# Undocumented arguments will be handled later (search for undocumented-parameter)
|
|
1878
1921
|
doc_options_arg = {}
|
|
1922
|
+
doc_option_name = None
|
|
1879
1923
|
else:
|
|
1880
|
-
|
|
1924
|
+
doc_option_name = doc_options_args[0]
|
|
1925
|
+
doc_options_arg = doc_options[doc_option_name]
|
|
1881
1926
|
if len(doc_options_args) > 1:
|
|
1882
1927
|
msg = "Argument '%s' in argument_spec" % arg
|
|
1883
1928
|
if context:
|
|
@@ -1892,6 +1937,26 @@ class ModuleValidator(Validator):
|
|
|
1892
1937
|
msg=msg
|
|
1893
1938
|
)
|
|
1894
1939
|
|
|
1940
|
+
all_aliases = set(aliases + [arg])
|
|
1941
|
+
all_docs_aliases = set(
|
|
1942
|
+
([doc_option_name] if doc_option_name is not None else [])
|
|
1943
|
+
+
|
|
1944
|
+
(doc_options_arg['aliases'] if isinstance(doc_options_arg.get('aliases'), list) else [])
|
|
1945
|
+
)
|
|
1946
|
+
if all_docs_aliases and all_aliases != all_docs_aliases:
|
|
1947
|
+
msg = "Argument '%s' in argument_spec" % arg
|
|
1948
|
+
if context:
|
|
1949
|
+
msg += " found in %s" % " -> ".join(context)
|
|
1950
|
+
msg += " has names %s, but its documentation has names %s" % (
|
|
1951
|
+
", ".join([("'%s'" % alias) for alias in sorted(all_aliases)]),
|
|
1952
|
+
", ".join([("'%s'" % alias) for alias in sorted(all_docs_aliases)])
|
|
1953
|
+
)
|
|
1954
|
+
self.reporter.error(
|
|
1955
|
+
path=self.object_path,
|
|
1956
|
+
code='parameter-documented-aliases-differ',
|
|
1957
|
+
msg=msg
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1895
1960
|
try:
|
|
1896
1961
|
doc_default = None
|
|
1897
1962
|
if 'default' in doc_options_arg and doc_options_arg['default'] is not None:
|
|
@@ -84,6 +84,22 @@ def date(error_code=None):
|
|
|
84
84
|
return Any(isodate, error_code=error_code)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
def require_only_one(keys):
|
|
88
|
+
def f(obj):
|
|
89
|
+
found = None
|
|
90
|
+
for k in obj.keys():
|
|
91
|
+
if k in keys:
|
|
92
|
+
if found is None:
|
|
93
|
+
found = k
|
|
94
|
+
else:
|
|
95
|
+
raise Invalid('Found conflicting keys, must contain only one of {}'.format(keys))
|
|
96
|
+
if found is None:
|
|
97
|
+
raise Invalid('Must contain one of {}'.format(keys))
|
|
98
|
+
|
|
99
|
+
return obj
|
|
100
|
+
return f
|
|
101
|
+
|
|
102
|
+
|
|
87
103
|
# Roles can also be referenced by semantic markup
|
|
88
104
|
_VALID_PLUGIN_TYPES = set(DOCUMENTABLE_PLUGINS + ('role', ))
|
|
89
105
|
|
|
@@ -568,7 +584,9 @@ def list_dict_option_schema(for_collection, plugin_type):
|
|
|
568
584
|
{
|
|
569
585
|
# This definition makes sure everything has the correct types/values
|
|
570
586
|
'why': doc_string,
|
|
571
|
-
'
|
|
587
|
+
# TODO: phase out either plural or singular, 'alt' is exclusive group
|
|
588
|
+
Exclusive('alternative', 'alt'): doc_string,
|
|
589
|
+
Exclusive('alternatives', 'alt'): doc_string,
|
|
572
590
|
# vod stands for 'version or date'; this is the name of the exclusive group
|
|
573
591
|
Exclusive('removed_at_date', 'vod'): date(),
|
|
574
592
|
Exclusive('version', 'vod'): version(for_collection),
|
|
@@ -577,7 +595,7 @@ def list_dict_option_schema(for_collection, plugin_type):
|
|
|
577
595
|
{
|
|
578
596
|
# This definition makes sure that everything we require is there
|
|
579
597
|
Required('why'): Any(*string_types),
|
|
580
|
-
'alternatives': Any(*string_types),
|
|
598
|
+
Required(Any('alternatives', 'alternative')): Any(*string_types),
|
|
581
599
|
Required(Any('removed_at_date', 'version')): Any(*string_types),
|
|
582
600
|
Required('collection_name'): Any(*string_types),
|
|
583
601
|
},
|
|
@@ -761,13 +779,16 @@ def return_schema(for_collection, plugin_type='module'):
|
|
|
761
779
|
|
|
762
780
|
|
|
763
781
|
def deprecation_schema(for_collection):
|
|
782
|
+
|
|
764
783
|
main_fields = {
|
|
765
784
|
Required('why'): doc_string,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
'removed': Any(True),
|
|
785
|
+
'alternative': doc_string,
|
|
786
|
+
'alternatives': doc_string,
|
|
769
787
|
}
|
|
770
788
|
|
|
789
|
+
if for_collection:
|
|
790
|
+
main_fields.update({Required('removed_from_collection'): collection_name, 'removed': Any(True)})
|
|
791
|
+
|
|
771
792
|
date_schema = {
|
|
772
793
|
Required('removed_at_date'): date(),
|
|
773
794
|
}
|
|
@@ -791,6 +812,7 @@ def deprecation_schema(for_collection):
|
|
|
791
812
|
if for_collection:
|
|
792
813
|
result = All(
|
|
793
814
|
result,
|
|
815
|
+
require_only_one(['alternative', 'alternatives']),
|
|
794
816
|
partial(check_removal_version,
|
|
795
817
|
version_field='removed_in',
|
|
796
818
|
collection_name_field='removed_from_collection',
|
|
File without changes
|
|
@@ -6,12 +6,15 @@ import importlib.util
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
8
|
|
|
9
|
+
NETWORKING_CLI_STUB_SCRIPT = 'ansible_connection_cli_stub.py'
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
def main():
|
|
11
13
|
"""Main entry point."""
|
|
12
14
|
name = os.path.basename(__file__)
|
|
13
15
|
args = [sys.executable]
|
|
14
16
|
|
|
17
|
+
ansible_lib_root = os.environ.get('ANSIBLE_TEST_ANSIBLE_LIB_ROOT')
|
|
15
18
|
coverage_config = os.environ.get('COVERAGE_CONF')
|
|
16
19
|
coverage_output = os.environ.get('COVERAGE_FILE')
|
|
17
20
|
|
|
@@ -33,6 +36,8 @@ def main():
|
|
|
33
36
|
args += ['-m', 'pytest']
|
|
34
37
|
elif name == 'importer.py':
|
|
35
38
|
args += [find_program(name, False)]
|
|
39
|
+
elif name == NETWORKING_CLI_STUB_SCRIPT:
|
|
40
|
+
args += [os.path.join(ansible_lib_root, 'cli/scripts', NETWORKING_CLI_STUB_SCRIPT)]
|
|
36
41
|
else:
|
|
37
42
|
args += [find_program(name, True)]
|
|
38
43
|
|
|
@@ -53,6 +53,12 @@ def pytest_configure():
|
|
|
53
53
|
|
|
54
54
|
def coverage_exit(*args, **kwargs):
|
|
55
55
|
for instance in coverage_instances:
|
|
56
|
+
# skip coverage instances which have no collector, or the collector is not the active collector
|
|
57
|
+
# this avoids issues with coverage 7.4.0+ when tests create subprocesses which inherit our overridden os._exit method
|
|
58
|
+
# pylint: disable=protected-access
|
|
59
|
+
if not instance._collector or not instance._collector._collectors or instance._collector != instance._collector._collectors[-1]:
|
|
60
|
+
continue
|
|
61
|
+
|
|
56
62
|
instance.stop()
|
|
57
63
|
instance.save()
|
|
58
64
|
|
|
@@ -159,7 +159,7 @@ def main():
|
|
|
159
159
|
loader = self._get_loader(fullname, path=path)
|
|
160
160
|
if loader is not None:
|
|
161
161
|
if has_py3_loader:
|
|
162
|
-
# loader is expected to be Optional[importlib.abc.Loader], but RestrictedModuleLoader does not inherit from importlib.abc.
|
|
162
|
+
# loader is expected to be Optional[importlib.abc.Loader], but RestrictedModuleLoader does not inherit from importlib.abc.Loader
|
|
163
163
|
return spec_from_loader(fullname, loader) # type: ignore[arg-type]
|
|
164
164
|
raise ImportError("Failed to import '%s' due to a bug in ansible-test. Check importlib imports for typos." % fullname)
|
|
165
165
|
return None
|
|
@@ -53,14 +53,14 @@ customize_bashrc()
|
|
|
53
53
|
fi
|
|
54
54
|
|
|
55
55
|
# Improve shell prompts for interactive use.
|
|
56
|
-
echo "export PS1='\[\e]0;\u@\h: \w\a\]\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '" >> ~/.bashrc
|
|
56
|
+
echo "export PS1='"'\[\e]0;\u@\h: \w\a\]\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '"'" >> ~/.bashrc
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
install_pip() {
|
|
60
60
|
if ! "${python_interpreter}" -m pip.__main__ --version --disable-pip-version-check 2>/dev/null; then
|
|
61
61
|
case "${python_version}" in
|
|
62
62
|
*)
|
|
63
|
-
pip_bootstrap_url="https://ci-files.testing.ansible.com/ansible-test/get-pip-
|
|
63
|
+
pip_bootstrap_url="https://ci-files.testing.ansible.com/ansible-test/get-pip-24.0.py"
|
|
64
64
|
;;
|
|
65
65
|
esac
|
|
66
66
|
|
|
@@ -176,10 +176,10 @@ bootstrap_remote_freebsd()
|
|
|
176
176
|
cryptography_pkg="" # not available
|
|
177
177
|
pyyaml_pkg="" # not available
|
|
178
178
|
;;
|
|
179
|
-
14.
|
|
179
|
+
14.1/3.9)
|
|
180
180
|
# defaults above 'just work'TM
|
|
181
181
|
;;
|
|
182
|
-
14.
|
|
182
|
+
14.1/3.11)
|
|
183
183
|
cryptography_pkg="" # not available
|
|
184
184
|
jinja2_pkg="" # not available
|
|
185
185
|
pyyaml_pkg="" # not available
|
|
@@ -268,19 +268,12 @@ bootstrap_remote_rhel_9()
|
|
|
268
268
|
packages="
|
|
269
269
|
gcc
|
|
270
270
|
${py_pkg_prefix}-devel
|
|
271
|
+
${py_pkg_prefix}-pip
|
|
271
272
|
"
|
|
272
273
|
|
|
273
|
-
# pip is not included in the Python devel package under Python 3.11
|
|
274
|
-
if [ "${python_version}" != "3.9" ]; then
|
|
275
|
-
packages="
|
|
276
|
-
${packages}
|
|
277
|
-
${py_pkg_prefix}-pip
|
|
278
|
-
"
|
|
279
|
-
fi
|
|
280
|
-
|
|
281
274
|
# Jinja2 is not installed with an OS package since the provided version is too old.
|
|
282
275
|
# Instead, ansible-test will install it using pip.
|
|
283
|
-
# packaging and resolvelib are missing for
|
|
276
|
+
# packaging and resolvelib are missing for controller supported Python versions, so we just
|
|
284
277
|
# skip them and let ansible-test install them from PyPI.
|
|
285
278
|
if [ "${controller}" ]; then
|
|
286
279
|
packages="
|
|
@@ -329,10 +322,6 @@ bootstrap_remote_ubuntu()
|
|
|
329
322
|
# For these ansible-test will use pip to install the requirements instead.
|
|
330
323
|
# Only the platform is checked since Ubuntu shares Python packages across Python versions.
|
|
331
324
|
case "${platform_version}" in
|
|
332
|
-
"20.04")
|
|
333
|
-
jinja2_pkg="" # too old
|
|
334
|
-
resolvelib_pkg="" # not available
|
|
335
|
-
;;
|
|
336
325
|
esac
|
|
337
326
|
|
|
338
327
|
packages="
|
|
@@ -22,27 +22,13 @@ import errno
|
|
|
22
22
|
import io
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
|
+
import shlex
|
|
25
26
|
import shutil
|
|
26
27
|
import subprocess
|
|
27
28
|
import sys
|
|
28
29
|
import tempfile
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import typing as t
|
|
32
|
-
except ImportError:
|
|
33
|
-
t = None
|
|
34
|
-
|
|
35
|
-
try:
|
|
36
|
-
from shlex import quote as cmd_quote
|
|
37
|
-
except ImportError:
|
|
38
|
-
# noinspection PyProtectedMember
|
|
39
|
-
from pipes import quote as cmd_quote
|
|
40
|
-
|
|
41
|
-
try:
|
|
42
|
-
from urllib.request import urlopen
|
|
43
|
-
except ImportError:
|
|
44
|
-
# noinspection PyCompatibility,PyUnresolvedReferences
|
|
45
|
-
from urllib2 import urlopen # pylint: disable=ansible-bad-import-from
|
|
30
|
+
import typing as t
|
|
31
|
+
import urllib.request
|
|
46
32
|
|
|
47
33
|
ENCODING = 'utf-8'
|
|
48
34
|
|
|
@@ -80,6 +66,8 @@ def bootstrap(pip, options): # type: (str, t.Dict[str, t.Any]) -> None
|
|
|
80
66
|
"""Bootstrap pip and related packages in an empty virtual environment."""
|
|
81
67
|
pip_version = options['pip_version']
|
|
82
68
|
packages = options['packages']
|
|
69
|
+
setuptools = options['setuptools']
|
|
70
|
+
wheel = options['wheel']
|
|
83
71
|
|
|
84
72
|
url = 'https://ci-files.testing.ansible.com/ansible-test/get-pip-%s.py' % pip_version
|
|
85
73
|
cache_path = os.path.expanduser('~/.ansible/test/cache/get_pip_%s.py' % pip_version.replace(".", "_"))
|
|
@@ -115,6 +103,12 @@ https://github.com/ansible/ansible/issues/77304
|
|
|
115
103
|
options = common_pip_options()
|
|
116
104
|
options.extend(packages)
|
|
117
105
|
|
|
106
|
+
if not setuptools:
|
|
107
|
+
options.append('--no-setuptools')
|
|
108
|
+
|
|
109
|
+
if not wheel:
|
|
110
|
+
options.append('--no-wheel')
|
|
111
|
+
|
|
118
112
|
command = [sys.executable, pip] + options
|
|
119
113
|
|
|
120
114
|
execute_command(command, env=env)
|
|
@@ -280,7 +274,7 @@ def devnull(): # type: () -> t.IO[bytes]
|
|
|
280
274
|
def download_file(url, path): # type: (str, str) -> None
|
|
281
275
|
"""Download the given URL to the specified file path."""
|
|
282
276
|
with open(to_bytes(path), 'wb') as saved_file:
|
|
283
|
-
with contextlib.closing(urlopen(url)) as download:
|
|
277
|
+
with contextlib.closing(urllib.request.urlopen(url)) as download:
|
|
284
278
|
shutil.copyfileobj(download, saved_file)
|
|
285
279
|
|
|
286
280
|
|
|
@@ -291,7 +285,7 @@ class ApplicationError(Exception):
|
|
|
291
285
|
class SubprocessError(ApplicationError):
|
|
292
286
|
"""A command returned a non-zero status."""
|
|
293
287
|
def __init__(self, cmd, status, stdout, stderr): # type: (t.List[str], int, str, str) -> None
|
|
294
|
-
message = 'A command failed with status %d: %s' % (status,
|
|
288
|
+
message = 'A command failed with status %d: %s' % (status, shlex.join(cmd))
|
|
295
289
|
|
|
296
290
|
if stderr:
|
|
297
291
|
message += '\n>>> Standard Error\n%s' % stderr.strip()
|
|
@@ -313,7 +307,7 @@ def log(message, verbosity=0): # type: (str, int) -> None
|
|
|
313
307
|
|
|
314
308
|
def execute_command(cmd, cwd=None, capture=False, env=None): # type: (t.List[str], t.Optional[str], bool, t.Optional[t.Dict[str, str]]) -> None
|
|
315
309
|
"""Execute the specified command."""
|
|
316
|
-
log('Execute command: %s' %
|
|
310
|
+
log('Execute command: %s' % shlex.join(cmd), verbosity=1)
|
|
317
311
|
|
|
318
312
|
cmd_bytes = [to_bytes(c) for c in cmd]
|
|
319
313
|
|
|
@@ -369,17 +363,17 @@ def open_binary_file(path, mode='rb'): # type: (str, str) -> t.IO[bytes]
|
|
|
369
363
|
return io.open(to_bytes(path), mode) # pylint: disable=consider-using-with,unspecified-encoding
|
|
370
364
|
|
|
371
365
|
|
|
372
|
-
def to_optional_bytes(value, errors='strict'): # type: (t.Optional[
|
|
366
|
+
def to_optional_bytes(value, errors='strict'): # type: (t.Optional[str | bytes], str) -> t.Optional[bytes]
|
|
373
367
|
"""Return the given value as bytes encoded using UTF-8 if not already bytes, or None if the value is None."""
|
|
374
368
|
return None if value is None else to_bytes(value, errors)
|
|
375
369
|
|
|
376
370
|
|
|
377
|
-
def to_optional_text(value, errors='strict'): # type: (t.Optional[
|
|
371
|
+
def to_optional_text(value, errors='strict'): # type: (t.Optional[str | bytes], str) -> t.Optional[t.Text]
|
|
378
372
|
"""Return the given value as text decoded using UTF-8 if not already text, or None if the value is None."""
|
|
379
373
|
return None if value is None else to_text(value, errors)
|
|
380
374
|
|
|
381
375
|
|
|
382
|
-
def to_bytes(value, errors='strict'): # type: (
|
|
376
|
+
def to_bytes(value, errors='strict'): # type: (str | bytes, str) -> bytes
|
|
383
377
|
"""Return the given value as bytes encoded using UTF-8 if not already bytes."""
|
|
384
378
|
if isinstance(value, bytes):
|
|
385
379
|
return value
|
|
@@ -390,7 +384,7 @@ def to_bytes(value, errors='strict'): # type: (t.AnyStr, str) -> bytes
|
|
|
390
384
|
raise Exception('value is not bytes or text: %s' % type(value))
|
|
391
385
|
|
|
392
386
|
|
|
393
|
-
def to_text(value, errors='strict'): # type: (
|
|
387
|
+
def to_text(value, errors='strict'): # type: (str | bytes, str) -> t.Text
|
|
394
388
|
"""Return the given value as text decoded using UTF-8 if not already text."""
|
|
395
389
|
if isinstance(value, bytes):
|
|
396
390
|
return value.decode(ENCODING, errors)
|
ansible_test/config/config.yml
CHANGED
|
@@ -17,7 +17,7 @@ modules:
|
|
|
17
17
|
# This is the default value if no configuration is provided.
|
|
18
18
|
# - 'controller' - All Python versions supported by the Ansible controller.
|
|
19
19
|
# This indicates the modules/module_utils can only run on the controller.
|
|
20
|
-
# Intended for use only with modules/module_utils that depend on
|
|
20
|
+
# Intended for use only with modules/module_utils that depend on the Ansible persistent connection helper, which only runs on the controller.
|
|
21
21
|
# Unit tests for modules/module_utils will be permitted to import any Ansible code, instead of only module_utils.
|
|
22
22
|
# - SpecifierSet - A PEP 440 specifier set indicating the supported Python versions.
|
|
23
23
|
# This is only needed when modules/module_utils do not support all Python versions supported by Ansible.
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!python
|
|
2
|
-
# PYTHON_ARGCOMPLETE_OK
|
|
3
|
-
"""Command line entry point for ansible-test."""
|
|
4
|
-
|
|
5
|
-
# NOTE: This file resides in the _util/target directory to ensure compatibility with all supported Python versions.
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
import sys
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def main(args=None):
|
|
14
|
-
"""Main program entry point."""
|
|
15
|
-
ansible_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
16
|
-
source_root = os.path.join(ansible_root, 'test', 'lib')
|
|
17
|
-
|
|
18
|
-
if os.path.exists(os.path.join(source_root, 'ansible_test', '_internal', '__init__.py')):
|
|
19
|
-
# running from source, use that version of ansible-test instead of any version that may already be installed
|
|
20
|
-
sys.path.insert(0, source_root)
|
|
21
|
-
|
|
22
|
-
# noinspection PyProtectedMember
|
|
23
|
-
from ansible_test._util.target.common.constants import CONTROLLER_PYTHON_VERSIONS
|
|
24
|
-
|
|
25
|
-
if version_to_str(sys.version_info[:2]) not in CONTROLLER_PYTHON_VERSIONS:
|
|
26
|
-
raise SystemExit('This version of ansible-test cannot be executed with Python version %s. Supported Python versions are: %s' % (
|
|
27
|
-
version_to_str(sys.version_info[:3]), ', '.join(CONTROLLER_PYTHON_VERSIONS)))
|
|
28
|
-
|
|
29
|
-
if any(not os.get_blocking(handle.fileno()) for handle in (sys.stdin, sys.stdout, sys.stderr)):
|
|
30
|
-
raise SystemExit('Standard input, output and error file handles must be blocking to run ansible-test.')
|
|
31
|
-
|
|
32
|
-
# noinspection PyProtectedMember
|
|
33
|
-
from ansible_test._internal import main as cli_main
|
|
34
|
-
|
|
35
|
-
cli_main(args)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def version_to_str(version):
|
|
39
|
-
"""Return a version string from a version tuple."""
|
|
40
|
-
return '.'.join(str(n) for n in version)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if __name__ == '__main__':
|
|
44
|
-
main()
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# edit "sanity.mypy.in" and generate with: hacking/update-sanity-requirements.py --test mypy
|
|
2
|
-
cffi==1.16.0
|
|
3
|
-
cryptography==42.0.5
|
|
4
|
-
Jinja2==3.1.3
|
|
5
|
-
MarkupSafe==2.1.5
|
|
6
|
-
mypy==1.9.0
|
|
7
|
-
mypy-extensions==1.0.0
|
|
8
|
-
packaging==24.0
|
|
9
|
-
pycparser==2.21
|
|
10
|
-
tomli==2.0.1
|
|
11
|
-
types-backports==0.1.3
|
|
12
|
-
types-paramiko==3.4.0.20240311
|
|
13
|
-
types-PyYAML==6.0.12.20240311
|
|
14
|
-
types-requests==2.31.0.20240311
|
|
15
|
-
types-setuptools==69.2.0.20240317
|
|
16
|
-
types-toml==0.10.8.20240310
|
|
17
|
-
typing_extensions==4.10.0
|
|
18
|
-
urllib3==2.2.1
|