Compare commits

..

101 Commits
6.5.0 ... 6.6.2

Author SHA1 Message Date
Felix Fontein
a3c711491a Release 6.6.2. 2023-06-19 19:29:50 +02:00
Felix Fontein
d6a57882d2 Prepare 6.6.2 release. 2023-06-19 19:12:11 +02:00
patchback[bot]
edbef2266d [PR #6735/9642be8b backport][stable-6] Update Datadog downtime URL (#6737)
Update Datadog downtime URL (#6735)

(cherry picked from commit 9642be8b19)

Co-authored-by: Pedro Dias <61794382+diasp-ppb@users.noreply.github.com>
2023-06-19 18:40:42 +02:00
patchback[bot]
2b88ee01d3 [PR #6575/f710a10f backport][stable-6] ini_file: try using inactive option before creating a new one (#6728)
ini_file: try using inactive option before creating a new one (#6575)

* ini_file: make inactive options as active if they exist, instead of creating a new option entry

Add changelog fragment

* Update changelogs/fragments/ini_file-use-inactive-options-when-possible.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix test

* Update tests

* Fix spelling

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f710a10f25)

Co-authored-by: njutn95 <njutn95@yahoo.com>
2023-06-19 08:01:18 +02:00
patchback[bot]
afd24ccd35 [PR #6712/e85b0080 backport][stable-6] gitlab_group => Make most options optional (#6724)
gitlab_group => Make most options optional (#6712)

* Make most options optional as they should be

* Add filter to create_group instead

* Remove whitespace

* Add changelog fragment

* Added description and extension to fragment

* Update changelogs/fragments/6712-gitlab_group-filtered-for-none-values.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/gitlab_group.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Make Python 2.6 compatible.

* Another shot at compatibility.

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit e85b008036)

Co-authored-by: Intellium <w.moeken@moeken.eu>
2023-06-18 21:27:29 +02:00
Felix Fontein
26ada26df1 ansible-core devel no longer supports the Ubuntu 20.04 VM.
(cherry picked from commit 3b13c30112)
2023-06-16 06:18:10 +02:00
patchback[bot]
59999a89f1 [PR #6662/bb216934 backport][stable-6] csv module utils: detect unicode BOM in content (#6694)
csv module utils: detect unicode BOM in content (#6662)

* csv module utils: detect unicode BOM in content

* fix handling of py2

* fix comment

* add changelog frag

* add missing link

* simplification

(cherry picked from commit bb2169340d)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-06-15 08:13:24 +02:00
patchback[bot]
e1f4be1e01 [PR #6688/032996e0 backport][stable-6] Fix composites comparison for role in is_struct_included keycloak.py … (#6689)
Fix composites comparison for role in is_struct_included keycloak.py … (#6688)

* Fix composites comparison for role in is_struct_included keycloak.py function

* Add changelog fragment and unit tests

* Update changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 032996e005)

Co-authored-by: Philippe Gauthier <philippe.gauthier@inspq.qc.ca>
2023-06-14 23:30:30 +02:00
patchback[bot]
e7e2f095ee [PR #6629/2cfbcb4e backport][stable-6] CI: ansible-core devel drops support for Python 3.5 (#6633)
CI: ansible-core devel drops support for Python 3.5 (#6629)

ansible-core devel drops support for Python 3.5.

(cherry picked from commit 2cfbcb4efd)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-06-07 06:59:41 +02:00
patchback[bot]
45200fc233 [PR #6620/91b69263 backport][stable-6] Fixed typo in record_values documentation (#6625)
Fixed typo in record_values documentation (#6620)

(cherry picked from commit 91b692634e)

Co-authored-by: Tristan Pinaudeau <tristan@tic.sh>
2023-06-06 07:39:13 +02:00
patchback[bot]
154d1b7024 [PR #6613/36e8653c backport][stable-6] Minor bitwarden plugin req. docs addition (#6622)
Minor bitwarden plugin req. docs addition (#6613)

The Bitwarden CLI requires a `login` followed by an `unlock` operation.
The later will display a message regarding setting (and exporting) the
`$BW_SESSION` env. var. When using the `bitwarden` lookup plugin, having
the env. var. set and available (exported) to Ansible is critical.
Without it, the plugin will simply return the error:

`Bitwarden Vault locked. Run 'bw unlock'.`

Make this clearer in the requirement documentation.

Signed-off-by: Chris Evich <cevich@redhat.com>
(cherry picked from commit 36e8653cf7)

Co-authored-by: Chris Evich <1183438+cevich@users.noreply.github.com>
2023-06-06 06:04:51 +02:00
patchback[bot]
541fcec900 [PR #6603/47f39675 backport][stable-6] Update BOTMETA.yml (#6606)
Update BOTMETA.yml (#6603)

Stepping down from maintaining parted module

(cherry picked from commit 47f39675a9)

Co-authored-by: Robert Osowiecki <robert.osowiecki@gmail.com>
2023-05-30 22:19:33 +02:00
patchback[bot]
dc6ccbea63 [PR #6584/a6dd7d78 backport][stable-6] Move ansible-core 2.12 to EOL CI (#6586)
Move ansible-core 2.12 to EOL CI (#6584)

* https://github.com/ansible/ansible/pull/79734 has been merged and backported for all branches but stable-2.10 and stable-2.11.

* Move ansible-core 2.12 to EOL CI.

(cherry picked from commit a6dd7d789b)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-29 18:39:15 +02:00
patchback[bot]
6cb044ac13 [PR #6568/27fe14bf backport][stable-6] Fix get_user_by_username in keycloak library (#6582)
Fix get_user_by_username in keycloak library (#6568)

* Fix get_user_by_username in keycloak library for keycloak_user_rolemapping module

* Add changelog fragment for keycloak.py bug fix

(cherry picked from commit 27fe14bfc1)

Co-authored-by: Philippe Gauthier <philippe.gauthier@inspq.qc.ca>
2023-05-29 15:58:56 +02:00
patchback[bot]
af78b2068a [PR #6578/ab046755 backport][stable-6] CI: restrict to python-nomad < 2.0.0 for Python < 3.7 (#6580)
CI: restrict to python-nomad < 2.0.0 for Python < 3.7 (#6578)

Restrict to python-nomad < 2.0.0 for Python < 3.7.

(cherry picked from commit ab046755bb)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-29 15:58:44 +02:00
Felix Fontein
73569b1c36 Next expected release is 6.6.2. 2023-05-22 22:33:13 +02:00
Felix Fontein
5914c1df8e Release 6.6.1. 2023-05-22 22:14:12 +02:00
patchback[bot]
f847531a35 [PR #6534/a9fd9f89 backport][stable-6] added handling of zypper exitcode 102: ZYPPER_EXIT_INF_REBOOT_NEEDED (#6559)
added handling of zypper exitcode 102: ZYPPER_EXIT_INF_REBOOT_NEEDED (#6534)

* added handling of zypper exitcode 102: ZYPPER_EXIT_INF_REBOOT_NEEDED - Returned after a successful installation of a patch which requires reboot of computer.

The exitcode 102 will be treated exactly like 0 by the module internally now, and the changed status will be reported correctly. However, since I preserve the rc 102 in the retvals to allow the playbook to react to the requested reboot, the task must still include a "failed_when: zypper_cmd.rc not in [0, 102]" to not fail in this case.

* removed trailing whitespaces

* added changelogs fragment

* Fix typo.

Co-authored-by: Alex <alexgubin@gmx.de>

* Add URL.

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alex <alexgubin@gmx.de>
(cherry picked from commit a9fd9f8982)

Co-authored-by: tover99 <101673769+tover99@users.noreply.github.com>
2023-05-22 04:54:14 +00:00
patchback[bot]
df01cde23d [PR #6554/29790df5 backport][stable-6] Don't require api_password when api_token_id is used in proxmox_tasks_info (#6561)
Don't require api_password when api_token_id is used in proxmox_tasks_info (#6554)

* Don't require api_password when api_token_id is used in proxmox_tasks_info

* Add changelog fragment

* Fix casing.

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 29790df583)

Co-authored-by: Sergei Antipov <greendayonfire@gmail.com>
2023-05-22 04:54:03 +00:00
patchback[bot]
cdd9ced441 [PR #6548/5f968fb0 backport][stable-6] portage: fix changed_use and newuse not triggering rebuilds (#6008) (#6557)
portage: fix changed_use and newuse not triggering rebuilds (#6008) (#6548)

* portage: fix changed_use and newuse not triggering rebuilds (#6008)

* Add changelog fragment

* Update changelogs/fragments/6548-portage-changed_use-newuse.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 5f968fb075)

Co-authored-by: s-hamann <10639154+s-hamann@users.noreply.github.com>
2023-05-21 22:02:37 +02:00
patchback[bot]
dfb61b283d [PR #6527/cb1e6376 backport][stable-6] nmcli: Fix bond option xmit_hash_policy (#6555)
nmcli: Fix bond option xmit_hash_policy (#6527)

* nmcli_bond_xmit_fix

* Create 6527-nmcli-bond-fix-xmit_hash_policy.yml

add changelog

* Update changelogs/fragments/6527-nmcli-bond-fix-xmit_hash_policy.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* unit tests extend

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit cb1e6376db)

Co-authored-by: Sergey Putko <mail@psvlan.com>
2023-05-21 17:23:56 +02:00
patchback[bot]
fe7b151a26 [PR #6549/d643bd57 backport][stable-6] Switch to Ansible Galaxy compatible requirements files for tests (#6551)
Switch to Ansible Galaxy compatible requirements files for tests (#6549)

Switch to Ansible Galaxy compatible requirements files for tests.

(cherry picked from commit d643bd5794)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-21 15:09:20 +02:00
patchback[bot]
9b983fba86 [PR #6538/08377df7 backport][stable-6] Fix code formatting in documentation of dconf.py (#6542)
Fix code formatting in documentation of dconf.py (#6538)

* Fix code formatting in documentation of dconf.py

* Update plugins/modules/dconf.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/dconf.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 08377df7a5)

Co-authored-by: Tamerlan Bimzhanov <bimzhanovt.net@gmail.com>
2023-05-20 14:57:38 +02:00
patchback[bot]
2d27dbd9ea [PR #6535/ddca52ee backport][stable-6] CI: include has finally been removed in ansible-core devel (#6536)
CI: include has finally been removed in ansible-core devel (#6535)

include: → include_tasks:.
(cherry picked from commit ddca52ee15)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-19 20:45:25 +02:00
patchback[bot]
55cbccf0fc [PR #6478/c740f89d backport][stable-6] CI: Add FreeBSD 13.2 (#6508)
CI: Add FreeBSD 13.2 (#6478)

* Add FreeBSD 13.2.

* Skip FreeBSD 13.2 for iso_extract tests.

* Fix autoremove test: m4 is no longer a dependency, or it was already installed beforehand.

* Also disable the jail tests for FreeBSD 13.2.

(cherry picked from commit c740f89df3)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-09 20:10:00 +02:00
Felix Fontein
675860c392 Remove stable-4 from CI cron; add stable-7 instead.
(cherry picked from commit 9708561131)
2023-05-08 21:40:57 +02:00
patchback[bot]
5e8914e00c [PR #6503/6558c998 backport][stable-6] fix typo in terraform docs (#6506)
fix typo in `terraform` docs (#6503)

fix typo

The [Docs](https://docs.ansible.com/ansible/latest/collections/community/general/terraform_module.html) have a typo in the examples. `camplex_vars` should be spelled `complex_vars`.

Since many people are copy-and-pasting from docs and modify them to their needs this typo is an annoyance for users.

(cherry picked from commit 6558c9981f)

Co-authored-by: Max Juhlke <max.juhlke@gmail.com>
2023-05-08 20:22:34 +02:00
patchback[bot]
c0230342b4 [PR #6491/9f3c86a5 backport][stable-6] dconf - Try to find a Python interpreter that has gi.repository.GLib (#6504)
dconf - Try to find a Python interpreter that has gi.repository.GLib (#6491)

* dconf - Try to find a Python interpreter that has gi.repository.GLib

If we're invoked in a Python interpreter that doesn't have access to
`gi.repository.GLib`, try to find one that does and respawn the task
in that interpreter.

* ChangeLog fragment for #6491

* Update changelogs/fragments/6491-dconf-respawn.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Simplify import code

* Get rid of ModuleNotFoundError

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 9f3c86a589)

Co-authored-by: Jonathan Kamens <jik@kamens.us>
2023-05-08 19:57:32 +02:00
Felix Fontein
ebf15447f0 Prepare 6.6.1 release. 2023-05-08 06:49:33 +02:00
patchback[bot]
86d10f53fd [PR #6498/61a0dc43 backport][stable-6] proxmox_kvm: remove non-working example (#6501)
proxmox_kvm: remove non-working example (#6498)

Remove non-working example.

(cherry picked from commit 61a0dc4370)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-07 21:29:55 +02:00
patchback[bot]
d679f51018 [PR #6497/4e57240e backport][stable-6] terraform: fix warn() call (#6499)
terraform: fix warn() call (#6497)

Fix warn() call.

(cherry picked from commit 4e57240e9f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-07 21:29:45 +02:00
patchback[bot]
e9e494e1ff [PR #6387/9c112308 backport][stable-6] Fixes #5143: xfs_quota fails to initialize new project quotas (#6495)
Fixes #5143: xfs_quota fails to initialize new project quotas (#6387)

This fix ensures that in case of a project quota, the corresponding project gets initialized, if required.

Signed-off-by: Christoph Fiehe <c.fiehe@eurodata.de>
Co-authored-by: Christoph Fiehe <c.fiehe@eurodata.de>
(cherry picked from commit 9c11230824)

Co-authored-by: cfiehe <cfiehe@users.noreply.github.com>
2023-05-06 07:20:03 +02:00
patchback[bot]
bf90b4e88a [PR #6479/57cfd1b4 backport][stable-6] deps module util: fix overzealous name cropping (#6489)
deps module util: fix overzealous name cropping (#6479)

Fix overzealous name cropping.

(cherry picked from commit 57cfd1b46d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-05 07:56:22 +02:00
patchback[bot]
874d7f7050 [PR #6456/fab717bb backport][stable-6] modules/portage: fix usage with withbdeps: false (#6480)
modules/portage: fix usage with withbdeps: false (#6456)

Using ``withbdeps: false`` was causing the underlying emerge command to
fail due to not passing an argument to the ``--with-bdeps`` flag.  Fix
by updating the logic for generating the emerge command arguments to
ensure that ``withbdeps: false`` results in a passing an ``n`` argument
with the ``--with-bdeps`` emerge flag.

(cherry picked from commit fab717bb2d)

Co-authored-by: Daniel Harding <dharding@living180.net>
2023-05-04 08:14:29 +02:00
patchback[bot]
3a19fbc89c [PR #6458/d254372d backport][stable-6] puppet: fix noop parameter error (#6481)
puppet: fix noop parameter error (#6458)

* puppet: fix noop parameter error

* add changelog frag

(cherry picked from commit d254372d37)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-05-04 08:14:19 +02:00
patchback[bot]
2d8e1339d1 [PR #6482/737d37e0 backport][stable-6] CI: Arch Linux now uses Python 3.11 (#6487)
CI: Arch Linux now uses Python 3.11 (#6482)

Arch Linux now uses Python 3.11.

(cherry picked from commit 737d37e019)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-04 08:12:38 +02:00
patchback[bot]
4892f954fa [PR #6472/cb81abc6 backport][stable-6] CI: Fix matrix entries (#6474)
CI: Fix matrix entries (#6472)

Fix CI matrix entries.

(cherry picked from commit cb81abc636)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-05-02 20:21:11 +02:00
patchback[bot]
6fd58ba388 [PR #6396/c411e125 backport][stable-6] Setup docker (#6466)
Setup docker (#6396)

* setup-docker: install and run docker inside integration test VMs

* must pip install requests to satisfy community.docker requirements

* add Ubuntu configuration to setup_docker

* Update tests/integration/targets/mssql_script/tasks/main.yml

* Update tests/integration/targets/mssql_script/tasks/main.yml

* docker_pacakges variable non-existent by default

* add setup_docker to keycloak_groups

* add setup_docker to keycloak_groups

* removed unused file tasks/nothing.yml

* add README

* add copyright notice to readme file

* Update tests/integration/targets/setup_docker/README.md

* rolled back the boilerplate disclaimer to mssql_script tasks/main.yml

(cherry picked from commit c411e12555)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-05-01 21:20:01 +02:00
patchback[bot]
49967547df [PR #6459/24efe6b9 backport][stable-6] Make sure that plugins/module_utils/deps.py is cleaned up before every test (#6462)
Make sure that plugins/module_utils/deps.py is cleaned up before every test (#6459)

Make sure that plugins/module_utils/deps.py is cleaned up before every test.

ci_complete

(cherry picked from commit 24efe6b9db)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-29 22:27:12 +02:00
patchback[bot]
492170b414 [PR #6442/b4628e27 backport][stable-6] awall: improve docs (#6455)
awall: improve docs (#6442)

* awall: improve docs

* Update plugins/modules/awall.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit b4628e27b2)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-28 15:26:04 +02:00
patchback[bot]
25b9391fb4 [PR #6447/c58dda14 backport][stable-6] passwordstore plugin: vendor FileLock that was removed from ansible-core devel (#6450)
passwordstore plugin: vendor FileLock that was removed from ansible-core devel (#6447)

Vendor FileLock that was removed from ansible-core devel.

(cherry picked from commit c58dda14c2)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-28 12:17:47 +02:00
patchback[bot]
1e30afa92f [PR #6439/6e913a3b backport][stable-6] dnsimple_info: remove extraneous importorskip from test (#6446)
dnsimple_info: remove extraneous importorskip from test (#6439)

* dnsimple_info: remove extraneous importorskip from test

* remove yet another extraneous importorskip from test

(cherry picked from commit 6e913a3b28)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-28 10:21:00 +02:00
Felix Fontein
7d4e69cdf7 The next expected release will be 6.6.1. 2023-04-24 21:39:56 +02:00
Felix Fontein
ff23e41c21 Release 6.6.0. 2023-04-24 20:56:30 +02:00
Felix Fontein
3ce7d2fc7e Prepare 6.6.0 release. 2023-04-23 22:46:58 +02:00
patchback[bot]
20a9d120aa [PR #6403/eab39ffc backport][stable-6] iso_customize: fix integration test (#6425)
iso_customize: fix integration test (#6403)

* Fix bad parameters in integration test.

* Remove unrelated thing.

* Simply remove test.

(cherry picked from commit eab39ffc23)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 20:59:27 +02:00
patchback[bot]
f4a40592a1 [PR #6415/f0fcc91a backport][stable-6] zypper_repository: disable failing repository (#6424)
zypper_repository: disable failing repository (#6415)

* Disable failing repository from zypper_repository tests.

* Also disable repo file for >= 15.4.

* Simply disable file test for now.

(cherry picked from commit f0fcc91ac7)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:50:08 +02:00
patchback[bot]
3d5145e924 [PR #6414/69d7f19c backport][stable-6] Restrict jail tests for sysrc to certain FreeBSD versions (#6421)
Restrict jail tests for sysrc to certain FreeBSD versions (#6414)

Restrict jail tests for sysrc to certain FreeBSD versions.

(cherry picked from commit 69d7f19c74)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:49:58 +02:00
patchback[bot]
1ae5a2cd05 [PR #6413/0edc3e82 backport][stable-6] xfs_quota: uninstalling packages breaks tests on Alpine (#6418)
xfs_quota: uninstalling packages breaks tests on Alpine (#6413)

Uninstalling packages breaks tests on Alpine.

(cherry picked from commit 0edc3e820e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:49:49 +02:00
patchback[bot]
75aa353281 [PR #6412/68614502 backport][stable-6] Fix kdeconfig tests (#6416)
Fix kdeconfig tests (#6412)

Fix kdeconfig tests.

(cherry picked from commit 6861450243)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:49:39 +02:00
patchback[bot]
5fe10915a8 [PR #6404/e49c6a33 backport][stable-6] ini_file: removing required=true for 'section' option (#6411)
ini_file: removing required=true for 'section' option (#6404)

Fix ini_file by removing required=true for 'section' option.

(cherry picked from commit e49c6a339e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:49:32 +02:00
patchback[bot]
eca6494503 [PR #6401/486c47f9 backport][stable-6] rhsm_release: removing required=true for 'release' option (#6409)
rhsm_release: removing required=true for 'release' option (#6401)

Fix rhsm_release by removing required=true for 'release' option.

(cherry picked from commit 486c47f922)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-23 18:49:20 +02:00
patchback[bot]
b12d422fcc [PR #6390/c1b16d53 backport][stable-6] office_365_connector_card: Remove references to dev.outlook.com (#6407)
office_365_connector_card: Remove references to dev.outlook.com (#6390)

* office_365_connector_card: Remove references to dev.outlook.com

Remove references to the deprecated dev.outlook.com and update them to the relevant learn.microsoft.com links.

Closed #6262

* Fix PEP 8 line length issue

* Apply suggestions from PR review

* Update plugins/modules/office_365_connector_card.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit c1b16d534f)

Co-authored-by: Marc Sensenich <marc-sensenich@users.noreply.github.com>
2023-04-23 16:50:35 +02:00
patchback[bot]
33809395ab [PR #6256/bc228d82 backport][stable-6] Add keycloak_authz_authorization scope module (#6400)
Add keycloak_authz_authorization scope module (#6256)

* Add keycloak_authz_authorization scope module

This module allows managing Keycloak client authorization scopes. The client has
to have authorization enable for this to work.

* botmeta: make mattock maintainer of keycloak_authz_authorization_scope

* botmeta: add mattock to team_keycloak

* keycloak_authz_authorization_scope: documentation and code layout fixes

* keycloak_authz_authorization_scope: do not fail on names with whitespace

* keycloak_authz_authorization_scope: use url quote method

Co-authored-by: Felix Fontein <felix@fontein.de>

* keycloak_authz_authorization_scope: style fixes to documentation

* keycloak_authz_authorization_scope: do not claim check/diff mode support

* keycloak_authz_authorization_scope: fix documentation

* keycloak_authz_authorization_scope: support check_mode and diff_mode

* keycloak_authz_authorization_scope: use more common terminology

Most keycloak modules use before_<object_type> and desired_<object_type> to
designate current and desired states of objects. Do the same for authorization
scopes.

* keycloak_authz_authorization_scope: fixes to check_mode and docs

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit bc228d82be)

Co-authored-by: Samuli Seppänen <samuli.seppanen@gmail.com>
2023-04-23 14:59:59 +02:00
patchback[bot]
60fe6ebc3c [PR #6383/e7cc9964 backport][stable-6] deps module utils: add function failed() to retrieve dependencies check result (#6399)
deps module utils: add function failed() to retrieve dependencies check result (#6383)

* deps module utils: add function failed() to retrieve dependencies check result

* add changelog frag

(cherry picked from commit e7cc996470)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-23 14:53:38 +02:00
patchback[bot]
957f3e6eca [PR #6382/3e338a1c backport][stable-6] udm_dns_record: minor refactor (#6398)
udm_dns_record: minor refactor (#6382)

* udm_dns_record: minor refactor

* remove unused import

* improve ptr_record zone validation

* add changelog frag

* undo zone validation change as it breaks for IPv6 addresses

(cherry picked from commit 3e338a1cca)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-23 14:46:07 +02:00
patchback[bot]
dbd918865f [PR #6385/ad6ff9b0 backport][stable-6] cpanm: apply MH feature (#6395)
cpanm: apply MH feature (#6385)

* cpanm: apply MH feature

* add changelog frag

(cherry picked from commit ad6ff9b0c5)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-23 12:29:06 +02:00
patchback[bot]
46a83df85e [PR #6352/0eb33c28 backport][stable-6] Add support env variables for nmap inventory plugin (#6394)
Add support env variables for nmap inventory plugin (#6352)

* Add support env variables for nmap inventory plugin

* Add  changelogs/fragments file

* Rename support-env-variables-to-nmap-dynamic-inventoiry to 6351-support-env-variables-to-nmap-dynamic-inventoiry

* Add extension for changelog file

* Fix https://github.com/ansible-collections/community.general/pull/6352#discussion_r1167973326

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix https://github.com/ansible-collections/community.general/pull/6352#discussion_r1167973295

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix https://github.com/ansible-collections/community.general/pull/6352#discussion_r1167973265

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix https://github.com/ansible-collections/community.general/pull/6352#discussion_r1167973193

* Fix linter

* Fix https://github.com/ansible-collections/community.general/pull/6352#discussion_r1168014445

* Fix changelog fragment.

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 0eb33c2839)

Co-authored-by: efrikin <sadeless@gmail.com>
2023-04-23 12:28:58 +02:00
patchback[bot]
57f262504d [PR #6318/114eb67f backport][stable-6] keycloak: Add option to create authentication sub-flow of type 'form flow' (#6393)
keycloak: Add option to create authentication sub-flow of type 'form flow' (#6318)

* keycloak: Improve API error message

* keycloak: Fix API error message

They key 'provider' is undefined.

* keycloak: Allow the creation of 'form-flow' authentication sub flows

To create something like keycloak's built-in registration flow,
we need to create a subflow with the type 'form-flow'.

* Add changelog fragment 6318

* Update changelogs/fragments/6318-add-form-flow.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/keycloak_authentication.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/keycloak_authentication.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* keycloak_authentication: Don't compare subFlowType

It is only useful for creation.

* Update changelogs/fragments/6318-add-form-flow.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 114eb67f58)

Co-authored-by: fachleitner <flo@fopen.at>
2023-04-23 12:28:49 +02:00
patchback[bot]
29671cb54c [PR #6322/1f2c7b17 backport][stable-6] Add a module to set the keycloak client scope type (#6392)
Add a module to set the keycloak client scope type (#6322)

The module keycloak_clientscope_type allows to set the client scope
types (optional/default) either on realm or client level.

(cherry picked from commit 1f2c7b1731)

Co-authored-by: Simon Pahl <simpahl@gmail.com>
2023-04-23 12:28:33 +02:00
patchback[bot]
fd91e94279 [PR #6203/f4dd4d5a backport][stable-6] Add append option to ipa_hostgroup module (#6391)
Add append option to ipa_hostgroup module (#6203)

* Add append option to ipa_hostgroup module

Signed-off-by: Jan Sobczak <jan.sobczak@soit.com.pl>

* Update plugins/modules/ipa_hostgroup.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/ipa_hostgroup.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Add changelog fragment

Signed-off-by: Jan Sobczak <jan.sobczak@soit.com.pl>

* Move choices argument to previous line

Signed-off-by: Jan Sobczak <jan.sobczak@soit.com.pl>

* Update plugins/modules/ipa_hostgroup.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/6203-add-append-option-to-ipa-hostgroup.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Signed-off-by: Jan Sobczak <jan.sobczak@soit.com.pl>
Co-authored-by: Jan Sobczak <jan.sobczak@soit.com.pl>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f4dd4d5ace)

Co-authored-by: Jan Sobczak <9196392+jansobczak@users.noreply.github.com>
2023-04-22 23:07:52 +02:00
patchback[bot]
b001e36fb3 [PR #6381/9b493d53 backport][stable-6] iptables_state: add comment sanity ignore files (#6386)
iptables_state: add comment sanity ignore files (#6381)

add comment to iptables_state line in the sanity ignore files

(cherry picked from commit 9b493d53a9)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-20 19:25:27 +02:00
patchback[bot]
5f63476404 [PR #6370/484f642c backport][stable-6] Redfish iDRAC: Allow for specifying an exact manager with 'resource_id' for CreateBiosConfigJob (#6380)
Redfish iDRAC: Allow for specifying an exact manager with 'resource_id' for CreateBiosConfigJob (#6370)

Allow for specifying an exact manager with 'resource_id' for CreateBiosConfigJob

Signed-off-by: Mike Raineri <michael.raineri@dell.com>
(cherry picked from commit 484f642c23)

Co-authored-by: Mike Raineri <michael.raineri@dell.com>
2023-04-20 07:50:42 +02:00
patchback[bot]
eff452e4a5 [PR #6361/4171418e backport][stable-6] snap_alias: fix regex expression when listing aliases (#6378)
snap_alias: fix regex expression when listing aliases (#6361)

* snap_alias: fix regex expression when listing aliases

* simplify regex, add changelog

(cherry picked from commit 4171418e37)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-20 07:27:53 +02:00
patchback[bot]
d393b16064 [PR #6303/996fc8c1 backport][stable-6] pipx: fix state=latest w/ install_deps=true (#6377)
pipx: fix state=latest w/ install_deps=true (#6303)

* pipx and pipx_info: Document that modules require pipx 0.16.2.1 or above

Since their introduction, these modules rely on 'pipx list --json' to
return machine-readable output about installed pipx applications. That
functionality was introduced in 0.16.2, along with a critical bug fix
(invalid json) in 0.16.2.1.

* pipx: fix state=latest with install_deps=true

"pipx upgrade" stopped supporting the "--include-deps" option
("install_deps" in the ansible module) in pipx 0.15
(https://pypa.github.io/pipx/changelog/#01500).

The lack of support causes the pipx module to fail if attempting to use
state=latest with install_deps, since the parameter is passed to both
pipx install (fine) and pipx upgrade (fails).

* Add changelog fragment

(cherry picked from commit 996fc8c18e)

Co-authored-by: Paul Aurich <paul@darkrain42.org>
2023-04-20 05:21:40 +00:00
patchback[bot]
40b5967fc3 [PR #5832/ae5090d9 backport][stable-6] New modules btrfs_subvolume / btrfs_info (#6376)
New modules btrfs_subvolume / btrfs_info (#5832)

* Initial implementation for new modules btrfs_subvolume and btrfs_info

* Improve/flesh out documentation. Add ability to target filesystem by uuid, label or device. Update tests to test targeting filesystem by each supported parameter and when only mountpoint.

* Updates for btrfs modules. Add missing copyright notices. Switch options to contains in return documentation. Update btrfs_subvolume to always use closest parent mount.

* Add maintainers for btrfs module(s) and remove unused class member cause lint failure.

* Add changelog fragment. Attempt to only run against the VMs as part of CI.

* Updates per code review. Remove changelog fragment. Switch use of map to list comprehension. Add trailing comma to last item in multi-line dicts. Clean up documentation with complete senstences for descriptions and correct/consistent use of macros.

* Improved error handling in btrfs_subvolume module: add custom exception type, favor exceptions over immediate call to fail_json and add single top level return for failure scenarios. Normalize name and snapshot_source parameters early in module execution and remove unecessary duplicate normalization throughout processing.

* Add azp/posix/3 to aliases per feedback

* Clean up automatic mounting. Prevent automount when check_mode=True. Immediately fail if a mount is identified as required and automount=True. Identify the minimal subset of subvolumes that need to be mounted instead of just finding a single common root.

* Skip btrfs_subvolume integration tests if btrfs-progs isn't successfully installed.

* Bump version_added for btrfs modules to 6.6.0. Ensure consistent trailing punctuation for module descriptions and document check_mode behavior as attribute description rather than a module level note.

* Remove unused imports from btrfs_subvolume module.

* Fix import.

* Docs improvements.

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit ae5090d90e)

Co-authored-by: Gregory Furlong <50386272+gnfzdz@users.noreply.github.com>
2023-04-20 07:12:45 +02:00
patchback[bot]
ccabc342b9 [PR #6286/76dd465e backport][stable-6] icinga2_host: make use of templates and template vars (#6375)
icinga2_host: make use of templates and template vars (#6286)

* icinga2_host: make use of templates, append vars instead of replacing all vars array.

* Initialize `template` variable. Add changelog fragment.

* Update changelogs/fragments/6286-icinga2_host-template-and-template-vars.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 76dd465e08)

Co-authored-by: yoannlr <32494673+yoannlr@users.noreply.github.com>
2023-04-20 06:59:05 +02:00
patchback[bot]
570f6a8791 [PR #6338/fe798e22 backport][stable-6] parted: improve docs (#6373)
parted: improve docs (#6338)

(cherry picked from commit fe798e22a6)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-20 06:34:38 +02:00
patchback[bot]
b10cf1e357 [PR #6289/6e0bc4f4 backport][stable-6] Remove --app by flatpak check if already installed (ansible-collectio… (#6372)
Remove --app by flatpak check if already installed (ansible-collectio… (#6289)

* Remove --app by flatpak check if already installed (ansible-collections#6265)

* Add Changelogfragment

* Fix syntax

* Update changelogs/fragments/6289-bugfix-flatpak-check-if-already-installed.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 6e0bc4f45c)

Co-authored-by: Svenum <43136984+Svenum@users.noreply.github.com>
2023-04-20 06:34:29 +02:00
patchback[bot]
acfe464a31 [PR #6366/1aa94a5a backport][stable-6] redhat_subscription: document the security of the registration (#6368)
redhat_subscription: document the security of the registration (#6366)

(cherry picked from commit 1aa94a5a1d)

Co-authored-by: Pino Toscano <ptoscano@redhat.com>
2023-04-19 21:14:34 +02:00
patchback[bot]
f32a8dc740 [PR #6367/087c5a53 backport][stable-6] Redfish iDRAC: Added job_id to response for CreateBiosConfigJob (#6369)
Redfish iDRAC: Added job_id to response for CreateBiosConfigJob (#6367)

* Redfish iDRAC: Added job_id to response for CreateBiosConfigJob

Signed-off-by: Mike Raineri <michael.raineri@dell.com>

* YAML fix to changelog fragment

Signed-off-by: Mike Raineri <michael.raineri@dell.com>

---------

Signed-off-by: Mike Raineri <michael.raineri@dell.com>
(cherry picked from commit 087c5a53d5)

Co-authored-by: Mike Raineri <michael.raineri@dell.com>
2023-04-19 21:14:29 +02:00
patchback[bot]
ea8f109056 [PR #6336/59cb5b2d backport][stable-6] dconf: minor refactor (#6353)
dconf: minor refactor (#6336)

* dconf: minor refactor

* add changelog frag

* Update plugins/modules/dconf.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 59cb5b2d6f)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-17 04:05:24 +00:00
patchback[bot]
7e7b84348b [PR #6337/67b921e4 backport][stable-6] parted: add integration test (#6348)
parted: add integration test (#6337)

* parted: add integration test

* Update tests/integration/targets/parted/aliases

Co-authored-by: Felix Fontein <felix@fontein.de>

* adjusted for Alpine

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 67b921e4e6)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-16 22:09:49 +02:00
patchback[bot]
baf552337d [PR #6345/ee11847c backport][stable-6] Do extra docs validation; explicitly disallow semantic markup in docs (#6350)
Do extra docs validation; explicitly disallow semantic markup in docs (#6345)

* Do extra docs validation. Explicitly disallow semantic markup in docs.

* Forgot to add new requirement.

* Fix prefixes.

* Remove superfluous condition.

* TEMP - make CI fail.

* Revert "TEMP - make CI fail."

This reverts commit 14f4d6b503.

* Remove unnecessary import.

* Make sure ANSIBLE_COLLECTIONS_PATH is set.

* Make sure sanity tests from older Ansible versions don't complain.

(cherry picked from commit ee11847c7e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-16 19:45:21 +02:00
patchback[bot]
3ff9161ab0 [PR #6334/64e797d0 backport][stable-6] dig: Support multiple domains in a single lookup (#6346)
dig: Support multiple domains in a single lookup (#6334)

* dig: Support multiple domains in a single lookup (#6334)

The docs for this plugin indicated that multiple domains could be
specified at once, but the code did not support multiple domains.

* Address review feedback.

(cherry picked from commit 64e797d077)

Co-authored-by: Kevin P. Fleming <kpfleming@users.noreply.github.com>
2023-04-16 15:34:50 +02:00
patchback[bot]
aa8728b22c [PR #6300/a35542d0 backport][stable-6] Rundeck modules fixes and improvements (#6343)
Rundeck modules fixes and improvements (#6300)

* feat: add token alias to api_token parameter

* fix: return (None, info) on empty content response

* feat: update the modules for using module_utils.rundeck funcs

* docs: add changelog fragment

* fix: add trailing commas

* fix: changelog fragment invalid syntax

* fix: changelog typos

* fix: remove token aliases from api_token

* fix: add token alias to api_token param

* fix: add partial overwrite of params and docs

(cherry picked from commit a35542d0d1)

Co-authored-by: Phillipe Smith <phsmithcc@gmail.com>
2023-04-16 13:47:47 +02:00
patchback[bot]
590ff351b4 [PR #6294/cb3ca05b backport][stable-6] one_vm: fix syntax error when creating VMs with a more complex template (#6342)
one_vm: fix syntax error when creating VMs with a more complex template (#6294)

* one_vm: fix syntax error when creating VMs with a more complex template

with more complex templates that make use of quoted strings the new
"render" method fails to produce a template that is accepted by
OpenNebula.  ==> escape double quotes in strings to make OpenNebula
happy again.

I also tested whether newlines need to be escaped, looks like they are
fine as they are.

Fixes #6225

* module_utils/opennebula: skip empty values in render

(cherry picked from commit cb3ca05bd1)

Co-authored-by: Georg Gadinger <nilsding@nilsding.org>
2023-04-16 13:40:09 +02:00
patchback[bot]
a5824a2a9d [PR #6312/29a7d24d backport][stable-6] nmcli: Add macvlan connection type support (#6341)
nmcli: Add macvlan connection type support (#6312)

* add nmcli macvlan type

* changelog

* improve docs

* macvlan params

* fix linter and improve module params

* improve_docs

* raise error if type macvlan and macvlan options not set

(cherry picked from commit 29a7d24d75)

Co-authored-by: Sergey Putko <mail@psvlan.com>
2023-04-16 13:40:00 +02:00
patchback[bot]
71975be3c1 [PR #5488/d7340945 backport][stable-6] snap: add tests for multiple commands (#6340)
snap: add tests for multiple commands (#5488)

* snap: add tests for multiple commands

* snap: add tests + become

* remove packages again for idempotency

* roll back become=true in tests

(cherry picked from commit d7340945a4)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-16 13:39:52 +02:00
patchback[bot]
564f87c775 [PR #6115/f95b8ab9 backport][stable-6] Adding CheckiLORebootStatus functionality to ilo_redfish_command (#6339)
Adding CheckiLORebootStatus functionality to ilo_redfish_command (#6115)

* Adding CheckiLORebootStatus functionality to ilo_redfish_command

* PR fix

* Update plugins/modules/ilo_redfish_command.py

Agreed

Co-authored-by: Felix Fontein <felix@fontein.de>

* sanity fix

* Changed command to WaitforiLORebootCompletion

* PR comment fix

* Update plugins/modules/ilo_redfish_command.py

Agreed

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/ilo_redfish_command.py

Agreed

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/ilo_redfish_command.py

Agreed

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/ilo_redfish_command.py

Agreed

Co-authored-by: Felix Fontein <felix@fontein.de>

* Updating documentation based on PR comment

* Adding tests and updating task name in module

---------

Co-authored-by: Kushal <t-s.kushal@hpe.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f95b8ab9cd)

Co-authored-by: TSKushal <44438079+TSKushal@users.noreply.github.com>
2023-04-16 13:39:44 +02:00
patchback[bot]
6750a866c6 [PR #6316/42bc2cb4 backport][stable-6] fix small typo (#6333)
fix small typo (#6316)

fix small typo toch -> touch

(cherry picked from commit 42bc2cb4d8)

Co-authored-by: Sharuzzaman Ahmat Raslan <sharuzzaman@gmail.com>
2023-04-13 09:57:26 +02:00
patchback[bot]
c6316c1153 [PR #6319/83994c0a backport][stable-6] redhat_subscription: use CLI when using environments (#6332)
redhat_subscription: use CLI when using environments (#6319)

It turns out that the 'environments' that the D-Bus Register*() APIs
accept are the IDs of the environments, and not the user-facing names of
the environments (which is what the module has been accepting so far).

Since there is no easy way to do the mapping manually, for now use again
the subscription-manager CLI for registering when environments are
specified.

(cherry picked from commit 83994c0a2d)

Co-authored-by: Pino Toscano <ptoscano@redhat.com>
2023-04-13 08:09:58 +02:00
patchback[bot]
2c825f04e7 [PR #6296/bf780ea7 backport][stable-6] terraform: remote state file exists check issue (#6331)
terraform: remote state file exists check issue (#6296)

* remote state file exists check

In the official CLI implementation of Terraform, if the state file does not exist, a new one will be created, and there is no need to check that the state file already exists and with an error if file not exists.

```bash
# Test command
terraform apply -state test.tfstate. # if state file not exists ,terraform will create a new one
terraform destroy -state test1.tfstate  ### Terraform will not throw any error, the command will succeed execute, only report no resource has destroy
```

* Update terraform.py

add 1 blank line to function end

* Create 6296-LanceNero-Terraform_statefile_check

remove file exists check (https://github.com/ansible-collections/community.general/pull/6296)

* resolve if case issue

* Add blank line

* Update 6296-LanceNero-Terraform_statefile_check

* Update changelogs/fragments/6296-LanceNero-Terraform_statefile_check

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* update code style

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update suffix to correct CI issue

* Update Code Style

* Update bug-fix to feature release

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
(cherry picked from commit bf780ea738)

Co-authored-by: LanceNero <Lance.nero@gmail.com>
2023-04-13 07:43:49 +02:00
patchback[bot]
8a33e070be [PR #6206/a5765143 backport][stable-6] dconf: three minor but useful fixes (#6330)
dconf: three minor but useful fixes (#6206)

* dconf: Correctly handle setting a key that has no value in DB

We need to check if the value in the database is None before we try to
parse it, because the GVariant parser won't accept None as an input
value. By definition if the value is None, i.e., there's no value in
the database, than any value the user is trying to set is a change, so
just indicate that it's a change without trying to compare the None to
whatever the user specified as the value.x

* dconf: Give a more useful error when writing a key fails

if writing a key fails, then include in the error that is returned the
exact key and value aguments that were given to the dconf command, to
assist in diagnosing failures caused by providing the key or value in
the wrong format.x

* dconf: Convert boolean values into the format that dconf expects

Even though we warn users to be careful to specify GVariant strings
for values, a common error is to be trying to specify a boolean string
which ends up getting converted into a boolean by the YAML parser or
Ansible. Then it gets converted to "True" or "False", the string
representations of Python booleans, which are not valid GVariants.

Rather than just failing with an obscure error when this happens,
let's be more user-friendly and detect when the user has specified a
boolean and convert it into the correct GVariant forms, "true" or
"false", so it just works. There's no good reason to be more pedantic
than that.

(cherry picked from commit a5765143f1)

Co-authored-by: Jonathan Kamens <jik@kamens.us>
2023-04-13 07:18:19 +02:00
patchback[bot]
d2a5fdfc71 [PR #6320/86f9a657 backport][stable-6] Remove nibalizer from puppet module (#6329)
Remove nibalizer from puppet module (#6320)

Sorry! I haven't had time to work on this in a while. Cheers!

(cherry picked from commit 86f9a657e7)

Co-authored-by: Spencer Krum <nibz@spencerkrum.com>
2023-04-13 07:14:01 +02:00
patchback[bot]
b6b2419206 [PR #6274/14b19afc backport][stable-6] archive: Generate crc32 over 16MiB chunks (#6326)
archive: Generate crc32 over 16MiB chunks (#6274)

* archive: Generate crc32 over 16MiB chunks

Running crc32 over the whole content of the compressed file potentially
requires a lot of RAM. The crc32 function in zlib allows for calculating
the checksum in chunks. This changes the code to calculate the checksum
over 16 MiB chunks instead. 16 MiB is the value also used by
shutil.copyfileobj().

* Update changelogs/fragments/6199-archive-generate-checksum-in-chunks.yml

Change the type of change to bugfix

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/6199-archive-generate-checksum-in-chunks.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 14b19afc9a)

Co-authored-by: Nils Meyer <nils@nm.cx>
2023-04-13 05:09:34 +00:00
patchback[bot]
4c399f1c01 [PR #6293/ea8720f5 backport][stable-6] Add skip_tags option to Puppet module (#6327)
Add skip_tags option to Puppet module (#6293)

* Add Puppet skip_tags option

* Include changelog fragment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit ea8720f572)

Co-authored-by: Jon-Paul Lindquist <jonpaul@lindquist.me>
2023-04-13 05:09:28 +00:00
patchback[bot]
923d335646 [PR #6267/6c5c05df backport][stable-6] homebrew: respect greedy flag for cask upgrade_all (#6328)
homebrew: respect greedy flag for cask upgrade_all (#6267)

* homebrew: respect greedy flag for cask upgrade_all

* homebrew: implement feedback from code review

(cherry picked from commit 6c5c05dfdc)

Co-authored-by: Claude Becker <becker@phys.ethz.ch>
2023-04-13 07:09:19 +02:00
patchback[bot]
be96c33257 [PR #6313/aa77a88f backport][stable-6] pkgng: skip jail tests also on FreeBSD 12.3 (#6315)
pkgng: skip jail tests also on FreeBSD 12.3 (#6313)

Skip jail tests also on FreeBSD 12.3.

(cherry picked from commit aa77a88f4b)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-10 21:30:59 +02:00
patchback[bot]
e68b1017e3 [PR #6307/28bdf1ed backport][stable-6] xfs_quota: fix integration tests for Alpine Linux (#6310)
xfs_quota: fix integration tests for Alpine Linux (#6307)

* xfs_quota: fix integration tests for Alpine Linux

* remove skip/alpine

(cherry picked from commit 28bdf1ed74)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-10 07:26:44 +00:00
patchback[bot]
44e4b1d202 [PR #6308/f93a1bf5 backport][stable-6] pipx: Add support for system_site_packages (#6311)
pipx: Add support for system_site_packages (#6308)

* pipx: Add support for system_site_packages

* Add changelog fragment

(cherry picked from commit f93a1bf5ec)

Co-authored-by: Paul Aurich <paul@darkrain42.org>
2023-04-10 07:26:31 +00:00
patchback[bot]
24378fd944 [PR #6304/08f14f3e backport][stable-6] pids tests: 'some-' is not that a unique pattern. (#6306)
pids tests: 'some-' is not that a unique pattern. (#6304)

* 'some-' is not that a unique pattern.

* Add debugging help.

* Avoid passing the name as a parameter to obtainpid.sh.

(cherry picked from commit 08f14f3eb0)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-09 15:10:11 +02:00
patchback[bot]
c3e1715233 [PR #6299/37cddb8c backport][stable-6] snap: enable ubuntu in integration tests (#6302)
snap: enable ubuntu in integration tests (#6299)

enable ubuntu in integration tests for snap

(cherry picked from commit 37cddb8c02)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-08 17:28:48 +00:00
patchback[bot]
ec86ebed98 [PR #6269/a64e3682 backport][stable-6] mksysb: improve visibility on the output (#6291)
mksysb: improve visibility on the output (#6269)

* mksysb: improve visibility on the output

* add changelog frag

(cherry picked from commit a64e36820f)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2023-04-05 14:10:47 +02:00
patchback[bot]
5aa2779a48 [PR #6284/7d19eca8 backport][stable-6] Update CI matrix: add stable-2.15 (#6285)
Update CI matrix: add stable-2.15 (#6284)

* Add ignore files for bumped devel version.

* Update CI matrix.

(cherry picked from commit 7d19eca8bf)

Co-authored-by: Felix Fontein <felix@fontein.de>
2023-04-04 10:10:25 +02:00
patchback[bot]
f6d15ec818 [PR #6278/cee5f31b backport][stable-6] Add support to topics on Gitlab Project module (#6283)
Add support to topics on Gitlab Project module (#6278)

* add topics to gitlab_project.py

* add chngelog fragment

* fix lint error

* Update changelogs/fragments/6277-add-topics-gitlab-project.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit cee5f31b70)

Co-authored-by: Lorenzo Tanganelli <35271287+tanganellilore@users.noreply.github.com>
2023-04-03 22:15:23 +02:00
patchback[bot]
f0320b5ac9 [PR #6275/c280b793 backport][stable-6] redhat_subscription: fix D-Bus option for environments on CentOS (#6282)
redhat_subscription: fix D-Bus option for environments on CentOS (#6275)

Factorize the current logic to determine whether use 'environments' as
D-Bus registration option (rather than 'environment') in an own
function, so it is easier to read it and maintain it.

With the small helper function in place, extend the logic to support
CentOS: it is in practice the same as the RHEL one, with an additional
check to support CentOS Stream 8 (which is a rolling release, and not
versioned).

(cherry picked from commit c280b793de)

Co-authored-by: Pino Toscano <ptoscano@redhat.com>
2023-04-03 22:02:12 +02:00
patchback[bot]
79578e5db3 [PR #6261/9bb768ae backport][stable-6] Update maintainership of rhsm modules (#6281)
Update maintainership of rhsm modules (#6261)

- remove Barnaby, Adrian, and Kevil from redhat_subscription, as they
  are no more working on subscription-manager
- create a new team_rhsm group to maintain redhat_subscription,
  rhsm_release, and rhsm_repository (all the modules related to
  subscription-manager)
- add myself and cnsnyder to team_rhsm

(cherry picked from commit 9bb768ae62)

Co-authored-by: Pino Toscano <ptoscano@redhat.com>
2023-04-03 21:26:27 +02:00
patchback[bot]
43beaf4b00 [PR #6259/c9e11e5c backport][stable-6] redhat_subscription: manually unregister only when registered (#6280)
redhat_subscription: manually unregister only when registered (#6259)

When registering using D-Bus and using a version of subscription-manager
with an unimplemented 'force' option, then unregister manually the
system only if it is registered. 'subscription-manager unregister'
errors out when trying to unregister an already unregistered system.

(cherry picked from commit c9e11e5c0c)

Co-authored-by: Pino Toscano <ptoscano@redhat.com>
2023-04-03 21:25:53 +02:00
patchback[bot]
6b0cc3c1de [PR #6212/33093e9f backport][stable-6] pipx: document parameters supported with state=latest, and fix include_injected parameter (#6266)
pipx: document parameters supported with state=latest, and fix include_injected parameter (#6212)

* pipx: Document parameters supported with state=latest

* pipx: Support "include_injected" with states "upgrade" and "latest"

- Contrary to documentation, "include_injected" doesn't appear to have
  worked with state=upgrade since it was introduced.
- Add support for include_injected with "latest", since "latest" is
  specified as being install+upgrade.

* Add changelog fragment

* Update plugins/modules/pipx.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelog fragment

Co-authored-by: Felix Fontein <felix@fontein.de>

* pipx: Update to correct target release of community.general

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 33093e9f3e)

Co-authored-by: Paul Aurich <paul@darkrain42.org>
2023-03-30 07:52:45 +02:00
Felix Fontein
81a95a347d Next expected release will be 6.6.0. 2023-03-27 22:11:59 +02:00
162 changed files with 5917 additions and 763 deletions

View File

@@ -29,14 +29,14 @@ schedules:
always: true
branches:
include:
- stable-7
- stable-6
- stable-5
- cron: 0 11 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-4
- stable-5
variables:
- name: checkoutPath
@@ -73,6 +73,19 @@ stages:
- test: 3
- test: 4
- test: extra
- stage: Sanity_2_15
displayName: Sanity 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.15/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_14
displayName: Sanity 2.14
dependsOn: []
@@ -99,19 +112,6 @@ stages:
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_12
displayName: Sanity 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.12/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
### Units
- stage: Units_devel
displayName: Units devel
@@ -123,13 +123,23 @@ stages:
testFormat: devel/units/{0}/1
targets:
- test: 2.7
- test: 3.5
- test: 3.6
- test: 3.7
- test: 3.8
- test: 3.9
- test: '3.10'
- test: '3.11'
- stage: Units_2_15
displayName: Units 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.15/units/{0}/1
targets:
- test: 3.5
- test: "3.10"
- stage: Units_2_14
displayName: Units 2.14
dependsOn: []
@@ -139,7 +149,6 @@ stages:
nameFormat: Python {0}
testFormat: 2.14/units/{0}/1
targets:
- test: 2.7
- test: 3.9
- stage: Units_2_13
displayName: Units 2.13
@@ -152,17 +161,6 @@ stages:
targets:
- test: 2.7
- test: 3.8
- stage: Units_2_12
displayName: Units 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.12/units/{0}/1
targets:
- test: 2.6
- test: 3.8
## Remote
- stage: Remote_devel_extra_vms
@@ -177,8 +175,6 @@ stages:
test: alpine/3.17
# - name: Fedora 37
# test: fedora/37
# - name: Ubuntu 20.04
# test: ubuntu/20.04
- name: Ubuntu 22.04
test: ubuntu/22.04
groups:
@@ -193,18 +189,32 @@ stages:
targets:
- name: macOS 13.2
test: macos/13.2
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 9.1
test: rhel/9.1
- name: FreeBSD 13.1
test: freebsd/13.1
- name: FreeBSD 13.2
test: freebsd/13.2
- name: FreeBSD 12.4
test: freebsd/12.4
groups:
- 1
- 2
- 3
- stage: Remote_2_15
displayName: Remote 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.15/{0}
targets:
- name: RHEL 7.9
test: rhel/7.9
- name: FreeBSD 13.1
test: freebsd/13.1
groups:
- 1
- 2
- 3
- stage: Remote_2_14
displayName: Remote 2.14
dependsOn: []
@@ -233,22 +243,6 @@ stages:
test: macos/12.0
- name: RHEL 8.5
test: rhel/8.5
groups:
- 1
- 2
- 3
- stage: Remote_2_12
displayName: Remote 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/{0}
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 8.4
test: rhel/8.4
- name: FreeBSD 13.0
test: freebsd/13.0
groups:
@@ -265,8 +259,6 @@ stages:
parameters:
testFormat: devel/linux/{0}
targets:
- name: CentOS 7
test: centos7
- name: Fedora 37
test: fedora37
- name: openSUSE 15
@@ -281,6 +273,20 @@ stages:
- 1
- 2
- 3
- stage: Docker_2_15
displayName: Docker 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.15/linux/{0}
targets:
- name: CentOS 7
test: centos7
groups:
- 1
- 2
- 3
- stage: Docker_2_14
displayName: Docker 2.14
dependsOn: []
@@ -313,24 +319,6 @@ stages:
- 1
- 2
- 3
- stage: Docker_2_12
displayName: Docker 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/linux/{0}
targets:
- name: CentOS 6
test: centos6
- name: Fedora 34
test: fedora34
- name: Ubuntu 18.04
test: ubuntu1804
groups:
- 1
- 2
- 3
### Community Docker
- stage: Docker_community_devel
@@ -344,7 +332,7 @@ stages:
- name: Debian Bullseye
test: debian-bullseye/3.9
- name: ArchLinux
test: archlinux/3.10
test: archlinux/3.11
- name: CentOS Stream 8
test: centos-stream8/3.9
groups:
@@ -364,6 +352,16 @@ stages:
targets:
- test: 2.7
- test: '3.11'
- stage: Generic_2_15
displayName: Generic 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.15/generic/{0}/1
targets:
- test: 3.9
- stage: Generic_2_14
displayName: Generic 2.14
dependsOn: []
@@ -384,42 +382,32 @@ stages:
testFormat: 2.13/generic/{0}/1
targets:
- test: 3.9
- stage: Generic_2_12
displayName: Generic 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.12/generic/{0}/1
targets:
- test: 3.8
- stage: Summary
condition: succeededOrFailed()
dependsOn:
- Sanity_devel
- Sanity_2_12
- Sanity_2_13
- Sanity_2_14
- Sanity_2_15
- Units_devel
- Units_2_12
- Units_2_13
- Units_2_14
- Units_2_15
- Remote_devel_extra_vms
- Remote_devel
- Remote_2_12
- Remote_2_13
- Remote_2_14
- Remote_2_15
- Docker_devel
- Docker_2_12
- Docker_2_13
- Docker_2_14
- Docker_2_15
- Docker_community_devel
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - Generic_devel
# - Generic_2_12
# - Generic_2_13
# - Generic_2_14
# - Generic_2_15
jobs:
- template: templates/coverage.yml

24
.github/BOTMETA.yml vendored
View File

@@ -267,6 +267,8 @@ files:
maintainers: delineaKrehl tylerezimmerman
$module_utils/:
labels: module_utils
$module_utils/btrfs.py:
maintainers: gnfzdz
$module_utils/deps.py:
maintainers: russoz
$module_utils/gconftool2.py:
@@ -395,6 +397,8 @@ files:
maintainers: catcombo
$modules/bower.py:
maintainers: mwarkentin
$modules/btrfs_:
maintainers: gnfzdz
$modules/bundler.py:
maintainers: thoiberg
$modules/bzr.py:
@@ -589,7 +593,7 @@ files:
ignore: jose-delarosa
maintainers: $team_redfish
$modules/ilo_:
ignore: jose-delarosa
ignore: jose-delarosa varini-hp
maintainers: $team_redfish
$modules/imc_rest.py:
labels: cisco
@@ -676,10 +680,14 @@ files:
maintainers: $team_keycloak
$modules/keycloak_authentication.py:
maintainers: elfelip Gaetan2907
$modules/keycloak_authz_authorization_scope.py:
maintainers: mattock
$modules/keycloak_client_rolemapping.py:
maintainers: Gaetan2907
$modules/keycloak_clientscope.py:
maintainers: Gaetan2907
$modules/keycloak_clientscope_type.py:
maintainers: simonpahl
$modules/keycloak_clientsecret_info.py:
maintainers: fynncfchen johncant
$modules/keycloak_clientsecret_regenerate.py:
@@ -921,7 +929,7 @@ files:
$modules/pamd.py:
maintainers: kevensen
$modules/parted.py:
maintainers: ColOfAbRiX rosowiecki jake2184
maintainers: ColOfAbRiX jake2184
$modules/pear.py:
ignore: jle64
labels: pear
@@ -990,7 +998,7 @@ files:
maintainers: sysadmind
$modules/puppet.py:
labels: puppet
maintainers: nibalizer emonty
maintainers: emonty
$modules/pushbullet.py:
maintainers: willybarro
$modules/pushover.py:
@@ -1045,7 +1053,8 @@ files:
maintainers: $team_redfish TSKushal
$modules/redhat_subscription.py:
labels: redhat_subscription
maintainers: barnabycourt alikins kahowell
maintainers: $team_rhsm
ignore: barnabycourt alikins kahowell
$modules/redis.py:
maintainers: slok
$modules/redis_data.py:
@@ -1068,9 +1077,9 @@ files:
labels: rhn_register
maintainers: jlaska $team_rhn
$modules/rhsm_release.py:
maintainers: seandst
maintainers: seandst $team_rhsm
$modules/rhsm_repository.py:
maintainers: giovannisciortino
maintainers: giovannisciortino $team_rhsm
$modules/riak.py:
maintainers: drewkerrigan jsmartin
$modules/rocketchat.py:
@@ -1389,7 +1398,7 @@ macros:
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
team_ipa: Akasurde Nosmoht fxfitz justchris1
team_jboss: Wolfant jairojunior wbrefvem
team_keycloak: eikef ndclt
team_keycloak: eikef ndclt mattock
team_linode: InTheCloudDan decentral1se displague rmcintosh Charliekenney23 LBGarber
team_macos: Akasurde kyleabenson martinm82 danieljaouen indrajitr
team_manageiq: abellotti cben gtanzillo yaacov zgalor dkorn evertmulder
@@ -1399,6 +1408,7 @@ macros:
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt
team_rhn: FlossWare alikins barnabycourt vritant
team_rhsm: cnsnyder ptoscano
team_scaleway: remyleone abarbare
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
team_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor

View File

@@ -30,6 +30,7 @@ jobs:
matrix:
ansible:
- '2.11'
- '2.12'
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
@@ -43,7 +44,7 @@ jobs:
- name: Perform sanity testing
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: felixfontein/ansible
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
pull-request-change-detection: 'true'
@@ -75,6 +76,10 @@ jobs:
python: '2.7'
- ansible: '2.11'
python: '3.5'
- ansible: '2.12'
python: '2.6'
- ansible: '2.12'
python: '3.8'
steps:
- name: >-
@@ -82,7 +87,7 @@ jobs:
Ansible version ${{ matrix.ansible }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: felixfontein/ansible
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
pre-test-cmd: >-
@@ -163,7 +168,49 @@ jobs:
# - ansible: '2.11'
# docker: default
# python: '3.5'
# target: azp/generic/2/
# target: azp/generic/1/
# 2.12
- ansible: '2.12'
docker: centos6
python: ''
target: azp/posix/1/
- ansible: '2.12'
docker: centos6
python: ''
target: azp/posix/2/
- ansible: '2.12'
docker: centos6
python: ''
target: azp/posix/3/
- ansible: '2.12'
docker: fedora34
python: ''
target: azp/posix/1/
- ansible: '2.12'
docker: fedora34
python: ''
target: azp/posix/2/
- ansible: '2.12'
docker: fedora34
python: ''
target: azp/posix/3/
- ansible: '2.12'
docker: ubuntu1804
python: ''
target: azp/posix/1/
- ansible: '2.12'
docker: ubuntu1804
python: ''
target: azp/posix/2/
- ansible: '2.12'
docker: ubuntu1804
python: ''
target: azp/posix/3/
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - ansible: '2.12'
# docker: default
# python: '3.8'
# target: azp/generic/1/
steps:
- name: >-
@@ -172,7 +219,7 @@ jobs:
under Python ${{ matrix.python }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: felixfontein/ansible
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
ansible-core-version: stable-${{ matrix.ansible }}
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
docker-image: ${{ matrix.docker }}

View File

@@ -6,6 +6,109 @@ Community General Release Notes
This changelog describes changes after version 5.0.0.
v6.6.2
======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- csv module utils - detects and remove unicode BOM markers from incoming CSV content (https://github.com/ansible-collections/community.general/pull/6662).
- gitlab_group - the module passed parameters to the API call even when not set. The module is now filtering out ``None`` values to remediate this (https://github.com/ansible-collections/community.general/pull/6712).
- ini_file - fix a bug where the inactive options were not used when possible (https://github.com/ansible-collections/community.general/pull/6575).
- keycloak module utils - fix ``is_struct_included`` handling of lists of lists/dictionaries (https://github.com/ansible-collections/community.general/pull/6688).
- keycloak module utils - the function ``get_user_by_username`` now return the user representation or ``None`` as stated in the documentation (https://github.com/ansible-collections/community.general/pull/6758).
v6.6.1
======
Release Summary
---------------
Regular bugfix release.
Minor Changes
-------------
- dconf - if ``gi.repository.GLib`` is missing, try to respawn in a Python interpreter that has it (https://github.com/ansible-collections/community.general/pull/6491).
Bugfixes
--------
- deps module utils - do not fail when dependency cannot be found (https://github.com/ansible-collections/community.general/pull/6479).
- nmcli - fix bond option ``xmit_hash_policy`` (https://github.com/ansible-collections/community.general/pull/6527).
- passwordstore lookup plugin - make compatible with ansible-core 2.16 (https://github.com/ansible-collections/community.general/pull/6447).
- portage - fix ``changed_use`` and ``newuse`` not triggering rebuilds (https://github.com/ansible-collections/community.general/issues/6008, https://github.com/ansible-collections/community.general/pull/6548).
- portage - update the logic for generating the emerge command arguments to ensure that ``withbdeps: false`` results in a passing an ``n`` argument with the ``--with-bdeps`` emerge flag (https://github.com/ansible-collections/community.general/issues/6451, https://github.com/ansible-collections/community.general/pull/6456).
- proxmox_tasks_info - remove ``api_user`` + ``api_password`` constraint from ``required_together`` as it causes to require ``api_password`` even when API token param is used (https://github.com/ansible-collections/community.general/issues/6201).
- puppet - handling ``noop`` parameter was not working at all, now it is has been fixed (https://github.com/ansible-collections/community.general/issues/6452, https://github.com/ansible-collections/community.general/issues/6458).
- terraform - fix broken ``warn()`` call (https://github.com/ansible-collections/community.general/pull/6497).
- xfs_quota - in case of a project quota, the call to ``xfs_quota`` did not initialize/reset the project (https://github.com/ansible-collections/community.general/issues/5143).
- zypper - added handling of zypper exitcode 102. Changed state is set correctly now and rc 102 is still preserved to be evaluated by the playbook (https://github.com/ansible-collections/community.general/pull/6534).
v6.6.0
======
Release Summary
---------------
Bugfix and feature release.
Minor Changes
-------------
- cpanm - minor change, use feature from ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/6385).
- dconf - be forgiving about boolean values: convert them to GVariant booleans automatically (https://github.com/ansible-collections/community.general/pull/6206).
- dconf - minor refactoring improving parameters and dependencies validation (https://github.com/ansible-collections/community.general/pull/6336).
- deps module utils - add function ``failed()`` providing the ability to check the dependency check result without triggering an exception (https://github.com/ansible-collections/community.general/pull/6383).
- dig lookup plugin - Support multiple domains to be queried as indicated in docs (https://github.com/ansible-collections/community.general/pull/6334).
- gitlab_project - add new option ``topics`` for adding topics to GitLab projects (https://github.com/ansible-collections/community.general/pull/6278).
- homebrew_cask - allows passing ``--greedy`` option to ``upgrade_all`` (https://github.com/ansible-collections/community.general/pull/6267).
- idrac_redfish_command - add ``job_id`` to ``CreateBiosConfigJob`` response (https://github.com/ansible-collections/community.general/issues/5603).
- ipa_hostgroup - add ``append`` parameter for adding a new hosts to existing hostgroups without changing existing hostgroup members (https://github.com/ansible-collections/community.general/pull/6203).
- keycloak_authentication - add flow type option to sub flows to allow the creation of 'form-flow' sub flows like in Keycloak's built-in registration flow (https://github.com/ansible-collections/community.general/pull/6318).
- mksysb - improved the output of the module in case of errors (https://github.com/ansible-collections/community.general/issues/6263).
- nmap inventory plugin - added environment variables for configure ``address`` and ``exclude`` (https://github.com/ansible-collections/community.general/issues/6351).
- nmcli - add ``macvlan`` connection type (https://github.com/ansible-collections/community.general/pull/6312).
- pipx - add ``system_site_packages`` parameter to give application access to system-wide packages (https://github.com/ansible-collections/community.general/pull/6308).
- pipx - ensure ``include_injected`` parameter works with ``state=upgrade`` and ``state=latest`` (https://github.com/ansible-collections/community.general/pull/6212).
- puppet - add new options ``skip_tags`` to exclude certain tagged resources during a puppet agent or apply (https://github.com/ansible-collections/community.general/pull/6293).
- terraform - remove state file check condition and error block, because in the native implementation of terraform will not cause errors due to the non-existent file (https://github.com/ansible-collections/community.general/pull/6296).
- udm_dns_record - minor refactor to the code (https://github.com/ansible-collections/community.general/pull/6382).
Bugfixes
--------
- archive - reduce RAM usage by generating CRC32 checksum over chunks (https://github.com/ansible-collections/community.general/pull/6274).
- flatpak - fixes idempotency detection issues. In some cases the module could fail to properly detect already existing Flatpaks because of a parameter witch only checks the installed apps (https://github.com/ansible-collections/community.general/pull/6289).
- icinga2_host - fix the data structure sent to Icinga to make use of host templates and template vars (https://github.com/ansible-collections/community.general/pull/6286).
- idrac_redfish_command - allow user to specify ``resource_id`` for ``CreateBiosConfigJob`` to specify an exact manager (https://github.com/ansible-collections/community.general/issues/2090).
- ini_file - make ``section`` parameter not required so it is possible to pass ``null`` as a value. This only was possible in the past due to a bug in ansible-core that now has been fixed (https://github.com/ansible-collections/community.general/pull/6404).
- keycloak - improve error messages (https://github.com/ansible-collections/community.general/pull/6318).
- one_vm - fix syntax error when creating VMs with a more complex template (https://github.com/ansible-collections/community.general/issues/6225).
- pipx - fixed handling of ``install_deps=true`` with ``state=latest`` and ``state=upgrade`` (https://github.com/ansible-collections/community.general/pull/6303).
- redhat_subscription - do not use D-Bus for registering when ``environment`` is specified, so it possible to specify again the environment names for registering, as the D-Bus APIs work only with IDs (https://github.com/ansible-collections/community.general/pull/6319).
- redhat_subscription - try to unregister only when already registered when ``force_register`` is specified (https://github.com/ansible-collections/community.general/issues/6258, https://github.com/ansible-collections/community.general/pull/6259).
- redhat_subscription - use the right D-Bus options for environments when registering a CentOS Stream 8 system and using ``environment`` (https://github.com/ansible-collections/community.general/pull/6275).
- rhsm_release - make ``release`` parameter not required so it is possible to pass ``null`` as a value. This only was possible in the past due to a bug in ansible-core that now has been fixed (https://github.com/ansible-collections/community.general/pull/6401).
- rundeck module utils - fix errors caused by the API empty responses (https://github.com/ansible-collections/community.general/pull/6300)
- rundeck_acl_policy - fix ``TypeError - byte indices must be integers or slices, not str`` error caused by empty API response. Update the module to use ``module_utils.rundeck`` functions (https://github.com/ansible-collections/community.general/pull/5887, https://github.com/ansible-collections/community.general/pull/6300).
- rundeck_project - update the module to use ``module_utils.rundeck`` functions (https://github.com/ansible-collections/community.general/issues/5742) (https://github.com/ansible-collections/community.general/pull/6300)
- snap_alias - module would only recognize snap names containing letter, numbers or the underscore character, failing to identify valid snap names such as ``lxd.lxc`` (https://github.com/ansible-collections/community.general/pull/6361).
New Modules
-----------
- btrfs_info - Query btrfs filesystem info
- btrfs_subvolume - Manage btrfs subvolumes
- ilo_redfish_command - Manages Out-Of-Band controllers using Redfish APIs
- keycloak_authz_authorization_scope - Allows administration of Keycloak client authorization scopes via Keycloak API
- keycloak_clientscope_type - Set the type of aclientscope in realm or client via Keycloak API
v6.5.0
======

View File

@@ -1237,3 +1237,190 @@ releases:
name: merge_variables
namespace: null
release_date: '2023-03-27'
6.6.0:
changes:
bugfixes:
- archive - reduce RAM usage by generating CRC32 checksum over chunks (https://github.com/ansible-collections/community.general/pull/6274).
- flatpak - fixes idempotency detection issues. In some cases the module could
fail to properly detect already existing Flatpaks because of a parameter witch
only checks the installed apps (https://github.com/ansible-collections/community.general/pull/6289).
- icinga2_host - fix the data structure sent to Icinga to make use of host templates
and template vars (https://github.com/ansible-collections/community.general/pull/6286).
- idrac_redfish_command - allow user to specify ``resource_id`` for ``CreateBiosConfigJob``
to specify an exact manager (https://github.com/ansible-collections/community.general/issues/2090).
- ini_file - make ``section`` parameter not required so it is possible to pass
``null`` as a value. This only was possible in the past due to a bug in ansible-core
that now has been fixed (https://github.com/ansible-collections/community.general/pull/6404).
- keycloak - improve error messages (https://github.com/ansible-collections/community.general/pull/6318).
- one_vm - fix syntax error when creating VMs with a more complex template (https://github.com/ansible-collections/community.general/issues/6225).
- pipx - fixed handling of ``install_deps=true`` with ``state=latest`` and ``state=upgrade``
(https://github.com/ansible-collections/community.general/pull/6303).
- redhat_subscription - do not use D-Bus for registering when ``environment``
is specified, so it possible to specify again the environment names for registering,
as the D-Bus APIs work only with IDs (https://github.com/ansible-collections/community.general/pull/6319).
- redhat_subscription - try to unregister only when already registered when
``force_register`` is specified (https://github.com/ansible-collections/community.general/issues/6258,
https://github.com/ansible-collections/community.general/pull/6259).
- redhat_subscription - use the right D-Bus options for environments when registering
a CentOS Stream 8 system and using ``environment`` (https://github.com/ansible-collections/community.general/pull/6275).
- rhsm_release - make ``release`` parameter not required so it is possible to
pass ``null`` as a value. This only was possible in the past due to a bug
in ansible-core that now has been fixed (https://github.com/ansible-collections/community.general/pull/6401).
- rundeck module utils - fix errors caused by the API empty responses (https://github.com/ansible-collections/community.general/pull/6300)
- rundeck_acl_policy - fix ``TypeError - byte indices must be integers or slices,
not str`` error caused by empty API response. Update the module to use ``module_utils.rundeck``
functions (https://github.com/ansible-collections/community.general/pull/5887,
https://github.com/ansible-collections/community.general/pull/6300).
- rundeck_project - update the module to use ``module_utils.rundeck`` functions
(https://github.com/ansible-collections/community.general/issues/5742) (https://github.com/ansible-collections/community.general/pull/6300)
- snap_alias - module would only recognize snap names containing letter, numbers
or the underscore character, failing to identify valid snap names such as
``lxd.lxc`` (https://github.com/ansible-collections/community.general/pull/6361).
minor_changes:
- cpanm - minor change, use feature from ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/6385).
- 'dconf - be forgiving about boolean values: convert them to GVariant booleans
automatically (https://github.com/ansible-collections/community.general/pull/6206).'
- dconf - minor refactoring improving parameters and dependencies validation
(https://github.com/ansible-collections/community.general/pull/6336).
- deps module utils - add function ``failed()`` providing the ability to check
the dependency check result without triggering an exception (https://github.com/ansible-collections/community.general/pull/6383).
- dig lookup plugin - Support multiple domains to be queried as indicated in
docs (https://github.com/ansible-collections/community.general/pull/6334).
- gitlab_project - add new option ``topics`` for adding topics to GitLab projects
(https://github.com/ansible-collections/community.general/pull/6278).
- homebrew_cask - allows passing ``--greedy`` option to ``upgrade_all`` (https://github.com/ansible-collections/community.general/pull/6267).
- idrac_redfish_command - add ``job_id`` to ``CreateBiosConfigJob`` response
(https://github.com/ansible-collections/community.general/issues/5603).
- ipa_hostgroup - add ``append`` parameter for adding a new hosts to existing
hostgroups without changing existing hostgroup members (https://github.com/ansible-collections/community.general/pull/6203).
- keycloak_authentication - add flow type option to sub flows to allow the creation
of 'form-flow' sub flows like in Keycloak's built-in registration flow (https://github.com/ansible-collections/community.general/pull/6318).
- mksysb - improved the output of the module in case of errors (https://github.com/ansible-collections/community.general/issues/6263).
- nmap inventory plugin - added environment variables for configure ``address``
and ``exclude`` (https://github.com/ansible-collections/community.general/issues/6351).
- nmcli - add ``macvlan`` connection type (https://github.com/ansible-collections/community.general/pull/6312).
- pipx - add ``system_site_packages`` parameter to give application access to
system-wide packages (https://github.com/ansible-collections/community.general/pull/6308).
- pipx - ensure ``include_injected`` parameter works with ``state=upgrade``
and ``state=latest`` (https://github.com/ansible-collections/community.general/pull/6212).
- puppet - add new options ``skip_tags`` to exclude certain tagged resources
during a puppet agent or apply (https://github.com/ansible-collections/community.general/pull/6293).
- terraform - remove state file check condition and error block, because in
the native implementation of terraform will not cause errors due to the non-existent
file (https://github.com/ansible-collections/community.general/pull/6296).
- udm_dns_record - minor refactor to the code (https://github.com/ansible-collections/community.general/pull/6382).
release_summary: Bugfix and feature release.
fragments:
- 2090-idrac-redfish-resource-id-fix.yml
- 5603-redfish-idrac-job-id-in-response.yml
- 6.6.0.yml
- 6199-archive-generate-checksum-in-chunks.yml
- 6203-add-append-option-to-ipa-hostgroup.yml
- 6206-dconf-booleans.yml
- 6212-pipx-include-injected.yml
- 6259-redhat_subscription-fix-force.yaml
- 6267-homebrew-cask-upgrade-all-greedy.yml
- 6269-mksysb-output.yml
- 6275-redhat_subscription-fix-environments-centos.yaml
- 6277-add-topics-gitlab-project.yml
- 6286-icinga2_host-template-and-template-vars.yml
- 6289-bugfix-flatpak-check-if-already-installed.yml
- 6293-add-puppet-skip-tags-option.yaml
- 6294-fix-one_vm-instantiation.yml
- 6296-LanceNero-Terraform_statefile_check.yml
- 6300-rundeck-modules-fixes-and-improvements.yml
- 6303-pipx-fix-state-latest-and-add-system-site-packages.yml
- 6308-pipx-add-system-site-packages.yml
- 6312-nmcli-add-macvlan-connection-type.yml
- 6318-add-form-flow.yml
- 6319-redhat_subscription-fix-environment-parameter.yaml
- 6334-dig-support-multiple-domains.yml
- 6336-dconf-refactor.yml
- 6351-support-env-variables-to-nmap-dynamic-inventoiry.yaml
- 6361-snap-alias-regex-bugfix.yml
- 6382-udm-dns-record-refactor.yml
- 6383-deps-failed.yml
- 6385-cpan-mh-feat.yml
- 6401-rhsm_release-required.yml
- 6404-ini_file-section.yml
modules:
- description: Query btrfs filesystem info
name: btrfs_info
namespace: ''
- description: Manage btrfs subvolumes
name: btrfs_subvolume
namespace: ''
- description: Manages Out-Of-Band controllers using Redfish APIs
name: ilo_redfish_command
namespace: ''
- description: Allows administration of Keycloak client authorization scopes via
Keycloak API
name: keycloak_authz_authorization_scope
namespace: ''
- description: Set the type of aclientscope in realm or client via Keycloak API
name: keycloak_clientscope_type
namespace: ''
release_date: '2023-04-24'
6.6.1:
changes:
bugfixes:
- deps module utils - do not fail when dependency cannot be found (https://github.com/ansible-collections/community.general/pull/6479).
- nmcli - fix bond option ``xmit_hash_policy`` (https://github.com/ansible-collections/community.general/pull/6527).
- passwordstore lookup plugin - make compatible with ansible-core 2.16 (https://github.com/ansible-collections/community.general/pull/6447).
- portage - fix ``changed_use`` and ``newuse`` not triggering rebuilds (https://github.com/ansible-collections/community.general/issues/6008,
https://github.com/ansible-collections/community.general/pull/6548).
- 'portage - update the logic for generating the emerge command arguments to
ensure that ``withbdeps: false`` results in a passing an ``n`` argument with
the ``--with-bdeps`` emerge flag (https://github.com/ansible-collections/community.general/issues/6451,
https://github.com/ansible-collections/community.general/pull/6456).'
- proxmox_tasks_info - remove ``api_user`` + ``api_password`` constraint from
``required_together`` as it causes to require ``api_password`` even when API
token param is used (https://github.com/ansible-collections/community.general/issues/6201).
- puppet - handling ``noop`` parameter was not working at all, now it is has
been fixed (https://github.com/ansible-collections/community.general/issues/6452,
https://github.com/ansible-collections/community.general/issues/6458).
- terraform - fix broken ``warn()`` call (https://github.com/ansible-collections/community.general/pull/6497).
- xfs_quota - in case of a project quota, the call to ``xfs_quota`` did not
initialize/reset the project (https://github.com/ansible-collections/community.general/issues/5143).
- zypper - added handling of zypper exitcode 102. Changed state is set correctly
now and rc 102 is still preserved to be evaluated by the playbook (https://github.com/ansible-collections/community.general/pull/6534).
minor_changes:
- dconf - if ``gi.repository.GLib`` is missing, try to respawn in a Python interpreter
that has it (https://github.com/ansible-collections/community.general/pull/6491).
release_summary: Regular bugfix release.
fragments:
- 5143-fix-xfs-quota-project-init.yml
- 6.6.1.yml
- 6456-fix-portage-withbdeps-false.yml
- 6458-puppet-noop.yml
- 6491-dconf-respawn.yml
- 6497-terraform-fix.yml
- 6527-nmcli-bond-fix-xmit_hash_policy.yml
- 6534-zypper-exitcode-102-handled.yaml
- 6548-portage-changed_use-newuse.yml
- 6554-proxmox-tasks-info-fix-required-password.yaml
- deps.yml
- passwordstore-lock.yml
release_date: '2023-05-22'
6.6.2:
changes:
bugfixes:
- csv module utils - detects and remove unicode BOM markers from incoming CSV
content (https://github.com/ansible-collections/community.general/pull/6662).
- gitlab_group - the module passed parameters to the API call even when not
set. The module is now filtering out ``None`` values to remediate this (https://github.com/ansible-collections/community.general/pull/6712).
- ini_file - fix a bug where the inactive options were not used when possible
(https://github.com/ansible-collections/community.general/pull/6575).
- keycloak module utils - fix ``is_struct_included`` handling of lists of lists/dictionaries
(https://github.com/ansible-collections/community.general/pull/6688).
- keycloak module utils - the function ``get_user_by_username`` now return the
user representation or ``None`` as stated in the documentation (https://github.com/ansible-collections/community.general/pull/6758).
release_summary: Regular bugfix release.
fragments:
- 6.6.2.yml
- 6568-fix-get-user-by-username-in-keycloak-module-utils.yml
- 6662-csv-bom.yml
- 6688-is-struct-included-bug-in-keycloak-py.yml
- 6712-gitlab_group-filtered-for-none-values.yml
- ini_file-use-inactive-options-when-possible.yml
release_date: '2023-06-19'

View File

@@ -5,7 +5,7 @@
namespace: community
name: general
version: 6.5.0
version: 6.6.2
readme: README.md
authors:
- Ansible (https://github.com/ansible)

View File

@@ -30,10 +30,18 @@ DOCUMENTATION = '''
address:
description: Network IP or range of IPs to scan, you can use a simple range (10.2.2.15-25) or CIDR notation.
required: true
env:
- name: ANSIBLE_NMAP_ADDRESS
version_added: 6.6.0
exclude:
description: list of addresses to exclude
description:
- List of addresses to exclude.
- For example C(10.2.2.15-25) or C(10.2.2.15,10.2.2.16).
type: list
elements: string
env:
- name: ANSIBLE_NMAP_EXCLUDE
version_added: 6.6.0
port:
description:
- Only scan specific port or port range (C(-p)).

View File

@@ -12,6 +12,8 @@ DOCUMENTATION = """
requirements:
- bw (command line utility)
- be logged into bitwarden
- bitwarden vault unlocked
- C(BW_SESSION) environment variable set
short_description: Retrieve secrets from Bitwarden
version_added: 5.4.0
description:

View File

@@ -61,6 +61,7 @@ DOCUMENTATION = '''
description:
- Return empty result without empty strings, and return empty list instead of C(NXDOMAIN).
- The default for this option will likely change to C(true) in the future.
- This option will be forced to C(true) if multiple domains to be queried are specified.
default: false
type: bool
version_added: 6.0.0
@@ -95,6 +96,21 @@ EXAMPLES = """
msg: "MX record for gmail.com {{ item }}"
with_items: "{{ lookup('community.general.dig', 'gmail.com./MX', wantlist=true) }}"
- name: Lookup multiple names at once
ansible.builtin.debug:
msg: "A record found {{ item }}"
loop: "{{ query('community.general.dig', 'example.org.', 'example.com.', 'gmail.com.') }}"
- name: Lookup multiple names at once (from list variable)
ansible.builtin.debug:
msg: "A record found {{ item }}"
loop: "{{ query('community.general.dig', *hosts) }}"
vars:
hosts:
- example.org.
- example.com.
- gmail.com.
- ansible.builtin.debug:
msg: "Reverse DNS for 192.0.2.5 is {{ lookup('community.general.dig', '192.0.2.5/PTR') }}"
- ansible.builtin.debug:
@@ -308,7 +324,7 @@ class LookupModule(LookupBase):
edns_size = 4096
myres.use_edns(0, ednsflags=dns.flags.DO, payload=edns_size)
domain = None
domains = []
qtype = self.get_option('qtype')
flat = self.get_option('flat')
fail_on_error = self.get_option('fail_on_error')
@@ -365,63 +381,71 @@ class LookupModule(LookupBase):
if '/' in t:
try:
domain, qtype = t.split('/')
domains.append(domain)
except Exception:
domain = t
domains.append(t)
else:
domain = t
domains.append(t)
# print "--- domain = {0} qtype={1} rdclass={2}".format(domain, qtype, rdclass)
if qtype.upper() == 'PTR':
reversed_domains = []
for domain in domains:
try:
n = dns.reversename.from_address(domain)
reversed_domains.append(n.to_text())
except dns.exception.SyntaxError:
pass
except Exception as e:
raise AnsibleError("dns.reversename unhandled exception %s" % to_native(e))
domains = reversed_domains
if len(domains) > 1:
real_empty = True
ret = []
if qtype.upper() == 'PTR':
for domain in domains:
try:
n = dns.reversename.from_address(domain)
domain = n.to_text()
except dns.exception.SyntaxError:
pass
except Exception as e:
raise AnsibleError("dns.reversename unhandled exception %s" % to_native(e))
answers = myres.query(domain, qtype, rdclass=rdclass)
for rdata in answers:
s = rdata.to_text()
if qtype.upper() == 'TXT':
s = s[1:-1] # Strip outside quotes on TXT rdata
try:
answers = myres.query(domain, qtype, rdclass=rdclass)
for rdata in answers:
s = rdata.to_text()
if qtype.upper() == 'TXT':
s = s[1:-1] # Strip outside quotes on TXT rdata
if flat:
ret.append(s)
else:
try:
rd = make_rdata_dict(rdata)
rd['owner'] = answers.canonical_name.to_text()
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
rd['ttl'] = answers.rrset.ttl
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
if flat:
ret.append(s)
else:
try:
rd = make_rdata_dict(rdata)
rd['owner'] = answers.canonical_name.to_text()
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
rd['ttl'] = answers.rrset.ttl
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
ret.append(rd)
except Exception as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
ret.append(str(err))
ret.append(rd)
except Exception as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
ret.append(str(err))
except dns.resolver.NXDOMAIN as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append('NXDOMAIN')
except dns.resolver.NoAnswer as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append("")
except dns.resolver.Timeout as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append("")
except dns.exception.DNSException as err:
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
except dns.resolver.NXDOMAIN as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append('NXDOMAIN')
except dns.resolver.NoAnswer as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append("")
except dns.resolver.Timeout as err:
if fail_on_error:
raise AnsibleError("Lookup failed: %s" % str(err))
if not real_empty:
ret.append("")
except dns.exception.DNSException as err:
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
return ret

View File

@@ -209,7 +209,6 @@ import time
import yaml
from ansible.errors import AnsibleError, AnsibleAssertionError
from ansible.module_utils.common.file import FileLock
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.utils.display import Display
@@ -217,6 +216,8 @@ from ansible.utils.encrypt import random_password
from ansible.plugins.lookup import LookupBase
from ansible import constants as C
from ansible_collections.community.general.plugins.module_utils._filelock import FileLock
display = Display()

View File

@@ -0,0 +1,109 @@
# Copyright (c) 2018, Ansible Project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause
# NOTE:
# This has been vendored from ansible.module_utils.common.file. This code has been removed from there for ansible-core 2.16.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import stat
import time
import fcntl
import sys
from contextlib import contextmanager
class LockTimeout(Exception):
pass
class FileLock:
'''
Currently FileLock is implemented via fcntl.flock on a lock file, however this
behaviour may change in the future. Avoid mixing lock types fcntl.flock,
fcntl.lockf and module_utils.common.file.FileLock as it will certainly cause
unwanted and/or unexpected behaviour
'''
def __init__(self):
self.lockfd = None
@contextmanager
def lock_file(self, path, tmpdir, lock_timeout=None):
'''
Context for lock acquisition
'''
try:
self.set_lock(path, tmpdir, lock_timeout)
yield
finally:
self.unlock()
def set_lock(self, path, tmpdir, lock_timeout=None):
'''
Create a lock file based on path with flock to prevent other processes
using given path.
Please note that currently file locking only works when it's executed by
the same user, I.E single user scenarios
:kw path: Path (file) to lock
:kw tmpdir: Path where to place the temporary .lock file
:kw lock_timeout:
Wait n seconds for lock acquisition, fail if timeout is reached.
0 = Do not wait, fail if lock cannot be acquired immediately,
Default is None, wait indefinitely until lock is released.
:returns: True
'''
lock_path = os.path.join(tmpdir, 'ansible-{0}.lock'.format(os.path.basename(path)))
l_wait = 0.1
r_exception = IOError
if sys.version_info[0] == 3:
r_exception = BlockingIOError
self.lockfd = open(lock_path, 'w')
if lock_timeout <= 0:
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
return True
if lock_timeout:
e_secs = 0
while e_secs < lock_timeout:
try:
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
return True
except r_exception:
time.sleep(l_wait)
e_secs += l_wait
continue
self.lockfd.close()
raise LockTimeout('{0} sec'.format(lock_timeout))
fcntl.flock(self.lockfd, fcntl.LOCK_EX)
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
return True
def unlock(self):
'''
Make sure lock file is available for everyone and Unlock the file descriptor
locked by set_lock
:returns: True
'''
if not self.lockfd:
return True
try:
fcntl.flock(self.lockfd, fcntl.LOCK_UN)
self.lockfd.close()
except ValueError: # file wasn't opened, let context manager fail gracefully
pass
return True

View File

@@ -0,0 +1,464 @@
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.module_utils.common.text.converters import to_bytes
import re
import os
def normalize_subvolume_path(path):
"""
Normalizes btrfs subvolume paths to ensure exactly one leading slash, no trailing slashes and no consecutive slashes.
In addition, if the path is prefixed with a leading <FS_TREE>, this value is removed.
"""
fstree_stripped = re.sub(r'^<FS_TREE>', '', path)
result = re.sub(r'/+$', '', re.sub(r'/+', '/', '/' + fstree_stripped))
return result if len(result) > 0 else '/'
class BtrfsModuleException(Exception):
pass
class BtrfsCommands(object):
"""
Provides access to a subset of the Btrfs command line
"""
def __init__(self, module):
self.__module = module
self.__btrfs = self.__module.get_bin_path("btrfs", required=True)
def filesystem_show(self):
command = "%s filesystem show -d" % (self.__btrfs)
result = self.__module.run_command(command, check_rc=True)
stdout = [x.strip() for x in result[1].splitlines()]
filesystems = []
current = None
for line in stdout:
if line.startswith('Label'):
current = self.__parse_filesystem(line)
filesystems.append(current)
elif line.startswith('devid'):
current['devices'].append(self.__parse_filesystem_device(line))
return filesystems
def __parse_filesystem(self, line):
label = re.sub(r'\s*uuid:.*$', '', re.sub(r'^Label:\s*', '', line))
id = re.sub(r'^.*uuid:\s*', '', line)
filesystem = {}
filesystem['label'] = label.strip("'") if label != 'none' else None
filesystem['uuid'] = id
filesystem['devices'] = []
filesystem['mountpoints'] = []
filesystem['subvolumes'] = []
filesystem['default_subvolid'] = None
return filesystem
def __parse_filesystem_device(self, line):
return re.sub(r'^.*path\s', '', line)
def subvolumes_list(self, filesystem_path):
command = "%s subvolume list -tap %s" % (self.__btrfs, filesystem_path)
result = self.__module.run_command(command, check_rc=True)
stdout = [x.split('\t') for x in result[1].splitlines()]
subvolumes = [{'id': 5, 'parent': None, 'path': '/'}]
if len(stdout) > 2:
subvolumes.extend([self.__parse_subvolume_list_record(x) for x in stdout[2:]])
return subvolumes
def __parse_subvolume_list_record(self, item):
return {
'id': int(item[0]),
'parent': int(item[2]),
'path': normalize_subvolume_path(item[5]),
}
def subvolume_get_default(self, filesystem_path):
command = [self.__btrfs, "subvolume", "get-default", to_bytes(filesystem_path)]
result = self.__module.run_command(command, check_rc=True)
# ID [n] ...
return int(result[1].strip().split()[1])
def subvolume_set_default(self, filesystem_path, subvolume_id):
command = [self.__btrfs, "subvolume", "set-default", str(subvolume_id), to_bytes(filesystem_path)]
result = self.__module.run_command(command, check_rc=True)
def subvolume_create(self, subvolume_path):
command = [self.__btrfs, "subvolume", "create", to_bytes(subvolume_path)]
result = self.__module.run_command(command, check_rc=True)
def subvolume_snapshot(self, snapshot_source, snapshot_destination):
command = [self.__btrfs, "subvolume", "snapshot", to_bytes(snapshot_source), to_bytes(snapshot_destination)]
result = self.__module.run_command(command, check_rc=True)
def subvolume_delete(self, subvolume_path):
command = [self.__btrfs, "subvolume", "delete", to_bytes(subvolume_path)]
result = self.__module.run_command(command, check_rc=True)
class BtrfsInfoProvider(object):
"""
Utility providing details of the currently available btrfs filesystems
"""
def __init__(self, module):
self.__module = module
self.__btrfs_api = BtrfsCommands(module)
self.__findmnt_path = self.__module.get_bin_path("findmnt", required=True)
def get_filesystems(self):
filesystems = self.__btrfs_api.filesystem_show()
mountpoints = self.__find_mountpoints()
for filesystem in filesystems:
device_mountpoints = self.__filter_mountpoints_for_devices(mountpoints, filesystem['devices'])
filesystem['mountpoints'] = device_mountpoints
if len(device_mountpoints) > 0:
# any path within the filesystem can be used to query metadata
mountpoint = device_mountpoints[0]['mountpoint']
filesystem['subvolumes'] = self.get_subvolumes(mountpoint)
filesystem['default_subvolid'] = self.get_default_subvolume_id(mountpoint)
return filesystems
def get_mountpoints(self, filesystem_devices):
mountpoints = self.__find_mountpoints()
return self.__filter_mountpoints_for_devices(mountpoints, filesystem_devices)
def get_subvolumes(self, filesystem_path):
return self.__btrfs_api.subvolumes_list(filesystem_path)
def get_default_subvolume_id(self, filesystem_path):
return self.__btrfs_api.subvolume_get_default(filesystem_path)
def __filter_mountpoints_for_devices(self, mountpoints, devices):
return [m for m in mountpoints if (m['device'] in devices)]
def __find_mountpoints(self):
command = "%s -t btrfs -nvP" % self.__findmnt_path
result = self.__module.run_command(command)
mountpoints = []
if result[0] == 0:
lines = result[1].splitlines()
for line in lines:
mountpoint = self.__parse_mountpoint_pairs(line)
mountpoints.append(mountpoint)
return mountpoints
def __parse_mountpoint_pairs(self, line):
pattern = re.compile(r'^TARGET="(?P<target>.*)"\s+SOURCE="(?P<source>.*)"\s+FSTYPE="(?P<fstype>.*)"\s+OPTIONS="(?P<options>.*)"\s*$')
match = pattern.search(line)
if match is not None:
groups = match.groupdict()
return {
'mountpoint': groups['target'],
'device': groups['source'],
'subvolid': self.__extract_mount_subvolid(groups['options']),
}
else:
raise BtrfsModuleException("Failed to parse findmnt result for line: '%s'" % line)
def __extract_mount_subvolid(self, mount_options):
for option in mount_options.split(','):
if option.startswith('subvolid='):
return int(option[len('subvolid='):])
raise BtrfsModuleException("Failed to find subvolid for mountpoint in options '%s'" % mount_options)
class BtrfsSubvolume(object):
"""
Wrapper class providing convenience methods for inspection of a btrfs subvolume
"""
def __init__(self, filesystem, subvolume_id):
self.__filesystem = filesystem
self.__subvolume_id = subvolume_id
def get_filesystem(self):
return self.__filesystem
def is_mounted(self):
mountpoints = self.get_mountpoints()
return mountpoints is not None and len(mountpoints) > 0
def is_filesystem_root(self):
return 5 == self.__subvolume_id
def is_filesystem_default(self):
return self.__filesystem.default_subvolid == self.__subvolume_id
def get_mounted_path(self):
mountpoints = self.get_mountpoints()
if mountpoints is not None and len(mountpoints) > 0:
return mountpoints[0]
elif self.parent is not None:
parent = self.__filesystem.get_subvolume_by_id(self.parent)
parent_path = parent.get_mounted_path()
if parent_path is not None:
return parent_path + os.path.sep + self.name
else:
return None
def get_mountpoints(self):
return self.__filesystem.get_mountpoints_by_subvolume_id(self.__subvolume_id)
def get_child_relative_path(self, absolute_child_path):
"""
Get the relative path from this subvolume to the named child subvolume.
The provided parameter is expected to be normalized as by normalize_subvolume_path.
"""
path = self.path
if absolute_child_path.startswith(path):
relative = absolute_child_path[len(path):]
return re.sub(r'^/*', '', relative)
else:
raise BtrfsModuleException("Path '%s' doesn't start with '%s'" % (absolute_child_path, path))
def get_parent_subvolume(self):
parent_id = self.parent
return self.__filesystem.get_subvolume_by_id(parent_id) if parent_id is not None else None
def get_child_subvolumes(self):
return self.__filesystem.get_subvolume_children(self.__subvolume_id)
@property
def __info(self):
return self.__filesystem.get_subvolume_info_for_id(self.__subvolume_id)
@property
def id(self):
return self.__subvolume_id
@property
def name(self):
return self.path.split('/').pop()
@property
def path(self):
return self.__info['path']
@property
def parent(self):
return self.__info['parent']
class BtrfsFilesystem(object):
"""
Wrapper class providing convenience methods for inspection of a btrfs filesystem
"""
def __init__(self, info, provider, module):
self.__provider = provider
# constant for module execution
self.__uuid = info['uuid']
self.__label = info['label']
self.__devices = info['devices']
# refreshable
self.__default_subvolid = info['default_subvolid'] if 'default_subvolid' in info else None
self.__update_mountpoints(info['mountpoints'] if 'mountpoints' in info else [])
self.__update_subvolumes(info['subvolumes'] if 'subvolumes' in info else [])
@property
def uuid(self):
return self.__uuid
@property
def label(self):
return self.__label
@property
def default_subvolid(self):
return self.__default_subvolid
@property
def devices(self):
return list(self.__devices)
def refresh(self):
self.refresh_mountpoints()
self.refresh_subvolumes()
self.refresh_default_subvolume()
def refresh_mountpoints(self):
mountpoints = self.__provider.get_mountpoints(list(self.__devices))
self.__update_mountpoints(mountpoints)
def __update_mountpoints(self, mountpoints):
self.__mountpoints = dict()
for i in mountpoints:
subvolid = i['subvolid']
mountpoint = i['mountpoint']
if subvolid not in self.__mountpoints:
self.__mountpoints[subvolid] = []
self.__mountpoints[subvolid].append(mountpoint)
def refresh_subvolumes(self):
filesystem_path = self.get_any_mountpoint()
if filesystem_path is not None:
subvolumes = self.__provider.get_subvolumes(filesystem_path)
self.__update_subvolumes(subvolumes)
def __update_subvolumes(self, subvolumes):
# TODO strategy for retaining information on deleted subvolumes?
self.__subvolumes = dict()
for subvolume in subvolumes:
self.__subvolumes[subvolume['id']] = subvolume
def refresh_default_subvolume(self):
filesystem_path = self.get_any_mountpoint()
if filesystem_path is not None:
self.__default_subvolid = self.__provider.get_default_subvolume_id(filesystem_path)
def contains_device(self, device):
return device in self.__devices
def contains_subvolume(self, subvolume):
return self.get_subvolume_by_name(subvolume) is not None
def get_subvolume_by_id(self, subvolume_id):
return BtrfsSubvolume(self, subvolume_id) if subvolume_id in self.__subvolumes else None
def get_subvolume_info_for_id(self, subvolume_id):
return self.__subvolumes[subvolume_id] if subvolume_id in self.__subvolumes else None
def get_subvolume_by_name(self, subvolume):
for subvolume_info in self.__subvolumes.values():
if subvolume_info['path'] == subvolume:
return BtrfsSubvolume(self, subvolume_info['id'])
return None
def get_any_mountpoint(self):
for subvol_mountpoints in self.__mountpoints.values():
if len(subvol_mountpoints) > 0:
return subvol_mountpoints[0]
# maybe error?
return None
def get_any_mounted_subvolume(self):
for subvolid, subvol_mountpoints in self.__mountpoints.items():
if len(subvol_mountpoints) > 0:
return self.get_subvolume_by_id(subvolid)
return None
def get_mountpoints_by_subvolume_id(self, subvolume_id):
return self.__mountpoints[subvolume_id] if subvolume_id in self.__mountpoints else []
def get_nearest_subvolume(self, subvolume):
"""Return the identified subvolume if existing, else the closest matching parent"""
subvolumes_by_path = self.__get_subvolumes_by_path()
while len(subvolume) > 1:
if subvolume in subvolumes_by_path:
return BtrfsSubvolume(self, subvolumes_by_path[subvolume]['id'])
else:
subvolume = re.sub(r'/[^/]+$', '', subvolume)
return BtrfsSubvolume(self, 5)
def get_mountpath_as_child(self, subvolume_name):
"""Find a path to the target subvolume through a mounted ancestor"""
nearest = self.get_nearest_subvolume(subvolume_name)
if nearest.path == subvolume_name:
nearest = nearest.get_parent_subvolume()
if nearest is None or nearest.get_mounted_path() is None:
raise BtrfsModuleException("Failed to find a path '%s' through a mounted parent subvolume" % subvolume_name)
else:
return nearest.get_mounted_path() + os.path.sep + nearest.get_child_relative_path(subvolume_name)
def get_subvolume_children(self, subvolume_id):
return [BtrfsSubvolume(self, x['id']) for x in self.__subvolumes.values() if x['parent'] == subvolume_id]
def __get_subvolumes_by_path(self):
result = {}
for s in self.__subvolumes.values():
path = s['path']
result[path] = s
return result
def is_mounted(self):
return self.__mountpoints is not None and len(self.__mountpoints) > 0
def get_summary(self):
subvolumes = []
sources = self.__subvolumes.values() if self.__subvolumes is not None else []
for subvolume in sources:
id = subvolume['id']
subvolumes.append({
'id': id,
'path': subvolume['path'],
'parent': subvolume['parent'],
'mountpoints': self.get_mountpoints_by_subvolume_id(id),
})
return {
'default_subvolume': self.__default_subvolid,
'devices': self.__devices,
'label': self.__label,
'uuid': self.__uuid,
'subvolumes': subvolumes,
}
class BtrfsFilesystemsProvider(object):
"""
Provides methods to query available btrfs filesystems
"""
def __init__(self, module):
self.__module = module
self.__provider = BtrfsInfoProvider(module)
self.__filesystems = None
def get_matching_filesystem(self, criteria):
if criteria['device'] is not None:
criteria['device'] = os.path.realpath(criteria['device'])
self.__check_init()
matching = [f for f in self.__filesystems.values() if self.__filesystem_matches_criteria(f, criteria)]
if len(matching) == 1:
return matching[0]
else:
raise BtrfsModuleException("Found %d filesystems matching criteria uuid=%s label=%s device=%s" % (
len(matching),
criteria['uuid'],
criteria['label'],
criteria['device']
))
def __filesystem_matches_criteria(self, filesystem, criteria):
return ((criteria['uuid'] is None or filesystem.uuid == criteria['uuid']) and
(criteria['label'] is None or filesystem.label == criteria['label']) and
(criteria['device'] is None or filesystem.contains_device(criteria['device'])))
def get_filesystem_for_device(self, device):
real_device = os.path.realpath(device)
self.__check_init()
for fs in self.__filesystems.values():
if fs.contains_device(real_device):
return fs
return None
def get_filesystems(self):
self.__check_init()
return list(self.__filesystems.values())
def __check_init(self):
if self.__filesystems is None:
self.__filesystems = dict()
for f in self.__provider.get_filesystems():
uuid = f['uuid']
self.__filesystems[uuid] = BtrfsFilesystem(f, self.__provider, self.__module)

View File

@@ -55,8 +55,10 @@ def initialize_dialect(dialect, **kwargs):
def read_csv(data, dialect, fieldnames=None):
BOM = to_native(u'\ufeff')
data = to_native(data, errors='surrogate_or_strict')
if data.startswith(BOM):
data = data[len(BOM):]
if PY3:
fake_fh = StringIO(data)

View File

@@ -50,7 +50,7 @@ class _Dependency(object):
def failed(self):
return self.state == 1
def verify(self, module):
def validate(self, module):
if self.failed:
module.fail_json(msg=self.message, exception=self.trace)
@@ -71,20 +71,28 @@ def declare(name, *args, **kwargs):
_deps[name] = dep
def validate(module, spec=None):
def _select_names(spec):
dep_names = sorted(_deps)
if spec is not None:
if spec:
if spec.startswith("-"):
spec_split = spec[1:].split(":")
for d in spec_split:
dep_names.remove(d)
else:
spec_split = spec[1:].split(":")
spec_split = spec.split(":")
dep_names = []
for d in spec_split:
_deps[d] # ensure it exists
dep_names.append(d)
for dep in dep_names:
_deps[dep].verify(module)
return dep_names
def validate(module, spec=None):
for dep in _select_names(spec):
_deps[dep].validate(module)
def failed(spec=None):
return any(_deps[d].failed for d in _select_names(spec))

View File

@@ -49,6 +49,16 @@ URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}"
URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models"
URL_CLIENTSCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}"
URL_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes"
URL_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}"
URL_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes"
URL_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}"
URL_CLIENT_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes"
URL_CLIENT_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}"
URL_CLIENT_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes"
URL_CLIENT_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}"
URL_CLIENT_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}"
URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/available"
URL_CLIENT_GROUP_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/composite"
@@ -80,6 +90,9 @@ URL_IDENTITY_PROVIDER_MAPPER = "{url}/admin/realms/{realm}/identity-provider/ins
URL_COMPONENTS = "{url}/admin/realms/{realm}/components"
URL_COMPONENT = "{url}/admin/realms/{realm}/components/{id}"
URL_AUTHZ_AUTHORIZATION_SCOPE = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope/{id}"
URL_AUTHZ_AUTHORIZATION_SCOPES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope"
def keycloak_argument_spec():
"""
@@ -194,24 +207,30 @@ def is_struct_included(struct1, struct2, exclude=None):
Return True if all element of dict 1 are present in dict 2, return false otherwise.
"""
if isinstance(struct1, list) and isinstance(struct2, list):
if not struct1 and not struct2:
return True
for item1 in struct1:
if isinstance(item1, (list, dict)):
for item2 in struct2:
if not is_struct_included(item1, item2, exclude):
return False
if is_struct_included(item1, item2, exclude):
break
else:
return False
else:
if item1 not in struct2:
return False
return True
elif isinstance(struct1, dict) and isinstance(struct2, dict):
if not struct1 and not struct2:
return True
try:
for key in struct1:
if not (exclude and key in exclude):
if not is_struct_included(struct1[key], struct2[key], exclude):
return False
return True
except KeyError:
return False
return True
elif isinstance(struct1, bool) and isinstance(struct2, bool):
return struct1 == struct2
else:
@@ -734,8 +753,15 @@ class KeycloakAPI(object):
users_url = URL_USERS.format(url=self.baseurl, realm=realm)
users_url += '?username=%s&exact=true' % username
try:
return json.loads(to_native(open_url(users_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
validate_certs=self.validate_certs).read()))
userrep = None
users = json.loads(to_native(open_url(users_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
validate_certs=self.validate_certs).read()))
for user in users:
if user['username'] == username:
userrep = user
break
return userrep
except ValueError as e:
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the user for realm %s and username %s: %s'
% (realm, username, str(e)))
@@ -1163,6 +1189,131 @@ class KeycloakAPI(object):
self.module.fail_json(msg='Could not update protocolmappers for clientscope %s in realm %s: %s'
% (mapper_rep, realm, str(e)))
def get_default_clientscopes(self, realm, client_id=None):
"""Fetch the name and ID of all clientscopes on the Keycloak server.
To fetch the full data of the client scope, make a subsequent call to
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
:return The default clientscopes of this realm or client
"""
url = URL_DEFAULT_CLIENTSCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENTSCOPES
return self._get_clientscopes_of_type(realm, url, 'default', client_id)
def get_optional_clientscopes(self, realm, client_id=None):
"""Fetch the name and ID of all clientscopes on the Keycloak server.
To fetch the full data of the client scope, make a subsequent call to
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
:return The optinal clientscopes of this realm or client
"""
url = URL_OPTIONAL_CLIENTSCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENTSCOPES
return self._get_clientscopes_of_type(realm, url, 'optional', client_id)
def _get_clientscopes_of_type(self, realm, url_template, scope_type, client_id=None):
"""Fetch the name and ID of all clientscopes on the Keycloak server.
To fetch the full data of the client scope, make a subsequent call to
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
:param realm: Realm in which the clientscope resides.
:param url_template the template for the right type
:param scope_type this can be either optinal or default
:param client_id: The client in which the clientscope resides.
:return The clientscopes of the specified type of this realm
"""
if client_id is None:
clientscopes_url = url_template.format(url=self.baseurl, realm=realm)
try:
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg="Could not fetch list of %s clientscopes in realm %s: %s" % (scope_type, realm, str(e)))
else:
cid = self.get_client_id(client_id=client_id, realm=realm)
clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid)
try:
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg="Could not fetch list of %s clientscopes in client %s: %s" % (scope_type, client_id, clientscopes_url))
def _decide_url_type_clientscope(self, client_id=None, scope_type="default"):
"""Decides which url to use.
:param scope_type this can be either optinal or default
:param client_id: The client in which the clientscope resides.
"""
if client_id is None:
if scope_type == "default":
return URL_DEFAULT_CLIENTSCOPE
if scope_type == "optional":
return URL_OPTIONAL_CLIENTSCOPE
else:
if scope_type == "default":
return URL_CLIENT_DEFAULT_CLIENTSCOPE
if scope_type == "optional":
return URL_CLIENT_OPTIONAL_CLIENTSCOPE
def add_default_clientscope(self, id, realm="master", client_id=None):
"""Add a client scope as default either on realm or client level.
:param id: Client scope Id.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
"""
self._action_type_clientscope(id, client_id, "default", realm, 'add')
def add_optional_clientscope(self, id, realm="master", client_id=None):
"""Add a client scope as optional either on realm or client level.
:param id: Client scope Id.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
"""
self._action_type_clientscope(id, client_id, "optional", realm, 'add')
def delete_default_clientscope(self, id, realm="master", client_id=None):
"""Remove a client scope as default either on realm or client level.
:param id: Client scope Id.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
"""
self._action_type_clientscope(id, client_id, "default", realm, 'delete')
def delete_optional_clientscope(self, id, realm="master", client_id=None):
"""Remove a client scope as optional either on realm or client level.
:param id: Client scope Id.
:param realm: Realm in which the clientscope resides.
:param client_id: The client in which the clientscope resides.
"""
self._action_type_clientscope(id, client_id, "optional", realm, 'delete')
def _action_type_clientscope(self, id=None, client_id=None, scope_type="default", realm="master", action='add'):
""" Delete or add a clientscope of type.
:param name: The name of the clientscope. A lookup will be performed to retrieve the clientscope ID.
:param client_id: The ID of the clientscope (preferred to name).
:param scope_type 'default' or 'optional'
:param realm: The realm in which this group resides, default "master".
"""
cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm)
# should have a good cid by here.
clientscope_type_url = self._decide_url_type_clientscope(client_id, scope_type).format(realm=realm, id=id, cid=cid, url=self.baseurl)
try:
method = 'PUT' if action == "add" else 'DELETE'
return open_url(clientscope_type_url, method=method, http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
validate_certs=self.validate_certs)
except Exception as e:
place = 'realm' if client_id is None else 'client ' + client_id
self.module.fail_json(msg="Unable to %s %s clientscope %s @ %s : %s" % (action, scope_type, id, place, str(e)))
def create_clientsecret(self, id, realm="master"):
""" Generate a new client secret by id
@@ -1795,6 +1946,9 @@ class KeycloakAPI(object):
data=json.dumps(updatedExec),
timeout=self.connection_timeout,
validate_certs=self.validate_certs)
except HTTPError as e:
self.module.fail_json(msg="Unable to update execution '%s': %s: %s %s" %
(flowAlias, repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(updatedExec)))
except Exception as e:
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
@@ -1819,7 +1973,7 @@ class KeycloakAPI(object):
except Exception as e:
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
def create_subflow(self, subflowName, flowAlias, realm='master'):
def create_subflow(self, subflowName, flowAlias, realm='master', flowType='basic-flow'):
""" Create new sublow on the flow
:param subflowName: name of the subflow to create
@@ -1830,7 +1984,7 @@ class KeycloakAPI(object):
newSubFlow = {}
newSubFlow["alias"] = subflowName
newSubFlow["provider"] = "registration-page-form"
newSubFlow["type"] = "basic-flow"
newSubFlow["type"] = flowType
open_url(
URL_AUTHENTICATION_FLOW_EXECUTIONS_FLOW.format(
url=self.baseurl,
@@ -1865,8 +2019,11 @@ class KeycloakAPI(object):
data=json.dumps(newExec),
timeout=self.connection_timeout,
validate_certs=self.validate_certs)
except HTTPError as e:
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s: %s %s" %
(flowAlias, execution["providerId"], repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(newExec)))
except Exception as e:
self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e)))
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s" % (flowAlias, execution["providerId"], repr(e)))
def change_execution_priority(self, executionId, diff, realm='master'):
""" Raise or lower execution priority of diff time
@@ -2190,3 +2347,44 @@ class KeycloakAPI(object):
except Exception as e:
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
% (cid, realm, str(e)))
def get_authz_authorization_scope_by_name(self, name, client_id, realm):
url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm)
search_url = "%s/search?name=%s" % (url, quote(name))
try:
return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
timeout=self.connection_timeout,
validate_certs=self.validate_certs).read()))
except Exception:
return False
def create_authz_authorization_scope(self, payload, client_id, realm):
"""Create an authorization scope for a Keycloak client"""
url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm)
try:
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
data=json.dumps(payload), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not create authorization scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
def update_authz_authorization_scope(self, payload, id, client_id, realm):
"""Update an authorization scope for a Keycloak client"""
url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm)
try:
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
data=json.dumps(payload), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not create update scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
def remove_authz_authorization_scope(self, id, client_id, realm):
"""Remove an authorization scope from a Keycloak client"""
url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm)
try:
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not delete scope %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))

View File

@@ -8,6 +8,7 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
import time
class iLORedfishUtils(RedfishUtils):
@@ -228,3 +229,79 @@ class iLORedfishUtils(RedfishUtils):
if not response['ret']:
return response
return {'ret': True, 'changed': True, 'msg': "Modified %s" % mgrattr['mgr_attr_name']}
def get_server_poststate(self):
# Get server details
response = self.get_request(self.root_uri + self.systems_uri)
if not response["ret"]:
return response
server_data = response["data"]
if "Hpe" in server_data["Oem"]:
return {
"ret": True,
"server_poststate": server_data["Oem"]["Hpe"]["PostState"]
}
else:
return {
"ret": True,
"server_poststate": server_data["Oem"]["Hp"]["PostState"]
}
def wait_for_ilo_reboot_completion(self, polling_interval=60, max_polling_time=1800):
# This method checks if OOB controller reboot is completed
time.sleep(10)
# Check server poststate
state = self.get_server_poststate()
if not state["ret"]:
return state
count = int(max_polling_time / polling_interval)
times = 0
# When server is powered OFF
pcount = 0
while state["server_poststate"] in ["PowerOff", "Off"] and pcount < 5:
time.sleep(10)
state = self.get_server_poststate()
if not state["ret"]:
return state
if state["server_poststate"] not in ["PowerOff", "Off"]:
break
pcount = pcount + 1
if state["server_poststate"] in ["PowerOff", "Off"]:
return {
"ret": False,
"changed": False,
"msg": "Server is powered OFF"
}
# When server is not rebooting
if state["server_poststate"] in ["InPostDiscoveryComplete", "FinishedPost"]:
return {
"ret": True,
"changed": False,
"msg": "Server is not rebooting"
}
while state["server_poststate"] not in ["InPostDiscoveryComplete", "FinishedPost"] and count > times:
state = self.get_server_poststate()
if not state["ret"]:
return state
if state["server_poststate"] in ["InPostDiscoveryComplete", "FinishedPost"]:
return {
"ret": True,
"changed": True,
"msg": "Server reboot is completed"
}
time.sleep(polling_interval)
times = times + 1
return {
"ret": False,
"changed": False,
"msg": "Server Reboot has failed, server state: {state} ".format(state=state)
}

View File

@@ -45,6 +45,8 @@ def render(to_render):
"""Converts dictionary to OpenNebula template."""
def recurse(to_render):
for key, value in sorted(to_render.items()):
if value is None:
continue
if isinstance(value, dict):
yield '{0:}=[{1:}]'.format(key, ','.join(recurse(value)))
continue
@@ -52,6 +54,9 @@ def render(to_render):
for item in value:
yield '{0:}=[{1:}]'.format(key, ','.join(recurse(item)))
continue
if isinstance(value, str):
yield '{0:}="{1:}"'.format(key, value.replace('\\', '\\\\').replace('"', '\\"'))
continue
yield '{0:}="{1:}"'.format(key, value)
return '\n'.join(recurse(to_render))

View File

@@ -39,6 +39,7 @@ def pipx_runner(module, command, **kwargs):
include_injected=fmt.as_bool("--include-injected"),
index_url=fmt.as_opt_val('--index-url'),
python=fmt.as_opt_val('--python'),
system_site_packages=fmt.as_bool("--system-site-packages"),
_list=fmt.as_fixed(['list', '--include-injected', '--json']),
editable=fmt.as_bool("--editable"),
pip_args=fmt.as_opt_val('--pip-args'),

View File

@@ -63,11 +63,7 @@ def puppet_runner(module):
return cmd
def noop_func(v):
_noop = cmd_runner_fmt.as_map({
True: "--noop",
False: "--no-noop",
})
return _noop(module.check_mode or v)
return ["--noop"] if module.check_mode or v else ["--no-noop"]
_logdest_map = {
"syslog": ["--logdest", "syslog"],
@@ -96,6 +92,7 @@ def puppet_runner(module):
confdir=cmd_runner_fmt.as_opt_val("--confdir"),
environment=cmd_runner_fmt.as_opt_val("--environment"),
tags=cmd_runner_fmt.as_func(lambda v: ["--tags", ",".join(v)]),
skip_tags=cmd_runner_fmt.as_func(lambda v: ["--skip_tags", ",".join(v)]),
certname=cmd_runner_fmt.as_opt_eq_val("--certname"),
noop=cmd_runner_fmt.as_func(noop_func),
use_srv_records=cmd_runner_fmt.as_map({

View File

@@ -81,12 +81,18 @@ def api_request(module, endpoint, data=None, method="GET"):
try:
content = response.read()
json_response = json.loads(content)
return json_response, info
if not content:
return None, info
else:
json_response = json.loads(content)
return json_response, info
except AttributeError as error:
module.fail_json(msg="Rundeck API request error",
exception=to_native(error),
execution_info=info)
module.fail_json(
msg="Rundeck API request error",
exception=to_native(error),
execution_info=info
)
except ValueError as error:
module.fail_json(
msg="No valid JSON response",

View File

@@ -608,7 +608,13 @@ class TarArchive(Archive):
# The python implementations of gzip, bz2, and lzma do not support restoring compressed files
# to their original names so only file checksum is returned
f = self._open_compressed_file(_to_native_ascii(path), 'r')
checksums = set([(b'', crc32(f.read()))])
checksum = 0
while True:
chunk = f.read(16 * 1024 * 1024)
if not chunk:
break
checksum = crc32(chunk, checksum)
checksums = set([(b'', checksum)])
f.close()
except Exception:
checksums = set()

View File

@@ -15,7 +15,7 @@ module: awall
short_description: Manage awall policies
author: Ted Trask (@tdtrask) <ttrask01@yahoo.com>
description:
- This modules allows for enable/disable/activate of I(awall) policies.
- This modules allows for enable/disable/activate of C(awall) policies.
- Alpine Wall (I(awall)) generates a firewall configuration from the enabled policy files
and activates the configuration on the system.
extends_documentation_fragment:
@@ -41,8 +41,11 @@ options:
description:
- Activate the new firewall rules.
- Can be run with other steps or on its own.
- Idempotency is affected if I(activate=true), as the module will always report a changed state.
type: bool
default: false
notes:
- At least one of I(name) and I(activate) is required.
'''
EXAMPLES = r'''

View File

@@ -0,0 +1,109 @@
#!/usr/bin/python
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: btrfs_info
short_description: Query btrfs filesystem info
version_added: "6.6.0"
description: Query status of available btrfs filesystems, including uuid, label, subvolumes and mountpoints.
author:
- Gregory Furlong (@gnfzdz)
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
'''
EXAMPLES = r'''
- name: Query information about mounted btrfs filesystems
community.general.btrfs_info:
register: my_btrfs_info
'''
RETURN = r'''
filesystems:
description: Summaries of the current state for all btrfs filesystems found on the target host.
type: list
elements: dict
returned: success
contains:
uuid:
description: A unique identifier assigned to the filesystem.
type: str
sample: 96c9c605-1454-49b8-a63a-15e2584c208e
label:
description: An optional label assigned to the filesystem.
type: str
sample: Tank
devices:
description: A list of devices assigned to the filesystem.
type: list
sample:
- /dev/sda1
- /dev/sdb1
default_subvolume:
description: The id of the filesystem's default subvolume.
type: int
sample: 5
subvolumes:
description: A list of dicts containing metadata for all of the filesystem's subvolumes.
type: list
elements: dict
contains:
id:
description: An identifier assigned to the subvolume, unique within the containing filesystem.
type: int
sample: 256
mountpoints:
description: Paths where the subvolume is mounted on the targeted host.
type: list
sample: ['/home']
parent:
description: The identifier of this subvolume's parent.
type: int
sample: 5
path:
description: The full path of the subvolume relative to the btrfs fileystem's root.
type: str
sample: /@home
'''
from ansible_collections.community.general.plugins.module_utils.btrfs import BtrfsFilesystemsProvider
from ansible.module_utils.basic import AnsibleModule
def run_module():
module_args = dict()
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
provider = BtrfsFilesystemsProvider(module)
filesystems = [x.get_summary() for x in provider.get_filesystems()]
result = {
"filesystems": filesystems,
}
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,682 @@
#!/usr/bin/python
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: btrfs_subvolume
short_description: Manage btrfs subvolumes
version_added: "6.6.0"
description: Creates, updates and deletes btrfs subvolumes and snapshots.
options:
automount:
description:
- Allow the module to temporarily mount the targeted btrfs filesystem in order to validate the current state and make any required changes.
type: bool
default: false
default:
description:
- Make the subvolume specified by I(name) the filesystem's default subvolume.
type: bool
default: false
filesystem_device:
description:
- A block device contained within the btrfs filesystem to be targeted.
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
type: path
filesystem_label:
description:
- A descriptive label assigned to the btrfs filesystem to be targeted.
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
type: str
filesystem_uuid:
description:
- A unique identifier assigned to the btrfs filesystem to be targeted.
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
type: str
name:
description:
- Name of the subvolume/snapshot to be targeted.
required: true
type: str
recursive:
description:
- When true, indicates that parent/child subvolumes should be created/removedas necessary
to complete the operation (for I(state=present) and I(state=absent) respectively).
type: bool
default: false
snapshot_source:
description:
- Identifies the source subvolume for the created snapshot.
- Infers that the created subvolume is a snapshot.
type: str
snapshot_conflict:
description:
- Policy defining behavior when a subvolume already exists at the path of the requested snapshot.
- C(skip) - Create a snapshot only if a subvolume does not yet exist at the target location, otherwise indicate that no change is required.
Warning, this option does not yet verify that the target subvolume was generated from a snapshot of the requested source.
- C(clobber) - If a subvolume already exists at the requested location, delete it first.
This option is not idempotent and will result in a new snapshot being generated on every execution.
- C(error) - If a subvolume already exists at the requested location, return an error.
This option is not idempotent and will result in an error on replay of the module.
type: str
choices: [ skip, clobber, error ]
default: skip
state:
description:
- Indicates the current state of the targeted subvolume.
type: str
choices: [ absent, present ]
default: present
notes:
- If any or all of the options I(filesystem_device), I(filesystem_label) or I(filesystem_uuid) parameters are provided, there is expected
to be a matching btrfs filesystem. If none are provided and only a single btrfs filesystem exists or only a single
btrfs filesystem is mounted, that filesystem will be used; otherwise, the module will take no action and return an error.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: partial
details:
- In some scenarios it may erroneously report intermediate subvolumes being created.
After mounting, if a directory like file is found where the subvolume would have been created, the operation is skipped.
diff_mode:
support: none
author:
- Gregory Furlong (@gnfzdz)
'''
EXAMPLES = r'''
- name: Create a @home subvolume under the root subvolume
community.general.btrfs_subvolume:
name: /@home
device: /dev/vda2
- name: Remove the @home subvolume if it exists
community.general.btrfs_subvolume:
name: /@home
state: absent
device: /dev/vda2
- name: Create a snapshot of the root subvolume named @
community.general.btrfs_subvolume:
name: /@
snapshot_source: /
device: /dev/vda2
- name: Create a snapshot of the root subvolume and make it the new default subvolume
community.general.btrfs_subvolume:
name: /@
snapshot_source: /
default: Yes
device: /dev/vda2
- name: Create a snapshot of the /@ subvolume and recursively creating intermediate subvolumes as required
community.general.btrfs_subvolume:
name: /@snapshots/@2022_06_09
snapshot_source: /@
recursive: True
device: /dev/vda2
- name: Remove the /@ subvolume and recursively delete child subvolumes as required
community.general.btrfs_subvolume:
name: /@snapshots/@2022_06_09
snapshot_source: /@
recursive: True
device: /dev/vda2
'''
RETURN = r'''
filesystem:
description:
- A summary of the final state of the targeted btrfs filesystem.
type: dict
returned: success
contains:
uuid:
description: A unique identifier assigned to the filesystem.
returned: success
type: str
sample: 96c9c605-1454-49b8-a63a-15e2584c208e
label:
description: An optional label assigned to the filesystem.
returned: success
type: str
sample: Tank
devices:
description: A list of devices assigned to the filesystem.
returned: success
type: list
sample:
- /dev/sda1
- /dev/sdb1
default_subvolume:
description: The ID of the filesystem's default subvolume.
returned: success and if filesystem is mounted
type: int
sample: 5
subvolumes:
description: A list of dicts containing metadata for all of the filesystem's subvolumes.
returned: success and if filesystem is mounted
type: list
elements: dict
contains:
id:
description: An identifier assigned to the subvolume, unique within the containing filesystem.
type: int
sample: 256
mountpoints:
description: Paths where the subvolume is mounted on the targeted host.
type: list
sample: ['/home']
parent:
description: The identifier of this subvolume's parent.
type: int
sample: 5
path:
description: The full path of the subvolume relative to the btrfs fileystem's root.
type: str
sample: /@home
modifications:
description:
- A list where each element describes a change made to the target btrfs filesystem.
type: list
returned: Success
elements: str
target_subvolume_id:
description:
- The ID of the subvolume specified with the I(name) parameter, either pre-existing or created as part of module execution.
type: int
sample: 257
returned: Success and subvolume exists after module execution
'''
from ansible_collections.community.general.plugins.module_utils.btrfs import BtrfsFilesystemsProvider, BtrfsCommands, BtrfsModuleException
from ansible_collections.community.general.plugins.module_utils.btrfs import normalize_subvolume_path
from ansible.module_utils.basic import AnsibleModule
import os
import tempfile
class BtrfsSubvolumeModule(object):
__BTRFS_ROOT_SUBVOLUME = '/'
__BTRFS_ROOT_SUBVOLUME_ID = 5
__BTRFS_SUBVOLUME_INODE_NUMBER = 256
__CREATE_SUBVOLUME_OPERATION = 'create'
__CREATE_SNAPSHOT_OPERATION = 'snapshot'
__DELETE_SUBVOLUME_OPERATION = 'delete'
__SET_DEFAULT_SUBVOLUME_OPERATION = 'set-default'
__UNKNOWN_SUBVOLUME_ID = '?'
def __init__(self, module):
self.module = module
self.__btrfs_api = BtrfsCommands(module)
self.__provider = BtrfsFilesystemsProvider(module)
# module parameters
name = self.module.params['name']
self.__name = normalize_subvolume_path(name) if name is not None else None
self.__state = self.module.params['state']
self.__automount = self.module.params['automount']
self.__default = self.module.params['default']
self.__filesystem_device = self.module.params['filesystem_device']
self.__filesystem_label = self.module.params['filesystem_label']
self.__filesystem_uuid = self.module.params['filesystem_uuid']
self.__recursive = self.module.params['recursive']
self.__snapshot_conflict = self.module.params['snapshot_conflict']
snapshot_source = self.module.params['snapshot_source']
self.__snapshot_source = normalize_subvolume_path(snapshot_source) if snapshot_source is not None else None
# execution state
self.__filesystem = None
self.__required_mounts = []
self.__unit_of_work = []
self.__completed_work = []
self.__temporary_mounts = dict()
def run(self):
error = None
try:
self.__load_filesystem()
self.__prepare_unit_of_work()
if not self.module.check_mode:
# check required mounts & mount
if len(self.__unit_of_work) > 0:
self.__execute_unit_of_work()
self.__filesystem.refresh()
else:
# check required mounts
self.__completed_work.extend(self.__unit_of_work)
except Exception as e:
error = e
finally:
self.__cleanup_mounts()
if self.__filesystem is not None:
self.__filesystem.refresh_mountpoints()
return (error, self.get_results())
# Identify the targeted filesystem and obtain the current state
def __load_filesystem(self):
if self.__has_filesystem_criteria():
filesystem = self.__find_matching_filesytem()
else:
filesystem = self.__find_default_filesystem()
# The filesystem must be mounted to obtain the current state (subvolumes, default, etc)
if not filesystem.is_mounted():
if not self.__automount:
raise BtrfsModuleException(
"Target filesystem uuid=%s is not currently mounted and automount=False."
"Mount explicitly before module execution or pass automount=True" % filesystem.uuid)
elif self.module.check_mode:
# TODO is failing the module an appropriate outcome in this scenario?
raise BtrfsModuleException(
"Target filesystem uuid=%s is not currently mounted. Unable to validate the current"
"state while running with check_mode=True" % filesystem.uuid)
else:
self.__mount_subvolume_id_to_tempdir(filesystem, self.__BTRFS_ROOT_SUBVOLUME_ID)
filesystem.refresh()
self.__filesystem = filesystem
def __has_filesystem_criteria(self):
return self.__filesystem_uuid is not None or self.__filesystem_label is not None or self.__filesystem_device is not None
def __find_matching_filesytem(self):
criteria = {
'uuid': self.__filesystem_uuid,
'label': self.__filesystem_label,
'device': self.__filesystem_device,
}
return self.__provider.get_matching_filesystem(criteria)
def __find_default_filesystem(self):
filesystems = self.__provider.get_filesystems()
filesystem = None
if len(filesystems) == 1:
filesystem = filesystems[0]
else:
mounted_filesystems = [x for x in filesystems if x.is_mounted()]
if len(mounted_filesystems) == 1:
filesystem = mounted_filesystems[0]
if filesystem is not None:
return filesystem
else:
raise BtrfsModuleException(
"Failed to automatically identify targeted filesystem. "
"No explicit device indicated and found %d available filesystems." % len(filesystems)
)
# Prepare unit of work
def __prepare_unit_of_work(self):
if self.__state == "present":
if self.__snapshot_source is None:
self.__prepare_subvolume_present()
else:
self.__prepare_snapshot_present()
if self.__default:
self.__prepare_set_default()
elif self.__state == "absent":
self.__prepare_subvolume_absent()
def __prepare_subvolume_present(self):
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
if subvolume is None:
self.__prepare_before_create_subvolume(self.__name)
self.__stage_create_subvolume(self.__name)
def __prepare_before_create_subvolume(self, subvolume_name):
closest_parent = self.__filesystem.get_nearest_subvolume(subvolume_name)
self.__stage_required_mount(closest_parent)
if self.__recursive:
self.__prepare_create_intermediates(closest_parent, subvolume_name)
def __prepare_create_intermediates(self, closest_subvolume, subvolume_name):
relative_path = closest_subvolume.get_child_relative_path(self.__name)
missing_subvolumes = [x for x in relative_path.split(os.path.sep) if len(x) > 0]
if len(missing_subvolumes) > 1:
current = closest_subvolume.path
for s in missing_subvolumes[:-1]:
separator = os.path.sep if current[-1] != os.path.sep else ""
current = current + separator + s
self.__stage_create_subvolume(current, True)
def __prepare_snapshot_present(self):
source_subvolume = self.__filesystem.get_subvolume_by_name(self.__snapshot_source)
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
subvolume_exists = subvolume is not None
if subvolume_exists:
if self.__snapshot_conflict == "skip":
# No change required
return
elif self.__snapshot_conflict == "error":
raise BtrfsModuleException("Target subvolume=%s already exists and snapshot_conflict='error'" % self.__name)
if source_subvolume is None:
raise BtrfsModuleException("Source subvolume %s does not exist" % self.__snapshot_source)
elif subvolume is not None and source_subvolume.id == subvolume.id:
raise BtrfsModuleException("Snapshot source and target are the same.")
else:
self.__stage_required_mount(source_subvolume)
if subvolume_exists and self.__snapshot_conflict == "clobber":
self.__prepare_delete_subvolume_tree(subvolume)
elif not subvolume_exists:
self.__prepare_before_create_subvolume(self.__name)
self.__stage_create_snapshot(source_subvolume, self.__name)
def __prepare_subvolume_absent(self):
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
if subvolume is not None:
self.__prepare_delete_subvolume_tree(subvolume)
def __prepare_delete_subvolume_tree(self, subvolume):
if subvolume.is_filesystem_root():
raise BtrfsModuleException("Can not delete the filesystem's root subvolume")
if not self.__recursive and len(subvolume.get_child_subvolumes()) > 0:
raise BtrfsModuleException("Subvolume targeted for deletion %s has children and recursive=False."
"Either explicitly delete the child subvolumes first or pass "
"parameter recursive=True." % subvolume.path)
self.__stage_required_mount(subvolume.get_parent_subvolume())
queue = self.__prepare_recursive_delete_order(subvolume) if self.__recursive else [subvolume]
# prepare unit of work
for s in queue:
if s.is_mounted():
# TODO potentially unmount the subvolume if automount=True ?
raise BtrfsModuleException("Can not delete mounted subvolume=%s" % s.path)
if s.is_filesystem_default():
self.__stage_set_default_subvolume(self.__BTRFS_ROOT_SUBVOLUME, self.__BTRFS_ROOT_SUBVOLUME_ID)
self.__stage_delete_subvolume(s)
def __prepare_recursive_delete_order(self, subvolume):
"""Return the subvolume and all descendents as a list, ordered so that descendents always occur before their ancestors"""
pending = [subvolume]
ordered = []
while len(pending) > 0:
next = pending.pop()
ordered.append(next)
pending.extend(next.get_child_subvolumes())
ordered.reverse() # reverse to ensure children are deleted before their parent
return ordered
def __prepare_set_default(self):
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
subvolume_id = subvolume.id if subvolume is not None else None
if self.__filesystem.default_subvolid != subvolume_id:
self.__stage_set_default_subvolume(self.__name, subvolume_id)
# Stage operations to the unit of work
def __stage_required_mount(self, subvolume):
if subvolume.get_mounted_path() is None:
if self.__automount:
self.__required_mounts.append(subvolume)
else:
raise BtrfsModuleException("The requested changes will require the subvolume '%s' to be mounted, but automount=False" % subvolume.path)
def __stage_create_subvolume(self, subvolume_path, intermediate=False):
"""
Add required creation of an intermediate subvolume to the unit of work
If intermediate is true, the action will be skipped if a directory like file is found at target
after mounting a parent subvolume
"""
self.__unit_of_work.append({
'action': self.__CREATE_SUBVOLUME_OPERATION,
'target': subvolume_path,
'intermediate': intermediate,
})
def __stage_create_snapshot(self, source_subvolume, target_subvolume_path):
"""Add creation of a snapshot from source to target to the unit of work"""
self.__unit_of_work.append({
'action': self.__CREATE_SNAPSHOT_OPERATION,
'source': source_subvolume.path,
'source_id': source_subvolume.id,
'target': target_subvolume_path,
})
def __stage_delete_subvolume(self, subvolume):
"""Add deletion of the target subvolume to the unit of work"""
self.__unit_of_work.append({
'action': self.__DELETE_SUBVOLUME_OPERATION,
'target': subvolume.path,
'target_id': subvolume.id,
})
def __stage_set_default_subvolume(self, subvolume_path, subvolume_id=None):
"""Add update of the filesystem's default subvolume to the unit of work"""
self.__unit_of_work.append({
'action': self.__SET_DEFAULT_SUBVOLUME_OPERATION,
'target': subvolume_path,
'target_id': subvolume_id,
})
# Execute the unit of work
def __execute_unit_of_work(self):
self.__check_required_mounts()
for op in self.__unit_of_work:
if op['action'] == self.__CREATE_SUBVOLUME_OPERATION:
self.__execute_create_subvolume(op)
elif op['action'] == self.__CREATE_SNAPSHOT_OPERATION:
self.__execute_create_snapshot(op)
elif op['action'] == self.__DELETE_SUBVOLUME_OPERATION:
self.__execute_delete_subvolume(op)
elif op['action'] == self.__SET_DEFAULT_SUBVOLUME_OPERATION:
self.__execute_set_default_subvolume(op)
else:
raise ValueError("Unknown operation type '%s'" % op['action'])
def __execute_create_subvolume(self, operation):
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
if not self.__is_existing_directory_like(target_mounted_path):
self.__btrfs_api.subvolume_create(target_mounted_path)
self.__completed_work.append(operation)
def __execute_create_snapshot(self, operation):
source_subvolume = self.__filesystem.get_subvolume_by_name(operation['source'])
source_mounted_path = source_subvolume.get_mounted_path()
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
self.__btrfs_api.subvolume_snapshot(source_mounted_path, target_mounted_path)
self.__completed_work.append(operation)
def __execute_delete_subvolume(self, operation):
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
self.__btrfs_api.subvolume_delete(target_mounted_path)
self.__completed_work.append(operation)
def __execute_set_default_subvolume(self, operation):
target = operation['target']
target_id = operation['target_id']
if target_id is None:
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
if target_subvolume is None:
self.__filesystem.refresh() # the target may have been created earlier in module execution
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
if target_subvolume is None:
raise BtrfsModuleException("Failed to find existing subvolume '%s'" % target)
else:
target_id = target_subvolume.id
self.__btrfs_api.subvolume_set_default(self.__filesystem.get_any_mountpoint(), target_id)
self.__completed_work.append(operation)
def __is_existing_directory_like(self, path):
return os.path.exists(path) and (
os.path.isdir(path) or
os.stat(path).st_ino == self.__BTRFS_SUBVOLUME_INODE_NUMBER
)
def __check_required_mounts(self):
filtered = self.__filter_child_subvolumes(self.__required_mounts)
if len(filtered) > 0:
for subvolume in filtered:
self.__mount_subvolume_id_to_tempdir(self.__filesystem, subvolume.id)
self.__filesystem.refresh_mountpoints()
def __filter_child_subvolumes(self, subvolumes):
"""Filter the provided list of subvolumes to remove any that are a child of another item in the list"""
filtered = []
last = None
ordered = sorted(subvolumes, key=lambda x: x.path)
for next in ordered:
if last is None or not next.path[0:len(last)] == last:
filtered.append(next)
last = next.path
return filtered
# Create/cleanup temporary mountpoints
def __mount_subvolume_id_to_tempdir(self, filesystem, subvolid):
# this check should be redundant
if self.module.check_mode or not self.__automount:
raise BtrfsModuleException("Unable to temporarily mount required subvolumes"
"with automount=%s and check_mode=%s" % (self.__automount, self.module.check_mode))
cache_key = "%s:%d" % (filesystem.uuid, subvolid)
# The subvolume was already mounted, so return the current path
if cache_key in self.__temporary_mounts:
return self.__temporary_mounts[cache_key]
device = filesystem.devices[0]
mountpoint = tempfile.mkdtemp(dir="/tmp")
self.__temporary_mounts[cache_key] = mountpoint
mount = self.module.get_bin_path("mount", required=True)
command = "%s -o noatime,subvolid=%d %s %s " % (mount,
subvolid,
device,
mountpoint)
result = self.module.run_command(command, check_rc=True)
return mountpoint
def __cleanup_mounts(self):
for key in self.__temporary_mounts.keys():
self.__cleanup_mount(self.__temporary_mounts[key])
def __cleanup_mount(self, mountpoint):
umount = self.module.get_bin_path("umount", required=True)
result = self.module.run_command("%s %s" % (umount, mountpoint))
if result[0] == 0:
rmdir = self.module.get_bin_path("rmdir", required=True)
self.module.run_command("%s %s" % (rmdir, mountpoint))
# Format and return results
def get_results(self):
target = self.__filesystem.get_subvolume_by_name(self.__name)
return dict(
changed=len(self.__completed_work) > 0,
filesystem=self.__filesystem.get_summary(),
modifications=self.__get_formatted_modifications(),
target_subvolume_id=(target.id if target is not None else None)
)
def __get_formatted_modifications(self):
return [self.__format_operation_result(op) for op in self.__completed_work]
def __format_operation_result(self, operation):
action_type = operation['action']
if action_type == self.__CREATE_SUBVOLUME_OPERATION:
return self.__format_create_subvolume_result(operation)
elif action_type == self.__CREATE_SNAPSHOT_OPERATION:
return self.__format_create_snapshot_result(operation)
elif action_type == self.__DELETE_SUBVOLUME_OPERATION:
return self.__format_delete_subvolume_result(operation)
elif action_type == self.__SET_DEFAULT_SUBVOLUME_OPERATION:
return self.__format_set_default_subvolume_result(operation)
else:
raise ValueError("Unknown operation type '%s'" % operation['action'])
def __format_create_subvolume_result(self, operation):
target = operation['target']
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
return "Created subvolume '%s' (%s)" % (target, target_id)
def __format_create_snapshot_result(self, operation):
source = operation['source']
source_id = operation['source_id']
target = operation['target']
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
return "Created snapshot '%s' (%s) from '%s' (%s)" % (target, target_id, source, source_id)
def __format_delete_subvolume_result(self, operation):
target = operation['target']
target_id = operation['target_id']
return "Deleted subvolume '%s' (%s)" % (target, target_id)
def __format_set_default_subvolume_result(self, operation):
target = operation['target']
if 'target_id' in operation:
target_id = operation['target_id']
else:
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
return "Updated default subvolume to '%s' (%s)" % (target, target_id)
def run_module():
module_args = dict(
automount=dict(type='bool', required=False, default=False),
default=dict(type='bool', required=False, default=False),
filesystem_device=dict(type='path', required=False),
filesystem_label=dict(type='str', required=False),
filesystem_uuid=dict(type='str', required=False),
name=dict(type='str', required=True),
recursive=dict(type='bool', default=False),
state=dict(type='str', required=False, default='present', choices=['present', 'absent']),
snapshot_source=dict(type='str', required=False),
snapshot_conflict=dict(type='str', required=False, default='skip', choices=['skip', 'clobber', 'error'])
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
subvolume = BtrfsSubvolumeModule(module)
error, result = subvolume.run()
if error is not None:
module.fail_json(str(error), **result)
else:
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -183,7 +183,7 @@ class CPANMinus(ModuleHelper):
if v.name and v.from_path:
self.do_raise("Parameters 'name' and 'from_path' are mutually exclusive when 'mode=new'")
self.command = self.module.get_bin_path(v.executable if v.executable else self.command)
self.command = self.get_bin_path(v.executable if v.executable else self.command)
self.vars.set("binary", self.command)
def _is_package_installed(self, name, locallib, version):

View File

@@ -16,7 +16,7 @@ short_description: Manages Datadog downtimes
version_added: 2.0.0
description:
- Manages downtimes within Datadog.
- Options as described on U(https://docs.datadoghq.com/api/v1/downtimes/s).
- Options as described on U(https://docs.datadoghq.com/api/v1/downtimes/).
author:
- Datadog (@Datadog)
requirements:

View File

@@ -49,8 +49,8 @@ notes:
I(value="'myvalue'") - with single quotes as part of the Ansible parameter
value.
- When using loops in combination with a value like
:code:`"[('xkb', 'us'), ('xkb', 'se')]"`, you need to be aware of possible
type conversions. Applying a filter :code:`"{{ item.value | string }}"`
"[('xkb', 'us'), ('xkb', 'se')]", you need to be aware of possible
type conversions. Applying a filter C({{ item.value | string }})
to the parameter variable can avoid potential conversion problems.
- The easiest way to figure out exact syntax/value you need to provide for a
key is by making the configuration change in application affected by the
@@ -70,13 +70,18 @@ options:
description:
- A dconf key to modify or read from the dconf database.
value:
type: str
type: raw
required: false
description:
- Value to set for the specified dconf key. Value should be specified in
GVariant format. Due to complexity of this format, it is best to have a
look at existing values in the dconf database.
- Required for I(state=present).
- Although the type is specified as "raw", it should typically be
specified as a string. However, boolean values in particular are
handled properly even when specified as booleans rather than strings
(in fact, handling booleans properly is why the type of this parameter
is "raw").
state:
type: str
required: false
@@ -138,7 +143,18 @@ EXAMPLES = r"""
import os
import traceback
import sys
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.respawn import (
has_respawned,
probe_interpreters_for_module,
respawn_module,
)
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.general.plugins.module_utils import deps
glib_module_name = 'gi.repository.GLib'
try:
from gi.repository.GLib import Variant, GError
@@ -146,15 +162,8 @@ except ImportError:
Variant = None
GError = AttributeError
PSUTIL_IMP_ERR = None
try:
with deps.declare("psutil"):
import psutil
HAS_PSUTIL = True
except ImportError:
PSUTIL_IMP_ERR = traceback.format_exc()
HAS_PSUTIL = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
class DBusWrapper(object):
@@ -288,6 +297,10 @@ class DconfPreference(object):
Returns True if the two values are equal.
"""
if canonical_value is None:
# It's unset in dconf database, so anything the user is trying to
# set is a change.
return False
try:
variant1 = Variant.parse(None, canonical_value)
variant2 = Variant.parse(variant1.get_type(), user_value)
@@ -349,7 +362,7 @@ class DconfPreference(object):
rc, out, err = dbus_wrapper.run_command(command)
if rc != 0:
self.module.fail_json(msg='dconf failed while write the value with error: %s' % err,
self.module.fail_json(msg='dconf failed while writing key %s, value %s with error: %s' % (key, value, err),
out=out,
err=err)
@@ -401,23 +414,62 @@ def main():
argument_spec=dict(
state=dict(default='present', choices=['present', 'absent', 'read']),
key=dict(required=True, type='str', no_log=False),
value=dict(required=False, default=None, type='str'),
# Converted to str below after special handling of bool.
value=dict(required=False, default=None, type='raw'),
),
supports_check_mode=True
supports_check_mode=True,
required_if=[
('state', 'present', ['value']),
],
)
if Variant is None:
# This interpreter can't see the GLib module. To try to fix that, we'll
# look in common locations for system-owned interpreters that can see
# it; if we find one, we'll respawn under it. Otherwise we'll proceed
# with degraded performance, without the ability to parse GVariants.
# Later (in a different PR) we'll actually deprecate this degraded
# performance level and fail with an error if the library can't be
# found.
if has_respawned():
# This shouldn't be possible; short-circuit early if it happens.
module.fail_json(
msg="%s must be installed and visible from %s." %
(glib_module_name, sys.executable))
interpreters = ['/usr/bin/python3', '/usr/bin/python2',
'/usr/bin/python']
interpreter = probe_interpreters_for_module(
interpreters, glib_module_name)
if interpreter:
# Found the Python bindings; respawn this module under the
# interpreter where we found them.
respawn_module(interpreter)
# This is the end of the line for this process, it will exit here
# once the respawned module has completed.
# Try to be forgiving about the user specifying a boolean as the value, or
# more accurately about the fact that YAML and Ansible are quite insistent
# about converting strings that look like booleans into booleans. Convert
# the boolean into a string of the type dconf will understand. Any type for
# the value other than boolean is just converted into a string directly.
if module.params['value'] is not None:
if isinstance(module.params['value'], bool):
module.params['value'] = 'true' if module.params['value'] else 'false'
else:
module.params['value'] = to_native(
module.params['value'], errors='surrogate_or_strict')
if Variant is None:
module.warn(
'WARNING: The gi.repository Python library is not available; '
'using string comparison to check value equality. This fallback '
'will be deprecated in a future version of community.general.')
if not HAS_PSUTIL:
module.fail_json(msg=missing_required_lib("psutil"), exception=PSUTIL_IMP_ERR)
# If present state was specified, value must be provided.
if module.params['state'] == 'present' and module.params['value'] is None:
module.fail_json(msg='State "present" requires "value" to be set.')
deps.validate(module)
# Create wrapper instance.
dconf = DconfPreference(module, module.check_mode)

View File

@@ -215,7 +215,7 @@ def uninstall_flat(module, binary, names, method):
def flatpak_exists(module, binary, names, method):
"""Check if the flatpaks are installed."""
command = [binary, "list", "--{0}".format(method), "--app"]
command = [binary, "list", "--{0}".format(method)]
output = _flatpak_command(module, False, command)
installed = []
not_installed = []

View File

@@ -255,7 +255,10 @@ class GitLabGroup(object):
return True
try:
group = self._gitlab.groups.create(arguments)
# Filter out None values
filtered = dict((arg_key, arg_value) for arg_key, arg_value in arguments.items() if arg_value is not None)
group = self._gitlab.groups.create(filtered)
except (gitlab.exceptions.GitlabCreateError) as e:
self._module.fail_json(msg="Failed to create group: %s " % to_native(e))

View File

@@ -251,6 +251,13 @@ options:
type: str
choices: ["private", "disabled", "enabled"]
version_added: "6.4.0"
topics:
description:
- A topic or list of topics to be assigned to a project.
- It is compatible with old GitLab server releases (versions before 14, correspond to C(tag_list)).
type: list
elements: str
version_added: "6.6.0"
'''
EXAMPLES = r'''
@@ -334,6 +341,8 @@ from ansible_collections.community.general.plugins.module_utils.gitlab import (
auth_argument_spec, find_group, find_project, gitlab_authentication, gitlab, ensure_gitlab_package
)
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
class GitLabProject(object):
def __init__(self, module, gitlab_instance):
@@ -376,6 +385,14 @@ class GitLabProject(object):
'monitor_access_level': options['monitor_access_level'],
'security_and_compliance_access_level': options['security_and_compliance_access_level'],
}
# topics was introduced on gitlab >=14 and replace tag_list. We get current gitlab version
# and check if less than 14. If yes we use tag_list instead topics
if LooseVersion(self._gitlab.version()[0]) < LooseVersion("14"):
project_options['tag_list'] = options['topics']
else:
project_options['topics'] = options['topics']
# Because we have already call userExists in main()
if self.project_object is None:
project_options.update({
@@ -514,6 +531,7 @@ def main():
infrastructure_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
monitor_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
security_and_compliance_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
topics=dict(type='list', elements='str'),
))
module = AnsibleModule(
@@ -570,6 +588,7 @@ def main():
infrastructure_access_level = module.params['infrastructure_access_level']
monitor_access_level = module.params['monitor_access_level']
security_and_compliance_access_level = module.params['security_and_compliance_access_level']
topics = module.params['topics']
if default_branch and not initialize_with_readme:
module.fail_json(msg="Param default_branch need param initialize_with_readme set to true")
@@ -648,6 +667,7 @@ def main():
"infrastructure_access_level": infrastructure_access_level,
"monitor_access_level": monitor_access_level,
"security_and_compliance_access_level": security_and_compliance_access_level,
"topics": topics,
}):
module.exit_json(changed=True, msg="Successfully created or updated the project %s" % project_name, project=gitlab_project.project_object._attrs)

View File

@@ -78,8 +78,9 @@ options:
greedy:
description:
- Upgrade casks that auto update.
- Passes --greedy to brew cask outdated when checking
if an installed cask has a newer version available.
- Passes C(--greedy) to C(brew outdated --cask) when checking
if an installed cask has a newer version available,
or to C(brew upgrade --cask) when upgrading all casks.
type: bool
default: false
'''
@@ -128,6 +129,11 @@ EXAMPLES = '''
community.general.homebrew_cask:
upgrade_all: true
- name: Upgrade all casks with greedy option
community.general.homebrew_cask:
upgrade_all: true
greedy: true
- name: Upgrade given cask with force option
community.general.homebrew_cask:
name: alfred
@@ -581,6 +587,9 @@ class HomebrewCask(object):
else:
cmd = [self.brew_path, 'cask', 'upgrade']
if self.greedy:
cmd = cmd + ['--greedy']
rc, out, err = '', '', ''
if self.sudo_password:

View File

@@ -256,9 +256,9 @@ def main():
state = module.params["state"]
name = module.params["name"]
zone = module.params["zone"]
template = [name]
template = []
if module.params["template"]:
template.append(module.params["template"])
template = [module.params["template"]]
check_command = module.params["check_command"]
ip = module.params["ip"]
display_name = module.params["display_name"]
@@ -273,20 +273,18 @@ def main():
module.fail_json(msg="unable to connect to Icinga. Exception message: %s" % (e))
data = {
'templates': template,
'attrs': {
'address': ip,
'display_name': display_name,
'check_command': check_command,
'zone': zone,
'vars': {
'made_by': "ansible",
},
'templates': template,
'vars.made_by': "ansible"
}
}
if variables:
data['attrs']['vars'].update(variables)
for key, value in variables.items():
data['attrs']['vars.' + key] = value
changed = False
if icinga.exists(name):

View File

@@ -85,6 +85,14 @@ msg:
returned: always
type: str
sample: "Action was successful"
return_values:
description: Dictionary containing command-specific response data from the action.
returned: on success
type: dict
version_added: 6.6.0
sample: {
"job_id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/JID_471269252011"
}
'''
import re
@@ -128,10 +136,9 @@ class IdracRedfishUtils(RedfishUtils):
return response
response_output = response['resp'].__dict__
job_id = response_output["headers"]["Location"]
job_id = re.search("JID_.+", job_id).group()
# Currently not passing job_id back to user but patch is coming
return {'ret': True, 'msg': "Config job %s created" % job_id}
job_id_full = response_output["headers"]["Location"]
job_id = re.search("JID_.+", job_id_full).group()
return {'ret': True, 'msg': "Config job %s created" % job_id, 'job_id': job_id_full}
CATEGORY_COMMANDS_ALL = {
@@ -143,6 +150,7 @@ CATEGORY_COMMANDS_ALL = {
def main():
result = {}
return_values = {}
module = AnsibleModule(
argument_spec=dict(
category=dict(required=True),
@@ -199,7 +207,20 @@ def main():
if category == "Systems":
# execute only if we find a System resource
# NOTE: Currently overriding the usage of 'data_modification' due to
# how 'resource_id' is processed. In the case of CreateBiosConfigJob,
# we interact with BOTH systems and managers, so you currently cannot
# specify a single 'resource_id' to make both '_find_systems_resource'
# and '_find_managers_resource' return success. Since
# CreateBiosConfigJob doesn't use the matched 'resource_id' for a
# system regardless of what's specified, disabling the 'resource_id'
# inspection for the next call allows a specific manager to be
# specified with 'resource_id'. If we ever need to expand the input
# to inspect a specific system and manager in parallel, this will need
# updates.
rf_utils.data_modification = False
result = rf_utils._find_systems_resource()
rf_utils.data_modification = True
if result['ret'] is False:
module.fail_json(msg=to_native(result['msg']))
@@ -210,11 +231,13 @@ def main():
if result['ret'] is False:
module.fail_json(msg=to_native(result['msg']))
result = rf_utils.create_bios_config_job()
if 'job_id' in result:
return_values['job_id'] = result['job_id']
# Return data back or fail with proper message
if result['ret'] is True:
del result['ret']
module.exit_json(changed=True, msg='Action was successful')
module.exit_json(changed=True, msg='Action was successful', return_values=return_values)
else:
module.fail_json(msg=to_native(result['msg']))

View File

@@ -0,0 +1,175 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ilo_redfish_command
short_description: Manages Out-Of-Band controllers using Redfish APIs
version_added: 6.6.0
description:
- Builds Redfish URIs locally and sends them to remote OOB controllers to
perform an action.
attributes:
check_mode:
support: none
diff_mode:
support: none
extends_documentation_fragment:
- community.general.attributes
options:
category:
required: true
description:
- Category to execute on OOB controller.
type: str
choices: ['Systems']
command:
required: true
description:
- List of commands to execute on OOB controller.
type: list
elements: str
baseuri:
required: true
description:
- Base URI of OOB controller.
type: str
username:
required: false
description:
- Username for authenticating to iLO.
type: str
password:
required: false
description:
- Password for authenticating to iLO.
type: str
auth_token:
required: false
description:
- Security token for authenticating to iLO.
type: str
timeout:
required: false
description:
- Timeout in seconds for HTTP requests to iLO.
default: 60
type: int
author:
- Varni H P (@varini-hp)
'''
EXAMPLES = '''
- name: Wait for iLO Reboot Completion
community.general.ilo_redfish_command:
category: Systems
command: WaitforiLORebootCompletion
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
'''
RETURN = '''
ilo_redfish_command:
description: Returns the status of the operation performed on the iLO.
type: dict
contains:
WaitforiLORebootCompletion:
description: Returns the output msg and whether the function executed successfully.
type: dict
contains:
ret:
description: Return True/False based on whether the operation was performed succesfully.
type: bool
msg:
description: Status of the operation performed on the iLO.
type: str
returned: always
'''
# More will be added as module features are expanded
CATEGORY_COMMANDS_ALL = {
"Systems": ["WaitforiLORebootCompletion"]
}
from ansible_collections.community.general.plugins.module_utils.ilo_redfish_utils import iLORedfishUtils
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native
def main():
result = {}
module = AnsibleModule(
argument_spec=dict(
category=dict(required=True, choices=list(CATEGORY_COMMANDS_ALL.keys())),
command=dict(required=True, type='list', elements='str'),
baseuri=dict(required=True),
timeout=dict(type="int", default=60),
username=dict(),
password=dict(no_log=True),
auth_token=dict(no_log=True)
),
required_together=[
('username', 'password'),
],
required_one_of=[
('username', 'auth_token'),
],
mutually_exclusive=[
('username', 'auth_token'),
],
supports_check_mode=False
)
category = module.params['category']
command_list = module.params['command']
# admin credentials used for authentication
creds = {'user': module.params['username'],
'pswd': module.params['password'],
'token': module.params['auth_token']}
timeout = module.params['timeout']
# Build root URI
root_uri = "https://" + module.params['baseuri']
rf_utils = iLORedfishUtils(creds, root_uri, timeout, module)
# Check that Category is valid
if category not in CATEGORY_COMMANDS_ALL:
module.fail_json(msg=to_native(
"Invalid Category '%s'. Valid Categories = %s" % (category, list(CATEGORY_COMMANDS_ALL.keys()))))
# Check that all commands are valid
for cmd in command_list:
# Fail if even one command given is invalid
if cmd not in CATEGORY_COMMANDS_ALL[category]:
module.fail_json(
msg=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, CATEGORY_COMMANDS_ALL[category])))
if category == "Systems":
# execute only if we find a System resource
result = rf_utils._find_systems_resource()
if result['ret'] is False:
module.fail_json(msg=to_native(result['msg']))
for command in command_list:
if command == "WaitforiLORebootCompletion":
result[command] = rf_utils.wait_for_ilo_reboot_completion()
# Return data back or fail with proper message
if not result[command]['ret']:
module.fail_json(msg=result)
changed = result[command].get('changed', False)
module.exit_json(ilo_redfish_command=result, changed=changed)
if __name__ == '__main__':
main()

View File

@@ -42,10 +42,9 @@ options:
description:
- Section name in INI file. This is added if I(state=present) automatically when
a single value is being set.
- If left empty or set to C(null), the I(option) will be placed before the first I(section).
- If left empty, being omitted, or being set to C(null), the I(option) will be placed before the first I(section).
- Using C(null) is also required if the config format does not support sections.
type: str
required: true
option:
description:
- If set (required for changing a I(value)), this is the name of the option.
@@ -317,14 +316,14 @@ def do_ini(module, filename, section=None, option=None, values=None,
# override option with no value to option with value if not allow_no_value
if len(values) > 0:
for index, line in enumerate(section_lines):
if not changed_lines[index] and match_active_opt(option, line):
if not changed_lines[index] and match_opt(option, line):
newline = assignment_format % (option, values.pop(0))
(changed, msg) = update_section_line(changed, section_lines, index, changed_lines, newline, msg)
if len(values) == 0:
break
# remove all remaining option occurrences from the rest of the section
for index in range(len(section_lines) - 1, 0, -1):
if not changed_lines[index] and match_active_opt(option, section_lines[index]):
if not changed_lines[index] and match_opt(option, section_lines[index]):
del section_lines[index]
del changed_lines[index]
changed = True
@@ -430,7 +429,7 @@ def main():
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', required=True, aliases=['dest']),
section=dict(type='str', required=True),
section=dict(type='str'),
option=dict(type='str'),
value=dict(type='str'),
values=dict(type='list', elements='str'),

View File

@@ -59,7 +59,7 @@ options:
record_values:
description:
- Manage DNS record name with this value.
- Mutually exclusive with I(record_values), and exactly one of I(record_value) and I(record_values) has to be specified.
- Mutually exclusive with I(record_value), and exactly one of I(record_value) and I(record_values) has to be specified.
- In the case of 'A' or 'AAAA' record types, this will be the IP address.
- In the case of 'A6' record type, this will be the A6 Record data.
- In the case of 'CNAME' record type, this will be the hostname.

View File

@@ -20,6 +20,13 @@ attributes:
diff_mode:
support: none
options:
append:
description:
- If C(true), add the listed I(host) to the I(hostgroup).
- If C(false), only the listed I(host) will be in I(hostgroup), removing any other hosts.
default: false
type: bool
version_added: 6.6.0
cn:
description:
- Name of host-group.
@@ -147,6 +154,7 @@ def ensure(module, client):
state = module.params['state']
host = module.params['host']
hostgroup = module.params['hostgroup']
append = module.params['append']
ipa_hostgroup = client.hostgroup_find(name=name)
module_hostgroup = get_hostgroup_dict(description=module.params['description'])
@@ -168,14 +176,18 @@ def ensure(module, client):
client.hostgroup_mod(name=name, item=data)
if host is not None:
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_host', []), [item.lower() for item in host],
client.hostgroup_add_host, client.hostgroup_remove_host) or changed
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_host', []),
[item.lower() for item in host],
client.hostgroup_add_host,
client.hostgroup_remove_host,
append=append) or changed
if hostgroup is not None:
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_hostgroup', []),
[item.lower() for item in hostgroup],
client.hostgroup_add_hostgroup,
client.hostgroup_remove_hostgroup) or changed
client.hostgroup_remove_hostgroup,
append=append) or changed
else:
if ipa_hostgroup:
@@ -192,7 +204,8 @@ def main():
description=dict(type='str'),
host=dict(type='list', elements='str'),
hostgroup=dict(type='list', elements='str'),
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']))
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']),
append=dict(type='bool', default=False))
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)

View File

@@ -79,6 +79,14 @@ options:
description:
- Priority order of the execution.
type: int
subFlowType:
description:
- For new subflows, optionally specify the type.
- Is only used at creation.
choices: ["basic-flow", "form-flow"]
default: "basic-flow"
type: str
version_added: 6.6.0
state:
description:
- Control if the authentication flow must exists or not.
@@ -264,7 +272,7 @@ def create_or_update_executions(kc, config, realm='master'):
exec_index = find_exec_in_executions(new_exec, existing_executions)
if exec_index != -1:
# Remove key that doesn't need to be compared with existing_exec
exclude_key = ["flowAlias"]
exclude_key = ["flowAlias", "subFlowType"]
for index_key, key in enumerate(new_exec, start=0):
if new_exec[key] is None:
exclude_key.append(key)
@@ -282,7 +290,7 @@ def create_or_update_executions(kc, config, realm='master'):
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
after += str(new_exec) + '\n'
elif new_exec["displayName"] is not None:
kc.create_subflow(new_exec["displayName"], flow_alias_parent, realm=realm)
kc.create_subflow(new_exec["displayName"], flow_alias_parent, realm=realm, flowType=new_exec["subFlowType"])
exec_found = True
exec_index = new_exec_index
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
@@ -299,7 +307,7 @@ def create_or_update_executions(kc, config, realm='master'):
kc.add_authenticationConfig_to_execution(updated_exec["id"], new_exec["authenticationConfig"], realm=realm)
for key in new_exec:
# remove unwanted key for the next API call
if key != "flowAlias" and key != "authenticationConfig":
if key not in ("flowAlias", "authenticationConfig", "subFlowType"):
updated_exec[key] = new_exec[key]
if new_exec["requirement"] is not None:
kc.update_authentication_executions(flow_alias_parent, updated_exec, realm=realm)
@@ -334,6 +342,7 @@ def main():
flowAlias=dict(type='str'),
authenticationConfig=dict(type='dict'),
index=dict(type='int'),
subFlowType=dict(choices=["basic-flow", "form-flow"], default='basic-flow', type='str'),
)),
state=dict(choices=["absent", "present"], default='present'),
force=dict(type='bool', default=False),

View File

@@ -0,0 +1,280 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
# Copyright (c) 2021, Christophe Gilles <christophe.gilles54@gmail.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
# https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: keycloak_authz_authorization_scope
short_description: Allows administration of Keycloak client authorization scopes via Keycloak API
version_added: 6.6.0
description:
- This module allows the administration of Keycloak client Authorization Scopes via the Keycloak REST
API. Authorization Scopes are only available if a client has Authorization enabled.
- This module requires access to the REST API via OpenID Connect; the user connecting and the realm
being used must have the requisite access rights. In a default Keycloak installation, admin-cli
and an admin user would work, as would a separate realm definition with the scope tailored
to your needs and a user having the expected roles.
- The names of module options are snake_cased versions of the camelCase options used by Keycloak.
The Authorization Services paths and payloads have not officially been documented by the Keycloak project.
U(https://www.puppeteers.net/blog/keycloak-authorization-services-rest-api-paths-and-payload/)
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the authorization scope.
- On C(present), the authorization scope will be created (or updated if it exists already).
- On C(absent), the authorization scope will be removed if it exists.
choices: ['present', 'absent']
default: 'present'
type: str
name:
description:
- Name of the authorization scope to create.
type: str
required: true
display_name:
description:
- The display name of the authorization scope.
type: str
required: false
icon_uri:
description:
- The icon URI for the authorization scope.
type: str
required: false
client_id:
description:
- The C(clientId) of the Keycloak client that should have the authorization scope.
- This is usually a human-readable name of the Keycloak client.
type: str
required: true
realm:
description:
- The name of the Keycloak realm the Keycloak client is in.
type: str
required: true
extends_documentation_fragment:
- community.general.keycloak
- community.general.attributes
author:
- Samuli Seppänen (@mattock)
'''
EXAMPLES = '''
- name: Manage Keycloak file:delete authorization scope
keycloak_authz_authorization_scope:
name: file:delete
state: present
display_name: File delete
client_id: myclient
realm: myrealm
auth_keycloak_url: http://localhost:8080/auth
auth_username: keycloak
auth_password: keycloak
auth_realm: master
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
end_state:
description: Representation of the authorization scope after module execution.
returned: on success
type: complex
contains:
id:
description: ID of the authorization scope.
type: str
returned: when I(state=present)
sample: a6ab1cf2-1001-40ec-9f39-48f23b6a0a41
name:
description: Name of the authorization scope.
type: str
returned: when I(state=present)
sample: file:delete
display_name:
description: Display name of the authorization scope.
type: str
returned: when I(state=present)
sample: File delete
icon_uri:
description: Icon URI for the authorization scope.
type: str
returned: when I(state=present)
sample: http://localhost/icon.png
'''
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
state=dict(type='str', default='present',
choices=['present', 'absent']),
name=dict(type='str', required=True),
display_name=dict(type='str', required=False),
icon_uri=dict(type='str', required=False),
client_id=dict(type='str', required=True),
realm=dict(type='str', required=True)
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=(
[['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
result = dict(changed=False, msg='', end_state={}, diff=dict(before={}, after={}))
# Obtain access token, initialize API
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
# Convenience variables
state = module.params.get('state')
name = module.params.get('name')
display_name = module.params.get('display_name')
icon_uri = module.params.get('icon_uri')
client_id = module.params.get('client_id')
realm = module.params.get('realm')
# Get the "id" of the client based on the usually more human-readable
# "clientId"
cid = kc.get_client_id(client_id, realm=realm)
if not cid:
module.fail_json(msg='Invalid client %s for realm %s' %
(client_id, realm))
# Get current state of the Authorization Scope using its name as the search
# filter. This returns False if it is not found.
before_authz_scope = kc.get_authz_authorization_scope_by_name(
name=name, client_id=cid, realm=realm)
# Generate a JSON payload for Keycloak Admin API. This is needed for
# "create" and "update" operations.
desired_authz_scope = {}
desired_authz_scope['name'] = name
desired_authz_scope['displayName'] = display_name
desired_authz_scope['iconUri'] = icon_uri
# Add "id" to payload for modify operations
if before_authz_scope:
desired_authz_scope['id'] = before_authz_scope['id']
# Ensure that undefined (null) optional parameters are presented as empty
# strings in the desired state. This makes comparisons with current state
# much easier.
for k, v in desired_authz_scope.items():
if not v:
desired_authz_scope[k] = ''
# Do the above for the current state
if before_authz_scope:
for k in ['displayName', 'iconUri']:
if k not in before_authz_scope:
before_authz_scope[k] = ''
if before_authz_scope and state == 'present':
changes = False
for k, v in desired_authz_scope.items():
if before_authz_scope[k] != v:
changes = True
# At this point we know we have to update the object anyways,
# so there's no need to do more work.
break
if changes:
if module._diff:
result['diff'] = dict(before=before_authz_scope, after=desired_authz_scope)
if module.check_mode:
result['changed'] = True
result['msg'] = 'Authorization scope would be updated'
module.exit_json(**result)
else:
kc.update_authz_authorization_scope(
payload=desired_authz_scope, id=before_authz_scope['id'], client_id=cid, realm=realm)
result['changed'] = True
result['msg'] = 'Authorization scope updated'
else:
result['changed'] = False
result['msg'] = 'Authorization scope not updated'
result['end_state'] = desired_authz_scope
elif not before_authz_scope and state == 'present':
if module._diff:
result['diff'] = dict(before={}, after=desired_authz_scope)
if module.check_mode:
result['changed'] = True
result['msg'] = 'Authorization scope would be created'
module.exit_json(**result)
else:
kc.create_authz_authorization_scope(
payload=desired_authz_scope, client_id=cid, realm=realm)
result['changed'] = True
result['msg'] = 'Authorization scope created'
result['end_state'] = desired_authz_scope
elif before_authz_scope and state == 'absent':
if module._diff:
result['diff'] = dict(before=before_authz_scope, after={})
if module.check_mode:
result['changed'] = True
result['msg'] = 'Authorization scope would be removed'
module.exit_json(**result)
else:
kc.remove_authz_authorization_scope(
id=before_authz_scope['id'], client_id=cid, realm=realm)
result['changed'] = True
result['msg'] = 'Authorization scope removed'
elif not before_authz_scope and state == 'absent':
result['changed'] = False
else:
module.fail_json(msg='Unable to determine what to do with authorization scope %s of client %s in realm %s' % (
name, client_id, realm))
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,285 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) Ansible project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: keycloak_clientscope_type
short_description: Set the type of aclientscope in realm or client via Keycloak API
version_added: 6.6.0
description:
- This module allows you to set the type (optional, default) of clientscopes
via the Keycloak REST API. It requires access to the REST API via OpenID
Connect; the user connecting and the client being used must have the
requisite access rights. In a default Keycloak installation, admin-cli and
an admin user would work, as would a separate client definition with the
scope tailored to your needs and a user having the expected roles.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
realm:
type: str
description:
- The Keycloak realm.
default: 'master'
client_id:
description:
- The I(client_id) of the client. If not set the clientscop types are set as a default for the realm.
aliases:
- clientId
type: str
default_clientscopes:
description:
- Client scopes that should be of type default.
type: list
elements: str
optional_clientscopes:
description:
- Client scopes that should be of type optional.
type: list
elements: str
extends_documentation_fragment:
- community.general.keycloak
- community.general.attributes
author:
- Simon Pahl (@simonpahl)
'''
EXAMPLES = '''
- name: Set default client scopes on realm level
community.general.keycloak_clientsecret_info:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
realm: "MyCustomRealm"
default_clientscopes: ['profile', 'roles']
delegate_to: localhost
- name: Set default and optional client scopes on client level with token auth
community.general.keycloak_clientsecret_info:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
token: TOKEN
realm: "MyCustomRealm"
client_id: "MyCustomClient"
default_clientscopes: ['profile', 'roles']
optional_clientscopes: ['phone']
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: ""
proposed:
description: Representation of proposed client-scope types mapping.
returned: always
type: dict
sample: {
default_clientscopes: ["profile", "role"],
optional_clientscopes: []
}
existing:
description:
- Representation of client scopes before module execution.
returned: always
type: dict
sample: {
default_clientscopes: ["profile", "role"],
optional_clientscopes: ["phone"]
}
end_state:
description:
- Representation of client scopes after module execution.
- The sample is truncated.
returned: on success
type: dict
sample: {
default_clientscopes: ["profile", "role"],
optional_clientscopes: []
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI, KeycloakError, get_token)
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import \
keycloak_argument_spec
def keycloak_clientscope_type_module():
"""
Returns an AnsibleModule definition.
:return: argument_spec dict
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
realm=dict(default='master'),
client_id=dict(type='str', aliases=['clientId']),
default_clientscopes=dict(type='list', elements='str'),
optional_clientscopes=dict(type='list', elements='str'),
)
argument_spec.update(meta_args)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([
['token', 'auth_realm', 'auth_username', 'auth_password'],
['default_clientscopes', 'optional_clientscopes']
]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]),
mutually_exclusive=[
['token', 'auth_realm'],
['token', 'auth_username'],
['token', 'auth_password']
])
return module
def clientscopes_to_add(existing, proposed):
to_add = []
existing_clientscope_ids = extract_field(existing, 'id')
for clientscope in proposed:
if not clientscope['id'] in existing_clientscope_ids:
to_add.append(clientscope)
return to_add
def clientscopes_to_delete(existing, proposed):
to_delete = []
proposed_clientscope_ids = extract_field(proposed, 'id')
for clientscope in existing:
if not clientscope['id'] in proposed_clientscope_ids:
to_delete.append(clientscope)
return to_delete
def extract_field(dictionary, field='name'):
return [cs[field] for cs in dictionary]
def main():
"""
Module keycloak_clientscope_type
:return:
"""
module = keycloak_clientscope_type_module()
# Obtain access token, initialize API
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params.get('realm')
client_id = module.params.get('client_id')
default_clientscopes = module.params.get('default_clientscopes')
optional_clientscopes = module.params.get('optional_clientscopes')
result = dict(changed=False, msg='', proposed={}, existing={}, end_state={})
all_clientscopes = kc.get_clientscopes(realm)
default_clientscopes_real = []
optional_clientscopes_real = []
for client_scope in all_clientscopes:
if default_clientscopes is not None and client_scope["name"] in default_clientscopes:
default_clientscopes_real.append(client_scope)
if optional_clientscopes is not None and client_scope["name"] in optional_clientscopes:
optional_clientscopes_real.append(client_scope)
if default_clientscopes is not None and len(default_clientscopes_real) != len(default_clientscopes):
module.fail_json(msg='At least one of the default_clientscopes does not exist!')
if optional_clientscopes is not None and len(optional_clientscopes_real) != len(optional_clientscopes):
module.fail_json(msg='At least one of the optional_clientscopes does not exist!')
result['proposed'].update({
'default_clientscopes': 'no-change' if default_clientscopes is None else default_clientscopes,
'optional_clientscopes': 'no-change' if optional_clientscopes is None else optional_clientscopes
})
default_clientscopes_existing = kc.get_default_clientscopes(realm, client_id)
optional_clientscopes_existing = kc.get_optional_clientscopes(realm, client_id)
result['existing'].update({
'default_clientscopes': extract_field(default_clientscopes_existing),
'optional_clientscopes': extract_field(optional_clientscopes_existing)
})
if module._diff:
result['diff'] = dict(before=result['existing'], after=result['proposed'])
if module.check_mode:
module.exit_json(**result)
default_clientscopes_add = clientscopes_to_add(default_clientscopes_existing, default_clientscopes_real)
optional_clientscopes_add = clientscopes_to_add(optional_clientscopes_existing, optional_clientscopes_real)
default_clientscopes_delete = clientscopes_to_delete(default_clientscopes_existing, default_clientscopes_real)
optional_clientscopes_delete = clientscopes_to_delete(optional_clientscopes_existing, optional_clientscopes_real)
# first delete so clientscopes can change type
for clientscope in default_clientscopes_delete:
kc.delete_default_clientscope(clientscope['id'], realm, client_id)
for clientscope in optional_clientscopes_delete:
kc.delete_optional_clientscope(clientscope['id'], realm, client_id)
for clientscope in default_clientscopes_add:
kc.add_default_clientscope(clientscope['id'], realm, client_id)
for clientscope in optional_clientscopes_add:
kc.add_optional_clientscope(clientscope['id'], realm, client_id)
result["changed"] = (
len(default_clientscopes_add) > 0
or len(optional_clientscopes_add) > 0
or len(default_clientscopes_delete) > 0
or len(optional_clientscopes_delete) > 0
)
result['end_state'].update({
'default_clientscopes': extract_field(kc.get_default_clientscopes(realm, client_id)),
'optional_clientscopes': extract_field(kc.get_optional_clientscopes(realm, client_id))
})
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -146,8 +146,7 @@ class MkSysB(ModuleHelper):
def __run__(self):
def process(rc, out, err):
if rc != 0:
self.do_raise("mksysb failed.")
self.vars.msg = out
self.do_raise("mksysb failed: {0}".format(out))
runner = CmdRunner(
self.module,
@@ -158,6 +157,8 @@ class MkSysB(ModuleHelper):
'extended_attrs', 'backup_crypt_files', 'backup_dmapi_fs', 'new_image_data', 'combined_path'],
output_process=process, check_mode_skip=True) as ctx:
ctx.run(combined_path=[self.vars.storage_path, self.vars.name])
if self.verbosity >= 4:
self.vars.run_info = ctx.run_info
self.changed = True

View File

@@ -52,7 +52,7 @@ options:
- If C(present), adds module name to C(/etc/modules-load.d/) and params to C(/etc/modprobe.d/) so the module will be loaded on next reboot.
- If C(absent), will comment out module name from C(/etc/modules-load.d/) and comment out params from C(/etc/modprobe.d/) so the module will not be
loaded on next reboot.
- If C(disabled), will not toch anything and leave C(/etc/modules-load.d/) and C(/etc/modprobe.d/) as it is.
- If C(disabled), will not touch anything and leave C(/etc/modules-load.d/) and C(/etc/modprobe.d/) as it is.
- Note that it is usually a better idea to rely on the automatic module loading by PCI IDs, USB IDs, DMI IDs or similar triggers encoded in the
kernel modules themselves instead of configuration like this.
- In fact, most modern kernel modules are prepared for automatic loading already.

View File

@@ -63,11 +63,12 @@ options:
- Type C(generic) is added in Ansible 2.5.
- Type C(infiniband) is added in community.general 2.0.0.
- Type C(gsm) is added in community.general 3.7.0.
- Type C(macvlan) is added in community.general 6.6.0.
- Type C(wireguard) is added in community.general 4.3.0.
- Type C(vpn) is added in community.general 5.1.0.
type: str
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, sit, team, team-slave, vlan, vxlan, wifi, gsm,
wireguard, vpn ]
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, macvlan, sit, team, team-slave, vlan, vxlan,
wifi, gsm, wireguard, vpn ]
mode:
description:
- This is the type of device or network connection that you wish to create for a bond or bridge.
@@ -879,6 +880,38 @@ options:
- The username used to authenticate with the network, if required.
- Many providers do not require a username, or accept any username.
- But if a username is required, it is specified here.
macvlan:
description:
- The configuration of the MAC VLAN connection.
- Note the list of suboption attributes may vary depending on which version of NetworkManager/nmcli is installed on the host.
- 'An up-to-date list of supported attributes can be found here:
U(https://networkmanager.dev/docs/api/latest/settings-macvlan.html).'
type: dict
version_added: 6.6.0
suboptions:
mode:
description:
- The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device.
- 'Following choices are allowed: C(1) B(vepa), C(2) B(bridge), C(3) B(private), C(4) B(passthru)
and C(5) B(source)'
type: int
choices: [ 1, 2, 3, 4, 5 ]
required: true
parent:
description:
- If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should
be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a
"mac-address" property.
type: str
required: true
promiscuous:
description:
- Whether the interface should be put in promiscuous mode.
type: bool
tap:
description:
- Whether the interface should be a MACVTAP.
type: bool
wireguard:
description:
- The configuration of the Wireguard connection.
@@ -1357,6 +1390,17 @@ EXAMPLES = r'''
autoconnect: true
state: present
- name: Create a macvlan connection
community.general.nmcli:
type: macvlan
conn_name: my-macvlan-connection
ifname: mymacvlan0
macvlan:
mode: 2
parent: eth1
autoconnect: true
state: present
- name: Create a wireguard connection
community.general.nmcli:
type: wireguard
@@ -1502,13 +1546,14 @@ class Nmcli(object):
self.wifi = module.params['wifi']
self.wifi_sec = module.params['wifi_sec']
self.gsm = module.params['gsm']
self.macvlan = module.params['macvlan']
self.wireguard = module.params['wireguard']
self.vpn = module.params['vpn']
self.transport_mode = module.params['transport_mode']
if self.method4:
self.ipv4_method = self.method4
elif self.type in ('dummy', 'wireguard') and not self.ip4:
elif self.type in ('dummy', 'macvlan', 'wireguard') and not self.ip4:
self.ipv4_method = 'disabled'
elif self.ip4:
self.ipv4_method = 'manual'
@@ -1517,7 +1562,7 @@ class Nmcli(object):
if self.method6:
self.ipv6_method = self.method6
elif self.type in ('dummy', 'wireguard') and not self.ip6:
elif self.type in ('dummy', 'macvlan', 'wireguard') and not self.ip6:
self.ipv6_method = 'disabled'
elif self.ip6:
self.ipv6_method = 'manual'
@@ -1700,6 +1745,14 @@ class Nmcli(object):
options.update({
'gsm.%s' % name: value,
})
elif self.type == 'macvlan':
if self.macvlan:
for name, value in self.macvlan.items():
options.update({
'macvlan.%s' % name: value,
})
elif self.state == 'present':
raise NmcliModuleError('type is macvlan but all of the following are missing: macvlan')
elif self.type == 'wireguard':
if self.wireguard:
for name, value in self.wireguard.items():
@@ -1777,6 +1830,7 @@ class Nmcli(object):
'wifi',
'802-11-wireless',
'gsm',
'macvlan',
'wireguard',
'vpn',
)
@@ -1992,6 +2046,9 @@ class Nmcli(object):
if key in self.SECRET_OPTIONS:
self.edit_commands += ['set %s %s' % (key, value)]
continue
if key == 'xmit_hash_policy':
cmd.extend(['+bond.options', 'xmit_hash_policy=%s' % value])
continue
cmd.extend([key, value])
return self.execute_command(cmd)
@@ -2239,6 +2296,7 @@ def main():
'vxlan',
'wifi',
'gsm',
'macvlan',
'wireguard',
'vpn',
]),
@@ -2342,6 +2400,11 @@ def main():
wifi=dict(type='dict'),
wifi_sec=dict(type='dict', no_log=True),
gsm=dict(type='dict'),
macvlan=dict(type='dict', options=dict(
mode=dict(type='int', choices=[1, 2, 3, 4, 5], required=True),
parent=dict(type='str', required=True),
promiscuous=dict(type='bool'),
tap=dict(type='bool'))),
wireguard=dict(type='dict'),
vpn=dict(type='dict'),
transport_mode=dict(type='str', choices=['datagram', 'connected']),

View File

@@ -13,7 +13,8 @@ module: office_365_connector_card
short_description: Use webhooks to create Connector Card messages within an Office 365 group
description:
- Creates Connector Card messages through
- Office 365 Connectors U(https://dev.outlook.com/Connectors)
Office 365 Connectors
U(https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-reference#connector-card-for-microsoft-365-groups).
author: "Marc Sensenich (@marc-sensenich)"
notes:
- This module is not idempotent, therefore if the same task is run twice
@@ -62,7 +63,7 @@ options:
elements: dict
description:
- Contains a list of sections to display in the card.
- For more information see https://dev.outlook.com/Connectors/reference.
- For more information see U(https://learn.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#section-fields).
'''
EXAMPLES = """

View File

@@ -20,10 +20,11 @@ description:
command line tool. For a full description of the fields and the options
check the GNU parted manual.
requirements:
- This module requires parted version 1.8.3 and above
- align option (except 'undefined') requires parted 2.1 and above
- If the version of parted is below 3.1, it requires a Linux version running
the sysfs file system C(/sys/).
- This module requires C(parted) version 1.8.3 and above.
- Option I(align) (except C(undefined)) requires C(parted) 2.1 or above.
- If the version of C(parted) is below 3.1, it requires a Linux version running
the C(sysfs) file system C(/sys/).
- Requires the C(resizepart) command when using the I(resize) parameter.
extends_documentation_fragment:
- community.general.attributes
attributes:
@@ -33,66 +34,70 @@ attributes:
support: none
options:
device:
description: The block device (disk) where to operate.
description:
- The block device (disk) where to operate.
- Regular files can also be partitioned, but it is recommended to create a
loopback device using C(losetup) to easily access its partitions.
type: str
required: true
align:
description: Set alignment for newly created partitions. Use 'undefined' for parted default aligment.
description:
- Set alignment for newly created partitions. Use C(undefined) for parted default aligment.
type: str
choices: [ cylinder, minimal, none, optimal, undefined ]
default: optimal
number:
description:
- The number of the partition to work with or the number of the partition
that will be created.
- Required when performing any action on the disk, except fetching information.
- The partition number being affected.
- Required when performing any action on the disk, except fetching information.
type: int
unit:
description:
- Selects the current default unit that Parted will use to display
locations and capacities on the disk and to interpret those given by the
user if they are not suffixed by an unit.
- When fetching information about a disk, it is always recommended to specify a unit.
- Selects the current default unit that Parted will use to display
locations and capacities on the disk and to interpret those given by the
user if they are not suffixed by an unit.
- When fetching information about a disk, it is recommended to always specify a unit.
type: str
choices: [ s, B, KB, KiB, MB, MiB, GB, GiB, TB, TiB, '%', cyl, chs, compact ]
default: KiB
label:
description:
- Disk label type to use.
- If C(device) already contains different label, it will be changed to C(label) and any previous partitions will be lost.
- Disk label type or partition table to use.
- If I(device) already contains a different label, it will be changed to I(label)
and any previous partitions will be lost.
- A I(name) must be specified for a C(gpt) partition table.
type: str
choices: [ aix, amiga, bsd, dvh, gpt, loop, mac, msdos, pc98, sun ]
default: msdos
part_type:
description:
- May be specified only with 'msdos' or 'dvh' partition tables.
- A C(name) must be specified for a 'gpt' partition table.
- Neither C(part_type) nor C(name) may be used with a 'sun' partition table.
- May be specified only with I(label=msdos) or I(label=dvh).
- Neither I(part_type) nor I(name) may be used with I(label=sun).
type: str
choices: [ extended, logical, primary ]
default: primary
part_start:
description:
- Where the partition will start as offset from the beginning of the disk,
that is, the "distance" from the start of the disk. Negative numbers
specify distance from the end of the disk.
- The distance can be specified with all the units supported by parted
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
- Using negative values may require setting of C(fs_type) (see notes).
- Where the partition will start as offset from the beginning of the disk,
that is, the "distance" from the start of the disk. Negative numbers
specify distance from the end of the disk.
- The distance can be specified with all the units supported by parted
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
- Using negative values may require setting of I(fs_type) (see notes).
type: str
default: 0%
part_end:
description:
- Where the partition will end as offset from the beginning of the disk,
that is, the "distance" from the start of the disk. Negative numbers
specify distance from the end of the disk.
- The distance can be specified with all the units supported by parted
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
- Where the partition will end as offset from the beginning of the disk,
that is, the "distance" from the start of the disk. Negative numbers
specify distance from the end of the disk.
- The distance can be specified with all the units supported by parted
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
type: str
default: 100%
name:
description:
- Sets the name for the partition number (GPT, Mac, MIPS and PC98 only).
- Sets the name for the partition number (GPT, Mac, MIPS and PC98 only).
type: str
flags:
description: A list of the flags that has to be set on the partition.
@@ -100,15 +105,15 @@ options:
elements: str
state:
description:
- Whether to create or delete a partition.
- If set to C(info) the module will only return the device information.
- Whether to create or delete a partition.
- If set to C(info) the module will only return the device information.
type: str
choices: [ absent, present, info ]
default: info
fs_type:
description:
- If specified and the partition does not exist, will set filesystem type to given partition.
- Parameter optional, but see notes below about negative C(part_start) values.
- If specified and the partition does not exist, will set filesystem type to given partition.
- Parameter optional, but see notes below about negative I(part_start) values.
type: str
version_added: '0.2.0'
resize:
@@ -123,9 +128,9 @@ notes:
installed on the system is before version 3.1, the module queries the kernel
through C(/sys/) to obtain disk information. In this case the units CHS and
CYL are not supported.
- Negative C(part_start) start values were rejected if C(fs_type) was not given.
This bug was fixed in parted 3.2.153. If you want to use negative C(part_start),
specify C(fs_type) as well or make sure your system contains newer parted.
- Negative I(part_start) start values were rejected if I(fs_type) was not given.
This bug was fixed in parted 3.2.153. If you want to use negative I(part_start),
specify I(fs_type) as well or make sure your system contains newer parted.
'''
RETURN = r'''

View File

@@ -57,7 +57,7 @@ options:
install_deps:
description:
- Include applications of dependent packages.
- Only used when I(state=install), I(state=upgrade), or I(state=inject).
- Only used when I(state=install), I(state=latest), or I(state=inject).
type: bool
default: false
inject_packages:
@@ -69,25 +69,33 @@ options:
force:
description:
- Force modification of the application's virtual environment. See C(pipx) for details.
- Only used when I(state=install), I(state=upgrade), I(state=upgrade_all), or I(state=inject).
- Only used when I(state=install), I(state=upgrade), I(state=upgrade_all), I(state=latest), or I(state=inject).
type: bool
default: false
include_injected:
description:
- Upgrade the injected packages along with the application.
- Only used when I(state=upgrade) or I(state=upgrade_all).
- Only used when I(state=upgrade), I(state=upgrade_all), or I(state=latest).
- This is used with I(state=upgrade) and I(state=latest) since community.general 6.6.0.
type: bool
default: false
index_url:
description:
- Base URL of Python Package Index.
- Only used when I(state=install), I(state=upgrade), or I(state=inject).
- Only used when I(state=install), I(state=upgrade), I(state=latest), or I(state=inject).
type: str
python:
description:
- Python version to be used when creating the application virtual environment. Must be 3.6+.
- Only used when I(state=install), I(state=reinstall), or I(state=reinstall_all).
- Only used when I(state=install), I(state=latest), I(state=reinstall), or I(state=reinstall_all).
type: str
system_site_packages:
description:
- Give application virtual environment access to the system site-packages directory.
- Only used when I(state=install) or I(state=latest).
type: bool
default: false
version_added: 6.6.0
executable:
description:
- Path to the C(pipx) installed in the system.
@@ -112,6 +120,7 @@ notes:
- >
This module will honor C(pipx) environment variables such as but not limited to C(PIPX_HOME) and C(PIPX_BIN_DIR)
passed using the R(environment Ansible keyword, playbooks_environment).
- This module requires C(pipx) version 0.16.2.1 or above.
- Please note that C(pipx) requires Python 3.6 or above.
- >
This first implementation does not verify whether a specified version constraint has been installed or not.
@@ -175,6 +184,7 @@ class PipX(StateModuleHelper):
include_injected=dict(type='bool', default=False),
index_url=dict(type='str'),
python=dict(type='str'),
system_site_packages=dict(type='bool', default=False),
executable=dict(type='path'),
editable=dict(type='bool', default=False),
pip_args=dict(type='str'),
@@ -242,7 +252,7 @@ class PipX(StateModuleHelper):
def state_install(self):
if not self.vars.application or self.vars.force:
self.changed = True
with self.runner('state index_url install_deps force python editable pip_args name_source', check_mode_skip=True) as ctx:
with self.runner('state index_url install_deps force python system_site_packages editable pip_args name_source', check_mode_skip=True) as ctx:
ctx.run(name_source=[self.vars.name, self.vars.source])
self._capture_results(ctx)
@@ -254,7 +264,7 @@ class PipX(StateModuleHelper):
if self.vars.force:
self.changed = True
with self.runner('state index_url install_deps force editable pip_args name', check_mode_skip=True) as ctx:
with self.runner('state include_injected index_url force editable pip_args name', check_mode_skip=True) as ctx:
ctx.run()
self._capture_results(ctx)
@@ -303,11 +313,11 @@ class PipX(StateModuleHelper):
def state_latest(self):
if not self.vars.application or self.vars.force:
self.changed = True
with self.runner('state index_url install_deps force python editable pip_args name_source', check_mode_skip=True) as ctx:
with self.runner('state index_url install_deps force python system_site_packages editable pip_args name_source', check_mode_skip=True) as ctx:
ctx.run(state='install', name_source=[self.vars.name, self.vars.source])
self._capture_results(ctx)
with self.runner('state index_url install_deps force editable pip_args name', check_mode_skip=True) as ctx:
with self.runner('state include_injected index_url force editable pip_args name', check_mode_skip=True) as ctx:
ctx.run(state='upgrade')
self._capture_results(ctx)

View File

@@ -53,6 +53,7 @@ notes:
- >
This module will honor C(pipx) environment variables such as but not limited to C(PIPX_HOME) and C(PIPX_BIN_DIR)
passed using the R(environment Ansible keyword, playbooks_environment).
- This module requires C(pipx) version 0.16.2.1 or above.
- Please note that C(pipx) requires Python 3.6 or above.
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
author:

View File

@@ -333,9 +333,9 @@ def emerge_packages(module, packages):
"""Run emerge command against given list of atoms."""
p = module.params
if p['noreplace'] and not (p['update'] or p['state'] == 'latest'):
if p['noreplace'] and not p['changed_use'] and not p['newuse'] and not (p['update'] or p['state'] == 'latest'):
for package in packages:
if p['noreplace'] and not query_package(module, package, 'emerge'):
if p['noreplace'] and not p['changed_use'] and not p['newuse'] and not query_package(module, package, 'emerge'):
break
else:
module.exit_json(changed=False, msg='Packages already present.')
@@ -383,14 +383,12 @@ def emerge_packages(module, packages):
"""Fallback to default: don't use this argument at all."""
continue
if not flag_val:
"""Add the --flag=value pair."""
if isinstance(flag_val, bool):
args.extend((arg, to_native('y' if flag_val else 'n')))
elif not flag_val:
"""If the value is 0 or 0.0: add the flag, but not the value."""
args.append(arg)
continue
"""Add the --flag=value pair."""
if isinstance(p[flag], bool):
args.extend((arg, to_native('y' if flag_val else 'n')))
else:
args.extend((arg, to_native(flag_val)))

View File

@@ -763,15 +763,6 @@ EXAMPLES = '''
node: sabrewulf
state: absent
- name: Get VM current state
community.general.proxmox_kvm:
api_user: root@pam
api_password: secret
api_host: helldorado
name: spynal
node: sabrewulf
state: current
- name: Update VM configuration
community.general.proxmox_kvm:
api_user: root@pam

View File

@@ -160,8 +160,7 @@ def main():
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret'),
('api_user', 'api_password')],
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
supports_check_mode=True)
result = dict(changed=False)

View File

@@ -81,6 +81,12 @@ options:
- A list of puppet tags to be used.
type: list
elements: str
skip_tags:
description:
- A list of puppet tags to be excluded.
type: list
elements: str
version_added: 6.6.0
execute:
description:
- Execute a specific piece of Puppet code.
@@ -143,6 +149,8 @@ EXAMPLES = r'''
tags:
- update
- nginx
skip_tags:
- service
- name: Run puppet agent in noop mode
community.general.puppet:
@@ -198,6 +206,7 @@ def main():
environment=dict(type='str'),
certname=dict(type='str'),
tags=dict(type='list', elements='str'),
skip_tags=dict(type='list', elements='str'),
execute=dict(type='str'),
summarize=dict(type='bool', default=False),
debug=dict(type='bool', default=False),
@@ -232,11 +241,11 @@ def main():
runner = puppet_utils.puppet_runner(module)
if not p['manifest'] and not p['execute']:
args_order = "_agent_fixed puppetmaster show_diff confdir environment tags certname noop use_srv_records"
args_order = "_agent_fixed puppetmaster show_diff confdir environment tags skip_tags certname noop use_srv_records"
with runner(args_order) as ctx:
rc, stdout, stderr = ctx.run()
else:
args_order = "_apply_fixed logdest modulepath environment certname tags noop _execute summarize debug verbose"
args_order = "_apply_fixed logdest modulepath environment certname tags skip_tags noop _execute summarize debug verbose"
with runner(args_order) as ctx:
rc, stdout, stderr = ctx.run(_execute=[p['execute'], p['manifest']])

View File

@@ -19,6 +19,16 @@ description:
registering using D-Bus if possible.
author: "Barnaby Court (@barnabycourt)"
notes:
- |
The module tries to use the D-Bus C(rhsm) service (part of C(subscription-manager))
to register, starting from community.general 6.5.0: this is done so credentials
(username, password, activation keys) can be passed to C(rhsm) in a secure way.
C(subscription-manager) itself gets credentials only as arguments of command line
parameters, which is I(not) secure, as they can be easily stolen by checking the
process listing on the system. Due to limitations of the D-Bus interface of C(rhsm),
the module will I(not) use D-Bus for registation when trying either to register
using I(token), or when specifying I(environment), or when the system is old
(typically RHEL 6 and older).
- In order to register a system, subscription-manager requires either a username and password, or an activationkey and an Organization ID.
- Since 2.5 values for I(server_hostname), I(server_insecure), I(rhsm_baseurl),
I(server_proxy_hostname), I(server_proxy_port), I(server_proxy_user) and
@@ -450,7 +460,7 @@ class Rhsm(RegistrationBase):
self.module.debug('Verified system D-Bus bus as usable')
return True
def register(self, username, password, token, auto_attach, activationkey, org_id,
def register(self, was_registered, username, password, token, auto_attach, activationkey, org_id,
consumer_type, consumer_name, consumer_id, force_register, environment,
release):
'''
@@ -461,9 +471,11 @@ class Rhsm(RegistrationBase):
* Exception - if any error occurs during the registration
'''
# There is no support for token-based registration in the D-Bus API
# of rhsm, so always use the CLI in that case.
if not token and self._can_connect_to_dbus():
self._register_using_dbus(username, password, auto_attach,
# of rhsm, so always use the CLI in that case;
# also, since the specified environments are names, and the D-Bus APIs
# require IDs for the environments, use the CLI also in that case
if not token and not environment and self._can_connect_to_dbus():
self._register_using_dbus(was_registered, username, password, auto_attach,
activationkey, org_id, consumer_type,
consumer_name, consumer_id,
force_register, environment, release)
@@ -521,7 +533,7 @@ class Rhsm(RegistrationBase):
rc, stderr, stdout = self.module.run_command(args, check_rc=True, expand_user_and_vars=False)
def _register_using_dbus(self, username, password, auto_attach,
def _register_using_dbus(self, was_registered, username, password, auto_attach,
activationkey, org_id, consumer_type, consumer_name,
consumer_id, force_register, environment, release):
'''
@@ -544,7 +556,8 @@ class Rhsm(RegistrationBase):
return default
distro_id = distro.id()
distro_version = tuple(str2int(p) for p in distro.version_parts())
distro_version_parts = distro.version_parts()
distro_version = tuple(str2int(p) for p in distro_version_parts)
# Stop the rhsm service when using systemd (which means Fedora or
# RHEL 7+): this is because the service may not use new configuration bits
@@ -570,7 +583,7 @@ class Rhsm(RegistrationBase):
distro_version[0] > 9)):
dbus_force_option_works = True
if force_register and not dbus_force_option_works:
if force_register and not dbus_force_option_works and was_registered:
self.unregister()
register_opts = {}
@@ -585,14 +598,34 @@ class Rhsm(RegistrationBase):
# of RHEL before 8.6, and then it changed to 'environments'; since
# the Register*() D-Bus functions reject unknown options, we have
# to pass the right option depending on the version -- funky.
def supports_option_environments():
# subscription-manager in any supported Fedora version
# has the new option.
if distro_id == 'fedora':
return True
# Check for RHEL 8 >= 8.6, or RHEL >= 9.
if distro_id == 'rhel' and \
((distro_version[0] == 8 and distro_version[1] >= 6) or
distro_version[0] >= 9):
return True
# CentOS: similar checks as for RHEL, with one extra bit:
# if the 2nd part of the version is empty, it means it is
# CentOS Stream, and thus we can assume it has the latest
# version of subscription-manager.
if distro_id == 'centos' and \
((distro_version[0] == 8 and
(distro_version[1] >= 6 or distro_version_parts[1] == '')) or
distro_version[0] >= 9):
return True
# Unknown or old distro: assume it does not support
# the new option.
return False
environment_key = 'environment'
if distro_id == 'fedora' or \
(distro_id == 'rhel' and
((distro_version[0] == 8 and distro_version[1] >= 6) or
distro_version[0] >= 9)):
if supports_option_environments():
environment_key = 'environments'
register_opts[environment_key] = environment
if force_register and dbus_force_option_works:
if force_register and dbus_force_option_works and was_registered:
register_opts['force'] = True
# Wrap it as proper D-Bus dict
register_opts = dbus.Dictionary(register_opts, signature='sv', variant_level=1)
@@ -1137,8 +1170,11 @@ def main():
# Ensure system is registered
if state == 'present':
# Cache the status of the system before the changes
was_registered = rhsm.is_registered
# Register system
if rhsm.is_registered and not force_register:
if was_registered and not force_register:
if syspurpose and 'sync' in syspurpose and syspurpose['sync'] is True:
try:
rhsm.sync_syspurpose()
@@ -1165,7 +1201,7 @@ def main():
try:
rhsm.enable()
rhsm.configure(**module.params)
rhsm.register(username, password, token, auto_attach, activationkey, org_id,
rhsm.register(was_registered, username, password, token, auto_attach, activationkey, org_id,
consumer_type, consumer_name, consumer_id, force_register,
environment, release)
if syspurpose and 'sync' in syspurpose and syspurpose['sync'] is True:

View File

@@ -32,8 +32,8 @@ attributes:
options:
release:
description:
- RHSM release version to use (use null to unset)
required: true
- RHSM release version to use.
- To unset either pass C(null) for this option, or omit this option.
type: str
author:
- Sean Myers (@seandst)
@@ -43,17 +43,17 @@ EXAMPLES = '''
# Set release version to 7.1
- name: Set RHSM release version
community.general.rhsm_release:
release: "7.1"
release: "7.1"
# Set release version to 6Server
- name: Set RHSM release version
community.general.rhsm_release:
release: "6Server"
release: "6Server"
# Unset release version
- name: Unset RHSM release release
community.general.rhsm_release:
release: null
release: null
'''
RETURN = '''
@@ -107,7 +107,7 @@ def set_release(module, release):
def main():
module = AnsibleModule(
argument_spec=dict(
release=dict(type='str', required=True),
release=dict(type='str'),
),
supports_check_mode=True
)

View File

@@ -36,22 +36,10 @@ options:
description:
- Sets the project name.
required: true
url:
type: str
description:
- Sets the rundeck instance URL.
required: true
api_version:
type: int
description:
- Sets the API version used by module.
- API version must be at least 14.
default: 14
token:
type: str
api_token:
description:
- Sets the token to authenticate against Rundeck API.
required: true
aliases: ["token"]
project:
type: str
description:
@@ -82,8 +70,9 @@ options:
validate_certs:
version_added: '0.2.0'
extends_documentation_fragment:
- ansible.builtin.url
- community.general.attributes
- ansible.builtin.url
- community.general.attributes
- community.general.rundeck
'''
EXAMPLES = '''
@@ -107,7 +96,7 @@ EXAMPLES = '''
- name: Remove a rundeck system policy
community.general.rundeck_acl_policy:
name: "Project_02"
name: "Project_01"
url: "https://rundeck.example.org"
token: "mytoken"
state: absent
@@ -129,49 +118,25 @@ after:
'''
# import module snippets
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url, url_argument_spec
from ansible.module_utils.common.text.converters import to_text
import json
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.rundeck import (
api_argument_spec,
api_request,
)
class RundeckACLManager:
def __init__(self, module):
self.module = module
def handle_http_code_if_needed(self, infos):
if infos["status"] == 403:
self.module.fail_json(msg="Token not allowed. Please ensure token is allowed or has the correct "
"permissions.", rundeck_response=infos["body"])
elif infos["status"] >= 500:
self.module.fail_json(msg="Fatal Rundeck API error.", rundeck_response=infos["body"])
def request_rundeck_api(self, query, data=None, method="GET"):
resp, info = fetch_url(self.module,
"%s/api/%d/%s" % (self.module.params["url"], self.module.params["api_version"], query),
data=json.dumps(data),
method=method,
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"X-Rundeck-Auth-Token": self.module.params["token"]
})
self.handle_http_code_if_needed(info)
if resp is not None:
resp = resp.read()
if resp != b"":
try:
json_resp = json.loads(to_text(resp, errors='surrogate_or_strict'))
return json_resp, info
except ValueError as e:
self.module.fail_json(msg="Rundeck response was not a valid JSON. Exception was: %s. "
"Object was: %s" % (str(e), resp))
return resp, info
def get_acl(self):
resp, info = self.request_rundeck_api("system/acl/%s.aclpolicy" % self.module.params["name"])
resp, info = api_request(
module=self.module,
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
)
return resp
def create_or_update_acl(self):
@@ -181,9 +146,12 @@ class RundeckACLManager:
if self.module.check_mode:
self.module.exit_json(changed=True, before={}, after=self.module.params["policy"])
dummy, info = self.request_rundeck_api("system/acl/%s.aclpolicy" % self.module.params["name"],
method="POST",
data={"contents": self.module.params["policy"]})
resp, info = api_request(
module=self.module,
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
method="POST",
data={"contents": self.module.params["policy"]},
)
if info["status"] == 201:
self.module.exit_json(changed=True, before={}, after=self.get_acl())
@@ -202,9 +170,12 @@ class RundeckACLManager:
if self.module.check_mode:
self.module.exit_json(changed=True, before=facts, after=facts)
dummy, info = self.request_rundeck_api("system/acl/%s.aclpolicy" % self.module.params["name"],
method="PUT",
data={"contents": self.module.params["policy"]})
resp, info = api_request(
module=self.module,
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
method="PUT",
data={"contents": self.module.params["policy"]},
)
if info["status"] == 200:
self.module.exit_json(changed=True, before=facts, after=self.get_acl())
@@ -216,34 +187,39 @@ class RundeckACLManager:
def remove_acl(self):
facts = self.get_acl()
if facts is None:
self.module.exit_json(changed=False, before={}, after={})
else:
# If not in check mode, remove the project
if not self.module.check_mode:
self.request_rundeck_api("system/acl/%s.aclpolicy" % self.module.params["name"], method="DELETE")
self.module.exit_json(changed=True, before=facts, after={})
api_request(
module=self.module,
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
method="DELETE",
)
self.module.exit_json(changed=True, before=facts, after={})
def main():
# Also allow the user to set values for fetch_url
argument_spec = url_argument_spec()
argument_spec = api_argument_spec()
argument_spec.update(dict(
state=dict(type='str', choices=['present', 'absent'], default='present'),
name=dict(required=True, type='str'),
url=dict(required=True, type='str'),
api_version=dict(type='int', default=14),
token=dict(required=True, type='str', no_log=True),
policy=dict(type='str'),
project=dict(type='str'),
))
argument_spec['api_token']['aliases'] = ['token']
module = AnsibleModule(
argument_spec=argument_spec,
required_if=[
['state', 'present', ['policy']],
],
supports_check_mode=True
supports_check_mode=True,
)
if not bool(re.match("[a-zA-Z0-9,.+_-]+", module.params["name"])):

View File

@@ -38,22 +38,10 @@ options:
description:
- Sets the project name.
required: true
url:
type: str
description:
- Sets the rundeck instance URL.
required: true
api_version:
type: int
description:
- Sets the API version used by module.
- API version must be at least 14.
default: 14
token:
type: str
api_token:
description:
- Sets the token to authenticate against Rundeck API.
required: true
aliases: ["token"]
client_cert:
version_added: '0.2.0'
client_key:
@@ -73,24 +61,27 @@ options:
validate_certs:
version_added: '0.2.0'
extends_documentation_fragment:
- ansible.builtin.url
- community.general.attributes
- ansible.builtin.url
- community.general.attributes
- community.general.rundeck
'''
EXAMPLES = '''
- name: Create a rundeck project
community.general.rundeck_project:
name: "Project_01"
api_version: 18
label: "Project 01"
description: "My Project 01"
url: "https://rundeck.example.org"
token: "mytoken"
api_version: 39
api_token: "mytoken"
state: present
- name: Remove a rundeck project
community.general.rundeck_project:
name: "Project_02"
name: "Project_01"
url: "https://rundeck.example.org"
token: "mytoken"
api_token: "mytoken"
state: absent
'''
@@ -111,60 +102,47 @@ after:
# import module snippets
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.urls import fetch_url, url_argument_spec
import json
from ansible_collections.community.general.plugins.module_utils.rundeck import (
api_argument_spec,
api_request,
)
class RundeckProjectManager(object):
def __init__(self, module):
self.module = module
def handle_http_code_if_needed(self, infos):
if infos["status"] == 403:
self.module.fail_json(msg="Token not allowed. Please ensure token is allowed or has the correct "
"permissions.", rundeck_response=infos["body"])
elif infos["status"] >= 500:
self.module.fail_json(msg="Fatal Rundeck API error.", rundeck_response=infos["body"])
def request_rundeck_api(self, query, data=None, method="GET"):
resp, info = fetch_url(self.module,
"%s/api/%d/%s" % (self.module.params["url"], self.module.params["api_version"], query),
data=json.dumps(data),
method=method,
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"X-Rundeck-Auth-Token": self.module.params["token"]
})
self.handle_http_code_if_needed(info)
if resp is not None:
resp = resp.read()
if resp != "":
try:
json_resp = json.loads(resp)
return json_resp, info
except ValueError as e:
self.module.fail_json(msg="Rundeck response was not a valid JSON. Exception was: %s. "
"Object was: %s" % (to_native(e), resp))
return resp, info
def get_project_facts(self):
resp, info = self.request_rundeck_api("project/%s" % self.module.params["name"])
resp, info = api_request(
module=self.module,
endpoint="project/%s" % self.module.params["name"],
)
return resp
def create_or_update_project(self):
facts = self.get_project_facts()
if facts is None:
# If in check mode don't create project, simulate a fake project creation
if self.module.check_mode:
self.module.exit_json(changed=True, before={}, after={"name": self.module.params["name"]})
self.module.exit_json(
changed=True,
before={},
after={
"name": self.module.params["name"]
},
)
resp, info = self.request_rundeck_api("projects", method="POST", data={
"name": self.module.params["name"],
"config": {}
})
resp, info = api_request(
module=self.module,
endpoint="projects",
method="POST",
data={
"name": self.module.params["name"],
"config": {},
}
)
if info["status"] == 201:
self.module.exit_json(changed=True, before={}, after=self.get_project_facts())
@@ -181,21 +159,25 @@ class RundeckProjectManager(object):
else:
# If not in check mode, remove the project
if not self.module.check_mode:
self.request_rundeck_api("project/%s" % self.module.params["name"], method="DELETE")
api_request(
module=self.module,
endpoint="project/%s" % self.module.params["name"],
method="DELETE",
)
self.module.exit_json(changed=True, before=facts, after={})
def main():
# Also allow the user to set values for fetch_url
argument_spec = url_argument_spec()
argument_spec = api_argument_spec()
argument_spec.update(dict(
state=dict(type='str', choices=['present', 'absent'], default='present'),
name=dict(required=True, type='str'),
url=dict(required=True, type='str'),
api_version=dict(type='int', default=14),
token=dict(required=True, type='str', no_log=True),
))
argument_spec['api_token']['aliases'] = ['token']
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True

View File

@@ -98,7 +98,7 @@ _state_map = dict(
class SnapAlias(StateModuleHelper):
_RE_ALIAS_LIST = re.compile(r"^(?P<snap>[\w-]+)\s+(?P<alias>[\w-]+)\s+.*$")
_RE_ALIAS_LIST = re.compile(r"^(?P<snap>\S+)\s+(?P<alias>[\w-]+)\s+.*$")
module = dict(
argument_spec={
@@ -142,7 +142,10 @@ class SnapAlias(StateModuleHelper):
return results
with self.runner("state name", check_rc=True, output_process=process) as ctx:
return ctx.run(state="info")
aliases = ctx.run(state="info")
if self.verbosity >= 4:
self.vars.get_aliases_run_info = ctx.run_info
return aliases
def _get_aliases_for(self, name):
return self._get_aliases().get(name, [])

View File

@@ -218,7 +218,7 @@ EXAMPLES = """
community.general.terraform:
project_path: '{{ project_dir }}'
state: present
camplex_vars: true
complex_vars: true
variables:
vm_name: "{{ inventory_hostname }}"
vm_vcpus: 2
@@ -312,11 +312,11 @@ def preflight_validation(bin_path, project_path, version, variables_args=None, p
def _state_args(state_file):
if state_file and os.path.exists(state_file):
return ['-state', state_file]
if state_file and not os.path.exists(state_file):
module.fail_json(msg='Could not find state_file "{0}", check the path and try again.'.format(state_file))
return []
if not state_file:
return []
if not os.path.exists(state_file):
module.warn('Could not find state_file "{0}", the process will not destroy any resources, please check your state file path.'.format(state_file))
return ['-state', state_file]
def init_plugins(bin_path, project_path, backend_config, backend_config_files, init_reconfigure, provider_upgrade, plugin_paths, workspace):

View File

@@ -97,19 +97,9 @@ EXAMPLES = '''
RETURN = '''#'''
HAVE_UNIVENTION = False
HAVE_IPADDRESS = False
try:
from univention.admin.handlers.dns import (
forward_zone,
reverse_zone,
)
HAVE_UNIVENTION = True
except ImportError:
pass
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.community.general.plugins.module_utils import deps
from ansible_collections.community.general.plugins.module_utils.univention_umc import (
umc_module_for_add,
umc_module_for_edit,
@@ -118,27 +108,26 @@ from ansible_collections.community.general.plugins.module_utils.univention_umc i
config,
uldap,
)
try:
with deps.declare("univention", msg="This module requires univention python bindings"):
from univention.admin.handlers.dns import (
forward_zone,
reverse_zone,
)
with deps.declare("ipaddress"):
import ipaddress
HAVE_IPADDRESS = True
except ImportError:
pass
def main():
module = AnsibleModule(
argument_spec=dict(
type=dict(required=True,
type='str'),
zone=dict(required=True,
type='str'),
name=dict(required=True,
type='str'),
data=dict(default={},
type='dict'),
state=dict(default='present',
choices=['present', 'absent'],
type='str')
type=dict(required=True, type='str'),
zone=dict(required=True, type='str'),
name=dict(required=True, type='str'),
data=dict(default={}, type='dict'),
state=dict(default='present', choices=['present', 'absent'], type='str')
),
supports_check_mode=True,
required_if=([
@@ -146,8 +135,7 @@ def main():
])
)
if not HAVE_UNIVENTION:
module.fail_json(msg="This module requires univention python bindings")
deps.validate(module, "univention")
type = module.params['type']
zone = module.params['zone']
@@ -159,8 +147,8 @@ def main():
workname = name
if type == 'ptr_record':
if not HAVE_IPADDRESS:
module.fail_json(msg=missing_required_lib('ipaddress'))
deps.validate(module, "ipaddress")
try:
if 'arpa' not in zone:
raise Exception("Zone must be reversed zone for ptr_record. (e.g. 1.1.192.in-addr.arpa)")
@@ -196,7 +184,7 @@ def main():
'(zoneName={0})'.format(zone),
scope='domain',
)
if len(so) == 0:
if not so == 0:
raise Exception("Did not find zone '{0}' in Univention".format(zone))
obj = umc_module_for_add('dns/{0}'.format(type), container, superordinate=so[0])
else:

View File

@@ -300,31 +300,35 @@ def main():
prj_set = False
break
if not prj_set and not module.check_mode:
cmd = "project -s"
rc, stdout, stderr = exec_quota(module, xfs_quota_bin, cmd, mountpoint)
if rc != 0:
result["cmd"] = cmd
result["rc"] = rc
result["stdout"] = stdout
result["stderr"] = stderr
module.fail_json(
msg="Could not get quota realtime block report.", **result
)
if state == "present" and not prj_set:
if not module.check_mode:
cmd = "project -s %s" % name
rc, stdout, stderr = exec_quota(module, xfs_quota_bin, cmd, mountpoint)
if rc != 0:
result["cmd"] = cmd
result["rc"] = rc
result["stdout"] = stdout
result["stderr"] = stderr
module.fail_json(
msg="Could not get quota realtime block report.", **result
)
result["changed"] = True
elif not prj_set and module.check_mode:
result["changed"] = True
elif state == "absent" and prj_set and name != quota_default:
if not module.check_mode:
cmd = "project -C %s" % name
rc, stdout, stderr = exec_quota(module, xfs_quota_bin, cmd, mountpoint)
if rc != 0:
result["cmd"] = cmd
result["rc"] = rc
result["stdout"] = stdout
result["stderr"] = stderr
module.fail_json(
msg="Failed to clear managed tree from project quota control.", **result
)
# Set limits
if state == "absent":
bhard = 0
bsoft = 0
ihard = 0
isoft = 0
rtbhard = 0
rtbsoft = 0
result["changed"] = True
current_bsoft, current_bhard = quota_report(
module, xfs_quota_bin, mountpoint, name, quota_type, "b"
@@ -336,6 +340,23 @@ def main():
module, xfs_quota_bin, mountpoint, name, quota_type, "rtb"
)
# Set limits
if state == "absent":
bhard = 0
bsoft = 0
ihard = 0
isoft = 0
rtbhard = 0
rtbsoft = 0
# Ensure that a non-existing quota does not trigger a change
current_bsoft = current_bsoft if current_bsoft is not None else 0
current_bhard = current_bhard if current_bhard is not None else 0
current_isoft = current_isoft if current_isoft is not None else 0
current_ihard = current_ihard if current_ihard is not None else 0
current_rtbsoft = current_rtbsoft if current_rtbsoft is not None else 0
current_rtbhard = current_rtbhard if current_rtbhard is not None else 0
result["xfs_quota"] = dict(
bsoft=current_bsoft,
bhard=current_bhard,
@@ -370,25 +391,23 @@ def main():
limit.append("rtbhard=%s" % rtbhard)
result["rtbhard"] = int(rtbhard)
if len(limit) > 0 and not module.check_mode:
if name == quota_default:
cmd = "limit %s -d %s" % (type_arg, " ".join(limit))
else:
cmd = "limit %s %s %s" % (type_arg, " ".join(limit), name)
if len(limit) > 0:
if not module.check_mode:
if name == quota_default:
cmd = "limit %s -d %s" % (type_arg, " ".join(limit))
else:
cmd = "limit %s %s %s" % (type_arg, " ".join(limit), name)
rc, stdout, stderr = exec_quota(module, xfs_quota_bin, cmd, mountpoint)
if rc != 0:
result["cmd"] = cmd
result["rc"] = rc
result["stdout"] = stdout
result["stderr"] = stderr
module.fail_json(msg="Could not set limits.", **result)
rc, stdout, stderr = exec_quota(module, xfs_quota_bin, cmd, mountpoint)
if rc != 0:
result["cmd"] = cmd
result["rc"] = rc
result["stdout"] = stdout
result["stderr"] = stderr
module.fail_json(msg="Could not set limits.", **result)
result["changed"] = True
elif len(limit) > 0 and module.check_mode:
result["changed"] = True
module.exit_json(**result)

View File

@@ -324,10 +324,11 @@ def parse_zypper_xml(m, cmd, fail_not_found=True, packages=None):
m.fail_json(msg=errmsg, rc=rc, stdout=stdout, stderr=stderr, cmd=cmd)
else:
return {}, rc, stdout, stderr
elif rc in [0, 106, 103]:
elif rc in [0, 102, 103, 106]:
# zypper exit codes
# 0: success
# 106: signature verification failed
# 102: ZYPPER_EXIT_INF_REBOOT_NEEDED - Returned after a successful installation of a patch which requires reboot of computer.
# 103: zypper was upgraded, run same command again
if packages is None:
firstrun = True
@@ -587,12 +588,12 @@ def main():
elif state in ['installed', 'present', 'latest']:
packages_changed, retvals = package_present(module, name, state == 'latest')
retvals['changed'] = retvals['rc'] == 0 and bool(packages_changed)
retvals['changed'] = retvals['rc'] in [0, 102] and bool(packages_changed)
if module._diff:
set_diff(module, retvals, packages_changed)
if retvals['rc'] != 0:
if retvals['rc'] not in [0, 102]:
module.fail_json(msg="Zypper run failed.", **retvals)
if not retvals['changed']:

View File

@@ -3,8 +3,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
integration_tests_dependencies:
collections:
- ansible.posix
- community.crypto
unit_tests_dependencies:
- community.internal_test_tools
- community.docker

View File

@@ -29,7 +29,7 @@
shell: apache2ctl -M | sort
register: modules_before
- name: include only on supported systems
include: actualtest.yml
include_tasks: actualtest.yml
always:
- name: get list of enabled modules
shell: apache2ctl -M | sort
@@ -47,6 +47,6 @@
# centos/RHEL does not have a2enmod/a2dismod
- name: include misleading warning test
include: 635-apache2-misleading-warning.yml
include_tasks: 635-apache2-misleading-warning.yml
when: ansible_os_family in ['Debian']
# Suse has mpm_event module compiled within the base apache2

View File

@@ -0,0 +1,12 @@
# Copyright (c) Ansible Projec
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
azp/posix/vm
destructive
needs/privileged
skip/aix
skip/freebsd
skip/osx
skip/macos

View File

@@ -0,0 +1,20 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
btrfs_subvolume_single_configs:
- file: "/tmp/disks0.img"
loop: "/dev/loop95"
btrfs_subvolume_multiple_configs:
- file: "/tmp/diskm0.img"
loop: "/dev/loop97"
- file: "/tmp/diskm1.img"
loop: "/dev/loop98"
- file: "/tmp/diskm2.img"
loop: "/dev/loop99"
btrfs_subvolume_configs: "{{ btrfs_subvolume_single_configs + btrfs_subvolume_multiple_configs }}"
btrfs_subvolume_single_devices: "{{ btrfs_subvolume_single_configs | map(attribute='loop') }}"
btrfs_subvolume_single_label: "single"
btrfs_subvolume_multiple_devices: "{{ btrfs_subvolume_multiple_configs | map(attribute='loop') }}"
btrfs_subvolume_multiple_label: "multiple"

View File

@@ -0,0 +1,29 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Install required packages
ansible.builtin.package:
name:
- btrfs-progs # btrfs userspace
- util-linux # losetup
ignore_errors: True
register: btrfs_installed
- name: Execute integration tests tests
block:
- ansible.builtin.include_tasks: 'setup.yml'
- name: "Execute test scenario for single device filesystem"
ansible.builtin.include_tasks: 'run_filesystem_tests.yml'
vars:
btrfs_subvolume_target_device: "{{ btrfs_subvolume_single_devices | first }}"
btrfs_subvolume_target_label: "{{ btrfs_subvolume_single_label }}"
- name: "Execute test scenario for multiple device configuration"
ansible.builtin.include_tasks: 'run_filesystem_tests.yml'
vars:
btrfs_subvolume_target_device: "{{ btrfs_subvolume_multiple_devices | first }}"
btrfs_subvolume_target_label: "{{ btrfs_subvolume_multiple_label }}"
when: btrfs_installed is success

View File

@@ -0,0 +1,15 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- ansible.builtin.include_tasks: 'test_subvolume_simple.yml'
- ansible.builtin.include_tasks: 'test_subvolume_nested.yml'
- ansible.builtin.include_tasks: 'test_subvolume_recursive.yml'
- ansible.builtin.include_tasks: 'test_subvolume_default.yml'
- ansible.builtin.include_tasks: 'test_snapshot_skip.yml'
- ansible.builtin.include_tasks: 'test_snapshot_clobber.yml'
- ansible.builtin.include_tasks: 'test_snapshot_error.yml'
- ansible.builtin.include_tasks: 'test_subvolume_whitespace.yml'

View File

@@ -0,0 +1,32 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- ansible.builtin.include_tasks: 'test_filesystem_matching.yml'
- name: "Execute all test scenario for unmounted filesystem"
ansible.builtin.include_tasks: 'run_common_tests.yml'
- name: "Execute test scenarios where non-root subvolume is mounted"
block:
- name: Create subvolume '/nonroot'
community.general.btrfs_subvolume:
automount: Yes
name: "/nonroot"
filesystem_label: "{{ btrfs_subvolume_target_label }}"
state: "present"
register: nonroot
- name: "Mount subvolume '/nonroot'"
ansible.posix.mount:
src: "{{ nonroot.filesystem.devices | first }}"
path: /mnt
opts: "subvolid={{ nonroot.target_subvolume_id }}"
fstype: btrfs
state: mounted
- name: "Run tests for explicit, mounted single device configuration"
ansible.builtin.include_tasks: 'run_common_tests.yml'
- name: "Unmount subvolume /nonroot"
ansible.posix.mount:
path: /mnt
state: absent

View File

@@ -0,0 +1,37 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: "Create file {{ item.file }} to back loop device {{ item.loop }}"
ansible.builtin.command:
cmd: "dd if=/dev/zero of={{ item.file }} bs=1M count=200" ## minimum count 109
creates: "{{ item.file }}"
with_items: "{{ btrfs_subvolume_configs }}"
- name: "Setup loop device {{ item.loop }}"
ansible.builtin.command:
cmd: "losetup {{ item.loop }} {{ item.file }}"
creates: "{{ item.loop }}"
with_items: "{{ btrfs_subvolume_configs }}"
- name: Create single device btrfs filesystem
ansible.builtin.command:
cmd: "mkfs.btrfs --label {{ btrfs_subvolume_single_label }} -f {{ btrfs_subvolume_single_devices | first }}"
changed_when: True
- name: Create multiple device btrfs filesystem
ansible.builtin.command:
cmd: "mkfs.btrfs --label {{ btrfs_subvolume_multiple_label }} -f -d raid0 {{ btrfs_subvolume_multiple_devices | join(' ') }}"
changed_when: True
# Typically created by udev, but apparently missing on Alpine
- name: Create btrfs control device node
ansible.builtin.command:
cmd: "mknod /dev/btrfs-control c 10 234"
creates: "/dev/btrfs-control"
- name: Force rescan to ensure all device are detected
ansible.builtin.command:
cmd: "btrfs device scan"
changed_when: True

View File

@@ -0,0 +1,80 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: "Match targeted filesystem by label"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by label
community.general.btrfs_subvolume:
automount: Yes
name: "/match_label"
filesystem_label: "{{ btrfs_subvolume_target_label }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match targeted filesystem by uuid"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by uuid
community.general.btrfs_subvolume:
automount: Yes
name: "/match_uuid"
filesystem_uuid: "{{ result.filesystem.uuid }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match targeted filesystem by devices"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by device
community.general.btrfs_subvolume:
automount: Yes
name: "/match_device"
filesystem_device: "{{ result.filesystem.devices | first }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match only mounted filesystem"
block:
- name: "Mount filesystem '{{ btrfs_subvolume_target_label }}'"
ansible.posix.mount:
src: "{{ result.filesystem.devices | first }}"
path: /mnt
opts: "subvolid={{ 5 }}"
fstype: btrfs
state: mounted
- name: Print current status
community.general.btrfs_info:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem when only mount
community.general.btrfs_subvolume:
automount: Yes
name: "/match_only_mounted"
state: "present"
register: result
- name: "Unmount filesystem '{{ btrfs_subvolume_target_label }}'"
ansible.posix.mount:
path: /mnt
state: absent
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
when: False # TODO don't attempt this if the host already has a pre-existing btrfs filesystem

View File

@@ -0,0 +1,41 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot, overwriting if one already exists at path
block:
- name: Create a snapshot named 'snapshot_clobber'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
snapshot_source: "/"
snapshot_conflict: "clobber"
state: "present"
register: result
- name: Snapshot 'snapshot_clobber' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_clobber' (no idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
snapshot_source: "/"
snapshot_conflict: "clobber"
state: "present"
register: result
- name: Snapshot 'snapshot_clobber' created (no idempotency)
ansible.builtin.assert:
that:
- result is changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
state: "absent"

View File

@@ -0,0 +1,42 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot, erroring if one already exists at path
block:
- name: Create a snapshot named 'snapshot_error'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
snapshot_source: "/"
snapshot_conflict: "error"
state: "present"
register: result
- name: Snapshot 'snapshot_error' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_error' (no idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
snapshot_source: "/"
snapshot_conflict: "error"
state: "present"
register: result
ignore_errors: true
- name: Snapshot 'snapshot_error' created (no idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
state: "absent"

View File

@@ -0,0 +1,41 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot if one does not already exist at path
block:
- name: Create a snapshot named 'snapshot_skip'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
snapshot_source: "/"
snapshot_conflict: "skip"
state: "present"
register: result
- name: Snapshot 'snapshot_skip' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_skip' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
snapshot_source: "/"
snapshot_conflict: "skip"
state: "present"
register: result
- name: Snapshot 'snapshot_skip' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
state: "absent"

View File

@@ -0,0 +1,99 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Change the default subvolume
block:
- name: Update filesystem default subvolume to '@'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Update filesystem default subvolume to '@' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Revert the default subvolume
block:
- name: Revert filesystem default subvolume to '/'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/"
state: "present"
register: result
- name: Subvolume '/' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Revert filesystem default subvolume to '/' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/"
state: "present"
register: result
- name: Subvolume '/' set to default (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Change the default subvolume again
block:
- name: Update filesystem default subvolume to '@'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Revert custom default subvolume to fs_tree root when deleted
block:
- name: Delete custom default subvolume '@'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "absent"
register: result
- name: Subvolume '@' deleted
ansible.builtin.assert:
that:
- result is changed
- name: Delete custom default subvolume '@' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "absent"
register: result
- name: Subvolume '@' deleted (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@@ -0,0 +1,61 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create parent subvolume 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container"
state: "present"
- name: Create a nested subvolume
block:
- name: Create a subvolume named 'nested' inside 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "present"
register: result
- name: Subvolume 'container/nested' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'nested' inside 'container' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "present"
register: result
- name: Subvolume 'container/nested' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a nested subvolume
block:
- name: Remove a subvolume named 'nested' inside 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "absent"
register: result
- name: Subvolume 'container/nested' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'nested' inside 'container' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "absent"
register: result
- name: Subvolume 'container/nested' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@@ -0,0 +1,86 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Recursively create subvolumes
block:
- name: Create a subvolume named '/recursive/son/grandson'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/son/grandson"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named '/recursive/son/grandson' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/son/grandson"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Create a subvolume named '/recursive/daughter/granddaughter'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/daughter/granddaughter"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named '/recursive/daughter/granddaughter' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/daughter/granddaughter"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Recursively remove subvolumes
block:
- name: Remove subvolume '/recursive' and all descendents
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive"
recursive: Yes
state: "absent"
register: result
- name: Subvolume '/recursive' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove subvolume '/recursive' and all descendents (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive"
recursive: Yes
state: "absent"
register: result
- name: Subvolume '/recursive' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@@ -0,0 +1,54 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a simple subvolume
block:
- name: Create a subvolume named 'simple'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "present"
register: result
- name: Subvolume named 'simple' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'simple' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "present"
register: result
- name: Subvolume named 'simple' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a simple subvolume
block:
- name: Remove a subvolume named 'simple'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "absent"
register: result
- name: Subvolume named 'simple' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'simple' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "absent"
register: result
- name: Subvolume named 'simple' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@@ -0,0 +1,62 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a subvolume named 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container"
state: "present"
- name: Create a subvolume with whitespace in the name
block:
- name: Create a subvolume named 'container/my data'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "present"
register: result
- name: Subvolume named 'container/my data' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'container/my data' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "present"
register: result
- name: Subvolume named 'container/my data' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a subvolume with whitespace in the name
block:
- name: Remove a subvolume named 'container/my data'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "absent"
register: result
- name: Subvolume named 'container/my data' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'container/my data' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "absent"
register: result
- name: Subvolume named 'container/my data' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
"$1" 100 &
echo "$!" > "$2"
unsupported

View File

@@ -0,0 +1,12 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Wait for iLO Reboot Completion
community.general.ilo_redfish_command:
category: Systems
command: WaitforiLORebootCompletion
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"

View File

@@ -8,5 +8,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- include: tests.yml
- include_tasks: tests.yml
when: ansible_distribution == 'Ubuntu' and ansible_distribution_release == 'trusty'

View File

@@ -453,7 +453,7 @@
- content17 == expected17
- name: "test-values 18 - Ensure 'beverage=coke' is 'abesent' in section '[drinks]'"
- name: "test-values 18 - Ensure 'beverage=coke' is 'absent' in section '[drinks]'"
ini_file:
path: "{{ output_file }}"
section: drinks
@@ -483,7 +483,7 @@
- content18 == expected18
- name: "test-values 19 - Ensure non-existant 'beverage=coke' is 'abesent' in section '[drinks]'"
- name: "test-values 19 - Ensure non-existent 'beverage=coke' is 'absent' in section '[drinks]'"
ini_file:
path: "{{ output_file }}"
section: drinks
@@ -579,8 +579,8 @@
[section1]
var1 = aaa
# comment in section
var2 = foo
# var2 = bar
# var2 = some value
# comment after section
[section2]
var3 = ccc
@@ -613,8 +613,8 @@
[section1]
var1 = aaa
# comment in section
var2 = foo
# var2 = bar
# var2 = some value
# comment after section
[section2]
var3 = ccc
@@ -629,14 +629,13 @@
- content22 == expected22
- name: "test-values 23 - Ensure 'var2=[foo, foobar]' is 'present' in section '[section1]'"
- name: "test-values 23 - Ensure 'var2=foo' is 'present' in section '[section1]', replacing commented option 'var2=some value'"
ini_file:
path: "{{ output_file }}"
section: section1
option: var2
values:
- foo
- foobar
state: present
register: result23
@@ -647,7 +646,6 @@
- name: test-values 23 - set expected content and get current ini file content
set_fact:
content23: "{{ output_content.content | b64decode }}"
expected23: |
# Some comment to test
@@ -659,28 +657,28 @@
var1 = aaa
# comment in section
var2 = foo
var2 = foobar
# var2 = bar
# comment after section
[section2]
var3 = ccc
# comment after section
- name: test-values 23 - assert 'changed' and msg 'option added' and content is as expected
content23: "{{ output_content.content | b64decode }}"
- name: test-values 23 - assert 'changed' and msg 'option changed' and content is as expected
assert:
that:
- result23 is changed
- result23.msg == 'option added'
- result23.msg == 'option changed'
- content23 == expected23
- name: "test-values 24 - Ensure 'var2=[foo, foobar, bar]' is 'present' in section '[section1]' replacing commented option 'var2=bar'"
- name: "test-values 24 - Ensure 'var2=[foo, foobar]' is 'present' in section '[section1]'"
ini_file:
path: "{{ output_file }}"
section: section1
option: var2
values:
values:
- foo
- bar
- foobar
state: present
register: result24
@@ -692,7 +690,6 @@
- name: test-values 24 - set expected content and get current ini file content
set_fact:
content24: "{{ output_content.content | b64decode }}"
expected24: |
# Some comment to test
@@ -705,16 +702,18 @@
# comment in section
var2 = foo
var2 = foobar
var2 = bar
# comment after section
[section2]
var3 = ccc
# comment after section
- name: test-values 24 - assert 'added' and msg 'option changed' and content is as expected
content24: "{{ output_content.content | b64decode }}"
- name: test-values 24 - assert 'changed' and msg 'option added' and content is as expected
assert:
that:
- result24 is changed
- result24.msg == 'option changed'
- result24.msg == 'option added'
- content24 == expected24

View File

@@ -10,7 +10,6 @@
- name: install pycdlib
pip:
name: pycdlib
# state: latest
extra_args: "-c {{ remote_constraints }}"
register: install_pycdlib
- debug: var=install_pycdlib

View File

@@ -17,16 +17,6 @@
register: customized_result
failed_when: customized_result.msg.find('does not exist') == -1
# Test: Get MODULE FAILURE when no add files data and no delete files data
- name: "Testcase:: no add files data and no delete files data"
community.general.iso_customize:
src_iso: "{{ test_dir }}/test1.iso"
dest_iso: "{{ test_dir }}/iso_customize_nodata.iso"
delete_files:
add_files:
register: customized_result
failed_when: customized_result.msg.find("MODULE FAILURE") == -1
# Test: nothing is changed when no options "add files" and "delete files"
- block:
- name: "Testcase: no options 'add files' and 'delete files'"

View File

@@ -10,3 +10,4 @@ skip/osx # FIXME
skip/rhel9.0 # FIXME
skip/rhel9.1 # FIXME
skip/freebsd12.4 # FIXME
skip/freebsd13.2 # FIXME

View File

@@ -355,7 +355,7 @@
path: "{{ output_file }}"
values:
- group: test1
key:
key: ''
value: test2
kwriteconfig_path: "{{ kwriteconf_fake }}"
register: result_ekey

View File

@@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
unsupported

View File

@@ -0,0 +1,27 @@
// Copyright (c) Ansible Project
// GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
// SPDX-License-Identifier: GPL-3.0-or-later
To be able to run these integration tests a keycloak server must be
reachable under a specific url with a specific admin user and password.
The exact values expected for these parameters can be found in
'vars/main.yml' file. A simple way to do this is to use the official
keycloak docker images like this:
----
docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=<url-path> -e KEYCLOAK_ADMIN=<admin_user> -e KEYCLOAK_ADMIN_PASSWORD=<admin_password> quay.io/keycloak/keycloak:20.0.2 start-dev
----
Example with concrete values inserted:
----
docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:20.0.2 start-dev
----
This test suite can run against a fresh unconfigured server instance
(no preconfiguration required) and cleans up after itself (undoes all
its config changes) as long as it runs through completly. While its active
it changes the server configuration in the following ways:
* creating, modifying and deleting some keycloak groups

View File

@@ -0,0 +1,234 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Remove keycloak client to avoid failures from previous failed runs
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: absent
- name: Create keycloak client with authorization services enabled
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
enabled: true
public_client: false
service_accounts_enabled: true
authorization_services_enabled: true
- name: Create an authorization scope (check mode)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
display_name: "File delete"
icon_uri: "http://localhost/icon.png"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
check_mode: true
diff: true
register: result
- name: Assert that authorization scope was not created in check mode
assert:
that:
- result is changed
- result.end_state == {}
- result.msg == 'Authorization scope would be created'
- result.diff.before == {}
- result.diff.after.name == 'file:delete'
- result.diff.after.displayName == 'File delete'
- result.diff.after.iconUri == 'http://localhost/icon.png'
- name: Create authorization scope
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
display_name: "File delete"
icon_uri: "http://localhost/icon.png"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that authorization scope was created
assert:
that:
- result is changed
- result.end_state != {}
- result.end_state.name == "file:delete"
- result.end_state.iconUri == "http://localhost/icon.png"
- result.end_state.displayName == "File delete"
- name: Create authorization scope (test for idempotency)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
display_name: "File delete"
icon_uri: "http://localhost/icon.png"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that nothing has changed
assert:
that:
- result is not changed
- result.end_state != {}
- result.end_state.name == "file:delete"
- result.end_state.iconUri == "http://localhost/icon.png"
- result.end_state.displayName == "File delete"
- name: Authorization scope update (check mode)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
diff: true
check_mode: true
register: result
- name: Assert that authorization scope was not updated in check mode
assert:
that:
- result is changed
- result.msg == 'Authorization scope would be updated'
- result.diff.before.displayName == 'File delete'
- result.diff.before.iconUri == 'http://localhost/icon.png'
- result.diff.after.displayName == ''
- result.diff.after.iconUri == ''
- name: Authorization scope update (remove optional parameters)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that optional parameters have been removed
assert:
that:
- result is changed
- result.end_state != {}
- result.end_state.name == "file:delete"
- result.end_state.iconUri == ""
- result.end_state.displayName == ""
- name: Authorization scope update (test for idempotency)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: present
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that nothing has changed
assert:
that:
- result is not changed
- result.end_state != {}
- result.end_state.name == "file:delete"
- result.end_state.iconUri == ""
- result.end_state.displayName == ""
- name: Authorization scope remove (check mode)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: absent
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
diff: true
check_mode: true
register: result
- name: Assert that authorization scope has not been removed in check mode
assert:
that:
- result is changed
- result.msg == 'Authorization scope would be removed'
- result.diff.before.name == 'file:delete'
- result.diff.after == {}
- name: Authorization scope remove
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: absent
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that authorization scope has been removed
assert:
that:
- result is changed
- result.end_state == {}
- name: Authorization scope remove (test for idempotency)
community.general.keycloak_authz_authorization_scope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
state: absent
name: "file:delete"
client_id: "{{ client_id }}"
realm: "{{ realm }}"
register: result
- name: Assert that nothing has changed
assert:
that:
- result is not changed
- result.end_state == {}
- name: Remove keycloak client
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: absent

View File

@@ -0,0 +1,11 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
url: http://localhost:8080/auth
admin_realm: master
admin_user: admin
admin_password: password
realm: master
client_id: authz

View File

@@ -0,0 +1,16 @@
<!--
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
-->
The integration test can be performed as follows:
```
# 1. Start docker-compose:
docker-compose -f tests/integration/targets/keycloak_clientscope_type/docker-compose.yml down
docker-compose -f tests/integration/targets/keycloak_clientscope_type/docker-compose.yml up -d
# 2. Run the integration tests:
ansible-test integration keycloak_clientscope_type --allow-unsupported -v
```

View File

@@ -0,0 +1,16 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
version: '3.4'
services:
keycloak:
image: quay.io/keycloak/keycloak:21.0.2
ports:
- 8080:8080
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: password
command: start-dev

View File

@@ -0,0 +1,164 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Fixtures
- name: Create keycloak realm
community.general.keycloak_realm:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
id: ""
state: present
enabled: true
- name: Create keycloak client
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
state: present
enabled: true
- name: Create a scope1 client scope
community.general.keycloak_clientscope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
name: scope1
description: "test 1"
protocol: openid-connect
- name: Create a scope2 client scope
community.general.keycloak_clientscope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
name: scope2
description: "test 2"
protocol: openid-connect
### Tests
### Realm
- name: adjust client-scope types in realm
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
default_clientscopes: ['scope1', 'scope2']
optional_clientscopes: []
register: result
- name: Assert that client scope types are set
assert:
that:
- result is changed
- result.end_state != {}
- '"scope1" in result.end_state.default_clientscopes'
- '"scope2" in result.end_state.default_clientscopes'
- result.end_state.default_clientscopes|length == 2
- result.end_state.optional_clientscopes|length == 0
- name: adjust client-scope types in realm again
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
default_clientscopes: ['scope1', 'scope2']
optional_clientscopes: []
register: result
failed_when: result is changed
- name: adjust client-scope types in realm move scope 2 to optional
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
default_clientscopes: ['scope1']
optional_clientscopes: ['scope2']
register: result
- name: Assert that client scope types are set
assert:
that:
- result is changed
- result.end_state != {}
- '"scope1" in result.end_state.default_clientscopes'
- '"scope2" in result.end_state.optional_clientscopes'
- result.end_state.default_clientscopes|length == 1
- result.end_state.optional_clientscopes|length == 1
### Client
- name: adjust client-scope types in client
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
default_clientscopes: ['scope1', 'scope2']
optional_clientscopes: []
register: result
- name: Assert that client scope types are set
assert:
that:
- result is changed
- result.end_state != {}
- '"scope1" in result.end_state.default_clientscopes'
- '"scope2" in result.end_state.default_clientscopes'
- result.end_state.default_clientscopes|length == 2
- result.end_state.optional_clientscopes|length == 0
- name: adjust client-scope types in client again
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
default_clientscopes: ['scope1', 'scope2']
optional_clientscopes: []
register: result
failed_when: result is changed
- name: adjust client-scope types in client move scope 2 to optional
community.general.keycloak_clientscope_type:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_id }}"
default_clientscopes: ['scope1']
optional_clientscopes: ['scope2']
register: result
- name: Assert that client scope types are set
assert:
that:
- result is changed
- result.end_state != {}
- '"scope1" in result.end_state.default_clientscopes'
- '"scope2" in result.end_state.optional_clientscopes'
- result.end_state.default_clientscopes|length == 1
- result.end_state.optional_clientscopes|length == 1

View File

@@ -0,0 +1,11 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
url: http://localhost:8080
admin_realm: master
admin_user: admin
admin_password: password
realm: clientscope-type-realm
client_id: clientscope-type-client

View File

@@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_docker

View File

@@ -3,6 +3,29 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Start container
community.docker.docker_container:
name: mykeycloak
image: "quay.io/keycloak/keycloak:20.0.2"
command: start-dev
env:
KC_HTTP_RELATIVE_PATH: /auth
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: password
ports:
- "8080:8080"
detach: true
auto_remove: true
memory: 2200M
- name: Check default ports
ansible.builtin.wait_for:
host: "localhost"
port: "8080"
state: started # Port should be open
delay: 30 # Wait before first check
timeout: 50 # Stop checking after timeout (sec)
- name: Create a keycloak group
community.general.keycloak_group:
auth_keycloak_url: "{{ url }}"
@@ -13,6 +36,9 @@
name: test-group
state: present
register: result
retries: 3
delay: 20
until: result is not failed
- name: Assert group was created
assert:

View File

@@ -8,5 +8,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- include: 'locale_gen.yml'
- include_tasks: 'locale_gen.yml'
when: ansible_distribution in ('Ubuntu', 'Debian')

View File

@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- include: ubuntu.yml
- include_tasks: ubuntu.yml
when:
- ansible_distribution == 'Ubuntu'
- ansible_distribution_release not in ['focal', 'jammy']

View File

@@ -7,3 +7,4 @@ skip/macos
skip/freebsd
skip/rhel
disabled
destructive

View File

@@ -4,6 +4,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
mssql_host: localhost
mssql_port: 14330
mssql_port: 1433
mssql_login_user: sa
mssql_login_password: "yourStrong(!)Password"
mssql_login_password: "Abcd!234"

View File

@@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_docker

Some files were not shown because too many files have changed in this diff Show More