ansible-core 2.16.7rc1__py3-none-any.whl → 2.17.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.

Files changed (597) hide show
  1. ansible/__init__.py +1 -3
  2. ansible/__main__.py +1 -0
  3. ansible/_vendor/__init__.py +1 -2
  4. ansible/cli/__init__.py +7 -8
  5. ansible/cli/adhoc.py +1 -2
  6. ansible/cli/arguments/__init__.py +1 -2
  7. ansible/cli/arguments/option_helpers.py +1 -2
  8. ansible/cli/config.py +1 -2
  9. ansible/cli/console.py +1 -2
  10. ansible/cli/doc.py +326 -202
  11. ansible/cli/galaxy.py +9 -3
  12. ansible/cli/inventory.py +4 -6
  13. ansible/cli/playbook.py +1 -2
  14. ansible/cli/pull.py +4 -5
  15. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -4
  16. ansible/cli/vault.py +1 -2
  17. ansible/collections/list.py +1 -0
  18. ansible/compat/__init__.py +1 -4
  19. ansible/compat/importlib_resources.py +1 -2
  20. ansible/compat/{selectors/__init__.py → selectors.py} +12 -12
  21. ansible/config/ansible_builtin_runtime.yml +2 -0
  22. ansible/config/base.yml +154 -149
  23. ansible/config/manager.py +33 -25
  24. ansible/constants.py +1 -2
  25. ansible/context.py +1 -4
  26. ansible/errors/__init__.py +1 -3
  27. ansible/errors/yaml_strings.py +1 -3
  28. ansible/executor/__init__.py +1 -3
  29. ansible/executor/discovery/python_target.py +1 -2
  30. ansible/executor/interpreter_discovery.py +9 -8
  31. ansible/executor/module_common.py +1 -3
  32. ansible/executor/play_iterator.py +1 -3
  33. ansible/executor/playbook_executor.py +1 -3
  34. ansible/executor/powershell/module_manifest.py +2 -3
  35. ansible/executor/process/__init__.py +1 -3
  36. ansible/executor/process/worker.py +1 -3
  37. ansible/executor/stats.py +1 -3
  38. ansible/executor/task_executor.py +2 -3
  39. ansible/executor/task_queue_manager.py +1 -3
  40. ansible/executor/task_result.py +2 -3
  41. ansible/galaxy/__init__.py +1 -2
  42. ansible/galaxy/api.py +10 -2
  43. ansible/galaxy/collection/__init__.py +38 -24
  44. ansible/galaxy/collection/concrete_artifact_manager.py +1 -2
  45. ansible/galaxy/collection/galaxy_api_proxy.py +1 -2
  46. ansible/galaxy/collection/gpg.py +4 -2
  47. ansible/galaxy/data/apb/meta/main.yml.j2 +0 -15
  48. ansible/galaxy/data/container/meta/main.yml.j2 +0 -18
  49. ansible/galaxy/data/default/role/meta/main.yml.j2 +0 -18
  50. ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -2
  51. ansible/galaxy/data/network/library/example_command.py.j2 +1 -2
  52. ansible/galaxy/data/network/library/example_config.py.j2 +1 -2
  53. ansible/galaxy/data/network/library/example_facts.py.j2 +1 -2
  54. ansible/galaxy/data/network/meta/main.yml.j2 +0 -15
  55. ansible/galaxy/data/network/module_utils/example.py.j2 +1 -2
  56. ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -2
  57. ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -2
  58. ansible/galaxy/dependency_resolution/__init__.py +1 -2
  59. ansible/galaxy/dependency_resolution/dataclasses.py +5 -6
  60. ansible/galaxy/dependency_resolution/errors.py +1 -2
  61. ansible/galaxy/dependency_resolution/providers.py +3 -4
  62. ansible/galaxy/dependency_resolution/reporters.py +2 -3
  63. ansible/galaxy/dependency_resolution/resolvers.py +1 -2
  64. ansible/galaxy/dependency_resolution/versioning.py +1 -2
  65. ansible/galaxy/role.py +2 -3
  66. ansible/galaxy/token.py +1 -2
  67. ansible/galaxy/user_agent.py +1 -2
  68. ansible/inventory/data.py +1 -2
  69. ansible/inventory/group.py +1 -2
  70. ansible/inventory/helpers.py +1 -2
  71. ansible/inventory/host.py +1 -3
  72. ansible/inventory/manager.py +1 -2
  73. ansible/module_utils/_text.py +1 -2
  74. ansible/module_utils/ansible_release.py +3 -5
  75. ansible/module_utils/api.py +1 -2
  76. ansible/module_utils/basic.py +113 -158
  77. ansible/module_utils/common/_collections_compat.py +1 -2
  78. ansible/module_utils/common/_utils.py +1 -3
  79. ansible/module_utils/common/arg_spec.py +1 -2
  80. ansible/module_utils/common/collections.py +1 -2
  81. ansible/module_utils/common/dict_transformations.py +1 -2
  82. ansible/module_utils/common/file.py +10 -5
  83. ansible/module_utils/common/json.py +1 -3
  84. ansible/module_utils/common/locale.py +1 -2
  85. ansible/module_utils/common/network.py +2 -3
  86. ansible/module_utils/common/parameters.py +9 -9
  87. ansible/module_utils/common/process.py +12 -5
  88. ansible/module_utils/common/respawn.py +2 -3
  89. ansible/module_utils/common/sys_info.py +1 -2
  90. ansible/module_utils/common/text/converters.py +1 -2
  91. ansible/module_utils/common/text/formatters.py +1 -2
  92. ansible/module_utils/common/validation.py +5 -6
  93. ansible/module_utils/common/warnings.py +1 -2
  94. ansible/module_utils/common/yaml.py +1 -2
  95. ansible/module_utils/compat/datetime.py +1 -3
  96. ansible/module_utils/compat/importlib.py +21 -13
  97. ansible/module_utils/compat/paramiko.py +1 -2
  98. ansible/module_utils/compat/selectors.py +10 -34
  99. ansible/module_utils/compat/selinux.py +1 -2
  100. ansible/module_utils/compat/typing.py +1 -2
  101. ansible/module_utils/compat/version.py +1 -2
  102. ansible/module_utils/connection.py +1 -2
  103. ansible/module_utils/csharp/Ansible.Basic.cs +20 -3
  104. ansible/module_utils/distro/__init__.py +3 -4
  105. ansible/module_utils/distro/_distro.py +230 -234
  106. ansible/module_utils/errors.py +1 -2
  107. ansible/module_utils/facts/__init__.py +1 -2
  108. ansible/module_utils/facts/ansible_collector.py +1 -2
  109. ansible/module_utils/facts/collector.py +1 -2
  110. ansible/module_utils/facts/compat.py +1 -2
  111. ansible/module_utils/facts/default_collectors.py +1 -2
  112. ansible/module_utils/facts/hardware/aix.py +1 -2
  113. ansible/module_utils/facts/hardware/base.py +1 -2
  114. ansible/module_utils/facts/hardware/darwin.py +1 -2
  115. ansible/module_utils/facts/hardware/dragonfly.py +1 -2
  116. ansible/module_utils/facts/hardware/freebsd.py +1 -2
  117. ansible/module_utils/facts/hardware/hpux.py +1 -2
  118. ansible/module_utils/facts/hardware/hurd.py +1 -2
  119. ansible/module_utils/facts/hardware/linux.py +8 -6
  120. ansible/module_utils/facts/hardware/netbsd.py +1 -2
  121. ansible/module_utils/facts/hardware/openbsd.py +1 -2
  122. ansible/module_utils/facts/hardware/sunos.py +2 -3
  123. ansible/module_utils/facts/namespace.py +1 -2
  124. ansible/module_utils/facts/network/aix.py +1 -2
  125. ansible/module_utils/facts/network/base.py +1 -2
  126. ansible/module_utils/facts/network/darwin.py +1 -2
  127. ansible/module_utils/facts/network/dragonfly.py +1 -2
  128. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  129. ansible/module_utils/facts/network/freebsd.py +1 -2
  130. ansible/module_utils/facts/network/generic_bsd.py +1 -2
  131. ansible/module_utils/facts/network/hpux.py +1 -2
  132. ansible/module_utils/facts/network/hurd.py +1 -2
  133. ansible/module_utils/facts/network/iscsi.py +1 -2
  134. ansible/module_utils/facts/network/linux.py +1 -2
  135. ansible/module_utils/facts/network/netbsd.py +1 -2
  136. ansible/module_utils/facts/network/nvme.py +1 -2
  137. ansible/module_utils/facts/network/openbsd.py +1 -2
  138. ansible/module_utils/facts/network/sunos.py +1 -2
  139. ansible/module_utils/facts/other/facter.py +1 -2
  140. ansible/module_utils/facts/other/ohai.py +1 -2
  141. ansible/module_utils/facts/packages.py +1 -2
  142. ansible/module_utils/facts/sysctl.py +1 -2
  143. ansible/module_utils/facts/system/apparmor.py +1 -2
  144. ansible/module_utils/facts/system/caps.py +1 -2
  145. ansible/module_utils/facts/system/chroot.py +1 -2
  146. ansible/module_utils/facts/system/cmdline.py +1 -2
  147. ansible/module_utils/facts/system/date_time.py +1 -2
  148. ansible/module_utils/facts/system/distribution.py +4 -5
  149. ansible/module_utils/facts/system/dns.py +1 -2
  150. ansible/module_utils/facts/system/env.py +1 -2
  151. ansible/module_utils/facts/system/fips.py +1 -2
  152. ansible/module_utils/facts/system/loadavg.py +1 -2
  153. ansible/module_utils/facts/system/local.py +1 -2
  154. ansible/module_utils/facts/system/lsb.py +1 -2
  155. ansible/module_utils/facts/system/pkg_mgr.py +7 -30
  156. ansible/module_utils/facts/system/platform.py +1 -2
  157. ansible/module_utils/facts/system/python.py +1 -2
  158. ansible/module_utils/facts/system/selinux.py +1 -2
  159. ansible/module_utils/facts/system/service_mgr.py +1 -2
  160. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -2
  161. ansible/module_utils/facts/system/user.py +1 -2
  162. ansible/module_utils/facts/timeout.py +1 -2
  163. ansible/module_utils/facts/utils.py +1 -2
  164. ansible/module_utils/facts/virtual/base.py +1 -2
  165. ansible/module_utils/facts/virtual/dragonfly.py +1 -2
  166. ansible/module_utils/facts/virtual/freebsd.py +1 -2
  167. ansible/module_utils/facts/virtual/hpux.py +1 -2
  168. ansible/module_utils/facts/virtual/linux.py +1 -2
  169. ansible/module_utils/facts/virtual/netbsd.py +1 -2
  170. ansible/module_utils/facts/virtual/openbsd.py +1 -2
  171. ansible/module_utils/facts/virtual/sunos.py +1 -2
  172. ansible/module_utils/facts/virtual/sysctl.py +1 -2
  173. ansible/module_utils/json_utils.py +1 -2
  174. ansible/module_utils/parsing/convert_bool.py +1 -2
  175. ansible/module_utils/powershell/Ansible.ModuleUtils.Legacy.psm1 +5 -2
  176. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  177. ansible/module_utils/pycompat24.py +24 -4
  178. ansible/module_utils/service.py +31 -5
  179. ansible/module_utils/six/__init__.py +1 -1
  180. ansible/module_utils/splitter.py +1 -2
  181. ansible/module_utils/urls.py +335 -1052
  182. ansible/module_utils/yumdnf.py +13 -35
  183. ansible/modules/add_host.py +1 -2
  184. ansible/modules/apt.py +38 -22
  185. ansible/modules/apt_key.py +1 -2
  186. ansible/modules/apt_repository.py +18 -10
  187. ansible/modules/assemble.py +1 -2
  188. ansible/modules/assert.py +8 -4
  189. ansible/modules/async_status.py +17 -14
  190. ansible/modules/async_wrapper.py +11 -9
  191. ansible/modules/blockinfile.py +2 -3
  192. ansible/modules/command.py +1 -2
  193. ansible/modules/copy.py +4 -76
  194. ansible/modules/cron.py +3 -3
  195. ansible/modules/deb822_repository.py +5 -5
  196. ansible/modules/debconf.py +19 -8
  197. ansible/modules/debug.py +1 -2
  198. ansible/modules/dnf.py +39 -46
  199. ansible/modules/dnf5.py +28 -26
  200. ansible/modules/dpkg_selections.py +1 -2
  201. ansible/modules/expect.py +23 -15
  202. ansible/modules/fail.py +1 -2
  203. ansible/modules/fetch.py +1 -2
  204. ansible/modules/file.py +4 -3
  205. ansible/modules/find.py +18 -5
  206. ansible/modules/gather_facts.py +1 -2
  207. ansible/modules/get_url.py +2 -3
  208. ansible/modules/getent.py +2 -3
  209. ansible/modules/git.py +28 -17
  210. ansible/modules/group.py +1 -2
  211. ansible/modules/group_by.py +1 -2
  212. ansible/modules/hostname.py +2 -19
  213. ansible/modules/import_playbook.py +1 -2
  214. ansible/modules/import_role.py +9 -2
  215. ansible/modules/import_tasks.py +1 -2
  216. ansible/modules/include_role.py +1 -2
  217. ansible/modules/include_tasks.py +1 -2
  218. ansible/modules/include_vars.py +1 -2
  219. ansible/modules/iptables.py +38 -21
  220. ansible/modules/known_hosts.py +15 -8
  221. ansible/modules/lineinfile.py +1 -6
  222. ansible/modules/meta.py +3 -2
  223. ansible/modules/package.py +6 -5
  224. ansible/modules/package_facts.py +1 -2
  225. ansible/modules/pause.py +2 -3
  226. ansible/modules/ping.py +1 -2
  227. ansible/modules/pip.py +40 -20
  228. ansible/modules/raw.py +1 -2
  229. ansible/modules/reboot.py +1 -2
  230. ansible/modules/replace.py +7 -5
  231. ansible/modules/rpm_key.py +1 -2
  232. ansible/modules/script.py +1 -2
  233. ansible/modules/service.py +3 -21
  234. ansible/modules/service_facts.py +3 -12
  235. ansible/modules/set_fact.py +1 -2
  236. ansible/modules/set_stats.py +1 -2
  237. ansible/modules/setup.py +1 -2
  238. ansible/modules/shell.py +1 -2
  239. ansible/modules/slurp.py +1 -2
  240. ansible/modules/stat.py +1 -2
  241. ansible/modules/subversion.py +2 -3
  242. ansible/modules/systemd.py +12 -9
  243. ansible/modules/systemd_service.py +12 -9
  244. ansible/modules/sysvinit.py +7 -2
  245. ansible/modules/tempfile.py +7 -2
  246. ansible/modules/template.py +2 -3
  247. ansible/modules/unarchive.py +13 -3
  248. ansible/modules/uri.py +12 -15
  249. ansible/modules/user.py +11 -4
  250. ansible/modules/validate_argument_spec.py +15 -16
  251. ansible/modules/wait_for.py +1 -2
  252. ansible/modules/wait_for_connection.py +1 -2
  253. ansible/modules/yum_repository.py +2 -3
  254. ansible/parsing/__init__.py +1 -3
  255. ansible/parsing/ajson.py +1 -3
  256. ansible/parsing/dataloader.py +23 -12
  257. ansible/parsing/mod_args.py +6 -4
  258. ansible/parsing/plugin_docs.py +1 -2
  259. ansible/parsing/quoting.py +1 -3
  260. ansible/parsing/splitter.py +1 -3
  261. ansible/parsing/utils/__init__.py +1 -3
  262. ansible/parsing/utils/addresses.py +1 -3
  263. ansible/parsing/utils/jsonify.py +1 -3
  264. ansible/parsing/utils/yaml.py +1 -3
  265. ansible/parsing/vault/__init__.py +6 -8
  266. ansible/parsing/yaml/__init__.py +1 -3
  267. ansible/parsing/yaml/constructor.py +1 -3
  268. ansible/parsing/yaml/dumper.py +1 -3
  269. ansible/parsing/yaml/loader.py +1 -3
  270. ansible/parsing/yaml/objects.py +1 -3
  271. ansible/playbook/__init__.py +1 -3
  272. ansible/playbook/attribute.py +1 -3
  273. ansible/playbook/base.py +2 -3
  274. ansible/playbook/block.py +1 -3
  275. ansible/playbook/collectionsearch.py +1 -2
  276. ansible/playbook/conditional.py +1 -3
  277. ansible/playbook/delegatable.py +1 -0
  278. ansible/playbook/handler.py +2 -4
  279. ansible/playbook/handler_task_include.py +1 -3
  280. ansible/playbook/helpers.py +1 -4
  281. ansible/playbook/included_file.py +4 -4
  282. ansible/playbook/loop_control.py +1 -3
  283. ansible/playbook/notifiable.py +1 -0
  284. ansible/playbook/play.py +1 -3
  285. ansible/playbook/play_context.py +1 -3
  286. ansible/playbook/playbook_include.py +1 -3
  287. ansible/playbook/role/__init__.py +1 -3
  288. ansible/playbook/role/definition.py +1 -3
  289. ansible/playbook/role/include.py +1 -3
  290. ansible/playbook/role/metadata.py +1 -3
  291. ansible/playbook/role/requirement.py +1 -3
  292. ansible/playbook/role_include.py +2 -10
  293. ansible/playbook/taggable.py +1 -3
  294. ansible/playbook/task.py +2 -4
  295. ansible/playbook/task_include.py +1 -3
  296. ansible/plugins/__init__.py +4 -5
  297. ansible/plugins/action/__init__.py +19 -13
  298. ansible/plugins/action/add_host.py +2 -4
  299. ansible/plugins/action/assemble.py +2 -3
  300. ansible/plugins/action/assert.py +1 -2
  301. ansible/plugins/action/async_status.py +1 -2
  302. ansible/plugins/action/command.py +1 -2
  303. ansible/plugins/action/copy.py +12 -9
  304. ansible/plugins/action/debug.py +1 -2
  305. ansible/plugins/action/dnf.py +4 -4
  306. ansible/plugins/action/fail.py +1 -2
  307. ansible/plugins/action/fetch.py +2 -3
  308. ansible/plugins/action/gather_facts.py +3 -4
  309. ansible/plugins/action/group_by.py +1 -2
  310. ansible/plugins/action/include_vars.py +1 -2
  311. ansible/plugins/action/normal.py +1 -2
  312. ansible/plugins/action/package.py +36 -21
  313. ansible/plugins/action/pause.py +1 -2
  314. ansible/plugins/action/raw.py +2 -3
  315. ansible/plugins/action/reboot.py +30 -15
  316. ansible/plugins/action/script.py +3 -4
  317. ansible/plugins/action/service.py +1 -2
  318. ansible/plugins/action/set_fact.py +1 -2
  319. ansible/plugins/action/set_stats.py +1 -2
  320. ansible/plugins/action/shell.py +1 -2
  321. ansible/plugins/action/template.py +1 -2
  322. ansible/plugins/action/unarchive.py +1 -2
  323. ansible/plugins/action/uri.py +2 -3
  324. ansible/plugins/action/validate_argument_spec.py +1 -2
  325. ansible/plugins/action/wait_for_connection.py +2 -3
  326. ansible/plugins/become/__init__.py +1 -2
  327. ansible/plugins/become/runas.py +1 -2
  328. ansible/plugins/become/su.py +1 -2
  329. ansible/plugins/become/sudo.py +1 -2
  330. ansible/plugins/cache/__init__.py +3 -3
  331. ansible/plugins/cache/base.py +1 -2
  332. ansible/plugins/cache/jsonfile.py +1 -3
  333. ansible/plugins/cache/memory.py +1 -2
  334. ansible/plugins/callback/__init__.py +2 -4
  335. ansible/plugins/callback/default.py +2 -3
  336. ansible/plugins/callback/junit.py +1 -2
  337. ansible/plugins/callback/minimal.py +1 -3
  338. ansible/plugins/callback/oneline.py +1 -3
  339. ansible/plugins/callback/tree.py +1 -2
  340. ansible/plugins/cliconf/__init__.py +1 -2
  341. ansible/plugins/connection/__init__.py +4 -5
  342. ansible/plugins/connection/local.py +3 -4
  343. ansible/plugins/connection/paramiko_ssh.py +1 -2
  344. ansible/plugins/connection/psrp.py +1 -2
  345. ansible/plugins/connection/ssh.py +18 -54
  346. ansible/plugins/connection/winrm.py +3 -4
  347. ansible/plugins/doc_fragments/action_common_attributes.py +2 -3
  348. ansible/plugins/doc_fragments/action_core.py +3 -4
  349. ansible/plugins/doc_fragments/backup.py +1 -2
  350. ansible/plugins/doc_fragments/connection_pipelining.py +1 -2
  351. ansible/plugins/doc_fragments/constructed.py +1 -2
  352. ansible/plugins/doc_fragments/decrypt.py +2 -3
  353. ansible/plugins/doc_fragments/default_callback.py +1 -2
  354. ansible/plugins/doc_fragments/files.py +1 -2
  355. ansible/plugins/doc_fragments/inventory_cache.py +1 -2
  356. ansible/plugins/doc_fragments/result_format_callback.py +1 -2
  357. ansible/plugins/doc_fragments/return_common.py +1 -2
  358. ansible/plugins/doc_fragments/shell_common.py +1 -2
  359. ansible/plugins/doc_fragments/shell_windows.py +1 -2
  360. ansible/plugins/doc_fragments/template_common.py +1 -2
  361. ansible/plugins/doc_fragments/url.py +1 -2
  362. ansible/plugins/doc_fragments/url_windows.py +1 -2
  363. ansible/plugins/doc_fragments/validate.py +1 -2
  364. ansible/plugins/doc_fragments/vars_plugin_staging.py +1 -2
  365. ansible/plugins/filter/__init__.py +1 -2
  366. ansible/plugins/filter/b64decode.yml +8 -8
  367. ansible/plugins/filter/b64encode.yml +4 -4
  368. ansible/plugins/filter/comment.yml +1 -1
  369. ansible/plugins/filter/core.py +11 -9
  370. ansible/plugins/filter/encryption.py +1 -3
  371. ansible/plugins/filter/extract.yml +1 -1
  372. ansible/plugins/filter/from_yaml_all.yml +1 -1
  373. ansible/plugins/filter/human_readable.yml +3 -3
  374. ansible/plugins/filter/human_to_bytes.yml +2 -2
  375. ansible/plugins/filter/mandatory.yml +1 -1
  376. ansible/plugins/filter/mathstuff.py +2 -2
  377. ansible/plugins/filter/password_hash.yml +3 -2
  378. ansible/plugins/filter/regex_replace.yml +15 -0
  379. ansible/plugins/filter/regex_search.yml +12 -0
  380. ansible/plugins/filter/strftime.yml +2 -9
  381. ansible/plugins/filter/to_datetime.yml +17 -2
  382. ansible/plugins/filter/to_nice_json.yml +4 -0
  383. ansible/plugins/filter/union.yml +1 -1
  384. ansible/plugins/filter/urls.py +1 -2
  385. ansible/plugins/filter/urlsplit.py +1 -2
  386. ansible/plugins/filter/zip.yml +2 -2
  387. ansible/plugins/filter/zip_longest.yml +1 -1
  388. ansible/plugins/httpapi/__init__.py +1 -2
  389. ansible/plugins/inventory/__init__.py +2 -4
  390. ansible/plugins/inventory/advanced_host_list.py +1 -2
  391. ansible/plugins/inventory/auto.py +2 -3
  392. ansible/plugins/inventory/constructed.py +3 -2
  393. ansible/plugins/inventory/generator.py +1 -2
  394. ansible/plugins/inventory/host_list.py +1 -2
  395. ansible/plugins/inventory/ini.py +1 -2
  396. ansible/plugins/inventory/script.py +122 -3
  397. ansible/plugins/inventory/toml.py +1 -2
  398. ansible/plugins/inventory/yaml.py +3 -4
  399. ansible/plugins/list.py +2 -3
  400. ansible/plugins/loader.py +10 -11
  401. ansible/plugins/lookup/__init__.py +1 -3
  402. ansible/plugins/lookup/config.py +19 -15
  403. ansible/plugins/lookup/csvfile.py +16 -7
  404. ansible/plugins/lookup/dict.py +2 -3
  405. ansible/plugins/lookup/env.py +4 -4
  406. ansible/plugins/lookup/file.py +1 -2
  407. ansible/plugins/lookup/fileglob.py +1 -2
  408. ansible/plugins/lookup/first_found.py +8 -7
  409. ansible/plugins/lookup/indexed_items.py +1 -2
  410. ansible/plugins/lookup/ini.py +6 -3
  411. ansible/plugins/lookup/inventory_hostnames.py +1 -2
  412. ansible/plugins/lookup/items.py +1 -2
  413. ansible/plugins/lookup/lines.py +1 -2
  414. ansible/plugins/lookup/list.py +1 -3
  415. ansible/plugins/lookup/nested.py +1 -2
  416. ansible/plugins/lookup/password.py +16 -14
  417. ansible/plugins/lookup/pipe.py +1 -2
  418. ansible/plugins/lookup/random_choice.py +1 -2
  419. ansible/plugins/lookup/sequence.py +21 -52
  420. ansible/plugins/lookup/subelements.py +1 -2
  421. ansible/plugins/lookup/template.py +1 -2
  422. ansible/plugins/lookup/together.py +1 -2
  423. ansible/plugins/lookup/unvault.py +1 -2
  424. ansible/plugins/lookup/url.py +9 -11
  425. ansible/plugins/lookup/varnames.py +1 -2
  426. ansible/plugins/lookup/vars.py +1 -2
  427. ansible/plugins/netconf/__init__.py +1 -2
  428. ansible/plugins/shell/__init__.py +3 -4
  429. ansible/plugins/shell/cmd.py +1 -2
  430. ansible/plugins/shell/powershell.py +1 -2
  431. ansible/plugins/shell/sh.py +1 -2
  432. ansible/plugins/strategy/__init__.py +33 -21
  433. ansible/plugins/strategy/debug.py +1 -2
  434. ansible/plugins/strategy/free.py +17 -7
  435. ansible/plugins/strategy/host_pinned.py +1 -3
  436. ansible/plugins/strategy/linear.py +22 -22
  437. ansible/plugins/terminal/__init__.py +2 -3
  438. ansible/plugins/test/__init__.py +1 -2
  439. ansible/plugins/test/change.yml +1 -1
  440. ansible/plugins/test/changed.yml +1 -1
  441. ansible/plugins/test/contains.yml +1 -1
  442. ansible/plugins/test/core.py +2 -4
  443. ansible/plugins/test/exists.yml +1 -1
  444. ansible/plugins/test/failed.yml +1 -1
  445. ansible/plugins/test/failure.yml +1 -1
  446. ansible/plugins/test/files.py +1 -3
  447. ansible/plugins/test/finished.yml +3 -3
  448. ansible/plugins/test/issuperset.yml +1 -1
  449. ansible/plugins/test/match.yml +1 -1
  450. ansible/plugins/test/mathstuff.py +1 -2
  451. ansible/plugins/test/reachable.yml +1 -1
  452. ansible/plugins/test/regex.yml +1 -1
  453. ansible/plugins/test/search.yml +1 -1
  454. ansible/plugins/test/skip.yml +1 -1
  455. ansible/plugins/test/skipped.yml +1 -1
  456. ansible/plugins/test/started.yml +1 -1
  457. ansible/plugins/test/succeeded.yml +1 -1
  458. ansible/plugins/test/success.yml +1 -1
  459. ansible/plugins/test/successful.yml +1 -1
  460. ansible/plugins/test/superset.yml +1 -1
  461. ansible/plugins/test/unreachable.yml +1 -1
  462. ansible/plugins/test/uri.py +1 -3
  463. ansible/plugins/vars/__init__.py +1 -2
  464. ansible/plugins/vars/host_group_vars.py +2 -3
  465. ansible/release.py +3 -5
  466. ansible/template/__init__.py +14 -27
  467. ansible/template/native_helpers.py +1 -3
  468. ansible/template/template.py +1 -3
  469. ansible/template/vars.py +1 -0
  470. ansible/utils/__init__.py +1 -3
  471. ansible/utils/cmd_functions.py +1 -2
  472. ansible/utils/collection_loader/__init__.py +1 -2
  473. ansible/utils/collection_loader/_collection_config.py +1 -2
  474. ansible/utils/collection_loader/_collection_finder.py +1 -2
  475. ansible/utils/collection_loader/_collection_meta.py +1 -2
  476. ansible/utils/color.py +1 -2
  477. ansible/utils/context_objects.py +1 -4
  478. ansible/utils/display.py +89 -30
  479. ansible/utils/encrypt.py +4 -105
  480. ansible/utils/fqcn.py +1 -2
  481. ansible/utils/galaxy.py +1 -3
  482. ansible/utils/hashing.py +1 -3
  483. ansible/utils/helpers.py +1 -3
  484. ansible/utils/jsonrpc.py +1 -2
  485. ansible/utils/listify.py +1 -3
  486. ansible/utils/lock.py +1 -3
  487. ansible/utils/multiprocessing.py +1 -3
  488. ansible/utils/native_jinja.py +1 -3
  489. ansible/utils/path.py +1 -2
  490. ansible/utils/plugin_docs.py +7 -6
  491. ansible/utils/py3compat.py +17 -55
  492. ansible/utils/sentinel.py +1 -3
  493. ansible/utils/shlex.py +1 -3
  494. ansible/utils/singleton.py +1 -3
  495. ansible/utils/ssh_functions.py +1 -3
  496. ansible/utils/unicode.py +1 -3
  497. ansible/utils/unsafe_proxy.py +1 -2
  498. ansible/utils/vars.py +6 -8
  499. ansible/utils/version.py +1 -3
  500. ansible/vars/clean.py +1 -3
  501. ansible/vars/fact_cache.py +1 -2
  502. ansible/vars/hostvars.py +3 -7
  503. ansible/vars/manager.py +24 -6
  504. ansible/vars/reserved.py +1 -3
  505. {ansible_core-2.16.7rc1.data → ansible_core-2.17.0.data}/scripts/ansible-test +1 -2
  506. {ansible_core-2.16.7rc1.dist-info → ansible_core-2.17.0.dist-info}/METADATA +3 -3
  507. ansible_core-2.17.0.dist-info/RECORD +987 -0
  508. ansible_test/__init__.py +1 -2
  509. ansible_test/_data/completion/docker.txt +7 -9
  510. ansible_test/_data/completion/remote.txt +6 -7
  511. ansible_test/_data/requirements/ansible-test.txt +0 -2
  512. ansible_test/_data/requirements/constraints.txt +1 -6
  513. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  514. ansible_test/_data/requirements/sanity.changelog.txt +3 -3
  515. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  516. ansible_test/_data/requirements/sanity.mypy.txt +12 -12
  517. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  518. ansible_test/_data/requirements/sanity.pylint.txt +7 -7
  519. ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -1
  520. ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
  521. ansible_test/_data/requirements/sanity.yamllint.txt +2 -2
  522. ansible_test/_internal/bootstrap.py +2 -2
  523. ansible_test/_internal/classification/__init__.py +0 -5
  524. ansible_test/_internal/commands/integration/cloud/cs.py +1 -1
  525. ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
  526. ansible_test/_internal/commands/sanity/__init__.py +1 -27
  527. ansible_test/_internal/commands/sanity/import.py +0 -18
  528. ansible_test/_internal/commands/sanity/mypy.py +7 -10
  529. ansible_test/_internal/commands/units/__init__.py +1 -1
  530. ansible_test/_internal/config.py +0 -1
  531. ansible_test/_internal/content_config.py +0 -5
  532. ansible_test/_internal/coverage_util.py +0 -1
  533. ansible_test/_internal/docker_util.py +1 -1
  534. ansible_test/_internal/host_profiles.py +5 -4
  535. ansible_test/_internal/pypi_proxy.py +1 -8
  536. ansible_test/_internal/python_requirements.py +1 -119
  537. ansible_test/_internal/ssh.py +1 -0
  538. ansible_test/_internal/util.py +1 -1
  539. ansible_test/_internal/util_common.py +1 -1
  540. ansible_test/_internal/venv.py +10 -108
  541. ansible_test/_util/__init__.py +1 -2
  542. ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -6
  543. ansible_test/_util/controller/sanity/mypy/modules.ini +0 -6
  544. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +0 -1
  545. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +0 -1
  546. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +0 -1
  547. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +0 -1
  548. ansible_test/_util/controller/sanity/pylint/config/default.cfg +0 -1
  549. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -2
  550. ansible_test/_util/controller/sanity/shellcheck/exclude.txt +0 -1
  551. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -59
  552. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +0 -7
  553. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +10 -2
  554. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -1
  555. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +16 -4
  556. ansible_test/_util/target/__init__.py +1 -2
  557. ansible_test/_util/target/cli/ansible_test_cli_stub.py +1 -2
  558. ansible_test/_util/target/common/constants.py +1 -4
  559. ansible_test/_util/target/injector/python.py +4 -19
  560. ansible_test/_util/target/pytest/plugins/ansible_forked.py +2 -9
  561. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +1 -5
  562. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -2
  563. ansible_test/_util/target/sanity/compile/compile.py +3 -12
  564. ansible_test/_util/target/sanity/import/importer.py +1 -12
  565. ansible_test/_util/target/setup/bootstrap.sh +49 -105
  566. ansible_test/_util/target/setup/probe_cgroups.py +1 -2
  567. ansible_test/_util/target/setup/quiet_pip.py +1 -16
  568. ansible_test/_util/target/setup/requirements.py +9 -2
  569. ansible_test/_util/target/tools/virtualenvcheck.py +1 -2
  570. ansible_test/_util/target/tools/yamlcheck.py +1 -2
  571. ansible/module_utils/common/_json_compat.py +0 -16
  572. ansible/module_utils/compat/_selectors2.py +0 -655
  573. ansible/modules/yum.py +0 -1821
  574. ansible/plugins/action/yum.py +0 -111
  575. ansible_core-2.16.7rc1.dist-info/RECORD +0 -1009
  576. ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.json +0 -7
  577. ansible_test/_util/controller/sanity/code-smell/future-import-boilerplate.py +0 -46
  578. ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.json +0 -7
  579. ansible_test/_util/controller/sanity/code-smell/metaclass-boilerplate.py +0 -44
  580. ansible_test/_util/controller/sanity/code-smell/no-basestring.json +0 -7
  581. ansible_test/_util/controller/sanity/code-smell/no-basestring.py +0 -21
  582. ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.json +0 -7
  583. ansible_test/_util/controller/sanity/code-smell/no-dict-iteritems.py +0 -21
  584. ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.json +0 -7
  585. ansible_test/_util/controller/sanity/code-smell/no-dict-iterkeys.py +0 -21
  586. ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.json +0 -7
  587. ansible_test/_util/controller/sanity/code-smell/no-dict-itervalues.py +0 -21
  588. ansible_test/_util/controller/sanity/code-smell/no-main-display.json +0 -10
  589. ansible_test/_util/controller/sanity/code-smell/no-main-display.py +0 -21
  590. ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.json +0 -7
  591. ansible_test/_util/controller/sanity/code-smell/no-unicode-literals.py +0 -21
  592. ansible_test/_util/controller/tools/sslcheck.py +0 -22
  593. ansible_test/_util/target/common/__init__.py +0 -2
  594. {ansible_core-2.16.7rc1.dist-info → ansible_core-2.17.0.dist-info}/COPYING +0 -0
  595. {ansible_core-2.16.7rc1.dist-info → ansible_core-2.17.0.dist-info}/WHEEL +0 -0
  596. {ansible_core-2.16.7rc1.dist-info → ansible_core-2.17.0.dist-info}/entry_points.txt +0 -0
  597. {ansible_core-2.16.7rc1.dist-info → ansible_core-2.17.0.dist-info}/top_level.txt +0 -0
ansible/modules/yum.py DELETED
@@ -1,1821 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright: (c) 2012, Red Hat, Inc
4
- # Written by Seth Vidal <skvidal at fedoraproject.org>
5
- # Copyright: (c) 2014, Epic Games, Inc.
6
-
7
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
8
-
9
- from __future__ import absolute_import, division, print_function
10
- __metaclass__ = type
11
-
12
-
13
- DOCUMENTATION = '''
14
- ---
15
- module: yum
16
- version_added: historical
17
- short_description: Manages packages with the I(yum) package manager
18
- description:
19
- - Installs, upgrade, downgrades, removes, and lists packages and groups with the I(yum) package manager.
20
- - This module only works on Python 2. If you require Python 3 support see the M(ansible.builtin.dnf) module.
21
- options:
22
- use_backend:
23
- description:
24
- - This module supports V(yum) (as it always has), this is known as C(yum3)/C(YUM3)/C(yum-deprecated) by
25
- upstream yum developers. As of Ansible 2.7+, this module also supports C(YUM4), which is the
26
- "new yum" and it has an V(dnf) backend. As of ansible-core 2.15+, this module will auto select the backend
27
- based on the C(ansible_pkg_mgr) fact.
28
- - By default, this module will select the backend based on the C(ansible_pkg_mgr) fact.
29
- default: "auto"
30
- choices: [ auto, yum, yum4, dnf, dnf4, dnf5 ]
31
- type: str
32
- version_added: "2.7"
33
- name:
34
- description:
35
- - A package name or package specifier with version, like V(name-1.0).
36
- - Comparison operators for package version are valid here C(>), C(<), C(>=), C(<=). Example - V(name>=1.0)
37
- - If a previous version is specified, the task also needs to turn O(allow_downgrade) on.
38
- See the O(allow_downgrade) documentation for caveats with downgrading packages.
39
- - When using O(state=latest), this can be V('*') which means run C(yum -y update).
40
- - You can also pass a url or a local path to an rpm file (using O(state=present)).
41
- To operate on several packages this can accept a comma separated string of packages or (as of 2.0) a list of packages.
42
- aliases: [ pkg ]
43
- type: list
44
- elements: str
45
- default: []
46
- exclude:
47
- description:
48
- - Package name(s) to exclude when state=present, or latest
49
- type: list
50
- elements: str
51
- default: []
52
- version_added: "2.0"
53
- list:
54
- description:
55
- - "Package name to run the equivalent of C(yum list --show-duplicates <package>) against. In addition to listing packages,
56
- use can also list the following: V(installed), V(updates), V(available) and V(repos)."
57
- - This parameter is mutually exclusive with O(name).
58
- type: str
59
- state:
60
- description:
61
- - Whether to install (V(present) or V(installed), V(latest)), or remove (V(absent) or V(removed)) a package.
62
- - V(present) and V(installed) will simply ensure that a desired package is installed.
63
- - V(latest) will update the specified package if it's not of the latest available version.
64
- - V(absent) and V(removed) will remove the specified package.
65
- - Default is V(None), however in effect the default action is V(present) unless the O(autoremove) option is
66
- enabled for this module, then V(absent) is inferred.
67
- type: str
68
- choices: [ absent, installed, latest, present, removed ]
69
- enablerepo:
70
- description:
71
- - I(Repoid) of repositories to enable for the install/update operation.
72
- These repos will not persist beyond the transaction.
73
- When specifying multiple repos, separate them with a C(",").
74
- - As of Ansible 2.7, this can alternatively be a list instead of C(",")
75
- separated string
76
- type: list
77
- elements: str
78
- default: []
79
- version_added: "0.9"
80
- disablerepo:
81
- description:
82
- - I(Repoid) of repositories to disable for the install/update operation.
83
- These repos will not persist beyond the transaction.
84
- When specifying multiple repos, separate them with a C(",").
85
- - As of Ansible 2.7, this can alternatively be a list instead of C(",")
86
- separated string
87
- type: list
88
- elements: str
89
- default: []
90
- version_added: "0.9"
91
- conf_file:
92
- description:
93
- - The remote yum configuration file to use for the transaction.
94
- type: str
95
- version_added: "0.6"
96
- disable_gpg_check:
97
- description:
98
- - Whether to disable the GPG checking of signatures of packages being
99
- installed. Has an effect only if O(state) is V(present) or V(latest).
100
- type: bool
101
- default: "no"
102
- version_added: "1.2"
103
- skip_broken:
104
- description:
105
- - Skip all unavailable packages or packages with broken dependencies
106
- without raising an error. Equivalent to passing the --skip-broken option.
107
- type: bool
108
- default: "no"
109
- version_added: "2.3"
110
- update_cache:
111
- description:
112
- - Force yum to check if cache is out of date and redownload if needed.
113
- Has an effect only if O(state) is V(present) or V(latest).
114
- type: bool
115
- default: "no"
116
- aliases: [ expire-cache ]
117
- version_added: "1.9"
118
- validate_certs:
119
- description:
120
- - This only applies if using a https url as the source of the rpm. e.g. for localinstall. If set to V(false), the SSL certificates will not be validated.
121
- - This should only set to V(false) used on personally controlled sites using self-signed certificates as it avoids verifying the source site.
122
- - Prior to 2.1 the code worked as if this was set to V(true).
123
- type: bool
124
- default: "yes"
125
- version_added: "2.1"
126
- sslverify:
127
- description:
128
- - Disables SSL validation of the repository server for this transaction.
129
- - This should be set to V(false) if one of the configured repositories is using an untrusted or self-signed certificate.
130
- type: bool
131
- default: "yes"
132
- version_added: "2.13"
133
- update_only:
134
- description:
135
- - When using latest, only update installed packages. Do not install packages.
136
- - Has an effect only if O(state) is V(latest)
137
- default: "no"
138
- type: bool
139
- version_added: "2.5"
140
-
141
- installroot:
142
- description:
143
- - Specifies an alternative installroot, relative to which all packages
144
- will be installed.
145
- default: "/"
146
- type: str
147
- version_added: "2.3"
148
- security:
149
- description:
150
- - If set to V(true), and O(state=latest) then only installs updates that have been marked security related.
151
- type: bool
152
- default: "no"
153
- version_added: "2.4"
154
- bugfix:
155
- description:
156
- - If set to V(true), and O(state=latest) then only installs updates that have been marked bugfix related.
157
- default: "no"
158
- type: bool
159
- version_added: "2.6"
160
- allow_downgrade:
161
- description:
162
- - Specify if the named package and version is allowed to downgrade
163
- a maybe already installed higher version of that package.
164
- Note that setting allow_downgrade=True can make this module
165
- behave in a non-idempotent way. The task could end up with a set
166
- of packages that does not match the complete list of specified
167
- packages to install (because dependencies between the downgraded
168
- package and others can cause changes to the packages which were
169
- in the earlier transaction).
170
- type: bool
171
- default: "no"
172
- version_added: "2.4"
173
- enable_plugin:
174
- description:
175
- - I(Plugin) name to enable for the install/update operation.
176
- The enabled plugin will not persist beyond the transaction.
177
- type: list
178
- elements: str
179
- default: []
180
- version_added: "2.5"
181
- disable_plugin:
182
- description:
183
- - I(Plugin) name to disable for the install/update operation.
184
- The disabled plugins will not persist beyond the transaction.
185
- type: list
186
- elements: str
187
- default: []
188
- version_added: "2.5"
189
- releasever:
190
- description:
191
- - Specifies an alternative release from which all packages will be
192
- installed.
193
- type: str
194
- version_added: "2.7"
195
- autoremove:
196
- description:
197
- - If V(true), removes all "leaf" packages from the system that were originally
198
- installed as dependencies of user-installed packages but which are no longer
199
- required by any such package. Should be used alone or when O(state) is V(absent)
200
- - "NOTE: This feature requires yum >= 3.4.3 (RHEL/CentOS 7+)"
201
- type: bool
202
- default: "no"
203
- version_added: "2.7"
204
- disable_excludes:
205
- description:
206
- - Disable the excludes defined in YUM config files.
207
- - If set to V(all), disables all excludes.
208
- - If set to V(main), disable excludes defined in [main] in yum.conf.
209
- - If set to V(repoid), disable excludes defined for given repo id.
210
- type: str
211
- version_added: "2.7"
212
- download_only:
213
- description:
214
- - Only download the packages, do not install them.
215
- default: "no"
216
- type: bool
217
- version_added: "2.7"
218
- lock_timeout:
219
- description:
220
- - Amount of time to wait for the yum lockfile to be freed.
221
- required: false
222
- default: 30
223
- type: int
224
- version_added: "2.8"
225
- install_weak_deps:
226
- description:
227
- - Will also install all packages linked by a weak dependency relation.
228
- - "NOTE: This feature requires yum >= 4 (RHEL/CentOS 8+)"
229
- type: bool
230
- default: "yes"
231
- version_added: "2.8"
232
- download_dir:
233
- description:
234
- - Specifies an alternate directory to store packages.
235
- - Has an effect only if O(download_only) is specified.
236
- type: str
237
- version_added: "2.8"
238
- install_repoquery:
239
- description:
240
- - If repoquery is not available, install yum-utils. If the system is
241
- registered to RHN or an RHN Satellite, repoquery allows for querying
242
- all channels assigned to the system. It is also required to use the
243
- 'list' parameter.
244
- - "NOTE: This will run and be logged as a separate yum transation which
245
- takes place before any other installation or removal."
246
- - "NOTE: This will use the system's default enabled repositories without
247
- regard for disablerepo/enablerepo given to the module."
248
- required: false
249
- version_added: "1.5"
250
- default: "yes"
251
- type: bool
252
- cacheonly:
253
- description:
254
- - Tells yum to run entirely from system cache; does not download or update metadata.
255
- default: "no"
256
- type: bool
257
- version_added: "2.12"
258
- extends_documentation_fragment:
259
- - action_common_attributes
260
- - action_common_attributes.flow
261
- attributes:
262
- action:
263
- details: In the case of yum, it has 2 action plugins that use it under the hood, M(ansible.builtin.yum) and M(ansible.builtin.package).
264
- support: partial
265
- async:
266
- support: none
267
- bypass_host_loop:
268
- support: none
269
- check_mode:
270
- support: full
271
- diff_mode:
272
- support: full
273
- platform:
274
- platforms: rhel
275
- notes:
276
- - When used with a C(loop:) each package will be processed individually,
277
- it is much more efficient to pass the list directly to the O(name) option.
278
- - In versions prior to 1.9.2 this module installed and removed each package
279
- given to the yum module separately. This caused problems when packages
280
- specified by filename or url had to be installed or removed together. In
281
- 1.9.2 this was fixed so that packages are installed in one yum
282
- transaction. However, if one of the packages adds a new yum repository
283
- that the other packages come from (such as epel-release) then that package
284
- needs to be installed in a separate task. This mimics yum's command line
285
- behaviour.
286
- - 'Yum itself has two types of groups. "Package groups" are specified in the
287
- rpm itself while "environment groups" are specified in a separate file
288
- (usually by the distribution). Unfortunately, this division becomes
289
- apparent to ansible users because ansible needs to operate on the group
290
- of packages in a single transaction and yum requires groups to be specified
291
- in different ways when used in that way. Package groups are specified as
292
- "@development-tools" and environment groups are "@^gnome-desktop-environment".
293
- Use the "yum group list hidden ids" command to see which category of group the group
294
- you want to install falls into.'
295
- - 'The yum module does not support clearing yum cache in an idempotent way, so it
296
- was decided not to implement it, the only method is to use command and call the yum
297
- command directly, namely "command: yum clean all"
298
- https://github.com/ansible/ansible/pull/31450#issuecomment-352889579'
299
- # informational: requirements for nodes
300
- requirements:
301
- - yum
302
- author:
303
- - Ansible Core Team
304
- - Seth Vidal (@skvidal)
305
- - Eduard Snesarev (@verm666)
306
- - Berend De Schouwer (@berenddeschouwer)
307
- - Abhijeet Kasurde (@Akasurde)
308
- - Adam Miller (@maxamillion)
309
- '''
310
-
311
- EXAMPLES = '''
312
- - name: Install the latest version of Apache
313
- ansible.builtin.yum:
314
- name: httpd
315
- state: latest
316
-
317
- - name: Install Apache >= 2.4
318
- ansible.builtin.yum:
319
- name: httpd>=2.4
320
- state: present
321
-
322
- - name: Install a list of packages (suitable replacement for 2.11 loop deprecation warning)
323
- ansible.builtin.yum:
324
- name:
325
- - nginx
326
- - postgresql
327
- - postgresql-server
328
- state: present
329
-
330
- - name: Install a list of packages with a list variable
331
- ansible.builtin.yum:
332
- name: "{{ packages }}"
333
- vars:
334
- packages:
335
- - httpd
336
- - httpd-tools
337
-
338
- - name: Remove the Apache package
339
- ansible.builtin.yum:
340
- name: httpd
341
- state: absent
342
-
343
- - name: Install the latest version of Apache from the testing repo
344
- ansible.builtin.yum:
345
- name: httpd
346
- enablerepo: testing
347
- state: present
348
-
349
- - name: Install one specific version of Apache
350
- ansible.builtin.yum:
351
- name: httpd-2.2.29-1.4.amzn1
352
- state: present
353
-
354
- - name: Upgrade all packages
355
- ansible.builtin.yum:
356
- name: '*'
357
- state: latest
358
-
359
- - name: Upgrade all packages, excluding kernel & foo related packages
360
- ansible.builtin.yum:
361
- name: '*'
362
- state: latest
363
- exclude: kernel*,foo*
364
-
365
- - name: Install the nginx rpm from a remote repo
366
- ansible.builtin.yum:
367
- name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
368
- state: present
369
-
370
- - name: Install nginx rpm from a local file
371
- ansible.builtin.yum:
372
- name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm
373
- state: present
374
-
375
- - name: Install the 'Development tools' package group
376
- ansible.builtin.yum:
377
- name: "@Development tools"
378
- state: present
379
-
380
- - name: Install the 'Gnome desktop' environment group
381
- ansible.builtin.yum:
382
- name: "@^gnome-desktop-environment"
383
- state: present
384
-
385
- - name: List ansible packages and register result to print with debug later
386
- ansible.builtin.yum:
387
- list: ansible
388
- register: result
389
-
390
- - name: Install package with multiple repos enabled
391
- ansible.builtin.yum:
392
- name: sos
393
- enablerepo: "epel,ol7_latest"
394
-
395
- - name: Install package with multiple repos disabled
396
- ansible.builtin.yum:
397
- name: sos
398
- disablerepo: "epel,ol7_latest"
399
-
400
- - name: Download the nginx package but do not install it
401
- ansible.builtin.yum:
402
- name:
403
- - nginx
404
- state: latest
405
- download_only: true
406
- '''
407
-
408
- from ansible.module_utils.basic import AnsibleModule
409
- from ansible.module_utils.common.locale import get_best_parsable_locale
410
- from ansible.module_utils.common.respawn import has_respawned, respawn_module
411
- from ansible.module_utils.common.text.converters import to_native, to_text
412
- from ansible.module_utils.yumdnf import YumDnf, yumdnf_argument_spec
413
-
414
- import errno
415
- import os
416
- import re
417
- import sys
418
- import tempfile
419
-
420
- try:
421
- import rpm
422
- HAS_RPM_PYTHON = True
423
- except ImportError:
424
- HAS_RPM_PYTHON = False
425
-
426
- try:
427
- import yum
428
- HAS_YUM_PYTHON = True
429
- except ImportError:
430
- HAS_YUM_PYTHON = False
431
-
432
- try:
433
- from yum.misc import find_unfinished_transactions, find_ts_remaining
434
- from rpmUtils.miscutils import splitFilename, compareEVR
435
- transaction_helpers = True
436
- except ImportError:
437
- transaction_helpers = False
438
-
439
- from contextlib import contextmanager
440
- from ansible.module_utils.urls import fetch_file
441
-
442
- def_qf = "%{epoch}:%{name}-%{version}-%{release}.%{arch}"
443
- rpmbin = None
444
-
445
-
446
- class YumModule(YumDnf):
447
- """
448
- Yum Ansible module back-end implementation
449
- """
450
-
451
- def __init__(self, module):
452
-
453
- # state=installed name=pkgspec
454
- # state=removed name=pkgspec
455
- # state=latest name=pkgspec
456
- #
457
- # informational commands:
458
- # list=installed
459
- # list=updates
460
- # list=available
461
- # list=repos
462
- # list=pkgspec
463
-
464
- # This populates instance vars for all argument spec params
465
- super(YumModule, self).__init__(module)
466
-
467
- self.pkg_mgr_name = "yum"
468
- self.lockfile = '/var/run/yum.pid'
469
- self._yum_base = None
470
-
471
- def _enablerepos_with_error_checking(self):
472
- # NOTE: This seems unintuitive, but it mirrors yum's CLI behavior
473
- if len(self.enablerepo) == 1:
474
- try:
475
- self.yum_base.repos.enableRepo(self.enablerepo[0])
476
- except yum.Errors.YumBaseError as e:
477
- if u'repository not found' in to_text(e):
478
- self.module.fail_json(msg="Repository %s not found." % self.enablerepo[0])
479
- else:
480
- raise e
481
- else:
482
- for rid in self.enablerepo:
483
- try:
484
- self.yum_base.repos.enableRepo(rid)
485
- except yum.Errors.YumBaseError as e:
486
- if u'repository not found' in to_text(e):
487
- self.module.warn("Repository %s not found." % rid)
488
- else:
489
- raise e
490
-
491
- def is_lockfile_pid_valid(self):
492
- try:
493
- try:
494
- with open(self.lockfile, 'r') as f:
495
- oldpid = int(f.readline())
496
- except ValueError:
497
- # invalid data
498
- os.unlink(self.lockfile)
499
- return False
500
-
501
- if oldpid == os.getpid():
502
- # that's us?
503
- os.unlink(self.lockfile)
504
- return False
505
-
506
- try:
507
- with open("/proc/%d/stat" % oldpid, 'r') as f:
508
- stat = f.readline()
509
-
510
- if stat.split()[2] == 'Z':
511
- # Zombie
512
- os.unlink(self.lockfile)
513
- return False
514
- except IOError:
515
- # either /proc is not mounted or the process is already dead
516
- try:
517
- # check the state of the process
518
- os.kill(oldpid, 0)
519
- except OSError as e:
520
- if e.errno == errno.ESRCH:
521
- # No such process
522
- os.unlink(self.lockfile)
523
- return False
524
-
525
- self.module.fail_json(msg="Unable to check PID %s in %s: %s" % (oldpid, self.lockfile, to_native(e)))
526
- except (IOError, OSError) as e:
527
- # lockfile disappeared?
528
- return False
529
-
530
- # another copy seems to be running
531
- return True
532
-
533
- @property
534
- def yum_base(self):
535
- if self._yum_base:
536
- return self._yum_base
537
- else:
538
- # Only init once
539
- self._yum_base = yum.YumBase()
540
- self._yum_base.preconf.debuglevel = 0
541
- self._yum_base.preconf.errorlevel = 0
542
- self._yum_base.preconf.plugins = True
543
- self._yum_base.preconf.enabled_plugins = self.enable_plugin
544
- self._yum_base.preconf.disabled_plugins = self.disable_plugin
545
- if self.releasever:
546
- self._yum_base.preconf.releasever = self.releasever
547
- if self.installroot != '/':
548
- # do not setup installroot by default, because of error
549
- # CRITICAL:yum.cli:Config Error: Error accessing file for config file:////etc/yum.conf
550
- # in old yum version (like in CentOS 6.6)
551
- self._yum_base.preconf.root = self.installroot
552
- self._yum_base.conf.installroot = self.installroot
553
- if self.conf_file and os.path.exists(self.conf_file):
554
- self._yum_base.preconf.fn = self.conf_file
555
- if os.geteuid() != 0:
556
- if hasattr(self._yum_base, 'setCacheDir'):
557
- self._yum_base.setCacheDir()
558
- else:
559
- cachedir = yum.misc.getCacheDir()
560
- self._yum_base.repos.setCacheDir(cachedir)
561
- self._yum_base.conf.cache = 0
562
- if self.disable_excludes:
563
- self._yum_base.conf.disable_excludes = self.disable_excludes
564
-
565
- # setting conf.sslverify allows retrieving the repo's metadata
566
- # without validating the certificate, but that does not allow
567
- # package installation from a bad-ssl repo.
568
- self._yum_base.conf.sslverify = self.sslverify
569
-
570
- # A sideeffect of accessing conf is that the configuration is
571
- # loaded and plugins are discovered
572
- self.yum_base.conf # pylint: disable=pointless-statement
573
-
574
- try:
575
- for rid in self.disablerepo:
576
- self.yum_base.repos.disableRepo(rid)
577
-
578
- self._enablerepos_with_error_checking()
579
-
580
- except Exception as e:
581
- self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e))
582
-
583
- return self._yum_base
584
-
585
- def po_to_envra(self, po):
586
- if hasattr(po, 'ui_envra'):
587
- return po.ui_envra
588
-
589
- return '%s:%s-%s-%s.%s' % (po.epoch, po.name, po.version, po.release, po.arch)
590
-
591
- def is_group_env_installed(self, name):
592
- name_lower = name.lower()
593
-
594
- if yum.__version_info__ >= (3, 4):
595
- groups_list = self.yum_base.doGroupLists(return_evgrps=True)
596
- else:
597
- groups_list = self.yum_base.doGroupLists()
598
-
599
- # list of the installed groups on the first index
600
- groups = groups_list[0]
601
- for group in groups:
602
- if name_lower.endswith(group.name.lower()) or name_lower.endswith(group.groupid.lower()):
603
- return True
604
-
605
- if yum.__version_info__ >= (3, 4):
606
- # list of the installed env_groups on the third index
607
- envs = groups_list[2]
608
- for env in envs:
609
- if name_lower.endswith(env.name.lower()) or name_lower.endswith(env.environmentid.lower()):
610
- return True
611
-
612
- return False
613
-
614
- def is_installed(self, repoq, pkgspec, qf=None, is_pkg=False):
615
- if qf is None:
616
- qf = "%{epoch}:%{name}-%{version}-%{release}.%{arch}\n"
617
-
618
- if not repoq:
619
- pkgs = []
620
- try:
621
- e, m, dummy = self.yum_base.rpmdb.matchPackageNames([pkgspec])
622
- pkgs = e + m
623
- if not pkgs and not is_pkg:
624
- pkgs.extend(self.yum_base.returnInstalledPackagesByDep(pkgspec))
625
- except Exception as e:
626
- self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e))
627
-
628
- return [self.po_to_envra(p) for p in pkgs]
629
-
630
- else:
631
- global rpmbin
632
- if not rpmbin:
633
- rpmbin = self.module.get_bin_path('rpm', required=True)
634
-
635
- cmd = [rpmbin, '-q', '--qf', qf, pkgspec]
636
- if '*' in pkgspec:
637
- cmd.append('-a')
638
- if self.installroot != '/':
639
- cmd.extend(['--root', self.installroot])
640
- # rpm localizes messages and we're screen scraping so make sure we use
641
- # an appropriate locale
642
- locale = get_best_parsable_locale(self.module)
643
- lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
644
- rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
645
- if rc != 0 and 'is not installed' not in out:
646
- self.module.fail_json(msg='Error from rpm: %s: %s' % (cmd, err))
647
- if 'is not installed' in out:
648
- out = ''
649
-
650
- pkgs = [p for p in out.replace('(none)', '0').split('\n') if p.strip()]
651
- if not pkgs and not is_pkg:
652
- cmd = [rpmbin, '-q', '--qf', qf, '--whatprovides', pkgspec]
653
- if self.installroot != '/':
654
- cmd.extend(['--root', self.installroot])
655
- rc2, out2, err2 = self.module.run_command(cmd, environ_update=lang_env)
656
- else:
657
- rc2, out2, err2 = (0, '', '')
658
-
659
- if rc2 != 0 and 'no package provides' not in out2:
660
- self.module.fail_json(msg='Error from rpm: %s: %s' % (cmd, err + err2))
661
- if 'no package provides' in out2:
662
- out2 = ''
663
- pkgs += [p for p in out2.replace('(none)', '0').split('\n') if p.strip()]
664
- return pkgs
665
-
666
- return []
667
-
668
- def is_available(self, repoq, pkgspec, qf=def_qf):
669
- if not repoq:
670
-
671
- pkgs = []
672
- try:
673
- e, m, dummy = self.yum_base.pkgSack.matchPackageNames([pkgspec])
674
- pkgs = e + m
675
- if not pkgs:
676
- pkgs.extend(self.yum_base.returnPackagesByDep(pkgspec))
677
- except Exception as e:
678
- self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e))
679
-
680
- return [self.po_to_envra(p) for p in pkgs]
681
-
682
- else:
683
- myrepoq = list(repoq)
684
-
685
- r_cmd = ['--disablerepo', ','.join(self.disablerepo)]
686
- myrepoq.extend(r_cmd)
687
-
688
- r_cmd = ['--enablerepo', ','.join(self.enablerepo)]
689
- myrepoq.extend(r_cmd)
690
-
691
- if self.releasever:
692
- myrepoq.extend('--releasever=%s' % self.releasever)
693
-
694
- cmd = myrepoq + ["--qf", qf, pkgspec]
695
- rc, out, err = self.module.run_command(cmd)
696
- if rc == 0:
697
- return [p for p in out.split('\n') if p.strip()]
698
- else:
699
- self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err))
700
-
701
- return []
702
-
703
- def is_update(self, repoq, pkgspec, qf=def_qf):
704
- if not repoq:
705
-
706
- pkgs = []
707
- updates = []
708
-
709
- try:
710
- pkgs = self.yum_base.returnPackagesByDep(pkgspec) + \
711
- self.yum_base.returnInstalledPackagesByDep(pkgspec)
712
- if not pkgs:
713
- e, m, dummy = self.yum_base.pkgSack.matchPackageNames([pkgspec])
714
- pkgs = e + m
715
- updates = self.yum_base.doPackageLists(pkgnarrow='updates').updates
716
- except Exception as e:
717
- self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e))
718
-
719
- retpkgs = (pkg for pkg in pkgs if pkg in updates)
720
-
721
- return set(self.po_to_envra(p) for p in retpkgs)
722
-
723
- else:
724
- myrepoq = list(repoq)
725
- r_cmd = ['--disablerepo', ','.join(self.disablerepo)]
726
- myrepoq.extend(r_cmd)
727
-
728
- r_cmd = ['--enablerepo', ','.join(self.enablerepo)]
729
- myrepoq.extend(r_cmd)
730
-
731
- if self.releasever:
732
- myrepoq.extend('--releasever=%s' % self.releasever)
733
-
734
- cmd = myrepoq + ["--pkgnarrow=updates", "--qf", qf, pkgspec]
735
- rc, out, err = self.module.run_command(cmd)
736
-
737
- if rc == 0:
738
- return set(p for p in out.split('\n') if p.strip())
739
- else:
740
- self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err))
741
-
742
- return set()
743
-
744
- def what_provides(self, repoq, req_spec, qf=def_qf):
745
- if not repoq:
746
-
747
- pkgs = []
748
- try:
749
- try:
750
- pkgs = self.yum_base.returnPackagesByDep(req_spec) + \
751
- self.yum_base.returnInstalledPackagesByDep(req_spec)
752
- except Exception as e:
753
- # If a repo with `repo_gpgcheck=1` is added and the repo GPG
754
- # key was never accepted, querying this repo will throw an
755
- # error: 'repomd.xml signature could not be verified'. In that
756
- # situation we need to run `yum -y makecache fast` which will accept
757
- # the key and try again.
758
- if 'repomd.xml signature could not be verified' in to_native(e):
759
- if self.releasever:
760
- self.module.run_command(self.yum_basecmd + ['makecache', 'fast', '--releasever=%s' % self.releasever])
761
- else:
762
- self.module.run_command(self.yum_basecmd + ['makecache', 'fast'])
763
- pkgs = self.yum_base.returnPackagesByDep(req_spec) + \
764
- self.yum_base.returnInstalledPackagesByDep(req_spec)
765
- else:
766
- raise
767
- if not pkgs:
768
- exact_matches, glob_matches = self.yum_base.pkgSack.matchPackageNames([req_spec])[0:2]
769
- pkgs.extend(exact_matches)
770
- pkgs.extend(glob_matches)
771
- exact_matches, glob_matches = self.yum_base.rpmdb.matchPackageNames([req_spec])[0:2]
772
- pkgs.extend(exact_matches)
773
- pkgs.extend(glob_matches)
774
- except Exception as e:
775
- self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e))
776
-
777
- return set(self.po_to_envra(p) for p in pkgs)
778
-
779
- else:
780
- myrepoq = list(repoq)
781
- r_cmd = ['--disablerepo', ','.join(self.disablerepo)]
782
- myrepoq.extend(r_cmd)
783
-
784
- r_cmd = ['--enablerepo', ','.join(self.enablerepo)]
785
- myrepoq.extend(r_cmd)
786
-
787
- if self.releasever:
788
- myrepoq.extend('--releasever=%s' % self.releasever)
789
-
790
- cmd = myrepoq + ["--qf", qf, "--whatprovides", req_spec]
791
- rc, out, err = self.module.run_command(cmd)
792
- cmd = myrepoq + ["--qf", qf, req_spec]
793
- rc2, out2, err2 = self.module.run_command(cmd)
794
- if rc == 0 and rc2 == 0:
795
- out += out2
796
- pkgs = {p for p in out.split('\n') if p.strip()}
797
- if not pkgs:
798
- pkgs = self.is_installed(repoq, req_spec, qf=qf)
799
- return pkgs
800
- else:
801
- self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2))
802
-
803
- return set()
804
-
805
- def transaction_exists(self, pkglist):
806
- """
807
- checks the package list to see if any packages are
808
- involved in an incomplete transaction
809
- """
810
-
811
- conflicts = []
812
- if not transaction_helpers:
813
- return conflicts
814
-
815
- # first, we create a list of the package 'nvreas'
816
- # so we can compare the pieces later more easily
817
- pkglist_nvreas = (splitFilename(pkg) for pkg in pkglist)
818
-
819
- # next, we build the list of packages that are
820
- # contained within an unfinished transaction
821
- unfinished_transactions = find_unfinished_transactions()
822
- for trans in unfinished_transactions:
823
- steps = find_ts_remaining(trans)
824
- for step in steps:
825
- # the action is install/erase/etc., but we only
826
- # care about the package spec contained in the step
827
- (action, step_spec) = step
828
- (n, v, r, e, a) = splitFilename(step_spec)
829
- # and see if that spec is in the list of packages
830
- # requested for installation/updating
831
- for pkg in pkglist_nvreas:
832
- # if the name and arch match, we're going to assume
833
- # this package is part of a pending transaction
834
- # the label is just for display purposes
835
- label = "%s-%s" % (n, a)
836
- if n == pkg[0] and a == pkg[4]:
837
- if label not in conflicts:
838
- conflicts.append("%s-%s" % (n, a))
839
- break
840
- return conflicts
841
-
842
- def local_envra(self, path):
843
- """return envra of a local rpm passed in"""
844
-
845
- ts = rpm.TransactionSet()
846
- ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
847
- fd = os.open(path, os.O_RDONLY)
848
- try:
849
- header = ts.hdrFromFdno(fd)
850
- except rpm.error as e:
851
- return None
852
- finally:
853
- os.close(fd)
854
-
855
- return '%s:%s-%s-%s.%s' % (
856
- header[rpm.RPMTAG_EPOCH] or '0',
857
- header[rpm.RPMTAG_NAME],
858
- header[rpm.RPMTAG_VERSION],
859
- header[rpm.RPMTAG_RELEASE],
860
- header[rpm.RPMTAG_ARCH]
861
- )
862
-
863
- @contextmanager
864
- def set_env_proxy(self):
865
- # setting system proxy environment and saving old, if exists
866
- namepass = ""
867
- scheme = ["http", "https"]
868
- old_proxy_env = [os.getenv("http_proxy"), os.getenv("https_proxy")]
869
- try:
870
- # "_none_" is a special value to disable proxy in yum.conf/*.repo
871
- if self.yum_base.conf.proxy and self.yum_base.conf.proxy not in ("_none_",):
872
- if self.yum_base.conf.proxy_username:
873
- namepass = namepass + self.yum_base.conf.proxy_username
874
- proxy_url = self.yum_base.conf.proxy
875
- if self.yum_base.conf.proxy_password:
876
- namepass = namepass + ":" + self.yum_base.conf.proxy_password
877
- elif '@' in self.yum_base.conf.proxy:
878
- namepass = self.yum_base.conf.proxy.split('@')[0].split('//')[-1]
879
- proxy_url = self.yum_base.conf.proxy.replace("{0}@".format(namepass), "")
880
-
881
- if namepass:
882
- namepass = namepass + '@'
883
- for item in scheme:
884
- os.environ[item + "_proxy"] = re.sub(
885
- r"(http://)",
886
- r"\g<1>" + namepass, proxy_url
887
- )
888
- else:
889
- for item in scheme:
890
- os.environ[item + "_proxy"] = self.yum_base.conf.proxy
891
- yield
892
- except yum.Errors.YumBaseError:
893
- raise
894
- finally:
895
- # revert back to previously system configuration
896
- for item in scheme:
897
- if os.getenv("{0}_proxy".format(item)):
898
- del os.environ["{0}_proxy".format(item)]
899
- if old_proxy_env[0]:
900
- os.environ["http_proxy"] = old_proxy_env[0]
901
- if old_proxy_env[1]:
902
- os.environ["https_proxy"] = old_proxy_env[1]
903
-
904
- def pkg_to_dict(self, pkgstr):
905
- if pkgstr.strip() and pkgstr.count('|') == 5:
906
- n, e, v, r, a, repo = pkgstr.split('|')
907
- else:
908
- return {'error_parsing': pkgstr}
909
-
910
- d = {
911
- 'name': n,
912
- 'arch': a,
913
- 'epoch': e,
914
- 'release': r,
915
- 'version': v,
916
- 'repo': repo,
917
- 'envra': '%s:%s-%s-%s.%s' % (e, n, v, r, a)
918
- }
919
-
920
- if repo == 'installed':
921
- d['yumstate'] = 'installed'
922
- else:
923
- d['yumstate'] = 'available'
924
-
925
- return d
926
-
927
- def repolist(self, repoq, qf="%{repoid}"):
928
- cmd = repoq + ["--qf", qf, "-a"]
929
- if self.releasever:
930
- cmd.extend(['--releasever=%s' % self.releasever])
931
- rc, out, err = self.module.run_command(cmd)
932
- if rc == 0:
933
- return set(p for p in out.split('\n') if p.strip())
934
- else:
935
- return []
936
-
937
- def list_stuff(self, repoquerybin, stuff):
938
-
939
- qf = "%{name}|%{epoch}|%{version}|%{release}|%{arch}|%{repoid}"
940
- # is_installed goes through rpm instead of repoquery so it needs a slightly different format
941
- is_installed_qf = "%{name}|%{epoch}|%{version}|%{release}|%{arch}|installed\n"
942
- repoq = [repoquerybin, '--show-duplicates', '--plugins', '--quiet']
943
- if self.disablerepo:
944
- repoq.extend(['--disablerepo', ','.join(self.disablerepo)])
945
- if self.enablerepo:
946
- repoq.extend(['--enablerepo', ','.join(self.enablerepo)])
947
- if self.installroot != '/':
948
- repoq.extend(['--installroot', self.installroot])
949
- if self.conf_file and os.path.exists(self.conf_file):
950
- repoq += ['-c', self.conf_file]
951
-
952
- if stuff == 'installed':
953
- return [self.pkg_to_dict(p) for p in sorted(self.is_installed(repoq, '-a', qf=is_installed_qf)) if p.strip()]
954
-
955
- if stuff == 'updates':
956
- return [self.pkg_to_dict(p) for p in sorted(self.is_update(repoq, '-a', qf=qf)) if p.strip()]
957
-
958
- if stuff == 'available':
959
- return [self.pkg_to_dict(p) for p in sorted(self.is_available(repoq, '-a', qf=qf)) if p.strip()]
960
-
961
- if stuff == 'repos':
962
- return [dict(repoid=name, state='enabled') for name in sorted(self.repolist(repoq)) if name.strip()]
963
-
964
- return [
965
- self.pkg_to_dict(p) for p in
966
- sorted(self.is_installed(repoq, stuff, qf=is_installed_qf) + self.is_available(repoq, stuff, qf=qf))
967
- if p.strip()
968
- ]
969
-
970
- def exec_install(self, items, action, pkgs, res):
971
- cmd = self.yum_basecmd + [action] + pkgs
972
- if self.releasever:
973
- cmd.extend(['--releasever=%s' % self.releasever])
974
-
975
- # setting sslverify using --setopt is required as conf.sslverify only
976
- # affects the metadata retrieval.
977
- if not self.sslverify:
978
- cmd.extend(['--setopt', 'sslverify=0'])
979
-
980
- if self.module.check_mode:
981
- self.module.exit_json(changed=True, results=res['results'], changes=dict(installed=pkgs))
982
- else:
983
- res['changes'] = dict(installed=pkgs)
984
-
985
- locale = get_best_parsable_locale(self.module)
986
- lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
987
- rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
988
-
989
- if rc == 1:
990
- for spec in items:
991
- # Fail on invalid urls:
992
- if ('://' in spec and ('No package %s available.' % spec in out or 'Cannot open: %s. Skipping.' % spec in err)):
993
- err = 'Package at %s could not be installed' % spec
994
- self.module.fail_json(changed=False, msg=err, rc=rc)
995
-
996
- res['rc'] = rc
997
- res['results'].append(out)
998
- res['msg'] += err
999
- res['changed'] = True
1000
-
1001
- if ('Nothing to do' in out and rc == 0) or ('does not have any packages' in err):
1002
- res['changed'] = False
1003
-
1004
- if rc != 0:
1005
- res['changed'] = False
1006
- self.module.fail_json(**res)
1007
-
1008
- # Fail if yum prints 'No space left on device' because that means some
1009
- # packages failed executing their post install scripts because of lack of
1010
- # free space (e.g. kernel package couldn't generate initramfs). Note that
1011
- # yum can still exit with rc=0 even if some post scripts didn't execute
1012
- # correctly.
1013
- if 'No space left on device' in (out or err):
1014
- res['changed'] = False
1015
- res['msg'] = 'No space left on device'
1016
- self.module.fail_json(**res)
1017
-
1018
- # FIXME - if we did an install - go and check the rpmdb to see if it actually installed
1019
- # look for each pkg in rpmdb
1020
- # look for each pkg via obsoletes
1021
-
1022
- return res
1023
-
1024
- def install(self, items, repoq):
1025
-
1026
- pkgs = []
1027
- downgrade_pkgs = []
1028
- res = {}
1029
- res['results'] = []
1030
- res['msg'] = ''
1031
- res['rc'] = 0
1032
- res['changed'] = False
1033
-
1034
- for spec in items:
1035
- pkg = None
1036
- downgrade_candidate = False
1037
-
1038
- # check if pkgspec is installed (if possible for idempotence)
1039
- if spec.endswith('.rpm') or '://' in spec:
1040
- if '://' not in spec and not os.path.exists(spec):
1041
- res['msg'] += "No RPM file matching '%s' found on system" % spec
1042
- res['results'].append("No RPM file matching '%s' found on system" % spec)
1043
- res['rc'] = 127 # Ensure the task fails in with-loop
1044
- self.module.fail_json(**res)
1045
-
1046
- if '://' in spec:
1047
- with self.set_env_proxy():
1048
- package = fetch_file(self.module, spec)
1049
- if not package.endswith('.rpm'):
1050
- # yum requires a local file to have the extension of .rpm and we
1051
- # can not guarantee that from an URL (redirects, proxies, etc)
1052
- new_package_path = '%s.rpm' % package
1053
- os.rename(package, new_package_path)
1054
- package = new_package_path
1055
- else:
1056
- package = spec
1057
-
1058
- # most common case is the pkg is already installed
1059
- envra = self.local_envra(package)
1060
- if envra is None:
1061
- self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec)
1062
- installed_pkgs = self.is_installed(repoq, envra)
1063
- if installed_pkgs:
1064
- res['results'].append('%s providing %s is already installed' % (installed_pkgs[0], package))
1065
- continue
1066
-
1067
- (name, ver, rel, epoch, arch) = splitFilename(envra)
1068
- installed_pkgs = self.is_installed(repoq, name)
1069
-
1070
- # case for two same envr but different archs like x86_64 and i686
1071
- if len(installed_pkgs) == 2:
1072
- (cur_name0, cur_ver0, cur_rel0, cur_epoch0, cur_arch0) = splitFilename(installed_pkgs[0])
1073
- (cur_name1, cur_ver1, cur_rel1, cur_epoch1, cur_arch1) = splitFilename(installed_pkgs[1])
1074
- cur_epoch0 = cur_epoch0 or '0'
1075
- cur_epoch1 = cur_epoch1 or '0'
1076
- compare = compareEVR((cur_epoch0, cur_ver0, cur_rel0), (cur_epoch1, cur_ver1, cur_rel1))
1077
- if compare == 0 and cur_arch0 != cur_arch1:
1078
- for installed_pkg in installed_pkgs:
1079
- if installed_pkg.endswith(arch):
1080
- installed_pkgs = [installed_pkg]
1081
-
1082
- if len(installed_pkgs) == 1:
1083
- installed_pkg = installed_pkgs[0]
1084
- (cur_name, cur_ver, cur_rel, cur_epoch, cur_arch) = splitFilename(installed_pkg)
1085
- cur_epoch = cur_epoch or '0'
1086
- compare = compareEVR((cur_epoch, cur_ver, cur_rel), (epoch, ver, rel))
1087
-
1088
- # compare > 0 -> higher version is installed
1089
- # compare == 0 -> exact version is installed
1090
- # compare < 0 -> lower version is installed
1091
- if compare > 0 and self.allow_downgrade:
1092
- downgrade_candidate = True
1093
- elif compare >= 0:
1094
- continue
1095
-
1096
- # else: if there are more installed packages with the same name, that would mean
1097
- # kernel, gpg-pubkey or like, so just let yum deal with it and try to install it
1098
-
1099
- pkg = package
1100
-
1101
- # groups
1102
- elif spec.startswith('@'):
1103
- if self.is_group_env_installed(spec):
1104
- continue
1105
-
1106
- pkg = spec
1107
-
1108
- # range requires or file-requires or pkgname :(
1109
- else:
1110
- # most common case is the pkg is already installed and done
1111
- # short circuit all the bs - and search for it as a pkg in is_installed
1112
- # if you find it then we're done
1113
- if not set(['*', '?']).intersection(set(spec)):
1114
- installed_pkgs = self.is_installed(repoq, spec, is_pkg=True)
1115
- if installed_pkgs:
1116
- res['results'].append('%s providing %s is already installed' % (installed_pkgs[0], spec))
1117
- continue
1118
-
1119
- # look up what pkgs provide this
1120
- pkglist = self.what_provides(repoq, spec)
1121
- if not pkglist:
1122
- res['msg'] += "No package matching '%s' found available, installed or updated" % spec
1123
- res['results'].append("No package matching '%s' found available, installed or updated" % spec)
1124
- res['rc'] = 126 # Ensure the task fails in with-loop
1125
- self.module.fail_json(**res)
1126
-
1127
- # if any of the packages are involved in a transaction, fail now
1128
- # so that we don't hang on the yum operation later
1129
- conflicts = self.transaction_exists(pkglist)
1130
- if conflicts:
1131
- res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts)
1132
- res['rc'] = 125 # Ensure the task fails in with-loop
1133
- self.module.fail_json(**res)
1134
-
1135
- # if any of them are installed
1136
- # then nothing to do
1137
-
1138
- found = False
1139
- for this in pkglist:
1140
- if self.is_installed(repoq, this, is_pkg=True):
1141
- found = True
1142
- res['results'].append('%s providing %s is already installed' % (this, spec))
1143
- break
1144
-
1145
- # if the version of the pkg you have installed is not in ANY repo, but there are
1146
- # other versions in the repos (both higher and lower) then the previous checks won't work.
1147
- # so we check one more time. This really only works for pkgname - not for file provides or virt provides
1148
- # but virt provides should be all caught in what_provides on its own.
1149
- # highly irritating
1150
- if not found:
1151
- if self.is_installed(repoq, spec):
1152
- found = True
1153
- res['results'].append('package providing %s is already installed' % (spec))
1154
-
1155
- if found:
1156
- continue
1157
-
1158
- # Downgrade - The yum install command will only install or upgrade to a spec version, it will
1159
- # not install an older version of an RPM even if specified by the install spec. So we need to
1160
- # determine if this is a downgrade, and then use the yum downgrade command to install the RPM.
1161
- if self.allow_downgrade:
1162
- for package in pkglist:
1163
- # Get the NEVRA of the requested package using pkglist instead of spec because pkglist
1164
- # contains consistently-formatted package names returned by yum, rather than user input
1165
- # that is often not parsed correctly by splitFilename().
1166
- (name, ver, rel, epoch, arch) = splitFilename(package)
1167
-
1168
- # Check if any version of the requested package is installed
1169
- inst_pkgs = self.is_installed(repoq, name, is_pkg=True)
1170
- if inst_pkgs:
1171
- (cur_name, cur_ver, cur_rel, cur_epoch, cur_arch) = splitFilename(inst_pkgs[0])
1172
- compare = compareEVR((cur_epoch, cur_ver, cur_rel), (epoch, ver, rel))
1173
- if compare > 0:
1174
- downgrade_candidate = True
1175
- else:
1176
- downgrade_candidate = False
1177
- break
1178
-
1179
- # If package needs to be installed/upgraded/downgraded, then pass in the spec
1180
- # we could get here if nothing provides it but that's not
1181
- # the error we're catching here
1182
- pkg = spec
1183
-
1184
- if downgrade_candidate and self.allow_downgrade:
1185
- downgrade_pkgs.append(pkg)
1186
- else:
1187
- pkgs.append(pkg)
1188
-
1189
- if downgrade_pkgs:
1190
- res = self.exec_install(items, 'downgrade', downgrade_pkgs, res)
1191
-
1192
- if pkgs:
1193
- res = self.exec_install(items, 'install', pkgs, res)
1194
-
1195
- return res
1196
-
1197
- def remove(self, items, repoq):
1198
-
1199
- pkgs = []
1200
- res = {}
1201
- res['results'] = []
1202
- res['msg'] = ''
1203
- res['changed'] = False
1204
- res['rc'] = 0
1205
-
1206
- for pkg in items:
1207
- if pkg.startswith('@'):
1208
- installed = self.is_group_env_installed(pkg)
1209
- else:
1210
- installed = self.is_installed(repoq, pkg)
1211
-
1212
- if installed:
1213
- pkgs.append(pkg)
1214
- else:
1215
- res['results'].append('%s is not installed' % pkg)
1216
-
1217
- if pkgs:
1218
- if self.module.check_mode:
1219
- self.module.exit_json(changed=True, results=res['results'], changes=dict(removed=pkgs))
1220
- else:
1221
- res['changes'] = dict(removed=pkgs)
1222
-
1223
- # run an actual yum transaction
1224
- if self.autoremove:
1225
- cmd = self.yum_basecmd + ["autoremove"] + pkgs
1226
- else:
1227
- cmd = self.yum_basecmd + ["remove"] + pkgs
1228
- rc, out, err = self.module.run_command(cmd)
1229
-
1230
- res['rc'] = rc
1231
- res['results'].append(out)
1232
- res['msg'] = err
1233
-
1234
- if rc != 0:
1235
- if self.autoremove and 'No such command' in out:
1236
- self.module.fail_json(msg='Version of YUM too old for autoremove: Requires yum 3.4.3 (RHEL/CentOS 7+)')
1237
- else:
1238
- self.module.fail_json(**res)
1239
-
1240
- # compile the results into one batch. If anything is changed
1241
- # then mark changed
1242
- # at the end - if we've end up failed then fail out of the rest
1243
- # of the process
1244
-
1245
- # at this point we check to see if the pkg is no longer present
1246
- self._yum_base = None # previous YumBase package index is now invalid
1247
- for pkg in pkgs:
1248
- if pkg.startswith('@'):
1249
- installed = self.is_group_env_installed(pkg)
1250
- else:
1251
- installed = self.is_installed(repoq, pkg, is_pkg=True)
1252
-
1253
- if installed:
1254
- # Return a message so it's obvious to the user why yum failed
1255
- # and which package couldn't be removed. More details:
1256
- # https://github.com/ansible/ansible/issues/35672
1257
- res['msg'] = "Package '%s' couldn't be removed!" % pkg
1258
- self.module.fail_json(**res)
1259
-
1260
- res['changed'] = True
1261
-
1262
- return res
1263
-
1264
- def run_check_update(self):
1265
- # run check-update to see if we have packages pending
1266
- if self.releasever:
1267
- rc, out, err = self.module.run_command(self.yum_basecmd + ['check-update'] + ['--releasever=%s' % self.releasever])
1268
- else:
1269
- rc, out, err = self.module.run_command(self.yum_basecmd + ['check-update'])
1270
- return rc, out, err
1271
-
1272
- @staticmethod
1273
- def parse_check_update(check_update_output):
1274
- # preprocess string and filter out empty lines so the regex below works
1275
- out = '\n'.join((l for l in check_update_output.splitlines() if l))
1276
-
1277
- # Remove incorrect new lines in longer columns in output from yum check-update
1278
- # yum line wrapping can move the repo to the next line:
1279
- # some_looooooooooooooooooooooooooooooooooooong_package_name 1:1.2.3-1.el7
1280
- # some-repo-label
1281
- out = re.sub(r'\n\W+(.*)', r' \1', out)
1282
-
1283
- updates = {}
1284
- obsoletes = {}
1285
- for line in out.split('\n'):
1286
- line = line.split()
1287
- # Ignore irrelevant lines:
1288
- # - '*' in line matches lines like mirror lists: "* base: mirror.corbina.net"
1289
- # - len(line) != 3 or 6 could be strings like:
1290
- # "This system is not registered with an entitlement server..."
1291
- # - len(line) = 6 is package obsoletes
1292
- # - checking for '.' in line[0] (package name) likely ensures that it is of format:
1293
- # "package_name.arch" (coreutils.x86_64)
1294
- if '*' in line or len(line) not in [3, 6] or '.' not in line[0]:
1295
- continue
1296
-
1297
- pkg, version, repo = line[0], line[1], line[2]
1298
- name, dist = pkg.rsplit('.', 1)
1299
-
1300
- if name not in updates:
1301
- updates[name] = []
1302
-
1303
- updates[name].append({'version': version, 'dist': dist, 'repo': repo})
1304
-
1305
- if len(line) == 6:
1306
- obsolete_pkg, obsolete_version, obsolete_repo = line[3], line[4], line[5]
1307
- obsolete_name, obsolete_dist = obsolete_pkg.rsplit('.', 1)
1308
-
1309
- if obsolete_name not in obsoletes:
1310
- obsoletes[obsolete_name] = []
1311
-
1312
- obsoletes[obsolete_name].append({'version': obsolete_version, 'dist': obsolete_dist, 'repo': obsolete_repo})
1313
-
1314
- return updates, obsoletes
1315
-
1316
- def latest(self, items, repoq):
1317
-
1318
- res = {}
1319
- res['results'] = []
1320
- res['msg'] = ''
1321
- res['changed'] = False
1322
- res['rc'] = 0
1323
- pkgs = {}
1324
- pkgs['update'] = []
1325
- pkgs['install'] = []
1326
- updates = {}
1327
- obsoletes = {}
1328
- update_all = False
1329
- cmd = self.yum_basecmd[:]
1330
-
1331
- # determine if we're doing an update all
1332
- if '*' in items:
1333
- update_all = True
1334
-
1335
- rc, out, err = self.run_check_update()
1336
-
1337
- if rc == 0 and update_all:
1338
- res['results'].append('Nothing to do here, all packages are up to date')
1339
- return res
1340
- elif rc == 100:
1341
- updates, obsoletes = self.parse_check_update(out)
1342
- elif rc == 1:
1343
- res['msg'] = err
1344
- res['rc'] = rc
1345
- self.module.fail_json(**res)
1346
-
1347
- if update_all:
1348
- cmd.append('update')
1349
- will_update = set(updates.keys())
1350
- will_update_from_other_package = dict()
1351
- else:
1352
- will_update = set()
1353
- will_update_from_other_package = dict()
1354
- for spec in items:
1355
- # some guess work involved with groups. update @<group> will install the group if missing
1356
- if spec.startswith('@'):
1357
- pkgs['update'].append(spec)
1358
- will_update.add(spec)
1359
- continue
1360
-
1361
- # check if pkgspec is installed (if possible for idempotence)
1362
- # localpkg
1363
- if spec.endswith('.rpm') and '://' not in spec:
1364
- if not os.path.exists(spec):
1365
- res['msg'] += "No RPM file matching '%s' found on system" % spec
1366
- res['results'].append("No RPM file matching '%s' found on system" % spec)
1367
- res['rc'] = 127 # Ensure the task fails in with-loop
1368
- self.module.fail_json(**res)
1369
-
1370
- # get the pkg e:name-v-r.arch
1371
- envra = self.local_envra(spec)
1372
-
1373
- if envra is None:
1374
- self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec)
1375
-
1376
- # local rpm files can't be updated
1377
- if self.is_installed(repoq, envra):
1378
- pkgs['update'].append(spec)
1379
- else:
1380
- pkgs['install'].append(spec)
1381
- continue
1382
-
1383
- # URL
1384
- if '://' in spec:
1385
- # download package so that we can check if it's already installed
1386
- with self.set_env_proxy():
1387
- package = fetch_file(self.module, spec)
1388
- envra = self.local_envra(package)
1389
-
1390
- if envra is None:
1391
- self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec)
1392
-
1393
- # local rpm files can't be updated
1394
- if self.is_installed(repoq, envra):
1395
- pkgs['update'].append(spec)
1396
- else:
1397
- pkgs['install'].append(spec)
1398
- continue
1399
-
1400
- # dep/pkgname - find it
1401
- if self.is_installed(repoq, spec):
1402
- pkgs['update'].append(spec)
1403
- else:
1404
- pkgs['install'].append(spec)
1405
- pkglist = self.what_provides(repoq, spec)
1406
- # FIXME..? may not be desirable to throw an exception here if a single package is missing
1407
- if not pkglist:
1408
- res['msg'] += "No package matching '%s' found available, installed or updated" % spec
1409
- res['results'].append("No package matching '%s' found available, installed or updated" % spec)
1410
- res['rc'] = 126 # Ensure the task fails in with-loop
1411
- self.module.fail_json(**res)
1412
-
1413
- nothing_to_do = True
1414
- for pkg in pkglist:
1415
- if spec in pkgs['install'] and self.is_available(repoq, pkg):
1416
- nothing_to_do = False
1417
- break
1418
-
1419
- # this contains the full NVR and spec could contain wildcards
1420
- # or virtual provides (like "python-*" or "smtp-daemon") while
1421
- # updates contains name only.
1422
- (pkgname, ver, rel, epoch, arch) = splitFilename(pkg)
1423
- if spec in pkgs['update'] and pkgname in updates:
1424
- nothing_to_do = False
1425
- will_update.add(spec)
1426
- # Massage the updates list
1427
- if spec != pkgname:
1428
- # For reporting what packages would be updated more
1429
- # succinctly
1430
- will_update_from_other_package[spec] = pkgname
1431
- break
1432
-
1433
- if not self.is_installed(repoq, spec) and self.update_only:
1434
- res['results'].append("Packages providing %s not installed due to update_only specified" % spec)
1435
- continue
1436
- if nothing_to_do:
1437
- res['results'].append("All packages providing %s are up to date" % spec)
1438
- continue
1439
-
1440
- # if any of the packages are involved in a transaction, fail now
1441
- # so that we don't hang on the yum operation later
1442
- conflicts = self.transaction_exists(pkglist)
1443
- if conflicts:
1444
- res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts)
1445
- res['results'].append("The following packages have pending transactions: %s" % ", ".join(conflicts))
1446
- res['rc'] = 128 # Ensure the task fails in with-loop
1447
- self.module.fail_json(**res)
1448
-
1449
- # check_mode output
1450
- to_update = []
1451
- for w in will_update:
1452
- if w.startswith('@'):
1453
- # yum groups
1454
- to_update.append((w, None))
1455
- elif w not in updates:
1456
- # There are (at least, probably more) 2 ways we can get here:
1457
- #
1458
- # * A virtual provides (our user specifies "webserver", but
1459
- # "httpd" is the key in 'updates').
1460
- #
1461
- # * A wildcard. emac* will get us here if there's a package
1462
- # called 'emacs' in the pending updates list. 'updates' will
1463
- # of course key on 'emacs' in that case.
1464
-
1465
- other_pkg = will_update_from_other_package[w]
1466
-
1467
- # We are guaranteed that: other_pkg in updates
1468
- # ...based on the logic above. But we only want to show one
1469
- # update in this case (given the wording of "at least") below.
1470
- # As an example, consider a package installed twice:
1471
- # foobar.x86_64, foobar.i686
1472
- # We want to avoid having both:
1473
- # ('foo*', 'because of (at least) foobar-1.x86_64 from repo')
1474
- # ('foo*', 'because of (at least) foobar-1.i686 from repo')
1475
- # We just pick the first one.
1476
- #
1477
- # TODO: This is something that might be nice to change, but it
1478
- # would be a module UI change. But without it, we're
1479
- # dropping potentially important information about what
1480
- # was updated. Instead of (given_spec, random_matching_package)
1481
- # it'd be nice if we appended (given_spec, [all_matching_packages])
1482
- #
1483
- # ... But then, we also drop information if multiple
1484
- # different (distinct) packages match the given spec and
1485
- # we should probably fix that too.
1486
- pkg = updates[other_pkg][0]
1487
- to_update.append(
1488
- (
1489
- w,
1490
- 'because of (at least) %s-%s.%s from %s' % (
1491
- other_pkg,
1492
- pkg['version'],
1493
- pkg['dist'],
1494
- pkg['repo']
1495
- )
1496
- )
1497
- )
1498
- else:
1499
- # Otherwise the spec is an exact match
1500
- for pkg in updates[w]:
1501
- to_update.append(
1502
- (
1503
- w,
1504
- '%s.%s from %s' % (
1505
- pkg['version'],
1506
- pkg['dist'],
1507
- pkg['repo']
1508
- )
1509
- )
1510
- )
1511
-
1512
- if self.update_only:
1513
- res['changes'] = dict(installed=[], updated=to_update)
1514
- else:
1515
- res['changes'] = dict(installed=pkgs['install'], updated=to_update)
1516
-
1517
- if obsoletes:
1518
- res['obsoletes'] = obsoletes
1519
-
1520
- # return results before we actually execute stuff
1521
- if self.module.check_mode:
1522
- if will_update or pkgs['install']:
1523
- res['changed'] = True
1524
- return res
1525
-
1526
- if self.releasever:
1527
- cmd.extend(['--releasever=%s' % self.releasever])
1528
-
1529
- # run commands
1530
- if update_all:
1531
- rc, out, err = self.module.run_command(cmd)
1532
- res['changed'] = True
1533
- elif self.update_only:
1534
- if pkgs['update']:
1535
- cmd += ['update'] + pkgs['update']
1536
- locale = get_best_parsable_locale(self.module)
1537
- lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
1538
- rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
1539
- out_lower = out.strip().lower()
1540
- if not out_lower.endswith("no packages marked for update") and \
1541
- not out_lower.endswith("nothing to do"):
1542
- res['changed'] = True
1543
- else:
1544
- rc, out, err = [0, '', '']
1545
- elif pkgs['install'] or will_update and not self.update_only:
1546
- cmd += ['install'] + pkgs['install'] + pkgs['update']
1547
- locale = get_best_parsable_locale(self.module)
1548
- lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale)
1549
- rc, out, err = self.module.run_command(cmd, environ_update=lang_env)
1550
- out_lower = out.strip().lower()
1551
- if not out_lower.endswith("no packages marked for update") and \
1552
- not out_lower.endswith("nothing to do"):
1553
- res['changed'] = True
1554
- else:
1555
- rc, out, err = [0, '', '']
1556
-
1557
- res['rc'] = rc
1558
- res['msg'] += err
1559
- res['results'].append(out)
1560
-
1561
- if rc:
1562
- res['failed'] = True
1563
-
1564
- return res
1565
-
1566
- def ensure(self, repoq):
1567
- pkgs = self.names
1568
-
1569
- # autoremove was provided without `name`
1570
- if not self.names and self.autoremove:
1571
- pkgs = []
1572
- self.state = 'absent'
1573
-
1574
- if self.conf_file and os.path.exists(self.conf_file):
1575
- self.yum_basecmd += ['-c', self.conf_file]
1576
-
1577
- if repoq:
1578
- repoq += ['-c', self.conf_file]
1579
-
1580
- if self.skip_broken:
1581
- self.yum_basecmd.extend(['--skip-broken'])
1582
-
1583
- if self.disablerepo:
1584
- self.yum_basecmd.extend(['--disablerepo=%s' % ','.join(self.disablerepo)])
1585
-
1586
- if self.enablerepo:
1587
- self.yum_basecmd.extend(['--enablerepo=%s' % ','.join(self.enablerepo)])
1588
-
1589
- if self.enable_plugin:
1590
- self.yum_basecmd.extend(['--enableplugin', ','.join(self.enable_plugin)])
1591
-
1592
- if self.disable_plugin:
1593
- self.yum_basecmd.extend(['--disableplugin', ','.join(self.disable_plugin)])
1594
-
1595
- if self.exclude:
1596
- e_cmd = ['--exclude=%s' % ','.join(self.exclude)]
1597
- self.yum_basecmd.extend(e_cmd)
1598
-
1599
- if self.disable_excludes:
1600
- self.yum_basecmd.extend(['--disableexcludes=%s' % self.disable_excludes])
1601
-
1602
- if self.cacheonly:
1603
- self.yum_basecmd.extend(['--cacheonly'])
1604
-
1605
- if self.download_only:
1606
- self.yum_basecmd.extend(['--downloadonly'])
1607
-
1608
- if self.download_dir:
1609
- self.yum_basecmd.extend(['--downloaddir=%s' % self.download_dir])
1610
-
1611
- if self.releasever:
1612
- self.yum_basecmd.extend(['--releasever=%s' % self.releasever])
1613
-
1614
- if self.installroot != '/':
1615
- # do not setup installroot by default, because of error
1616
- # CRITICAL:yum.cli:Config Error: Error accessing file for config file:////etc/yum.conf
1617
- # in old yum version (like in CentOS 6.6)
1618
- e_cmd = ['--installroot=%s' % self.installroot]
1619
- self.yum_basecmd.extend(e_cmd)
1620
-
1621
- if self.state in ('installed', 'present', 'latest'):
1622
- # The need of this entire if conditional has to be changed
1623
- # this function is the ensure function that is called
1624
- # in the main section.
1625
- #
1626
- # This conditional tends to disable/enable repo for
1627
- # install present latest action, same actually
1628
- # can be done for remove and absent action
1629
- #
1630
- # As solution I would advice to cal
1631
- # try: self.yum_base.repos.disableRepo(disablerepo)
1632
- # and
1633
- # try: self.yum_base.repos.enableRepo(enablerepo)
1634
- # right before any yum_cmd is actually called regardless
1635
- # of yum action.
1636
- #
1637
- # Please note that enable/disablerepo options are general
1638
- # options, this means that we can call those with any action
1639
- # option. https://linux.die.net/man/8/yum
1640
- #
1641
- # This docstring will be removed together when issue: #21619
1642
- # will be solved.
1643
- #
1644
- # This has been triggered by: #19587
1645
-
1646
- if self.update_cache:
1647
- self.module.run_command(self.yum_basecmd + ['clean', 'expire-cache'])
1648
-
1649
- try:
1650
- current_repos = self.yum_base.repos.repos.keys()
1651
- if self.enablerepo:
1652
- try:
1653
- new_repos = self.yum_base.repos.repos.keys()
1654
- for i in new_repos:
1655
- if i not in current_repos:
1656
- rid = self.yum_base.repos.getRepo(i)
1657
- a = rid.repoXML.repoid # nopep8 - https://github.com/ansible/ansible/pull/21475#pullrequestreview-22404868
1658
- current_repos = new_repos
1659
- except yum.Errors.YumBaseError as e:
1660
- self.module.fail_json(msg="Error setting/accessing repos: %s" % to_native(e))
1661
- except yum.Errors.YumBaseError as e:
1662
- self.module.fail_json(msg="Error accessing repos: %s" % to_native(e))
1663
- if self.state == 'latest' or self.update_only:
1664
- if self.disable_gpg_check:
1665
- self.yum_basecmd.append('--nogpgcheck')
1666
- if self.security:
1667
- self.yum_basecmd.append('--security')
1668
- if self.bugfix:
1669
- self.yum_basecmd.append('--bugfix')
1670
- res = self.latest(pkgs, repoq)
1671
- elif self.state in ('installed', 'present'):
1672
- if self.disable_gpg_check:
1673
- self.yum_basecmd.append('--nogpgcheck')
1674
- res = self.install(pkgs, repoq)
1675
- elif self.state in ('removed', 'absent'):
1676
- res = self.remove(pkgs, repoq)
1677
- else:
1678
- # should be caught by AnsibleModule argument_spec
1679
- self.module.fail_json(
1680
- msg="we should never get here unless this all failed",
1681
- changed=False,
1682
- results='',
1683
- errors='unexpected state'
1684
- )
1685
- return res
1686
-
1687
- @staticmethod
1688
- def has_yum():
1689
- return HAS_YUM_PYTHON
1690
-
1691
- def run(self):
1692
- """
1693
- actually execute the module code backend
1694
- """
1695
-
1696
- if (not HAS_RPM_PYTHON or not HAS_YUM_PYTHON) and sys.executable != '/usr/bin/python' and not has_respawned():
1697
- respawn_module('/usr/bin/python')
1698
- # end of the line for this process; we'll exit here once the respawned module has completed
1699
-
1700
- error_msgs = []
1701
- if not HAS_RPM_PYTHON:
1702
- error_msgs.append('The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.')
1703
- if not HAS_YUM_PYTHON:
1704
- error_msgs.append('The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.')
1705
-
1706
- self.wait_for_lock()
1707
-
1708
- if error_msgs:
1709
- self.module.fail_json(msg='. '.join(error_msgs))
1710
-
1711
- # fedora will redirect yum to dnf, which has incompatibilities
1712
- # with how this module expects yum to operate. If yum-deprecated
1713
- # is available, use that instead to emulate the old behaviors.
1714
- if self.module.get_bin_path('yum-deprecated'):
1715
- yumbin = self.module.get_bin_path('yum-deprecated')
1716
- else:
1717
- yumbin = self.module.get_bin_path('yum')
1718
-
1719
- # need debug level 2 to get 'Nothing to do' for groupinstall.
1720
- self.yum_basecmd = [yumbin, '-d', '2', '-y']
1721
-
1722
- if self.update_cache and not self.names and not self.list:
1723
- rc, stdout, stderr = self.module.run_command(self.yum_basecmd + ['clean', 'expire-cache'])
1724
- if rc == 0:
1725
- self.module.exit_json(
1726
- changed=False,
1727
- msg="Cache updated",
1728
- rc=rc,
1729
- results=[]
1730
- )
1731
- else:
1732
- self.module.exit_json(
1733
- changed=False,
1734
- msg="Failed to update cache",
1735
- rc=rc,
1736
- results=[stderr],
1737
- )
1738
-
1739
- repoquerybin = self.module.get_bin_path('repoquery', required=False)
1740
-
1741
- if self.install_repoquery and not repoquerybin and not self.module.check_mode:
1742
- yum_path = self.module.get_bin_path('yum')
1743
- if yum_path:
1744
- if self.releasever:
1745
- self.module.run_command('%s -y install yum-utils --releasever %s' % (yum_path, self.releasever))
1746
- else:
1747
- self.module.run_command('%s -y install yum-utils' % yum_path)
1748
- repoquerybin = self.module.get_bin_path('repoquery', required=False)
1749
-
1750
- if self.list:
1751
- if not repoquerybin:
1752
- self.module.fail_json(msg="repoquery is required to use list= with this module. Please install the yum-utils package.")
1753
- results = {'results': self.list_stuff(repoquerybin, self.list)}
1754
- else:
1755
- # If rhn-plugin is installed and no rhn-certificate is available on
1756
- # the system then users will see an error message using the yum API.
1757
- # Use repoquery in those cases.
1758
-
1759
- repoquery = None
1760
- try:
1761
- yum_plugins = self.yum_base.plugins._plugins
1762
- except AttributeError:
1763
- pass
1764
- else:
1765
- if 'rhnplugin' in yum_plugins:
1766
- if repoquerybin:
1767
- repoquery = [repoquerybin, '--show-duplicates', '--plugins', '--quiet']
1768
- if self.installroot != '/':
1769
- repoquery.extend(['--installroot', self.installroot])
1770
-
1771
- if self.disable_excludes:
1772
- # repoquery does not support --disableexcludes,
1773
- # so make a temp copy of yum.conf and get rid of the 'exclude=' line there
1774
- try:
1775
- with open('/etc/yum.conf', 'r') as f:
1776
- content = f.readlines()
1777
-
1778
- tmp_conf_file = tempfile.NamedTemporaryFile(dir=self.module.tmpdir, delete=False)
1779
- self.module.add_cleanup_file(tmp_conf_file.name)
1780
-
1781
- tmp_conf_file.writelines([c for c in content if not c.startswith("exclude=")])
1782
- tmp_conf_file.close()
1783
- except Exception as e:
1784
- self.module.fail_json(msg="Failure setting up repoquery: %s" % to_native(e))
1785
-
1786
- repoquery.extend(['-c', tmp_conf_file.name])
1787
-
1788
- results = self.ensure(repoquery)
1789
- if repoquery:
1790
- results['msg'] = '%s %s' % (
1791
- results.get('msg', ''),
1792
- 'Warning: Due to potential bad behaviour with rhnplugin and certificates, used slower repoquery calls instead of Yum API.'
1793
- )
1794
-
1795
- self.module.exit_json(**results)
1796
-
1797
-
1798
- def main():
1799
- # state=installed name=pkgspec
1800
- # state=removed name=pkgspec
1801
- # state=latest name=pkgspec
1802
- #
1803
- # informational commands:
1804
- # list=installed
1805
- # list=updates
1806
- # list=available
1807
- # list=repos
1808
- # list=pkgspec
1809
-
1810
- yumdnf_argument_spec['argument_spec']['use_backend'] = dict(default='auto', choices=['auto', 'yum', 'yum4', 'dnf', 'dnf4', 'dnf5'])
1811
-
1812
- module = AnsibleModule(
1813
- **yumdnf_argument_spec
1814
- )
1815
-
1816
- module_implementation = YumModule(module)
1817
- module_implementation.run()
1818
-
1819
-
1820
- if __name__ == '__main__':
1821
- main()