ansible-core 2.17.5rc1__py3-none-any.whl → 2.18.0rc1__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.

Files changed (330) hide show
  1. ansible/__main__.py +2 -17
  2. ansible/cli/__init__.py +3 -15
  3. ansible/cli/config.py +187 -24
  4. ansible/cli/console.py +1 -1
  5. ansible/cli/doc.py +38 -16
  6. ansible/cli/galaxy.py +3 -49
  7. ansible/cli/inventory.py +2 -2
  8. ansible/cli/pull.py +2 -2
  9. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -10
  10. ansible/config/base.yml +127 -57
  11. ansible/config/manager.py +89 -11
  12. ansible/constants.py +32 -9
  13. ansible/errors/__init__.py +5 -0
  14. ansible/executor/interpreter_discovery.py +1 -1
  15. ansible/executor/play_iterator.py +34 -0
  16. ansible/executor/playbook_executor.py +1 -4
  17. ansible/executor/powershell/become_wrapper.ps1 +4 -5
  18. ansible/executor/powershell/bootstrap_wrapper.ps1 +2 -3
  19. ansible/executor/powershell/exec_wrapper.ps1 +1 -1
  20. ansible/executor/powershell/module_manifest.py +2 -2
  21. ansible/executor/task_executor.py +50 -39
  22. ansible/executor/task_queue_manager.py +1 -1
  23. ansible/executor/task_result.py +1 -1
  24. ansible/galaxy/api.py +3 -4
  25. ansible/galaxy/collection/__init__.py +21 -10
  26. ansible/galaxy/collection/concrete_artifact_manager.py +10 -5
  27. ansible/galaxy/collection/galaxy_api_proxy.py +10 -16
  28. ansible/galaxy/collection/gpg.py +17 -23
  29. ansible/galaxy/data/COPYING +7 -0
  30. ansible/galaxy/data/apb/Dockerfile.j2 +1 -0
  31. ansible/galaxy/data/apb/Makefile.j2 +1 -0
  32. ansible/galaxy/data/apb/README.md +7 -3
  33. ansible/galaxy/data/apb/apb.yml.j2 +1 -0
  34. ansible/galaxy/data/apb/defaults/main.yml.j2 +1 -0
  35. ansible/galaxy/data/apb/handlers/main.yml.j2 +1 -0
  36. ansible/galaxy/data/apb/meta/main.yml.j2 +1 -0
  37. ansible/galaxy/data/apb/playbooks/deprovision.yml.j2 +1 -0
  38. ansible/galaxy/data/apb/playbooks/provision.yml.j2 +1 -0
  39. ansible/galaxy/data/apb/tasks/main.yml.j2 +1 -0
  40. ansible/galaxy/data/apb/tests/ansible.cfg +1 -0
  41. ansible/galaxy/data/apb/tests/inventory +1 -0
  42. ansible/galaxy/data/apb/tests/test.yml.j2 +1 -0
  43. ansible/galaxy/data/apb/vars/main.yml.j2 +1 -0
  44. ansible/galaxy/data/collections_galaxy_meta.yml +1 -0
  45. ansible/galaxy/data/container/defaults/main.yml.j2 +1 -0
  46. ansible/galaxy/data/container/handlers/main.yml.j2 +1 -0
  47. ansible/galaxy/data/container/meta/container.yml.j2 +1 -0
  48. ansible/galaxy/data/container/meta/main.yml.j2 +1 -0
  49. ansible/galaxy/data/container/tasks/main.yml.j2 +1 -0
  50. ansible/galaxy/data/container/tests/ansible.cfg +1 -0
  51. ansible/galaxy/data/container/tests/inventory +1 -0
  52. ansible/galaxy/data/container/tests/test.yml.j2 +1 -0
  53. ansible/galaxy/data/container/vars/main.yml.j2 +1 -0
  54. ansible/galaxy/data/default/collection/README.md.j2 +1 -0
  55. ansible/galaxy/data/default/collection/galaxy.yml.j2 +1 -0
  56. ansible/galaxy/data/default/collection/meta/runtime.yml +1 -0
  57. ansible/galaxy/data/default/collection/plugins/README.md.j2 +1 -0
  58. ansible/galaxy/data/default/role/defaults/main.yml.j2 +1 -0
  59. ansible/galaxy/data/default/role/handlers/main.yml.j2 +1 -0
  60. ansible/galaxy/data/default/role/meta/main.yml.j2 +1 -0
  61. ansible/galaxy/data/default/role/tasks/main.yml.j2 +1 -0
  62. ansible/galaxy/data/default/role/tests/inventory +1 -0
  63. ansible/galaxy/data/default/role/tests/test.yml.j2 +1 -0
  64. ansible/galaxy/data/default/role/vars/main.yml.j2 +1 -0
  65. ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -0
  66. ansible/galaxy/data/network/defaults/main.yml.j2 +1 -0
  67. ansible/galaxy/data/network/library/example_command.py.j2 +1 -0
  68. ansible/galaxy/data/network/library/example_config.py.j2 +1 -0
  69. ansible/galaxy/data/network/library/example_facts.py.j2 +1 -0
  70. ansible/galaxy/data/network/meta/main.yml.j2 +1 -0
  71. ansible/galaxy/data/network/module_utils/example.py.j2 +1 -0
  72. ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -0
  73. ansible/galaxy/data/network/tasks/main.yml.j2 +1 -0
  74. ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -0
  75. ansible/galaxy/data/network/tests/inventory +1 -0
  76. ansible/galaxy/data/network/tests/test.yml.j2 +1 -0
  77. ansible/galaxy/data/network/vars/main.yml.j2 +1 -0
  78. ansible/galaxy/dependency_resolution/providers.py +3 -3
  79. ansible/galaxy/role.py +1 -1
  80. ansible/galaxy/token.py +20 -8
  81. ansible/keyword_desc.yml +1 -1
  82. ansible/module_utils/_internal/__init__.py +0 -0
  83. ansible/module_utils/_internal/_concurrent/__init__.py +0 -0
  84. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +28 -0
  85. ansible/module_utils/_internal/_concurrent/_futures.py +21 -0
  86. ansible/module_utils/ansible_release.py +2 -2
  87. ansible/module_utils/api.py +2 -2
  88. ansible/module_utils/basic.py +8 -8
  89. ansible/module_utils/common/collections.py +1 -1
  90. ansible/module_utils/common/file.py +0 -6
  91. ansible/module_utils/common/process.py +22 -9
  92. ansible/module_utils/common/text/converters.py +5 -8
  93. ansible/module_utils/common/text/formatters.py +20 -4
  94. ansible/module_utils/common/validation.py +33 -25
  95. ansible/module_utils/compat/paramiko.py +6 -1
  96. ansible/module_utils/compat/selinux.py +2 -2
  97. ansible/module_utils/connection.py +8 -24
  98. ansible/module_utils/csharp/Ansible.Become.cs +14 -25
  99. ansible/module_utils/csharp/Ansible.Process.cs +1 -1
  100. ansible/module_utils/distro/__init__.py +1 -1
  101. ansible/module_utils/distro/_distro.py +8 -4
  102. ansible/module_utils/facts/collector.py +2 -0
  103. ansible/module_utils/facts/default_collectors.py +3 -1
  104. ansible/module_utils/facts/hardware/aix.py +54 -52
  105. ansible/module_utils/facts/hardware/darwin.py +37 -34
  106. ansible/module_utils/facts/hardware/freebsd.py +55 -15
  107. ansible/module_utils/facts/hardware/hpux.py +3 -0
  108. ansible/module_utils/facts/hardware/linux.py +101 -57
  109. ansible/module_utils/facts/hardware/netbsd.py +3 -0
  110. ansible/module_utils/facts/hardware/openbsd.py +4 -1
  111. ansible/module_utils/facts/hardware/sunos.py +7 -1
  112. ansible/module_utils/facts/network/aix.py +16 -17
  113. ansible/module_utils/facts/network/fc_wwn.py +4 -1
  114. ansible/module_utils/facts/network/hpux.py +21 -4
  115. ansible/module_utils/facts/network/iscsi.py +7 -8
  116. ansible/module_utils/facts/network/linux.py +0 -2
  117. ansible/module_utils/facts/other/facter.py +9 -4
  118. ansible/module_utils/facts/other/ohai.py +5 -5
  119. ansible/module_utils/facts/packages.py +49 -7
  120. ansible/module_utils/facts/sysctl.py +33 -31
  121. ansible/module_utils/facts/system/distribution.py +2 -2
  122. ansible/module_utils/facts/system/local.py +12 -22
  123. ansible/module_utils/facts/system/service_mgr.py +3 -1
  124. ansible/module_utils/facts/system/systemd.py +47 -0
  125. ansible/module_utils/facts/timeout.py +1 -1
  126. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  127. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  128. ansible/module_utils/splitter.py +1 -1
  129. ansible/modules/add_host.py +1 -1
  130. ansible/modules/apt.py +43 -32
  131. ansible/modules/apt_key.py +6 -6
  132. ansible/modules/apt_repository.py +23 -14
  133. ansible/modules/assemble.py +7 -2
  134. ansible/modules/assert.py +4 -4
  135. ansible/modules/blockinfile.py +3 -6
  136. ansible/modules/command.py +1 -1
  137. ansible/modules/copy.py +4 -4
  138. ansible/modules/cron.py +13 -10
  139. ansible/modules/deb822_repository.py +16 -17
  140. ansible/modules/debconf.py +25 -22
  141. ansible/modules/debug.py +1 -1
  142. ansible/modules/dnf.py +79 -164
  143. ansible/modules/dnf5.py +54 -29
  144. ansible/modules/dpkg_selections.py +2 -2
  145. ansible/modules/expect.py +2 -2
  146. ansible/modules/fetch.py +2 -2
  147. ansible/modules/file.py +5 -3
  148. ansible/modules/find.py +40 -12
  149. ansible/modules/gather_facts.py +4 -2
  150. ansible/modules/get_url.py +29 -24
  151. ansible/modules/git.py +35 -35
  152. ansible/modules/group.py +71 -1
  153. ansible/modules/hostname.py +2 -4
  154. ansible/modules/include_vars.py +5 -5
  155. ansible/modules/iptables.py +13 -16
  156. ansible/modules/known_hosts.py +16 -13
  157. ansible/modules/lineinfile.py +1 -4
  158. ansible/modules/meta.py +6 -1
  159. ansible/modules/mount_facts.py +651 -0
  160. ansible/modules/package_facts.py +63 -80
  161. ansible/modules/pause.py +4 -3
  162. ansible/modules/pip.py +14 -14
  163. ansible/modules/replace.py +1 -4
  164. ansible/modules/rpm_key.py +31 -11
  165. ansible/modules/service.py +8 -8
  166. ansible/modules/service_facts.py +20 -5
  167. ansible/modules/set_stats.py +1 -1
  168. ansible/modules/setup.py +3 -3
  169. ansible/modules/stat.py +3 -3
  170. ansible/modules/subversion.py +1 -1
  171. ansible/modules/systemd.py +16 -10
  172. ansible/modules/systemd_service.py +16 -10
  173. ansible/modules/sysvinit.py +4 -4
  174. ansible/modules/unarchive.py +35 -22
  175. ansible/modules/uri.py +24 -18
  176. ansible/modules/user.py +148 -13
  177. ansible/modules/validate_argument_spec.py +3 -3
  178. ansible/modules/wait_for_connection.py +2 -1
  179. ansible/modules/yum_repository.py +136 -179
  180. ansible/parsing/dataloader.py +2 -2
  181. ansible/parsing/mod_args.py +11 -10
  182. ansible/parsing/vault/__init__.py +8 -3
  183. ansible/parsing/yaml/constructor.py +10 -8
  184. ansible/parsing/yaml/objects.py +1 -1
  185. ansible/playbook/base.py +12 -23
  186. ansible/playbook/helpers.py +4 -0
  187. ansible/playbook/loop_control.py +8 -0
  188. ansible/playbook/play.py +4 -22
  189. ansible/playbook/play_context.py +0 -16
  190. ansible/playbook/playbook_include.py +2 -2
  191. ansible/playbook/role/__init__.py +2 -2
  192. ansible/plugins/__init__.py +2 -0
  193. ansible/plugins/action/__init__.py +7 -9
  194. ansible/plugins/action/dnf.py +7 -5
  195. ansible/plugins/action/package.py +5 -4
  196. ansible/plugins/action/reboot.py +2 -2
  197. ansible/plugins/become/__init__.py +1 -1
  198. ansible/plugins/callback/__init__.py +44 -3
  199. ansible/plugins/callback/default.py +1 -1
  200. ansible/plugins/cliconf/__init__.py +1 -1
  201. ansible/plugins/connection/paramiko_ssh.py +2 -80
  202. ansible/plugins/connection/psrp.py +33 -82
  203. ansible/plugins/connection/ssh.py +0 -8
  204. ansible/plugins/connection/winrm.py +46 -1
  205. ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
  206. ansible/plugins/doc_fragments/constructed.py +10 -10
  207. ansible/plugins/doc_fragments/default_callback.py +8 -8
  208. ansible/plugins/doc_fragments/files.py +5 -5
  209. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  210. ansible/plugins/doc_fragments/result_format_callback.py +6 -6
  211. ansible/plugins/doc_fragments/return_common.py +1 -1
  212. ansible/plugins/doc_fragments/shell_common.py +2 -10
  213. ansible/plugins/doc_fragments/shell_windows.py +0 -9
  214. ansible/plugins/doc_fragments/url.py +2 -2
  215. ansible/plugins/doc_fragments/url_windows.py +4 -5
  216. ansible/plugins/doc_fragments/validate.py +1 -1
  217. ansible/plugins/filter/core.py +2 -0
  218. ansible/plugins/filter/human_to_bytes.yml +9 -0
  219. ansible/plugins/filter/password_hash.yml +1 -1
  220. ansible/plugins/filter/strftime.yml +1 -1
  221. ansible/plugins/filter/to_nice_json.yml +7 -3
  222. ansible/plugins/filter/to_uuid.yml +1 -1
  223. ansible/plugins/filter/unique.yml +28 -0
  224. ansible/plugins/inventory/script.py +1 -1
  225. ansible/plugins/list.py +1 -1
  226. ansible/plugins/loader.py +0 -11
  227. ansible/plugins/lookup/config.py +1 -1
  228. ansible/plugins/lookup/csvfile.py +21 -9
  229. ansible/plugins/lookup/env.py +8 -9
  230. ansible/plugins/lookup/ini.py +10 -1
  231. ansible/plugins/lookup/random_choice.py +2 -2
  232. ansible/plugins/lookup/url.py +7 -2
  233. ansible/plugins/shell/__init__.py +15 -20
  234. ansible/plugins/shell/powershell.py +9 -6
  235. ansible/plugins/strategy/__init__.py +18 -7
  236. ansible/plugins/strategy/linear.py +1 -13
  237. ansible/plugins/test/core.py +23 -1
  238. ansible/plugins/test/issubset.yml +1 -1
  239. ansible/plugins/test/subset.yml +1 -1
  240. ansible/plugins/test/timedout.yml +20 -0
  241. ansible/plugins/test/vault_encrypted.yml +6 -6
  242. ansible/plugins/test/vaulted_file.yml +19 -0
  243. ansible/release.py +2 -2
  244. ansible/template/__init__.py +3 -8
  245. ansible/utils/collection_loader/_collection_finder.py +23 -55
  246. ansible/utils/display.py +44 -31
  247. ansible/utils/galaxy.py +1 -1
  248. ansible/utils/jsonrpc.py +1 -1
  249. ansible/utils/listify.py +1 -5
  250. ansible/utils/path.py +3 -0
  251. ansible/utils/vars.py +18 -27
  252. ansible/vars/manager.py +7 -150
  253. ansible/vars/plugins.py +1 -1
  254. ansible_core-2.18.0rc1.dist-info/Apache-License.txt +202 -0
  255. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/METADATA +36 -23
  256. ansible_core-2.18.0rc1.dist-info/MIT-license.txt +14 -0
  257. ansible_core-2.18.0rc1.dist-info/PSF-license.txt +48 -0
  258. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/RECORD +321 -316
  259. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/entry_points.txt +1 -1
  260. ansible_core-2.18.0rc1.dist-info/simplified_bsd.txt +8 -0
  261. ansible_test/_data/completion/docker.txt +7 -7
  262. ansible_test/_data/completion/remote.txt +5 -4
  263. ansible_test/_data/completion/windows.txt +4 -4
  264. ansible_test/_data/requirements/ansible-test.txt +1 -2
  265. ansible_test/_data/requirements/constraints.txt +1 -2
  266. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  267. ansible_test/_data/requirements/sanity.changelog.in +1 -1
  268. ansible_test/_data/requirements/sanity.changelog.txt +4 -4
  269. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  270. ansible_test/_data/requirements/sanity.import.txt +1 -1
  271. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
  272. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  273. ansible_test/_data/requirements/sanity.pylint.txt +6 -8
  274. ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -2
  275. ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
  276. ansible_test/_data/requirements/sanity.yamllint.in +1 -0
  277. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  278. ansible_test/_internal/ansible_util.py +8 -35
  279. ansible_test/_internal/ci/azp.py +1 -1
  280. ansible_test/_internal/classification/__init__.py +0 -2
  281. ansible_test/_internal/cli/parsers/key_value_parsers.py +3 -0
  282. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -1
  283. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  284. ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
  285. ansible_test/_internal/commands/sanity/__init__.py +96 -19
  286. ansible_test/_internal/commands/sanity/pylint.py +20 -24
  287. ansible_test/_internal/completion.py +2 -0
  288. ansible_test/_internal/constants.py +0 -1
  289. ansible_test/_internal/coverage_util.py +1 -2
  290. ansible_test/_internal/docker_util.py +10 -2
  291. ansible_test/_internal/encoding.py +4 -4
  292. ansible_test/_internal/host_configs.py +10 -0
  293. ansible_test/_internal/host_profiles.py +9 -13
  294. ansible_test/_internal/pypi_proxy.py +1 -1
  295. ansible_test/_internal/python_requirements.py +5 -14
  296. ansible_test/_internal/timeout.py +1 -1
  297. ansible_test/_internal/util.py +56 -8
  298. ansible_test/_internal/util_common.py +5 -1
  299. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
  300. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
  301. ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
  302. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +5 -0
  303. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +5 -0
  304. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +5 -0
  305. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +6 -0
  306. ansible_test/_util/controller/sanity/pylint/config/default.cfg +6 -0
  307. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
  308. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +3 -4
  309. ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
  310. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
  311. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
  312. ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
  313. ansible_test/_util/target/common/constants.py +2 -2
  314. ansible_test/_util/target/injector/python.py +5 -0
  315. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
  316. ansible_test/_util/target/sanity/import/importer.py +1 -1
  317. ansible_test/_util/target/setup/bootstrap.sh +6 -17
  318. ansible_test/_util/target/setup/requirements.py +18 -24
  319. ansible_test/config/config.yml +1 -1
  320. ansible_core-2.17.5rc1.data/scripts/ansible-test +0 -44
  321. ansible_test/_data/requirements/sanity.mypy.in +0 -10
  322. ansible_test/_data/requirements/sanity.mypy.txt +0 -18
  323. ansible_test/_internal/commands/sanity/mypy.py +0 -274
  324. ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
  325. ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
  326. ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
  327. ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
  328. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/COPYING +0 -0
  329. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/WHEEL +0 -0
  330. {ansible_core-2.17.5rc1.dist-info → ansible_core-2.18.0rc1.dist-info}/top_level.txt +0 -0
ansible/__main__.py CHANGED
@@ -3,9 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
- import importlib
7
- import os
8
- import sys
9
6
 
10
7
  from importlib.metadata import distribution
11
8
 
@@ -19,22 +16,10 @@ def main():
19
16
  ep_map = {_short_name(ep.name): ep for ep in dist.entry_points if ep.group == 'console_scripts'}
20
17
 
21
18
  parser = argparse.ArgumentParser(prog='python -m ansible', add_help=False)
22
- parser.add_argument('entry_point', choices=list(ep_map) + ['test'])
19
+ parser.add_argument('entry_point', choices=list(ep_map))
23
20
  args, extra = parser.parse_known_args()
24
21
 
25
- if args.entry_point == 'test':
26
- ansible_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
27
- source_root = os.path.join(ansible_root, 'test', 'lib')
28
-
29
- if os.path.exists(os.path.join(source_root, 'ansible_test', '_internal', '__init__.py')):
30
- # running from source, use that version of ansible-test instead of any version that may already be installed
31
- sys.path.insert(0, source_root)
32
-
33
- module = importlib.import_module('ansible_test._util.target.cli.ansible_test_cli_stub')
34
- main = module.main
35
- else:
36
- main = ep_map[args.entry_point].load()
37
-
22
+ main = ep_map[args.entry_point].load()
38
23
  main([args.entry_point] + extra)
39
24
 
40
25
 
ansible/cli/__init__.py CHANGED
@@ -11,9 +11,9 @@ import sys
11
11
 
12
12
  # Used for determining if the system is running a new enough python version
13
13
  # and should only restrict on our documented minimum versions
14
- if sys.version_info < (3, 10):
14
+ if sys.version_info < (3, 11):
15
15
  raise SystemExit(
16
- 'ERROR: Ansible requires Python 3.10 or newer on the controller. '
16
+ 'ERROR: Ansible requires Python 3.11 or newer on the controller. '
17
17
  'Current version: %s' % ''.join(sys.version.splitlines())
18
18
  )
19
19
 
@@ -167,19 +167,7 @@ class CLI(ABC):
167
167
  else:
168
168
  display.v(u"No config file found; using defaults")
169
169
 
170
- # warn about deprecated config options
171
- for deprecated in C.config.DEPRECATED:
172
- name = deprecated[0]
173
- why = deprecated[1]['why']
174
- if 'alternatives' in deprecated[1]:
175
- alt = ', use %s instead' % deprecated[1]['alternatives']
176
- else:
177
- alt = ''
178
- ver = deprecated[1].get('version')
179
- date = deprecated[1].get('date')
180
- collection_name = deprecated[1].get('collection_name')
181
- display.deprecated("%s option, %s%s" % (name, why, alt),
182
- version=ver, date=date, collection_name=collection_name)
170
+ C.handle_config_noise(display)
183
171
 
184
172
  @staticmethod
185
173
  def split_vault_id(vault_id):
ansible/cli/config.py CHANGED
@@ -9,9 +9,10 @@ from __future__ import annotations
9
9
  from ansible.cli import CLI
10
10
 
11
11
  import os
12
- import yaml
13
12
  import shlex
14
13
  import subprocess
14
+ import sys
15
+ import yaml
15
16
 
16
17
  from collections.abc import Mapping
17
18
 
@@ -21,7 +22,7 @@ import ansible.plugins.loader as plugin_loader
21
22
  from ansible import constants as C
22
23
  from ansible.cli.arguments import option_helpers as opt_help
23
24
  from ansible.config.manager import ConfigManager, Setting
24
- from ansible.errors import AnsibleError, AnsibleOptionsError
25
+ from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleRequiredOptionError
25
26
  from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
26
27
  from ansible.module_utils.common.json import json_dump
27
28
  from ansible.module_utils.six import string_types
@@ -34,6 +35,9 @@ from ansible.utils.path import unfrackpath
34
35
  display = Display()
35
36
 
36
37
 
38
+ _IGNORE_CHANGED = frozenset({'_terms', '_input'})
39
+
40
+
37
41
  def yaml_dump(data, default_flow_style=False, default_style=None):
38
42
  return yaml.dump(data, Dumper=AnsibleDumper, default_flow_style=default_flow_style, default_style=default_style)
39
43
 
@@ -49,6 +53,37 @@ def get_constants():
49
53
  return get_constants.cvars
50
54
 
51
55
 
56
+ def _ansible_env_vars(varname):
57
+ ''' return true or false depending if variable name is possibly a 'configurable' ansible env variable '''
58
+ return all(
59
+ [
60
+ varname.startswith("ANSIBLE_"),
61
+ not varname.startswith(("ANSIBLE_TEST_", "ANSIBLE_LINT_")),
62
+ varname not in ("ANSIBLE_CONFIG", "ANSIBLE_DEV_HOME"),
63
+ ]
64
+ )
65
+
66
+
67
+ def _get_evar_list(settings):
68
+ data = []
69
+ for setting in settings:
70
+ if 'env' in settings[setting] and settings[setting]['env']:
71
+ for varname in settings[setting]['env']:
72
+ data.append(varname.get('name'))
73
+ return data
74
+
75
+
76
+ def _get_ini_entries(settings):
77
+ data = {}
78
+ for setting in settings:
79
+ if 'ini' in settings[setting] and settings[setting]['ini']:
80
+ for kv in settings[setting]['ini']:
81
+ if not kv['section'] in data:
82
+ data[kv['section']] = set()
83
+ data[kv['section']].add(kv['key'])
84
+ return data
85
+
86
+
52
87
  class ConfigCLI(CLI):
53
88
  """ Config command line class """
54
89
 
@@ -99,9 +134,13 @@ class ConfigCLI(CLI):
99
134
  init_parser.add_argument('--disabled', dest='commented', action='store_true', default=False,
100
135
  help='Prefixes all entries with a comment character to disable them')
101
136
 
102
- # search_parser = subparsers.add_parser('find', help='Search configuration')
103
- # search_parser.set_defaults(func=self.execute_search)
104
- # search_parser.add_argument('args', help='Search term', metavar='<search term>')
137
+ validate_parser = subparsers.add_parser('validate',
138
+ help='Validate the configuration file and environment variables. '
139
+ 'By default it only checks the base settings without accounting for plugins (see -t).',
140
+ parents=[common])
141
+ validate_parser.set_defaults(func=self.execute_validate)
142
+ validate_parser.add_argument('--format', '-f', dest='format', action='store', choices=['ini', 'env'] , default='ini',
143
+ help='Output format for init')
105
144
 
106
145
  def post_process_args(self, options):
107
146
  options = super(ConfigCLI, self).post_process_args(options)
@@ -113,6 +152,10 @@ class ConfigCLI(CLI):
113
152
 
114
153
  super(ConfigCLI, self).run()
115
154
 
155
+ # initialize each galaxy server's options from known listed servers
156
+ self._galaxy_servers = [s for s in C.GALAXY_SERVER_LIST or [] if s] # clean list, reused later here
157
+ C.config.load_galaxy_server_defs(self._galaxy_servers)
158
+
116
159
  if context.CLIARGS['config_file']:
117
160
  self.config_file = unfrackpath(context.CLIARGS['config_file'], follow=False)
118
161
  b_config = to_bytes(self.config_file)
@@ -226,11 +269,17 @@ class ConfigCLI(CLI):
226
269
  '''
227
270
  build a dict with the list requested configs
228
271
  '''
272
+
229
273
  config_entries = {}
230
274
  if context.CLIARGS['type'] in ('base', 'all'):
231
275
  # this dumps main/common configs
232
276
  config_entries = self.config.get_configuration_definitions(ignore_private=True)
233
277
 
278
+ # for base and all, we include galaxy servers
279
+ config_entries['GALAXY_SERVERS'] = {}
280
+ for server in self._galaxy_servers:
281
+ config_entries['GALAXY_SERVERS'][server] = self.config.get_configuration_definitions('galaxy_server', server)
282
+
234
283
  if context.CLIARGS['type'] != 'base':
235
284
  config_entries['PLUGINS'] = {}
236
285
 
@@ -239,6 +288,7 @@ class ConfigCLI(CLI):
239
288
  for ptype in C.CONFIGURABLE_PLUGINS:
240
289
  config_entries['PLUGINS'][ptype.upper()] = self._list_plugin_settings(ptype)
241
290
  elif context.CLIARGS['type'] != 'base':
291
+ # only for requested types
242
292
  config_entries['PLUGINS'][context.CLIARGS['type']] = self._list_plugin_settings(context.CLIARGS['type'], context.CLIARGS['args'])
243
293
 
244
294
  return config_entries
@@ -358,7 +408,7 @@ class ConfigCLI(CLI):
358
408
  elif default is None:
359
409
  default = ''
360
410
 
361
- if context.CLIARGS['commented']:
411
+ if context.CLIARGS.get('commented', False):
362
412
  entry['key'] = ';%s' % entry['key']
363
413
 
364
414
  key = desc + '\n%s=%s' % (entry['key'], default)
@@ -408,13 +458,13 @@ class ConfigCLI(CLI):
408
458
 
409
459
  entries = []
410
460
  for setting in sorted(config):
411
- changed = (config[setting].origin not in ('default', 'REQUIRED'))
461
+ changed = (config[setting].origin not in ('default', 'REQUIRED') and setting not in _IGNORE_CHANGED)
412
462
 
413
463
  if context.CLIARGS['format'] == 'display':
414
464
  if isinstance(config[setting], Setting):
415
465
  # proceed normally
416
466
  value = config[setting].value
417
- if config[setting].origin == 'default':
467
+ if config[setting].origin == 'default' or setting in _IGNORE_CHANGED:
418
468
  color = 'green'
419
469
  value = self.config.template_default(value, get_constants())
420
470
  elif config[setting].origin == 'REQUIRED':
@@ -431,6 +481,8 @@ class ConfigCLI(CLI):
431
481
  else:
432
482
  entry = {}
433
483
  for key in config[setting]._fields:
484
+ if key == 'type':
485
+ continue
434
486
  entry[key] = getattr(config[setting], key)
435
487
 
436
488
  if not context.CLIARGS['only_changed'] or changed:
@@ -439,7 +491,10 @@ class ConfigCLI(CLI):
439
491
  return entries
440
492
 
441
493
  def _get_global_configs(self):
442
- config = self.config.get_configuration_definitions(ignore_private=True).copy()
494
+
495
+ # Add base
496
+ config = self.config.get_configuration_definitions(ignore_private=True)
497
+ # convert to settings
443
498
  for setting in config.keys():
444
499
  v, o = C.config.get_config_value_and_origin(setting, cfile=self.config_file, variables=get_constants())
445
500
  config[setting] = Setting(setting, v, o, None)
@@ -451,7 +506,7 @@ class ConfigCLI(CLI):
451
506
  # prep loading
452
507
  loader = getattr(plugin_loader, '%s_loader' % ptype)
453
508
 
454
- # acumulators
509
+ # accumulators
455
510
  output = []
456
511
  config_entries = {}
457
512
 
@@ -468,7 +523,7 @@ class ConfigCLI(CLI):
468
523
  plugin_cs = loader.all(class_only=True)
469
524
 
470
525
  for plugin in plugin_cs:
471
- # in case of deprecastion they diverge
526
+ # in case of deprecation they diverge
472
527
  finalname = name = plugin._load_name
473
528
  if name.startswith('_'):
474
529
  if os.path.islink(plugin._original_path):
@@ -491,12 +546,9 @@ class ConfigCLI(CLI):
491
546
  for setting in config_entries[finalname].keys():
492
547
  try:
493
548
  v, o = C.config.get_config_value_and_origin(setting, cfile=self.config_file, plugin_type=ptype, plugin_name=name, variables=get_constants())
494
- except AnsibleError as e:
495
- if to_text(e).startswith('No setting was provided for required configuration'):
496
- v = None
497
- o = 'REQUIRED'
498
- else:
499
- raise e
549
+ except AnsibleRequiredOptionError:
550
+ v = None
551
+ o = 'REQUIRED'
500
552
 
501
553
  if v is None and o is None:
502
554
  # not all cases will be error
@@ -516,17 +568,60 @@ class ConfigCLI(CLI):
516
568
 
517
569
  return output
518
570
 
571
+ def _get_galaxy_server_configs(self):
572
+
573
+ output = []
574
+ # add galaxy servers
575
+ for server in self._galaxy_servers:
576
+ server_config = {}
577
+ s_config = self.config.get_configuration_definitions('galaxy_server', server)
578
+ for setting in s_config.keys():
579
+ try:
580
+ v, o = C.config.get_config_value_and_origin(setting, plugin_type='galaxy_server', plugin_name=server, cfile=self.config_file)
581
+ except AnsibleError as e:
582
+ if s_config[setting].get('required', False):
583
+ v = None
584
+ o = 'REQUIRED'
585
+ else:
586
+ raise e
587
+ if v is None and o is None:
588
+ # not all cases will be error
589
+ o = 'REQUIRED'
590
+ server_config[setting] = Setting(setting, v, o, None)
591
+ if context.CLIARGS['format'] == 'display':
592
+ if not context.CLIARGS['only_changed'] or server_config:
593
+ equals = '=' * len(server)
594
+ output.append(f'\n{server}\n{equals}')
595
+ output.extend(self._render_settings(server_config))
596
+ else:
597
+ output.append({server: server_config})
598
+
599
+ return output
600
+
519
601
  def execute_dump(self):
520
602
  '''
521
603
  Shows the current settings, merges ansible.cfg if specified
522
604
  '''
523
- if context.CLIARGS['type'] == 'base':
524
- # deal with base
525
- output = self._get_global_configs()
526
- elif context.CLIARGS['type'] == 'all':
605
+ output = []
606
+ if context.CLIARGS['type'] in ('base', 'all'):
527
607
  # deal with base
528
608
  output = self._get_global_configs()
529
- # deal with plugins
609
+
610
+ # add galaxy servers
611
+ server_config_list = self._get_galaxy_server_configs()
612
+ if context.CLIARGS['format'] == 'display':
613
+ output.append('\nGALAXY_SERVERS:\n')
614
+ output.extend(server_config_list)
615
+ else:
616
+ configs = {}
617
+ for server_config in server_config_list:
618
+ server = list(server_config.keys())[0]
619
+ server_reduced_config = server_config.pop(server)
620
+ configs[server] = server_reduced_config
621
+ output.append({'GALAXY_SERVERS': configs})
622
+
623
+ if context.CLIARGS['type'] == 'all':
624
+ # add all plugins
530
625
  for ptype in C.CONFIGURABLE_PLUGINS:
531
626
  plugin_list = self._get_plugin_configs(ptype, context.CLIARGS['args'])
532
627
  if context.CLIARGS['format'] == 'display':
@@ -539,8 +634,9 @@ class ConfigCLI(CLI):
539
634
  else:
540
635
  pname = '%s_PLUGINS' % ptype.upper()
541
636
  output.append({pname: plugin_list})
542
- else:
543
- # deal with plugins
637
+
638
+ elif context.CLIARGS['type'] != 'base':
639
+ # deal with specific plugin
544
640
  output = self._get_plugin_configs(context.CLIARGS['type'], context.CLIARGS['args'])
545
641
 
546
642
  if context.CLIARGS['format'] == 'display':
@@ -552,6 +648,73 @@ class ConfigCLI(CLI):
552
648
 
553
649
  self.pager(to_text(text, errors='surrogate_or_strict'))
554
650
 
651
+ def execute_validate(self):
652
+
653
+ found = False
654
+ config_entries = self._list_entries_from_args()
655
+ plugin_types = config_entries.pop('PLUGINS', None)
656
+ galaxy_servers = config_entries.pop('GALAXY_SERVERS', None)
657
+
658
+ if context.CLIARGS['format'] == 'ini':
659
+ if C.CONFIG_FILE is not None:
660
+ # validate ini config since it is found
661
+
662
+ sections = _get_ini_entries(config_entries)
663
+ # Also from plugins
664
+ if plugin_types:
665
+ for ptype in plugin_types:
666
+ for plugin in plugin_types[ptype].keys():
667
+ plugin_sections = _get_ini_entries(plugin_types[ptype][plugin])
668
+ for s in plugin_sections:
669
+ if s in sections:
670
+ sections[s].update(plugin_sections[s])
671
+ else:
672
+ sections[s] = plugin_sections[s]
673
+ if galaxy_servers:
674
+ for server in galaxy_servers:
675
+ server_sections = _get_ini_entries(galaxy_servers[server])
676
+ for s in server_sections:
677
+ if s in sections:
678
+ sections[s].update(server_sections[s])
679
+ else:
680
+ sections[s] = server_sections[s]
681
+ if sections:
682
+ p = C.config._parsers[C.CONFIG_FILE]
683
+ for s in p.sections():
684
+ # check for valid sections
685
+ if s not in sections:
686
+ display.error(f"Found unknown section '{s}' in '{C.CONFIG_FILE}.")
687
+ found = True
688
+ continue
689
+
690
+ # check keys in valid sections
691
+ for k in p.options(s):
692
+ if k not in sections[s]:
693
+ display.error(f"Found unknown key '{k}' in section '{s}' in '{C.CONFIG_FILE}.")
694
+ found = True
695
+
696
+ elif context.CLIARGS['format'] == 'env':
697
+ # validate any 'ANSIBLE_' env vars found
698
+ evars = [varname for varname in os.environ.keys() if _ansible_env_vars(varname)]
699
+ if evars:
700
+ data = _get_evar_list(config_entries)
701
+ if plugin_types:
702
+ for ptype in plugin_types:
703
+ for plugin in plugin_types[ptype].keys():
704
+ data.extend(_get_evar_list(plugin_types[ptype][plugin]))
705
+
706
+ for evar in evars:
707
+ if evar not in data:
708
+ display.error(f"Found unknown environment variable '{evar}'.")
709
+ found = True
710
+
711
+ # we found discrepancies!
712
+ if found:
713
+ sys.exit(1)
714
+
715
+ # allsgood
716
+ display.display("All configurations seem valid!")
717
+
555
718
 
556
719
  def main(args=None):
557
720
  ConfigCLI.cli_executor(args)
ansible/cli/console.py CHANGED
@@ -545,7 +545,7 @@ class ConsoleCLI(CLI, cmd.Cmd):
545
545
  if path:
546
546
  module_loader.add_directory(path)
547
547
 
548
- # dynamically add 'cannonical' modules as commands, aliases coudld be used and dynamically loaded
548
+ # dynamically add 'canonical' modules as commands, aliases could be used and dynamically loaded
549
549
  self.modules = self.list_modules()
550
550
  for module in self.modules:
551
551
  setattr(self, 'do_' + module, lambda arg, module=module: self.default(module + ' ' + arg))
ansible/cli/doc.py CHANGED
@@ -50,7 +50,7 @@ PB_OBJECTS = ['Play', 'Role', 'Block', 'Task', 'Handler']
50
50
  PB_LOADED = {}
51
51
  SNIPPETS = ['inventory', 'lookup', 'module']
52
52
 
53
- # harcoded from ascii values
53
+ # hardcoded from ascii values
54
54
  STYLE = {
55
55
  'BLINK': '\033[5m',
56
56
  'BOLD': '\033[1m',
@@ -71,8 +71,14 @@ NOCOLOR = {
71
71
  'PLUGIN': r'[%s]',
72
72
  }
73
73
 
74
- # TODO: make configurable
75
- ref_style = {'MODULE': 'yellow', 'REF': 'magenta', 'LINK': 'cyan', 'DEP': 'magenta', 'CONSTANT': 'dark gray', 'PLUGIN': 'yellow'}
74
+ ref_style = {
75
+ 'MODULE': C.COLOR_DOC_MODULE,
76
+ 'REF': C.COLOR_DOC_REFERENCE,
77
+ 'LINK': C.COLOR_DOC_LINK,
78
+ 'DEP': C.COLOR_DOC_DEPRECATED,
79
+ 'CONSTANT': C.COLOR_DOC_CONSTANT,
80
+ 'PLUGIN': C.COLOR_DOC_PLUGIN,
81
+ }
76
82
 
77
83
 
78
84
  def jdump(text):
@@ -381,6 +387,12 @@ class RoleMixin(object):
381
387
 
382
388
  for role, collection, role_path in (roles | collroles):
383
389
  argspec = self._load_argspec(role, role_path, collection)
390
+ if 'error' in argspec:
391
+ if fail_on_errors:
392
+ raise argspec['exception']
393
+ else:
394
+ display.warning('Skipping role (%s) due to: %s' % (role, argspec['error']), True)
395
+ continue
384
396
  fqcn, doc = self._build_doc(role, role_path, collection, argspec, entry_point)
385
397
  if doc:
386
398
  result[fqcn] = doc
@@ -881,6 +893,7 @@ class DocCLI(CLI, RoleMixin):
881
893
  plugin_type = context.CLIARGS['type'].lower()
882
894
  do_json = context.CLIARGS['json_format'] or context.CLIARGS['dump']
883
895
  listing = context.CLIARGS['list_files'] or context.CLIARGS['list_dir']
896
+ no_fail = bool(not context.CLIARGS['no_fail_on_errors'])
884
897
 
885
898
  if context.CLIARGS['list_files']:
886
899
  content = 'files'
@@ -903,7 +916,6 @@ class DocCLI(CLI, RoleMixin):
903
916
  docs['all'] = {}
904
917
  for ptype in ptypes:
905
918
 
906
- no_fail = bool(not context.CLIARGS['no_fail_on_errors'])
907
919
  if ptype == 'role':
908
920
  roles = self._create_role_list(fail_on_errors=no_fail)
909
921
  docs['all'][ptype] = self._create_role_doc(roles.keys(), context.CLIARGS['entry_point'], fail_on_errors=no_fail)
@@ -929,7 +941,7 @@ class DocCLI(CLI, RoleMixin):
929
941
  if plugin_type == 'keyword':
930
942
  docs = DocCLI._get_keywords_docs(context.CLIARGS['args'])
931
943
  elif plugin_type == 'role':
932
- docs = self._create_role_doc(context.CLIARGS['args'], context.CLIARGS['entry_point'])
944
+ docs = self._create_role_doc(context.CLIARGS['args'], context.CLIARGS['entry_point'], fail_on_errors=no_fail)
933
945
  else:
934
946
  # display specific plugin docs
935
947
  docs = self._get_plugins_docs(plugin_type, context.CLIARGS['args'])
@@ -1083,7 +1095,7 @@ class DocCLI(CLI, RoleMixin):
1083
1095
  text = DocCLI.get_man_text(doc, collection_name, plugin_type)
1084
1096
  except Exception as e:
1085
1097
  display.vvv(traceback.format_exc())
1086
- raise AnsibleError("Unable to retrieve documentation from '%s' due to: %s" % (plugin, to_native(e)), orig_exc=e)
1098
+ raise AnsibleError("Unable to retrieve documentation from '%s'" % (plugin), orig_exc=e)
1087
1099
 
1088
1100
  return text
1089
1101
 
@@ -1189,7 +1201,7 @@ class DocCLI(CLI, RoleMixin):
1189
1201
  opt_leadin = "-"
1190
1202
  key = "%s%s %s" % (base_indent, opt_leadin, _format(o, 'yellow'))
1191
1203
 
1192
- # description is specifically formated and can either be string or list of strings
1204
+ # description is specifically formatted and can either be string or list of strings
1193
1205
  if 'description' not in opt:
1194
1206
  raise AnsibleError("All (sub-)options and return values must have a 'description' field")
1195
1207
  text.append('')
@@ -1339,6 +1351,17 @@ class DocCLI(CLI, RoleMixin):
1339
1351
  # use empty indent since this affects the start of the yaml doc, not it's keys
1340
1352
  text.append(DocCLI._indent_lines(DocCLI._dump_yaml({k.upper(): doc[k]}), ''))
1341
1353
 
1354
+ if doc.get('examples', False):
1355
+ text.append('')
1356
+ text.append(_format("EXAMPLES:", 'bold'))
1357
+ if isinstance(doc['examples'], string_types):
1358
+ text.append(doc.pop('examples').strip())
1359
+ else:
1360
+ try:
1361
+ text.append(yaml_dump(doc.pop('examples'), indent=2, default_flow_style=False))
1362
+ except Exception as e:
1363
+ raise AnsibleParserError("Unable to parse examples section", orig_exc=e)
1364
+
1342
1365
  return text
1343
1366
 
1344
1367
  @staticmethod
@@ -1370,16 +1393,15 @@ class DocCLI(CLI, RoleMixin):
1370
1393
  if doc.get('deprecated', False):
1371
1394
  text.append(_format("DEPRECATED: ", 'bold', 'DEP'))
1372
1395
  if isinstance(doc['deprecated'], dict):
1373
- if 'removed_at_date' in doc['deprecated']:
1374
- text.append(
1375
- "\tReason: %(why)s\n\tWill be removed in a release after %(removed_at_date)s\n\tAlternatives: %(alternative)s" % doc.pop('deprecated')
1376
- )
1377
- else:
1378
- if 'version' in doc['deprecated'] and 'removed_in' not in doc['deprecated']:
1379
- doc['deprecated']['removed_in'] = doc['deprecated']['version']
1380
- text.append("\tReason: %(why)s\n\tWill be removed in: Ansible %(removed_in)s\n\tAlternatives: %(alternative)s" % doc.pop('deprecated'))
1396
+ if 'removed_at_date' not in doc['deprecated'] and 'version' in doc['deprecated'] and 'removed_in' not in doc['deprecated']:
1397
+ doc['deprecated']['removed_in'] = doc['deprecated']['version']
1398
+ try:
1399
+ text.append('\t' + C.config.get_deprecated_msg_from_config(doc['deprecated'], True))
1400
+ except KeyError as e:
1401
+ raise AnsibleError("Invalid deprecation documentation structure", orig_exc=e)
1381
1402
  else:
1382
- text.append("%s" % doc.pop('deprecated'))
1403
+ text.append("%s" % doc['deprecated'])
1404
+ del doc['deprecated']
1383
1405
 
1384
1406
  if doc.pop('has_action', False):
1385
1407
  text.append("")
ansible/cli/galaxy.py CHANGED
@@ -55,7 +55,6 @@ from ansible.module_utils.common.yaml import yaml_dump, yaml_load
55
55
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
56
56
  from ansible.module_utils import six
57
57
  from ansible.parsing.dataloader import DataLoader
58
- from ansible.parsing.yaml.loader import AnsibleLoader
59
58
  from ansible.playbook.role.requirement import RoleRequirement
60
59
  from ansible.template import Templar
61
60
  from ansible.utils.collection_loader import AnsibleCollectionConfig
@@ -66,27 +65,6 @@ from ansible.utils.vars import load_extra_vars
66
65
  display = Display()
67
66
  urlparse = six.moves.urllib.parse.urlparse
68
67
 
69
- # config definition by position: name, required, type
70
- SERVER_DEF = [
71
- ('url', True, 'str'),
72
- ('username', False, 'str'),
73
- ('password', False, 'str'),
74
- ('token', False, 'str'),
75
- ('auth_url', False, 'str'),
76
- ('api_version', False, 'int'),
77
- ('validate_certs', False, 'bool'),
78
- ('client_id', False, 'str'),
79
- ('timeout', False, 'int'),
80
- ]
81
-
82
- # config definition fields
83
- SERVER_ADDITIONAL = {
84
- 'api_version': {'default': None, 'choices': [2, 3]},
85
- 'validate_certs': {'cli': [{'name': 'validate_certs'}]},
86
- 'timeout': {'default': C.GALAXY_SERVER_TIMEOUT, 'cli': [{'name': 'timeout'}]},
87
- 'token': {'default': None},
88
- }
89
-
90
68
 
91
69
  def with_collection_artifacts_manager(wrapped_method):
92
70
  """Inject an artifacts manager if not passed explicitly.
@@ -641,25 +619,8 @@ class GalaxyCLI(CLI):
641
619
 
642
620
  self.galaxy = Galaxy()
643
621
 
644
- def server_config_def(section, key, required, option_type):
645
- config_def = {
646
- 'description': 'The %s of the %s Galaxy server' % (key, section),
647
- 'ini': [
648
- {
649
- 'section': 'galaxy_server.%s' % section,
650
- 'key': key,
651
- }
652
- ],
653
- 'env': [
654
- {'name': 'ANSIBLE_GALAXY_SERVER_%s_%s' % (section.upper(), key.upper())},
655
- ],
656
- 'required': required,
657
- 'type': option_type,
658
- }
659
- if key in SERVER_ADDITIONAL:
660
- config_def.update(SERVER_ADDITIONAL[key])
661
-
662
- return config_def
622
+ # dynamically add per server config depending on declared servers
623
+ C.config.load_galaxy_server_defs(C.GALAXY_SERVER_LIST)
663
624
 
664
625
  galaxy_options = {}
665
626
  for optional_key in ['clear_response_cache', 'no_cache']:
@@ -667,19 +628,12 @@ class GalaxyCLI(CLI):
667
628
  galaxy_options[optional_key] = context.CLIARGS[optional_key]
668
629
 
669
630
  config_servers = []
670
-
671
631
  # Need to filter out empty strings or non truthy values as an empty server list env var is equal to [''].
672
632
  server_list = [s for s in C.GALAXY_SERVER_LIST or [] if s]
673
633
  for server_priority, server_key in enumerate(server_list, start=1):
674
- # Abuse the 'plugin config' by making 'galaxy_server' a type of plugin
675
- # Config definitions are looked up dynamically based on the C.GALAXY_SERVER_LIST entry. We look up the
676
- # section [galaxy_server.<server>] for the values url, username, password, and token.
677
- config_dict = dict((k, server_config_def(server_key, k, req, ensure_type)) for k, req, ensure_type in SERVER_DEF)
678
- defs = AnsibleLoader(yaml_dump(config_dict)).get_single_data()
679
- C.config.initialize_plugin_configuration_definitions('galaxy_server', server_key, defs)
680
634
 
681
635
  # resolve the config created options above with existing config and user options
682
- server_options = C.config.get_plugin_options('galaxy_server', server_key)
636
+ server_options = C.config.get_plugin_options(plugin_type='galaxy_server', name=server_key)
683
637
 
684
638
  # auth_url is used to create the token, but not directly by GalaxyAPI, so
685
639
  # it doesn't need to be passed as kwarg to GalaxyApi, same for others we pop here
ansible/cli/inventory.py CHANGED
@@ -73,12 +73,12 @@ class InventoryCLI(CLI):
73
73
 
74
74
  # list
75
75
  self.parser.add_argument("--export", action="store_true", default=C.INVENTORY_EXPORT, dest='export',
76
- help="When doing an --list, represent in a way that is optimized for export,"
76
+ help="When doing --list, represent in a way that is optimized for export,"
77
77
  "not as an accurate representation of how Ansible has processed it")
78
78
  self.parser.add_argument('--output', default=None, dest='output_file',
79
79
  help="When doing --list, send the inventory to a file instead of to the screen")
80
80
  # self.parser.add_argument("--ignore-vars-plugins", action="store_true", default=False, dest='ignore_vars_plugins',
81
- # help="When doing an --list, skip vars data from vars plugins, by default, this would include group_vars/ and host_vars/")
81
+ # help="When doing --list, skip vars data from vars plugins, by default, this would include group_vars/ and host_vars/")
82
82
 
83
83
  def post_process_args(self, options):
84
84
  options = super(InventoryCLI, self).post_process_args(options)
ansible/cli/pull.py CHANGED
@@ -12,7 +12,7 @@ from ansible.cli import CLI
12
12
  import datetime
13
13
  import os
14
14
  import platform
15
- import random
15
+ import secrets
16
16
  import shlex
17
17
  import shutil
18
18
  import socket
@@ -140,7 +140,7 @@ class PullCLI(CLI):
140
140
 
141
141
  if options.sleep:
142
142
  try:
143
- secs = random.randint(0, int(options.sleep))
143
+ secs = secrets.randbelow(int(options.sleep))
144
144
  options.sleep = secs
145
145
  except ValueError:
146
146
  raise AnsibleOptionsError("%s is not a number." % options.sleep)