Compare commits

..

56 Commits
9.4.0 ... 9.5.0

Author SHA1 Message Date
Felix Fontein
8c45cba53c Release 9.5.0. 2024-10-07 23:14:49 +02:00
patchback[bot]
9fe31235d8 [PR #8973/464812a2 backport][stable-9] keycloak_client add option to support client-x509 authentication (#9005)
keycloak_client add option to support client-x509 authentication (#8973)

* keycloak_client: add client-x509 option to client_authenticator_type

Signed-off-by: boolman <boolman@gmail.com>

* keycloak_client: add attributes for client-x509

Signed-off-by: boolman <boolman@gmail.com>

* keycloak_client update description

Signed-off-by: boolman <boolman@gmail.com>

* keycloak_client add fragment

Signed-off-by: boolman <boolman@gmail.com>

* remove trailing whitespace

Signed-off-by: boolman <boolman@gmail.com>

* keycloak_client add example with x509 authentication

Signed-off-by: boolman <boolman@gmail.com>

* Update plugins/modules/keycloak_client.py

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

* Update changelogs/fragments/8973-keycloak_client-add-x509-auth.yml

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

* keycloak_client added type on new suboptions

Signed-off-by: boolman <boolman@gmail.com>

---------

Signed-off-by: boolman <boolman@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 464812a2c2)

Co-authored-by: Boolman <boolman@gmail.com>
2024-10-07 23:10:46 +02:00
patchback[bot]
fc7609628e [PR #8532/24b74cc4 backport][stable-9] opennebula inventory: add VM ID and VM host to data (#9003)
opennebula inventory: add VM ID and VM host to data (#8532)

* Add VM id and VM host to opennebula inventory data

##### SUMMARY
<!--- Describe the change below, including rationale and design decisions --> To enable greater use of the inventory, add the ID of the VM, and the hostname of the host the VM is running on to the inventory output

<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->

<!--- Please do not forget to include a changelog fragment:
      https://docs.ansible.com/ansible/devel/community/collection_development_process.html#creating-changelog-fragments
      No need to include one for docs-only or test-only PR, and for new plugin/module PRs.
      Read about more details in CONTRIBUTING.md.
      -->

##### ISSUE TYPE
<!--- Pick one or more below and delete the rest.
      'Test Pull Request' is for PRs that add/extend tests without code changes. -->
- Feature Pull Request

##### COMPONENT NAME
<!--- Write the SHORT NAME of the module, plugin, task or feature below. --> opennebula.py

##### ADDITIONAL INFORMATION
<!--- Include additional information to help people understand the change here --> <!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->

<!--- Paste verbatim command output below, e.g. before and after your change -->
```paste below
                "host": "foo23.host",
                "id": 1234,
```

* Create 8532-expand-opennuebula-inventory-data.yml

* Update opennebula.py

* Update changelogs/fragments/8532-expand-opennuebula-inventory-data.yml

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

* Add check for empty records and add test

* fix attribute test

* fix attribute test

* fix attribute test

* fix attribute test

* Update plugins/inventory/opennebula.py

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

* update as per guidance

* restore attribute checks

* fix attr

* fix indent

* PR Fixes

* add attribute check in case of empty variable

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Александр Бакановский <abakanovskii@astralinux.ru>
(cherry picked from commit 24b74cc4b9)

Co-authored-by: Tom Paine <github@aioue.net>
2024-10-07 23:10:38 +02:00
patchback[bot]
a1d4051a12 [PR #8940/c814fd05 backport][stable-9] keycloak_userprofile: improve diff by deserializing fetched kc.user.profile.config and serializing it before sending (#9002)
keycloak_userprofile: improve diff by deserializing fetched `kc.user.profile.config` and serializing it before sending (#8940)

* deserialize fetched `kc.user.profile.config` and serialize it before sending

* change `kc.user.profile.config` to JSON formatted string in mock `get_component` responses

* add changelog fragment

(cherry picked from commit c814fd0530)

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-10-07 23:10:29 +02:00
patchback[bot]
acdf19c9e6 [PR #8898/3b109abe backport][stable-9] keycloak_user_federation: add module argument that allows excluding bindCredential from update check (#8999)
keycloak_user_federation: add module argument that allows excluding `bindCredential` from update check (#8898)

* add module argument that allows excluding `bindCredential` from update check

* add changelog fragment

* change option name to `bind_credential_update_mode` and change type to str

(cherry picked from commit 3b109abe18)

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-10-07 23:10:19 +02:00
patchback[bot]
e2513b318e [PR #8920/cc800962 backport][stable-9] ipa_host: Fix enabled and disabled states (#8998)
ipa_host: Fix enabled and disabled states (#8920)

* Fix ipa_host

* PR Fixes

* PR Fixes

* PR Doc fixes

* PR Doc fixes 2

* Fix default value

(cherry picked from commit cc8009621f)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-10-07 23:10:13 +02:00
patchback[bot]
cc2794ad05 [PR #8966/5e6b8e53 backport][stable-9] dig lookup: Allow to pass port for DNS lookup (#9004)
dig lookup: Allow to pass port for DNS lookup (#8966)

dnspython accepts a port as part of the nameserver.

Currently, the nameservers are passed as strings which
leads dnspython to create Nameserver objects out of them
using the port that is currently set in the Resolver instance.
That creation of Nameserver objects is done right when the
`nameservers` property is set.
If a port is to be set by us, the `port` attribute of the
Resolver needs to be set before the nameservers are passed
to the Resolver so when the nameservers are passed, that new
port is used to create the Nameserver objects.
Therefore, the assignment of the `nameservers` property of the
Resolver is moved after the argument processing so the `port`
attribute is (if it's given in the lookup-call) definitely set
before the `nameservers` property.

(cherry picked from commit 5e6b8e5327)

Co-authored-by: JaegerMaKn <max.jaeger@knauf.com>
2024-10-07 23:10:04 +02:00
patchback[bot]
154b5f86fd [PR #8990/447d4b02 backport][stable-9] redfish_config new bool parameter to automatically delete 'None' type volumes. (#9008)
redfish_config new bool parameter to automatically delete 'None' type volumes. (#8990)

* Add a new boolean parameter storage_none_volume_deletion to the volume creation command of redfish_config

* Add description for storage_none_volume_deletion redfish_config parameter

* Update plugins/module_utils/redfish_utils.py

Co-authored-by: Mike Raineri <mraineri@gmail.com>

* Update plugins/modules/redfish_config.py

Co-authored-by: Mike Raineri <mraineri@gmail.com>

* Add CHANGELOG fragment

* Add punctuation.

---------

Co-authored-by: Pierre-yves FONTANIERE <pyf@cc.in2p3.fr>
Co-authored-by: Mike Raineri <mraineri@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 447d4b0267)

Co-authored-by: Pierre-yves Fontaniere <pyfontan@cc.in2p3.fr>
2024-10-07 23:09:55 +02:00
patchback[bot]
e302058e2d [PR #8948/1bdf8fc0 backport][stable-9] cloudflare_dns: Update SRV record handling for Cloudflare API changes (#9001)
cloudflare_dns: Update SRV record handling for Cloudflare API changes (#8948)

(cherry picked from commit 1bdf8fc025)

Co-authored-by: salty <salty@salty.dk>
2024-10-07 23:09:44 +02:00
patchback[bot]
1affd48260 [PR #8956/b523d1b1 backport][stable-9] Remove 'CapacityBytes' from list of required parameters (#9007)
Remove 'CapacityBytes' from list of required parameters (#8956)

* Remove 'CapacityBytes' from list of required parameters

* Add CHANGELOG fragment

* Fix sanity test failure whitespace before ']'

* Update changelogs/fragments/8956-remove-capacitybytes-from-the-required-parameters_list.yml

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

* Add  description for the volume_details key CapacityBytes

* Update plugins/modules/redfish_config.py

Co-authored-by: Mike Raineri <mraineri@gmail.com>

* Adjust description.

---------

Co-authored-by: Pierre-yves FONTANIERE <pyf@cc.in2p3.fr>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Mike Raineri <mraineri@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit b523d1b1c9)

Co-authored-by: Pierre-yves Fontaniere <pyfontan@cc.in2p3.fr>
2024-10-07 23:09:25 +02:00
patchback[bot]
dc5a89b040 [PR #8954/c7e2875a backport][stable-9] keycloak_user_federation: add user federation config parameter referral to module args (#8997)
keycloak_user_federation: add user federation config parameter `referral` to module args (#8954)

* add keycloak referral parameter to module args

* add changelog fragment

* Update plugins/modules/keycloak_user_federation.py

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

* Update changelogs/fragments/8954-keycloak-user-federation-add-referral-parameter.yml

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

---------

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

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-10-07 22:36:58 +02:00
patchback[bot]
71d8109275 [PR #8952/24f2b980 backport][stable-9] passwordstore: Support subkey creation and update (#8996)
passwordstore: Support subkey creation and update (#8952)

(cherry picked from commit 24f2b980b7)

Co-authored-by: Manuel Luzarreta <mluzarreta.pro@pm.me>
2024-10-07 22:31:04 +02:00
patchback[bot]
73362a1e43 [PR #8938/1d86d496 backport][stable-9] ipa_getkeytab: Create module (#8995)
ipa_getkeytab: Create module (#8938)

* Add ipa_getkeytab

* Parameters fix

* PR fixes

* PR fixes 2

* Fix unit tests

* Fix doc and unit tests

* Fix doc

* Fix doc 2

* Fix doc 3

* PR fixes

* PR fixes 2

* Fix name

* Fix description typo

* Fix variable names

* Update tests

* Add man reference

(cherry picked from commit 1d86d49688)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-10-07 22:30:52 +02:00
patchback[bot]
bc4fda8b14 [PR #8983/5b4f4174 backport][stable-9] Update docs with references to man pages (#8994)
Update docs with references to man pages (#8983)

* update docs with references to man pages

* reformat module docs

* gconftool2/_info: docs adjustments

(cherry picked from commit 5b4f41748d)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-10-07 22:19:29 +02:00
patchback[bot]
2766898ea8 [PR #8987/29a2df8e backport][stable-9] udm_user, homectl: use legacycrypt on Python 3.13+ (#8993)
udm_user, homectl: use legacycrypt on Python 3.13+ (#8987)

Use legacycrypt on Python 3.13+.

(cherry picked from commit 29a2df8e6b)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-10-07 22:19:14 +02:00
patchback[bot]
02f123877a [PR #8889/fea0ffa5 backport][stable-9] one_image/one_image_info: refactor (#8984)
one_image/one_image_info: refactor (#8889)

* Refactor one_image

* Refactor one_image_info

* Add examples one_image

* Add CHANGELOG fragment

* Add integration tests for one_image

* Add integration tests for one_image_info

* Update one_image DOC

* Update one_image_info DOC

* Update one_image DOC

* Update one_image_info DOC

* Fix f-strings for one_image

* Update CHANGELOG fragment

* PR fixes

* PR fixes

(cherry picked from commit fea0ffa5aa)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-10-05 15:17:27 +02:00
patchback[bot]
d4c29e19c0 [PR #8970/8610223d backport][stable-9] dig lookup plugin: Fix using only last nameserver specified (#8986)
dig lookup plugin: Fix using only last nameserver specified (#8970)

* dig plugin: Fix using only last nameserver given

Currently, when specifying multiple nameservers
either using multiple `@ns.example.com` arguments
or by specifying multiple nameservers in a single
argument (@ns1.example.com,ns2.example.com), due
to a bug only the very last nameserver that is
specified is actually used.
This is because for every iteration of the
    for ns in nsset
loop, the local list of nameservers is cleared
and after adding the currently processed nameserver
entry, the whole `nameservers` list of the Resolver
instance is overridden with that new list with just
one element. And as far as I can see, when setting
that `nameserver` property, the dnspython library
actually overrides the existing list and doesn't
do some trickery to append the new nameservers or
something like that.

Therefore, the assignment of the `nameservers`
property of the Resolver is moved after the argument
processing so all nameservers are added and then
collectively written to the `nameservers` property
of the Resolver.

* Add CHANGELOG fragment

(cherry picked from commit 8610223d03)

Co-authored-by: JaegerMaKn <max.jaeger@knauf.com>
2024-10-05 15:17:17 +02:00
patchback[bot]
200ab045fa [PR #8908/e7ccbc2f backport][stable-9] Add gitlab group params (#8981)
Add gitlab group params (#8908)

Add new gitlab_group parameters

(cherry picked from commit e7ccbc2f18)

Co-authored-by: Julien Lecomte <julien-lecomte@users.noreply.github.com>
2024-10-04 09:19:24 +02:00
patchback[bot]
9b4decd831 [PR #8897/d4fb6bf8 backport][stable-9] nmcli: conn_reload param and up/down states (#8975)
nmcli: conn_reload param and up/down states (#8897)

* Update nmcli module

* Update nmcli state

* Update test_nmcli

* Add CHANGELOG fragment

* PR Fixes

* Fix DOCUMENTATION block

(cherry picked from commit d4fb6bf8a6)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-10-03 16:13:11 +03:00
patchback[bot]
566ec0a002 [PR #8812/5d9a7ab2 backport][stable-9] keycloak_user_federation: remove lastSync param from kc API responses (#8977)
keycloak_user_federation: remove `lastSync` param from kc API responses (#8812)

* remove `lastSync` param from kc API responses

* add blank line to satisfy sanity check

* add changelog fragment

* fix NoneType error introduced by changed normalize func return value

(cherry picked from commit 5d9a7ab240)

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-10-03 16:13:02 +03:00
patchback[bot]
2e72051b6c [PR #8719/92df5e8f backport][stable-9] open_iscsi: Make targets optional for a portal login (#8978)
open_iscsi: Make targets optional for a portal login (#8719)

* Make targets optional for a portal login

* Add changelog

* Fix check_rc variable

* Fix idempotence

* Fix linting

* PR fixes

* Linter fixes

* PR fixes

* Change variable name

(cherry picked from commit 92df5e8fec)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-10-03 16:12:50 +03:00
patchback[bot]
89d33bbd7b [PR #8971/7fc7af30 backport][stable-9] fix doc for cmd_runner_fmt.as_bool() (#8974)
fix doc for cmd_runner_fmt.as_bool() (#8971)

(cherry picked from commit 7fc7af306c)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-10-03 15:30:49 +03:00
patchback[bot]
76b6c8e184 [PR #8963/96dfb89b backport][stable-9] cmd_runner_guide: docs improvements (#8968)
cmd_runner_guide: docs improvements (#8963)

* cmd_runner_guide: docs improvements

* add note about suboptions

(cherry picked from commit 96dfb89b01)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-10-02 21:29:40 +03:00
patchback[bot]
fdc279def9 [PR #8965/daaa0087 backport][stable-9] pipx: remove unused param from the runner ctx.run() call (#8967)
pipx: remove unused param from the runner ctx.run() call (#8965)

remove unused param from the runner ctx.run() call

(cherry picked from commit daaa008713)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-10-02 21:23:11 +03:00
patchback[bot]
a471fa88b8 [PR #8909/2d660a12 backport][stable-9] flatpak: improve flatpak name parsing in _parse_flatpak_name (#8961)
flatpak: improve flatpak name parsing in `_parse_flatpak_name` (#8909)

* flatpak: improve flatpak name parsing in `_parse_flatpak_name`

* changelog: add changelog fragment

* flatpak: fix condition in `_is_flatpak_id` function

* chore: update changelog fragment

* docs(flatpak): add guidelines for application IDs in comments

(cherry picked from commit 2d660a1252)

Co-authored-by: Járedy Alves <jaredyalves@undefinedname.com>
2024-10-02 10:34:07 +03:00
patchback[bot]
bf4e5dc3c0 [PR #8923/83080cc0 backport][stable-9] keycloak_userprofile: fix empty response by removing parent filter when fetching userprofile component (#8960)
keycloak_userprofile: fix empty response by removing `parent` filter when fetching userprofile component (#8923)

* remove parent filter when fetching userprofile component

* add changelog fragment

* Update changelogs/fragments/8923-keycloak_userprofile-fix-empty-response-when-fetching-userprofile.yml

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

---------

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

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-10-02 09:55:34 +03:00
patchback[bot]
185bdaaa39 [PR #8925/7c913b23 backport][stable-9] Pass absolute paths to atomic_move() (#8959)
Pass absolute paths to atomic_move() (#8925)

Pass absolute paths to atmoic_move().

(cherry picked from commit 7c913b239a)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-10-01 22:52:56 +03:00
patchback[bot]
55d44975dd [PR #8944/a7d1b0fc backport][stable-9] python_runner/django_command: bugfixes (#8946)
python_runner/django_command: bugfixes (#8944)

* python_runner/django_command: bugfixes

* fix indentation

* join path_prefix with : when concatenating with PATH

* add changelog frag

(cherry picked from commit a7d1b0fc52)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-30 17:41:21 +03:00
patchback[bot]
43772cfbbb [PR #8899/ab84f163 backport][stable-9] Sort parameters in gitlab_group to aid in adding more params (#8945)
Sort parameters in gitlab_group to aid in adding more params (#8899)

(cherry picked from commit ab84f1632f)

Co-authored-by: Julien Lecomte <julien-lecomte@users.noreply.github.com>
2024-09-30 09:37:09 +03:00
Felix Fontein
e93b6231ec Prepare 9.5.0 release. 2024-09-28 08:36:54 +03:00
patchback[bot]
605a557a8d [PR #8894/8ef77d86 backport][stable-9] unit test helper: big revamp (#8943)
unit test helper: big revamp (#8894)

* initial commit

* multiple changes:

- TestCaseContext fixture no longer need to autouse=True
- Helper.from_module() allows extra param to specify yaml file
- test_django_check: adjusted .py and .yaml

* set fixtures per testcase

* set fixtures per testcase

* rollback to original state

* patch_ansible_module fixture

- now it works not only in parametrized functions but also directly with args

* tests/unit/plugins/modules/helper.py

- improved encapsulation, class Helper no longer knows details about test cases
- test functions no longer parametrized, that allows using test case fixtures per test function
- renamed 'context' to 'mock'

* enable Helper.from_list(), better param name 'ansible_module'

* adjusted test fiels to new helper

* remove unnecessary .license file

* fix bracket

* fix reference name

* Update tests/unit/plugins/modules/helper.py

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

* revert to parametrized test func instead of multiple funcs

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-28 08:33:00 +03:00
patchback[bot]
d97f1a31ba [PR #8937/84e0190e backport][stable-9] Disk description now contains a StorageId and a RedfishURI (#8942)
Disk description now contains a StorageId and a RedfishURI (#8937)

* Disks controller is now uniquely identified by the controller ID

* Fix typo `StorageID` to `StorageId`

* Add changelog fragments

---------

Co-authored-by: Pierre-yves FONTANIERE <pyf@cc.in2p3.fr>
(cherry picked from commit 84e0190eee)

Co-authored-by: Pierre-yves Fontaniere <pyfontan@cc.in2p3.fr>
2024-09-28 08:10:49 +03:00
patchback[bot]
5949b29a12 [PR #8933/bc6ae849 backport][stable-9] Move ansible-core 2.15 tests to EOL tests (#8934)
Move ansible-core 2.15 tests to EOL tests (#8933)

Move ansible-core 2.15 tests to EOL tests.

(cherry picked from commit bc6ae849b3)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-09-26 16:06:03 +03:00
patchback[bot]
993d580adc [PR #8887/0bc5f248 backport][stable-9] one_service: fix recreation (#8932)
one_service: fix recreation (#8887)

* Fix one_service unique creation

* Revert empty space

* Add CHANGELOG fragment

* Update CHANGELOG fragment

(cherry picked from commit 0bc5f24863)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-09-26 11:38:34 +03:00
patchback[bot]
1ac7783c5c [PR #8929/4700accb backport][stable-9] CmdRunner: missing parameter for get_best_parsable_locale() (#8930)
CmdRunner: missing parameter for get_best_parsable_locale() (#8929)

* CmdRunner: missing parameter for get_best_parsable_locale()

* add changelog frag

(cherry picked from commit 4700accbff)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-26 11:28:16 +03:00
patchback[bot]
f0724c0975 [PR #8922/d356e255 backport][stable-9] Deprecate hipchat module (#8924)
Deprecate hipchat module (#8922)

Deprecate hipchat module.

(cherry picked from commit d356e255e0)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-09-25 22:33:28 +03:00
patchback[bot]
f3aca8a575 [PR #8917/89ad40db backport][stable-9] proxmox inventory: remove duplicated credentials line (#8919)
proxmox inventory: remove duplicated credentials line (#8917)

* proxmox inventory: remove duplicated credentials line

* fixup! proxmox inventory: remove duplicated credentials line

* fixup! proxmox inventory: remove duplicated credentials line

(cherry picked from commit 89ad40db41)

Co-authored-by: Per Fide <perfide@users.noreply.github.com>
2024-09-25 14:01:03 +03:00
patchback[bot]
ad1cf82a34 [PR #8913/199ba0a1 backport][stable-9] Fix parameter name (#8916)
Fix parameter name (#8913)

(cherry picked from commit 199ba0a170)

Co-authored-by: Niko Ehrenfeuchter <mail@he1ix.org>
2024-09-24 23:10:55 +03:00
patchback[bot]
42d0a55984 [PR #8910/293021c3 backport][stable-9] Add stable-2.18 to CI (#8912)
Add stable-2.18 to CI (#8910)

Add stable-2.18 to CI.

(cherry picked from commit 293021c3dd)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-09-24 14:00:45 +03:00
patchback[bot]
4f4d962f7c [PR #8877/deaad6e5 backport][stable-9] keycloak_realm: fix change detection in check mode by normalizing realms beforehand (#8903)
keycloak_realm: fix change detection in check mode by normalizing realms beforehand (#8877)

* keycloak_realm: fix change detection in check mode by normalizing realms beforehand

* add changelog fragment

(cherry picked from commit deaad6e547)

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-09-23 21:46:37 +03:00
patchback[bot]
5343880fa5 [PR #8900/a32f1d69 backport][stable-9] ipa_hostgroup: fix state params (#8906)
ipa_hostgroup: fix state params (#8900)

* Fix ipa_hostgroup

* Add CHANGELOG fragment

(cherry picked from commit a32f1d699b)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-09-23 21:46:25 +03:00
patchback[bot]
5ea44edc64 [PR #8895/0bf84ba2 backport][stable-9] fix comprehension (#8896)
fix comprehension (#8895)

* fix comprehension

* add changelog frag

* Update changelogs/fragments/8895-fix-comprehension.yaml

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

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-22 13:15:21 +03:00
patchback[bot]
8152cb3e1f [PR #8785/ac302eb7 backport][stable-9] keycloak_user_federation: set krbPrincipalAttribute to '' if unset in kc responses (#8892)
keycloak_user_federation: set `krbPrincipalAttribute` to `''` if unset in kc responses (#8785)

* set `krbPrincipalAttribute` to `''` if unset in kc before and after responses

* add changelog fragment

* Update changelogs/fragments/8785-keycloak_user_federation-set-krbPrincipalAttribute-to-empty-string-if-missing.yaml

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

---------

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

Co-authored-by: fgruenbauer <gruenbauer@b1-systems.de>
2024-09-21 10:42:43 +03:00
patchback[bot]
eae0c4f92b [PR #8885/38479ee9 backport][stable-9] npm: Add force flag (#8893)
npm: Add force flag (#8885)

* Add force flag for nmp module

* Add CHANGELOG fragment

* Add force to cmdrunner

* Update CHANGELOG

* Add comma

(cherry picked from commit 38479ee9ff)

Co-authored-by: alexander <79072457+abakanovskii@users.noreply.github.com>
2024-09-21 10:42:31 +03:00
patchback[bot]
57277e0661 [PR #8886/27cb0c90 backport][stable-9] Update example for community.general.homebrew_services (#8890)
Update example for community.general.homebrew_services (#8886)

(cherry picked from commit 27cb0c9090)

Co-authored-by: Florian Weber <florian@webflo.org>
2024-09-20 20:15:48 +02:00
patchback[bot]
53a941cee7 [PR #8876/6af74d1b backport][stable-9] multiple modules: improve dict.items() loops (#8882)
multiple modules: improve dict.items() loops (#8876)

* multiple modules: improve dict.items() loops

* simplify in memset_* modules

* add changelog frag

(cherry picked from commit 6af74d1ba6)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-18 17:50:02 +02:00
patchback[bot]
e6edf9cdea [PR #8653/80f48cce backport][stable-9] Redfish: Added steps to allow a user to change their password when their account requires a password change (#8881)
Redfish: Added steps to allow a user to change their password when their account requires a password change (#8653)

* Redfish: Added steps to allow a user to change their password when their account requires a password change

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

* Bug fix

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

* Bug fix

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

* Bug fixes with return data handling

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

* Added changelog fragment

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

* Update changelogs/fragments/8652-Redfish-Password-Change-Required.yml

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

---------

Signed-off-by: Mike Raineri <michael.raineri@dell.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 80f48cceb4)

Co-authored-by: Mike Raineri <mraineri@gmail.com>
2024-09-18 17:49:15 +02:00
patchback[bot]
5550ba1946 [PR #8860/f93883aa backport][stable-9] gitlab_runner: update requirements in docs (#8880)
gitlab_runner: update requirements in docs (#8860)

* Update gitlab_runner.py

Be specific related requirements for package version. This difference change the whole dependency chain for playbook.

* Update plugins/modules/gitlab_runner.py

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

---------

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

Co-authored-by: Péter Mikáczó <petermikaczo@gmail.com>
2024-09-17 15:13:34 +02:00
patchback[bot]
374378beeb [PR #8875/4123934b backport][stable-9] reformat module docs (#8878)
reformat xfconf docs (#8875)

* reformat module docs

* fix sanity

(cherry picked from commit 4123934b46)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-17 15:13:23 +02:00
patchback[bot]
5222df306b [PR #8870/2f1df973 backport][stable-9] Remove private key and certificates from documentation (#8874)
Remove private key and certificates from documentation (#8870)

* Remove private key and certificate from example.

* Censor certificates in examples.

(cherry picked from commit 2f1df973a6)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-09-16 21:56:17 +02:00
patchback[bot]
788c722b3e [PR #8859/e4472b32 backport][stable-9] pipx/pipx_info: refactor doc fragment (#8869)
pipx/pipx_info: refactor doc fragment (#8859)

* pipx/pipx_info: refactor doc fragment

* pipx/pipx_info: refactor common options to module_utils

(cherry picked from commit e4472b322b)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-14 09:38:19 +02:00
patchback[bot]
2ddbda2aa7 [PR #8856/37dd6ec8 backport][stable-9] jira: adjust module for old vardict deprecation (#8864)
jira: adjust module for old vardict deprecation (#8856)

* jira: adjust module for old vardict deprecation

* add changelog frag

(cherry picked from commit 37dd6ec8a3)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-13 22:59:05 +02:00
patchback[bot]
cb939cbb75 [PR #8858/94472dd7 backport][stable-9] use dict comprehension in plugins, part 4 (#8865)
use dict comprehension in plugins, part 4 (#8858)

* use dict comprehension in plugins, part 4

* add changelog frag

(cherry picked from commit 94472dd7e5)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-13 22:58:57 +02:00
patchback[bot]
7db1613730 [PR #8861/76ebda7f backport][stable-9] snap tests: re-enable test for --dangerous using smaller snap (#8868)
snap tests: re-enable test for --dangerous using smaller snap (#8861)

(cherry picked from commit 76ebda7faf)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-13 22:58:47 +02:00
patchback[bot]
38a16b421d [PR #8855/41d87f5c backport][stable-9] gio_mime: adjust module for old vardict deprecation (#8863)
gio_mime: adjust module for old vardict deprecation (#8855)

* gio_mime: adjust module for old vardict deprecation

* add changelog frag

(cherry picked from commit 41d87f5c9d)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-09-13 22:58:33 +02:00
Felix Fontein
bd8b7e3737 The next expected release will be 9.5.0. 2024-09-09 19:45:07 +02:00
135 changed files with 4587 additions and 2180 deletions

View File

@@ -73,6 +73,19 @@ stages:
- test: 3
- test: 4
- test: extra
- stage: Sanity_2_18
displayName: Sanity 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.18/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_17
displayName: Sanity 2.17
dependsOn: []
@@ -99,19 +112,6 @@ stages:
- test: 2
- test: 3
- test: 4
- 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
### Units
- stage: Units_devel
displayName: Units devel
@@ -128,6 +128,17 @@ stages:
- test: '3.11'
- test: '3.12'
- test: '3.13'
- stage: Units_2_18
displayName: Units 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.18/units/{0}/1
targets:
- test: 3.8
- test: "3.13"
- stage: Units_2_17
displayName: Units 2.17
dependsOn: []
@@ -151,17 +162,6 @@ stages:
- test: 2.7
- test: 3.6
- 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"
## Remote
- stage: Remote_devel_extra_vms
@@ -200,6 +200,20 @@ stages:
- 1
- 2
- 3
- stage: Remote_2_18
displayName: Remote 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/{0}
targets:
- name: RHEL 9.4
test: rhel/9.4
groups:
- 1
- 2
- 3
- stage: Remote_2_17
displayName: Remote 2.17
dependsOn: []
@@ -232,30 +246,10 @@ stages:
test: rhel/9.2
- name: RHEL 8.8
test: rhel/8.8
# - name: FreeBSD 13.2
# test: freebsd/13.2
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 9.1
test: rhel/9.1
- name: RHEL 8.7
test: rhel/8.7
- name: RHEL 7.9
test: rhel/7.9
# - name: FreeBSD 13.1
# test: freebsd/13.1
# - name: FreeBSD 12.4
# test: freebsd/12.4
# - name: FreeBSD 13.2
# test: freebsd/13.2
groups:
- 1
- 2
@@ -282,6 +276,20 @@ stages:
- 1
- 2
- 3
- stage: Docker_2_18
displayName: Docker 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/linux/{0}
targets:
- name: Ubuntu 24.04
test: ubuntu2404
groups:
- 1
- 2
- 3
- stage: Docker_2_17
displayName: Docker 2.17
dependsOn: []
@@ -314,20 +322,6 @@ stages:
test: opensuse15
- name: Alpine 3
test: alpine3
groups:
- 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: Fedora 37
test: fedora37
- name: CentOS 7
test: centos7
groups:
@@ -356,77 +350,79 @@ stages:
- 3
### Generic
- stage: Generic_devel
displayName: Generic devel
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: devel/generic/{0}/1
targets:
- test: '3.8'
- test: '3.11'
- test: '3.13'
- stage: Generic_2_17
displayName: Generic 2.17
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.17/generic/{0}/1
targets:
- test: '3.7'
- test: '3.12'
- stage: Generic_2_16
displayName: Generic 2.16
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.16/generic/{0}/1
targets:
- test: '2.7'
- test: '3.6'
- 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'
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - stage: Generic_devel
# displayName: Generic devel
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: devel/generic/{0}/1
# targets:
# - test: '3.8'
# - test: '3.11'
# - test: '3.13'
# - stage: Generic_2_18
# displayName: Generic 2.18
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.18/generic/{0}/1
# targets:
# - test: '3.8'
# - test: '3.13'
# - stage: Generic_2_17
# displayName: Generic 2.17
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.17/generic/{0}/1
# targets:
# - test: '3.7'
# - test: '3.12'
# - stage: Generic_2_16
# displayName: Generic 2.16
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.16/generic/{0}/1
# targets:
# - test: '2.7'
# - test: '3.6'
# - test: '3.11'
- stage: Summary
condition: succeededOrFailed()
dependsOn:
- Sanity_devel
- Sanity_2_18
- Sanity_2_17
- Sanity_2_16
- Sanity_2_15
- Units_devel
- Units_2_18
- Units_2_17
- Units_2_16
- Units_2_15
- Remote_devel_extra_vms
- Remote_devel
- Remote_2_18
- Remote_2_17
- Remote_2_16
- Remote_2_15
- Docker_devel
- Docker_2_18
- Docker_2_17
- Docker_2_16
- 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_18
# - Generic_2_17
# - Generic_2_16
# - Generic_2_15
jobs:
- template: templates/coverage.yml

4
.github/BOTMETA.yml vendored
View File

@@ -131,6 +131,8 @@ files:
maintainers: $team_huawei
$doc_fragments/nomad.py:
maintainers: chris93111 apecnascimento
$doc_fragments/pipx.py:
maintainers: russoz
$doc_fragments/xenserver.py:
labels: xenserver
maintainers: bvitnik
@@ -712,6 +714,8 @@ files:
$modules/ipa_:
maintainers: $team_ipa
ignore: fxfitz
$modules/ipa_getkeytab.py:
maintainers: abakanovskii
$modules/ipa_dnsrecord.py:
maintainers: $team_ipa jwbernin
$modules/ipbase_info.py:

View File

@@ -31,6 +31,7 @@ jobs:
ansible:
- '2.13'
- '2.14'
- '2.15'
# 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
@@ -76,6 +77,10 @@ jobs:
python: '3.8'
- ansible: '2.14'
python: '3.9'
- ansible: '2.15'
python: '3.5'
- ansible: '2.15'
python: '3.10'
steps:
- name: >-
@@ -166,16 +171,32 @@ jobs:
docker: alpine3
python: ''
target: azp/posix/3/
# 2.15
- ansible: '2.15'
docker: fedora37
python: ''
target: azp/posix/1/
- ansible: '2.15'
docker: fedora37
python: ''
target: azp/posix/2/
- ansible: '2.15'
docker: fedora37
python: ''
target: azp/posix/3/
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - ansible: '2.13'
# docker: default
# python: '3.9'
# target: azp/generic/1/
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - ansible: '2.14'
# docker: default
# python: '3.10'
# target: azp/generic/1/
# - ansible: '2.15'
# docker: default
# python: '3.9'
# target: azp/generic/1/
steps:
- name: >-

View File

@@ -2,45 +2,51 @@
**Topics**
- <a href="#v9-4-0">v9\.4\.0</a>
- <a href="#v9-5-0">v9\.5\.0</a>
- <a href="#release-summary">Release Summary</a>
- <a href="#minor-changes">Minor Changes</a>
- <a href="#deprecated-features">Deprecated Features</a>
- <a href="#bugfixes">Bugfixes</a>
- <a href="#new-modules">New Modules</a>
- <a href="#v9-3-0">v9\.3\.0</a>
- <a href="#v9-4-0">v9\.4\.0</a>
- <a href="#release-summary-1">Release Summary</a>
- <a href="#minor-changes-1">Minor Changes</a>
- <a href="#deprecated-features-1">Deprecated Features</a>
- <a href="#bugfixes-1">Bugfixes</a>
- <a href="#new-modules-1">New Modules</a>
- <a href="#v9-2-0">v9\.2\.0</a>
- <a href="#v9-3-0">v9\.3\.0</a>
- <a href="#release-summary-2">Release Summary</a>
- <a href="#minor-changes-2">Minor Changes</a>
- <a href="#bugfixes-2">Bugfixes</a>
- <a href="#new-modules-2">New Modules</a>
- <a href="#v9-2-0">v9\.2\.0</a>
- <a href="#release-summary-3">Release Summary</a>
- <a href="#minor-changes-3">Minor Changes</a>
- <a href="#bugfixes-3">Bugfixes</a>
- <a href="#new-plugins">New Plugins</a>
- <a href="#filter">Filter</a>
- <a href="#test">Test</a>
- <a href="#v9-1-0">v9\.1\.0</a>
- <a href="#release-summary-3">Release Summary</a>
- <a href="#minor-changes-3">Minor Changes</a>
- <a href="#deprecated-features-1">Deprecated Features</a>
- <a href="#bugfixes-3">Bugfixes</a>
- <a href="#release-summary-4">Release Summary</a>
- <a href="#minor-changes-4">Minor Changes</a>
- <a href="#deprecated-features-2">Deprecated Features</a>
- <a href="#bugfixes-4">Bugfixes</a>
- <a href="#known-issues">Known Issues</a>
- <a href="#new-plugins-1">New Plugins</a>
- <a href="#filter-1">Filter</a>
- <a href="#new-modules-2">New Modules</a>
- <a href="#new-modules-3">New Modules</a>
- <a href="#v9-0-1">v9\.0\.1</a>
- <a href="#release-summary-4">Release Summary</a>
- <a href="#minor-changes-4">Minor Changes</a>
- <a href="#bugfixes-4">Bugfixes</a>
- <a href="#v9-0-0">v9\.0\.0</a>
- <a href="#release-summary-5">Release Summary</a>
- <a href="#minor-changes-5">Minor Changes</a>
- <a href="#bugfixes-5">Bugfixes</a>
- <a href="#v9-0-0">v9\.0\.0</a>
- <a href="#release-summary-6">Release Summary</a>
- <a href="#minor-changes-6">Minor Changes</a>
- <a href="#breaking-changes--porting-guide">Breaking Changes / Porting Guide</a>
- <a href="#deprecated-features-2">Deprecated Features</a>
- <a href="#deprecated-features-3">Deprecated Features</a>
- <a href="#removed-features-previously-deprecated">Removed Features \(previously deprecated\)</a>
- <a href="#security-fixes">Security Fixes</a>
- <a href="#bugfixes-5">Bugfixes</a>
- <a href="#bugfixes-6">Bugfixes</a>
- <a href="#new-plugins-2">New Plugins</a>
- <a href="#become">Become</a>
- <a href="#callback">Callback</a>
@@ -48,20 +54,119 @@
- <a href="#filter-2">Filter</a>
- <a href="#lookup">Lookup</a>
- <a href="#test-1">Test</a>
- <a href="#new-modules-3">New Modules</a>
- <a href="#new-modules-4">New Modules</a>
This changelog describes changes after version 8\.0\.0\.
<a id="v9-4-0"></a>
## v9\.4\.0
<a id="v9-5-0"></a>
## v9\.5\.0
<a id="release-summary"></a>
### Release Summary
Bugfix and feature release\.
Regular bugfix and feature release\.
Please note that this is the last feature release for community\.general 9\.x\.y\.
From now on\, new features will only go into community\.general 10\.x\.y\.
<a id="minor-changes"></a>
### Minor Changes
* dig lookup plugin \- add <code>port</code> option to specify DNS server port \([https\://github\.com/ansible\-collections/community\.general/pull/8966](https\://github\.com/ansible\-collections/community\.general/pull/8966)\)\.
* flatpak \- improve the parsing of Flatpak application IDs based on official guidelines \([https\://github\.com/ansible\-collections/community\.general/pull/8909](https\://github\.com/ansible\-collections/community\.general/pull/8909)\)\.
* gio\_mime \- adjust code ahead of the old <code>VardDict</code> deprecation \([https\://github\.com/ansible\-collections/community\.general/pull/8855](https\://github\.com/ansible\-collections/community\.general/pull/8855)\)\.
* gitlab\_deploy\_key \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* gitlab\_group \- add many new parameters \([https\://github\.com/ansible\-collections/community\.general/pull/8908](https\://github\.com/ansible\-collections/community\.general/pull/8908)\)\.
* gitlab\_group \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* gitlab\_issue \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* gitlab\_merge\_request \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* gitlab\_runner \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* icinga2\_host \- replace loop with dict comprehension \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* jira \- adjust code ahead of the old <code>VardDict</code> deprecation \([https\://github\.com/ansible\-collections/community\.general/pull/8856](https\://github\.com/ansible\-collections/community\.general/pull/8856)\)\.
* keycloak\_client \- add <code>client\-x509</code> choice to <code>client\_authenticator\_type</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8973](https\://github\.com/ansible\-collections/community\.general/pull/8973)\)\.
* keycloak\_user\_federation \- add the user federation config parameter <code>referral</code> to the module arguments \([https\://github\.com/ansible\-collections/community\.general/pull/8954](https\://github\.com/ansible\-collections/community\.general/pull/8954)\)\.
* memset\_dns\_reload \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* memset\_memstore\_info \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* memset\_server\_info \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* memset\_zone \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* memset\_zone\_domain \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* memset\_zone\_record \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* nmcli \- add <code>conn\_enable</code> param to reload connection \([https\://github\.com/ansible\-collections/community\.general/issues/3752](https\://github\.com/ansible\-collections/community\.general/issues/3752)\, [https\://github\.com/ansible\-collections/community\.general/issues/8704](https\://github\.com/ansible\-collections/community\.general/issues/8704)\, [https\://github\.com/ansible\-collections/community\.general/pull/8897](https\://github\.com/ansible\-collections/community\.general/pull/8897)\)\.
* nmcli \- add <code>state\=up</code> and <code>state\=down</code> to enable/disable connections \([https\://github\.com/ansible\-collections/community\.general/issues/3752](https\://github\.com/ansible\-collections/community\.general/issues/3752)\, [https\://github\.com/ansible\-collections/community\.general/issues/8704](https\://github\.com/ansible\-collections/community\.general/issues/8704)\, [https\://github\.com/ansible\-collections/community\.general/issues/7152](https\://github\.com/ansible\-collections/community\.general/issues/7152)\, [https\://github\.com/ansible\-collections/community\.general/pull/8897](https\://github\.com/ansible\-collections/community\.general/pull/8897)\)\.
* nmcli \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* npm \- add <code>force</code> parameter to allow <code>\-\-force</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8885](https\://github\.com/ansible\-collections/community\.general/pull/8885)\)\.
* one\_image \- add option <code>persistent</code> to manage image persistence \([https\://github\.com/ansible\-collections/community\.general/issues/3578](https\://github\.com/ansible\-collections/community\.general/issues/3578)\, [https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
* one\_image \- extend xsd scheme to make it return a lot more info about image \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
* one\_image \- refactor code to make it more similar to <code>one\_template</code> and <code>one\_vnet</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
* one\_image\_info \- extend xsd scheme to make it return a lot more info about image \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
* one\_image\_info \- refactor code to make it more similar to <code>one\_template</code> and <code>one\_vnet</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
* open\_iscsi \- allow login to a portal with multiple targets without specifying any of them \([https\://github\.com/ansible\-collections/community\.general/pull/8719](https\://github\.com/ansible\-collections/community\.general/pull/8719)\)\.
* opennebula\.py \- add VM <code>id</code> and VM <code>host</code> to inventory host data \([https\://github\.com/ansible\-collections/community\.general/pull/8532](https\://github\.com/ansible\-collections/community\.general/pull/8532)\)\.
* passwordstore lookup plugin \- add subkey creation/update support \([https\://github\.com/ansible\-collections/community\.general/pull/8952](https\://github\.com/ansible\-collections/community\.general/pull/8952)\)\.
* proxmox inventory plugin \- clean up authentication code \([https\://github\.com/ansible\-collections/community\.general/pull/8917](https\://github\.com/ansible\-collections/community\.general/pull/8917)\)\.
* redfish\_command \- add handling of the <code>PasswordChangeRequired</code> message from services in the <code>UpdateUserPassword</code> command to directly modify the user\'s password if the requested user is the one invoking the operation \([https\://github\.com/ansible\-collections/community\.general/issues/8652](https\://github\.com/ansible\-collections/community\.general/issues/8652)\, [https\://github\.com/ansible\-collections/community\.general/pull/8653](https\://github\.com/ansible\-collections/community\.general/pull/8653)\)\.
* redfish\_confg \- remove <code>CapacityBytes</code> from required paramaters of the <code>CreateVolume</code> command \([https\://github\.com/ansible\-collections/community\.general/pull/8956](https\://github\.com/ansible\-collections/community\.general/pull/8956)\)\.
* redfish\_config \- add parameter <code>storage\_none\_volume\_deletion</code> to <code>CreateVolume</code> command in order to control the automatic deletion of non\-RAID volumes \([https\://github\.com/ansible\-collections/community\.general/pull/8990](https\://github\.com/ansible\-collections/community\.general/pull/8990)\)\.
* redfish\_info \- adds <code>RedfishURI</code> and <code>StorageId</code> to Disk inventory \([https\://github\.com/ansible\-collections/community\.general/pull/8937](https\://github\.com/ansible\-collections/community\.general/pull/8937)\)\.
* scaleway\_container \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_container\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_container\_namespace \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_container\_namespace\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_container\_registry \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_container\_registry\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_function \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_function\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_function\_namespace \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_function\_namespace\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
* scaleway\_user\_data \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
* udm\_dns\_record \- replace loop with <code>dict\.update\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
<a id="deprecated-features"></a>
### Deprecated Features
* hipchat \- the hipchat service has been discontinued and the self\-hosted variant has been End of Life since 2020\. The module is therefore deprecated and will be removed from community\.general 11\.0\.0 if nobody provides compelling reasons to still keep it \([https\://github\.com/ansible\-collections/community\.general/pull/8919](https\://github\.com/ansible\-collections/community\.general/pull/8919)\)\.
<a id="bugfixes"></a>
### Bugfixes
* cloudflare\_dns \- fix changing Cloudflare SRV records \([https\://github\.com/ansible\-collections/community\.general/issues/8679](https\://github\.com/ansible\-collections/community\.general/issues/8679)\, [https\://github\.com/ansible\-collections/community\.general/pull/8948](https\://github\.com/ansible\-collections/community\.general/pull/8948)\)\.
* cmd\_runner module utils \- call to <code>get\_best\_parsable\_locales\(\)</code> was missing parameter \([https\://github\.com/ansible\-collections/community\.general/pull/8929](https\://github\.com/ansible\-collections/community\.general/pull/8929)\)\.
* dig lookup plugin \- fix using only the last nameserver specified \([https\://github\.com/ansible\-collections/community\.general/pull/8970](https\://github\.com/ansible\-collections/community\.general/pull/8970)\)\.
* django\_command \- option <code>command</code> is now split lexically before passed to underlying PythonRunner \([https\://github\.com/ansible\-collections/community\.general/pull/8944](https\://github\.com/ansible\-collections/community\.general/pull/8944)\)\.
* homectl \- the module now tries to use <code>legacycrypt</code> on Python 3\.13\+ \([https\://github\.com/ansible\-collections/community\.general/issues/4691](https\://github\.com/ansible\-collections/community\.general/issues/4691)\, [https\://github\.com/ansible\-collections/community\.general/pull/8987](https\://github\.com/ansible\-collections/community\.general/pull/8987)\)\.
* ini\_file \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
* ipa\_host \- add <code>force\_create</code>\, fix <code>enabled</code> and <code>disabled</code> states \([https\://github\.com/ansible\-collections/community\.general/issues/1094](https\://github\.com/ansible\-collections/community\.general/issues/1094)\, [https\://github\.com/ansible\-collections/community\.general/pull/8920](https\://github\.com/ansible\-collections/community\.general/pull/8920)\)\.
* ipa\_hostgroup \- fix <code>enabled \`\` and \`\`disabled</code> states \([https\://github\.com/ansible\-collections/community\.general/issues/8408](https\://github\.com/ansible\-collections/community\.general/issues/8408)\, [https\://github\.com/ansible\-collections/community\.general/pull/8900](https\://github\.com/ansible\-collections/community\.general/pull/8900)\)\.
* java\_keystore \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
* jenkins\_plugin \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
* kdeconfig \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
* keycloak\_realm \- fix change detection in check mode by sorting the lists in the realms beforehand \([https\://github\.com/ansible\-collections/community\.general/pull/8877](https\://github\.com/ansible\-collections/community\.general/pull/8877)\)\.
* keycloak\_user\_federation \- add module argument allowing users to configure the update mode for the parameter <code>bindCredential</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8898](https\://github\.com/ansible\-collections/community\.general/pull/8898)\)\.
* keycloak\_user\_federation \- minimize change detection by setting <code>krbPrincipalAttribute</code> to <code>\'\'</code> in Keycloak responses if missing \([https\://github\.com/ansible\-collections/community\.general/pull/8785](https\://github\.com/ansible\-collections/community\.general/pull/8785)\)\.
* keycloak\_user\_federation \- remove <code>lastSync</code> parameter from Keycloak responses to minimize diff/changes \([https\://github\.com/ansible\-collections/community\.general/pull/8812](https\://github\.com/ansible\-collections/community\.general/pull/8812)\)\.
* keycloak\_userprofile \- fix empty response when fetching userprofile component by removing <code>parent\=parent\_id</code> filter \([https\://github\.com/ansible\-collections/community\.general/pull/8923](https\://github\.com/ansible\-collections/community\.general/pull/8923)\)\.
* keycloak\_userprofile \- improve diff by deserializing the fetched <code>kc\.user\.profile\.config</code> and serialize it only when sending back \([https\://github\.com/ansible\-collections/community\.general/pull/8940](https\://github\.com/ansible\-collections/community\.general/pull/8940)\)\.
* lxd\_container \- fix bug introduced in previous commit \([https\://github\.com/ansible\-collections/community\.general/pull/8895](https\://github\.com/ansible\-collections/community\.general/pull/8895)\, [https\://github\.com/ansible\-collections/community\.general/issues/8888](https\://github\.com/ansible\-collections/community\.general/issues/8888)\)\.
* one\_service \- fix service creation after it was deleted with <code>unique</code> parameter \([https\://github\.com/ansible\-collections/community\.general/issues/3137](https\://github\.com/ansible\-collections/community\.general/issues/3137)\, [https\://github\.com/ansible\-collections/community\.general/pull/8887](https\://github\.com/ansible\-collections/community\.general/pull/8887)\)\.
* pam\_limits \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
* python\_runner module utils \- parameter <code>path\_prefix</code> was being handled as string when it should be a list \([https\://github\.com/ansible\-collections/community\.general/pull/8944](https\://github\.com/ansible\-collections/community\.general/pull/8944)\)\.
* udm\_user \- the module now tries to use <code>legacycrypt</code> on Python 3\.13\+ \([https\://github\.com/ansible\-collections/community\.general/issues/4690](https\://github\.com/ansible\-collections/community\.general/issues/4690)\, [https\://github\.com/ansible\-collections/community\.general/pull/8987](https\://github\.com/ansible\-collections/community\.general/pull/8987)\)\.
<a id="new-modules"></a>
### New Modules
* community\.general\.ipa\_getkeytab \- Manage keytab file in FreeIPA\.
<a id="v9-4-0"></a>
## v9\.4\.0
<a id="release-summary-1"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-1"></a>
### Minor Changes
* MH module utils \- add parameter <code>when</code> to <code>cause\_changes</code> decorator \([https\://github\.com/ansible\-collections/community\.general/pull/8766](https\://github\.com/ansible\-collections/community\.general/pull/8766)\)\.
* MH module utils \- minor refactor in decorators \([https\://github\.com/ansible\-collections/community\.general/pull/8766](https\://github\.com/ansible\-collections/community\.general/pull/8766)\)\.
* alternatives \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8833](https\://github\.com/ansible\-collections/community\.general/pull/8833)\)\.
@@ -139,14 +244,14 @@ Bugfix and feature release\.
* vars MH module utils \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8814](https\://github\.com/ansible\-collections/community\.general/pull/8814)\)\.
* vmadm \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8822](https\://github\.com/ansible\-collections/community\.general/pull/8822)\)\.
<a id="deprecated-features"></a>
<a id="deprecated-features-1"></a>
### Deprecated Features
* MH decorator cause\_changes module utils \- deprecate parameters <code>on\_success</code> and <code>on\_failure</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8791](https\://github\.com/ansible\-collections/community\.general/pull/8791)\)\.
* pipx \- support for versions of the command line tool <code>pipx</code> older than <code>1\.7\.0</code> is deprecated and will be removed in community\.general 11\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8793](https\://github\.com/ansible\-collections/community\.general/pull/8793)\)\.
* pipx\_info \- support for versions of the command line tool <code>pipx</code> older than <code>1\.7\.0</code> is deprecated and will be removed in community\.general 11\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8793](https\://github\.com/ansible\-collections/community\.general/pull/8793)\)\.
<a id="bugfixes"></a>
<a id="bugfixes-1"></a>
### Bugfixes
* gitlab\_group\_access\_token \- fix crash in check mode caused by attempted access to a newly created access token \([https\://github\.com/ansible\-collections/community\.general/pull/8796](https\://github\.com/ansible\-collections/community\.general/pull/8796)\)\.
@@ -159,7 +264,7 @@ Bugfix and feature release\.
* keycloak\_user\_federation \- sort desired and after mapper list by name \(analog to before mapper list\) to minimize diff and make change detection more accurate \([https\://github\.com/ansible\-collections/community\.general/pull/8761](https\://github\.com/ansible\-collections/community\.general/pull/8761)\)\.
* proxmox inventory plugin \- fixed a possible error on concatenating responses from proxmox\. In case an API call unexpectedly returned an empty result\, the inventory failed with a fatal error\. Added check for empty response \([https\://github\.com/ansible\-collections/community\.general/issues/8798](https\://github\.com/ansible\-collections/community\.general/issues/8798)\, [https\://github\.com/ansible\-collections/community\.general/pull/8794](https\://github\.com/ansible\-collections/community\.general/pull/8794)\)\.
<a id="new-modules"></a>
<a id="new-modules-1"></a>
### New Modules
* community\.general\.keycloak\_userprofile \- Allows managing Keycloak User Profiles\.
@@ -168,12 +273,12 @@ Bugfix and feature release\.
<a id="v9-3-0"></a>
## v9\.3\.0
<a id="release-summary-1"></a>
<a id="release-summary-2"></a>
### Release Summary
Regular bugfix and feature release\.
<a id="minor-changes-1"></a>
<a id="minor-changes-2"></a>
### Minor Changes
* cgroup\_memory\_recap\, hipchat\, jabber\, log\_plays\, loganalytics\, logentries\, logstash\, slack\, splunk\, sumologic\, syslog\_json callback plugins \- make sure that all options are typed \([https\://github\.com/ansible\-collections/community\.general/pull/8628](https\://github\.com/ansible\-collections/community\.general/pull/8628)\)\.
@@ -196,7 +301,7 @@ Regular bugfix and feature release\.
* proxmox inventory plugin \- add new fact for LXC interface details \([https\://github\.com/ansible\-collections/community\.general/pull/8713](https\://github\.com/ansible\-collections/community\.general/pull/8713)\)\.
* redis\, redis\_info \- add <code>client\_cert</code> and <code>client\_key</code> options to specify path to certificate for Redis authentication \([https\://github\.com/ansible\-collections/community\.general/pull/8654](https\://github\.com/ansible\-collections/community\.general/pull/8654)\)\.
<a id="bugfixes-1"></a>
<a id="bugfixes-2"></a>
### Bugfixes
* gitlab\_runner \- fix <code>paused</code> parameter being ignored \([https\://github\.com/ansible\-collections/community\.general/pull/8648](https\://github\.com/ansible\-collections/community\.general/pull/8648)\)\.
@@ -207,7 +312,7 @@ Regular bugfix and feature release\.
* proxmox \- fixed an issue where volume strings where overwritten instead of appended to in the new <code>build\_volume\(\)</code> method \([https\://github\.com/ansible\-collections/community\.general/pull/8646](https\://github\.com/ansible\-collections/community\.general/pull/8646)\)\.
* proxmox \- removed the forced conversion of non\-string values to strings to be consistent with the module documentation \([https\://github\.com/ansible\-collections/community\.general/pull/8646](https\://github\.com/ansible\-collections/community\.general/pull/8646)\)\.
<a id="new-modules-1"></a>
<a id="new-modules-2"></a>
### New Modules
* community\.general\.bootc\_manage \- Bootc Switch and Upgrade\.
@@ -217,12 +322,12 @@ Regular bugfix and feature release\.
<a id="v9-2-0"></a>
## v9\.2\.0
<a id="release-summary-2"></a>
<a id="release-summary-3"></a>
### Release Summary
Regular bugfix and feature release\.
<a id="minor-changes-2"></a>
<a id="minor-changes-3"></a>
### Minor Changes
* CmdRunner module utils \- the parameter <code>force\_lang</code> now supports the special value <code>auto</code> which will automatically try and determine the best parsable locale in the system \([https\://github\.com/ansible\-collections/community\.general/pull/8517](https\://github\.com/ansible\-collections/community\.general/pull/8517)\)\.
@@ -234,7 +339,7 @@ Regular bugfix and feature release\.
* virtualbox inventory plugin \- expose a new parameter <code>enable\_advanced\_group\_parsing</code> to change how the VirtualBox dynamic inventory parses VM groups \([https\://github\.com/ansible\-collections/community\.general/issues/8508](https\://github\.com/ansible\-collections/community\.general/issues/8508)\, [https\://github\.com/ansible\-collections/community\.general/pull/8510](https\://github\.com/ansible\-collections/community\.general/pull/8510)\)\.
* wdc\_redfish\_command \- minor change to handle upgrade file for Redfish WD platforms \([https\://github\.com/ansible\-collections/community\.general/pull/8444](https\://github\.com/ansible\-collections/community\.general/pull/8444)\)\.
<a id="bugfixes-2"></a>
<a id="bugfixes-3"></a>
### Bugfixes
* bitwarden lookup plugin \- fix <code>KeyError</code> in <code>search\_field</code> \([https\://github\.com/ansible\-collections/community\.general/issues/8549](https\://github\.com/ansible\-collections/community\.general/issues/8549)\, [https\://github\.com/ansible\-collections/community\.general/pull/8557](https\://github\.com/ansible\-collections/community\.general/pull/8557)\)\.
@@ -259,12 +364,12 @@ Regular bugfix and feature release\.
<a id="v9-1-0"></a>
## v9\.1\.0
<a id="release-summary-3"></a>
<a id="release-summary-4"></a>
### Release Summary
Regular feature and bugfix release\.
<a id="minor-changes-3"></a>
<a id="minor-changes-4"></a>
### Minor Changes
* CmdRunner module util \- argument formats can be specified as plain functions without calling <code>cmd\_runner\_fmt\.as\_func\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8479](https\://github\.com/ansible\-collections/community\.general/pull/8479)\)\.
@@ -281,14 +386,14 @@ Regular feature and bugfix release\.
* redfish\_info \- add command <code>CheckAvailability</code> to check if a service is accessible \([https\://github\.com/ansible\-collections/community\.general/issues/8051](https\://github\.com/ansible\-collections/community\.general/issues/8051)\, [https\://github\.com/ansible\-collections/community\.general/pull/8434](https\://github\.com/ansible\-collections/community\.general/pull/8434)\)\.
* redis\_info \- adds support for getting cluster info \([https\://github\.com/ansible\-collections/community\.general/pull/8464](https\://github\.com/ansible\-collections/community\.general/pull/8464)\)\.
<a id="deprecated-features-1"></a>
<a id="deprecated-features-2"></a>
### Deprecated Features
* CmdRunner module util \- setting the value of the <code>ignore\_none</code> parameter within a <code>CmdRunner</code> context is deprecated and that feature should be removed in community\.general 12\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8479](https\://github\.com/ansible\-collections/community\.general/pull/8479)\)\.
* git\_config \- the <code>list\_all</code> option has been deprecated and will be removed in community\.general 11\.0\.0\. Use the <code>community\.general\.git\_config\_info</code> module instead \([https\://github\.com/ansible\-collections/community\.general/pull/8453](https\://github\.com/ansible\-collections/community\.general/pull/8453)\)\.
* git\_config \- using <code>state\=present</code> without providing <code>value</code> is deprecated and will be disallowed in community\.general 11\.0\.0\. Use the <code>community\.general\.git\_config\_info</code> module instead to read a value \([https\://github\.com/ansible\-collections/community\.general/pull/8453](https\://github\.com/ansible\-collections/community\.general/pull/8453)\)\.
<a id="bugfixes-3"></a>
<a id="bugfixes-4"></a>
### Bugfixes
* git\_config \- fix behavior of <code>state\=absent</code> if <code>value</code> is present \([https\://github\.com/ansible\-collections/community\.general/issues/8436](https\://github\.com/ansible\-collections/community\.general/issues/8436)\, [https\://github\.com/ansible\-collections/community\.general/pull/8452](https\://github\.com/ansible\-collections/community\.general/pull/8452)\)\.
@@ -315,7 +420,7 @@ Regular feature and bugfix release\.
* community\.general\.remove\_keys \- Remove specific keys from dictionaries in a list\.
* community\.general\.replace\_keys \- Replace specific keys in a list of dictionaries\.
<a id="new-modules-2"></a>
<a id="new-modules-3"></a>
### New Modules
* community\.general\.consul\_agent\_check \- Add\, modify\, and delete checks within a consul cluster\.
@@ -326,17 +431,17 @@ Regular feature and bugfix release\.
<a id="v9-0-1"></a>
## v9\.0\.1
<a id="release-summary-4"></a>
<a id="release-summary-5"></a>
### Release Summary
Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
<a id="minor-changes-4"></a>
<a id="minor-changes-5"></a>
### Minor Changes
* ansible\_galaxy\_install \- minor refactor in the module \([https\://github\.com/ansible\-collections/community\.general/pull/8413](https\://github\.com/ansible\-collections/community\.general/pull/8413)\)\.
<a id="bugfixes-4"></a>
<a id="bugfixes-5"></a>
### Bugfixes
* cpanm \- use new <code>VarDict</code> to prevent deprecation warning \([https\://github\.com/ansible\-collections/community\.general/issues/8410](https\://github\.com/ansible\-collections/community\.general/issues/8410)\, [https\://github\.com/ansible\-collections/community\.general/pull/8411](https\://github\.com/ansible\-collections/community\.general/pull/8411)\)\.
@@ -355,12 +460,12 @@ Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
<a id="v9-0-0"></a>
## v9\.0\.0
<a id="release-summary-5"></a>
<a id="release-summary-6"></a>
### Release Summary
This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-05\-20\.
<a id="minor-changes-5"></a>
<a id="minor-changes-6"></a>
### Minor Changes
* PythonRunner module utils \- specialisation of <code>CmdRunner</code> to execute Python scripts \([https\://github\.com/ansible\-collections/community\.general/pull/8289](https\://github\.com/ansible\-collections/community\.general/pull/8289)\)\.
@@ -489,7 +594,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
* django\_manage \- the module will now fail if <code>virtualenv</code> is specified but no virtual environment exists at that location \([https\://github\.com/ansible\-collections/community\.general/pull/8198](https\://github\.com/ansible\-collections/community\.general/pull/8198)\)\.
* redfish\_command\, redfish\_config\, redfish\_info \- change the default for <code>timeout</code> from 10 to 60 \([https\://github\.com/ansible\-collections/community\.general/pull/8198](https\://github\.com/ansible\-collections/community\.general/pull/8198)\)\.
<a id="deprecated-features-2"></a>
<a id="deprecated-features-3"></a>
### Deprecated Features
* MH DependencyCtxMgr module\_utils \- deprecate <code>module\_utils\.mh\.mixin\.deps\.DependencyCtxMgr</code> in favour of <code>module\_utils\.deps</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8280](https\://github\.com/ansible\-collections/community\.general/pull/8280)\)\.
@@ -530,7 +635,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
* cobbler\, gitlab\_runners\, icinga2\, linode\, lxd\, nmap\, online\, opennebula\, proxmox\, scaleway\, stackpath\_compute\, virtualbox\, and xen\_orchestra inventory plugin \- make sure all data received from the remote servers is marked as unsafe\, so remote code execution by obtaining texts that can be evaluated as templates is not possible \([https\://www\.die\-welt\.net/2024/03/remote\-code\-execution\-in\-ansible\-dynamic\-inventory\-plugins/](https\://www\.die\-welt\.net/2024/03/remote\-code\-execution\-in\-ansible\-dynamic\-inventory\-plugins/)\, [https\://github\.com/ansible\-collections/community\.general/pull/8098](https\://github\.com/ansible\-collections/community\.general/pull/8098)\)\.
* keycloak\_identity\_provider \- the client secret was not correctly sanitized by the module\. The return values <code>proposed</code>\, <code>existing</code>\, and <code>end\_state</code>\, as well as the diff\, did contain the client secret unmasked \([https\://github\.com/ansible\-collections/community\.general/pull/8355](https\://github\.com/ansible\-collections/community\.general/pull/8355)\)\.
<a id="bugfixes-5"></a>
<a id="bugfixes-6"></a>
### Bugfixes
* aix\_filesystem \- fix <code>\_validate\_vg</code> not passing VG name to <code>lsvg\_cmd</code> \([https\://github\.com/ansible\-collections/community\.general/issues/8151](https\://github\.com/ansible\-collections/community\.general/issues/8151)\)\.
@@ -645,7 +750,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
* community\.general\.fqdn\_valid \- Validates fully\-qualified domain names against RFC 1123\.
<a id="new-modules-3"></a>
<a id="new-modules-4"></a>
### New Modules
* community\.general\.consul\_acl\_bootstrap \- Bootstrap ACLs in Consul\.

View File

@@ -6,6 +6,105 @@ Community General Release Notes
This changelog describes changes after version 8.0.0.
v9.5.0
======
Release Summary
---------------
Regular bugfix and feature release.
Please note that this is the last feature release for community.general 9.x.y.
From now on, new features will only go into community.general 10.x.y.
Minor Changes
-------------
- dig lookup plugin - add ``port`` option to specify DNS server port (https://github.com/ansible-collections/community.general/pull/8966).
- flatpak - improve the parsing of Flatpak application IDs based on official guidelines (https://github.com/ansible-collections/community.general/pull/8909).
- gio_mime - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8855).
- gitlab_deploy_key - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_group - add many new parameters (https://github.com/ansible-collections/community.general/pull/8908).
- gitlab_group - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_issue - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_merge_request - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_runner - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- icinga2_host - replace loop with dict comprehension (https://github.com/ansible-collections/community.general/pull/8876).
- jira - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8856).
- keycloak_client - add ``client-x509`` choice to ``client_authenticator_type`` (https://github.com/ansible-collections/community.general/pull/8973).
- keycloak_user_federation - add the user federation config parameter ``referral`` to the module arguments (https://github.com/ansible-collections/community.general/pull/8954).
- memset_dns_reload - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_memstore_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_server_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone_domain - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone_record - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- nmcli - add ``conn_enable`` param to reload connection (https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/pull/8897).
- nmcli - add ``state=up`` and ``state=down`` to enable/disable connections (https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/issues/7152, https://github.com/ansible-collections/community.general/pull/8897).
- nmcli - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- npm - add ``force`` parameter to allow ``--force`` (https://github.com/ansible-collections/community.general/pull/8885).
- one_image - add option ``persistent`` to manage image persistence (https://github.com/ansible-collections/community.general/issues/3578, https://github.com/ansible-collections/community.general/pull/8889).
- one_image - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
- one_image - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
- one_image_info - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
- one_image_info - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
- open_iscsi - allow login to a portal with multiple targets without specifying any of them (https://github.com/ansible-collections/community.general/pull/8719).
- opennebula.py - add VM ``id`` and VM ``host`` to inventory host data (https://github.com/ansible-collections/community.general/pull/8532).
- passwordstore lookup plugin - add subkey creation/update support (https://github.com/ansible-collections/community.general/pull/8952).
- proxmox inventory plugin - clean up authentication code (https://github.com/ansible-collections/community.general/pull/8917).
- redfish_command - add handling of the ``PasswordChangeRequired`` message from services in the ``UpdateUserPassword`` command to directly modify the user's password if the requested user is the one invoking the operation (https://github.com/ansible-collections/community.general/issues/8652, https://github.com/ansible-collections/community.general/pull/8653).
- redfish_confg - remove ``CapacityBytes`` from required paramaters of the ``CreateVolume`` command (https://github.com/ansible-collections/community.general/pull/8956).
- redfish_config - add parameter ``storage_none_volume_deletion`` to ``CreateVolume`` command in order to control the automatic deletion of non-RAID volumes (https://github.com/ansible-collections/community.general/pull/8990).
- redfish_info - adds ``RedfishURI`` and ``StorageId`` to Disk inventory (https://github.com/ansible-collections/community.general/pull/8937).
- scaleway_container - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_namespace - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_namespace_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_registry - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_registry_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_namespace - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_namespace_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_user_data - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- udm_dns_record - replace loop with ``dict.update()`` (https://github.com/ansible-collections/community.general/pull/8876).
Deprecated Features
-------------------
- hipchat - the hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020. The module is therefore deprecated and will be removed from community.general 11.0.0 if nobody provides compelling reasons to still keep it (https://github.com/ansible-collections/community.general/pull/8919).
Bugfixes
--------
- cloudflare_dns - fix changing Cloudflare SRV records (https://github.com/ansible-collections/community.general/issues/8679, https://github.com/ansible-collections/community.general/pull/8948).
- cmd_runner module utils - call to ``get_best_parsable_locales()`` was missing parameter (https://github.com/ansible-collections/community.general/pull/8929).
- dig lookup plugin - fix using only the last nameserver specified (https://github.com/ansible-collections/community.general/pull/8970).
- django_command - option ``command`` is now split lexically before passed to underlying PythonRunner (https://github.com/ansible-collections/community.general/pull/8944).
- homectl - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4691, https://github.com/ansible-collections/community.general/pull/8987).
- ini_file - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
- ipa_host - add ``force_create``, fix ``enabled`` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/1094, https://github.com/ansible-collections/community.general/pull/8920).
- ipa_hostgroup - fix ``enabled `` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/8408, https://github.com/ansible-collections/community.general/pull/8900).
- java_keystore - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
- jenkins_plugin - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
- kdeconfig - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
- keycloak_realm - fix change detection in check mode by sorting the lists in the realms beforehand (https://github.com/ansible-collections/community.general/pull/8877).
- keycloak_user_federation - add module argument allowing users to configure the update mode for the parameter ``bindCredential`` (https://github.com/ansible-collections/community.general/pull/8898).
- keycloak_user_federation - minimize change detection by setting ``krbPrincipalAttribute`` to ``''`` in Keycloak responses if missing (https://github.com/ansible-collections/community.general/pull/8785).
- keycloak_user_federation - remove ``lastSync`` parameter from Keycloak responses to minimize diff/changes (https://github.com/ansible-collections/community.general/pull/8812).
- keycloak_userprofile - fix empty response when fetching userprofile component by removing ``parent=parent_id`` filter (https://github.com/ansible-collections/community.general/pull/8923).
- keycloak_userprofile - improve diff by deserializing the fetched ``kc.user.profile.config`` and serialize it only when sending back (https://github.com/ansible-collections/community.general/pull/8940).
- lxd_container - fix bug introduced in previous commit (https://github.com/ansible-collections/community.general/pull/8895, https://github.com/ansible-collections/community.general/issues/8888).
- one_service - fix service creation after it was deleted with ``unique`` parameter (https://github.com/ansible-collections/community.general/issues/3137, https://github.com/ansible-collections/community.general/pull/8887).
- pam_limits - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
- python_runner module utils - parameter ``path_prefix`` was being handled as string when it should be a list (https://github.com/ansible-collections/community.general/pull/8944).
- udm_user - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4690, https://github.com/ansible-collections/community.general/pull/8987).
New Modules
-----------
- community.general.ipa_getkeytab - Manage keytab file in FreeIPA.
v9.4.0
======

View File

@@ -37,7 +37,7 @@ For more information about communication, see the [Ansible communication guide](
## Tested with Ansible
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
## External requirements

View File

@@ -1248,3 +1248,181 @@ releases:
name: one_vnet
namespace: ''
release_date: '2024-09-09'
9.5.0:
changes:
bugfixes:
- cloudflare_dns - fix changing Cloudflare SRV records (https://github.com/ansible-collections/community.general/issues/8679,
https://github.com/ansible-collections/community.general/pull/8948).
- cmd_runner module utils - call to ``get_best_parsable_locales()`` was missing
parameter (https://github.com/ansible-collections/community.general/pull/8929).
- dig lookup plugin - fix using only the last nameserver specified (https://github.com/ansible-collections/community.general/pull/8970).
- django_command - option ``command`` is now split lexically before passed
to underlying PythonRunner (https://github.com/ansible-collections/community.general/pull/8944).
- homectl - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4691,
https://github.com/ansible-collections/community.general/pull/8987).
- ini_file - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
https://github.com/ansible-collections/community.general/pull/8925).
- ipa_host - add ``force_create``, fix ``enabled`` and ``disabled`` states
(https://github.com/ansible-collections/community.general/issues/1094, https://github.com/ansible-collections/community.general/pull/8920).
- ipa_hostgroup - fix ``enabled `` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/8408,
https://github.com/ansible-collections/community.general/pull/8900).
- java_keystore - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
https://github.com/ansible-collections/community.general/pull/8925).
- jenkins_plugin - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
https://github.com/ansible-collections/community.general/pull/8925).
- kdeconfig - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
https://github.com/ansible-collections/community.general/pull/8925).
- keycloak_realm - fix change detection in check mode by sorting the lists
in the realms beforehand (https://github.com/ansible-collections/community.general/pull/8877).
- keycloak_user_federation - add module argument allowing users to configure
the update mode for the parameter ``bindCredential`` (https://github.com/ansible-collections/community.general/pull/8898).
- keycloak_user_federation - minimize change detection by setting ``krbPrincipalAttribute``
to ``''`` in Keycloak responses if missing (https://github.com/ansible-collections/community.general/pull/8785).
- keycloak_user_federation - remove ``lastSync`` parameter from Keycloak responses
to minimize diff/changes (https://github.com/ansible-collections/community.general/pull/8812).
- keycloak_userprofile - fix empty response when fetching userprofile component
by removing ``parent=parent_id`` filter (https://github.com/ansible-collections/community.general/pull/8923).
- keycloak_userprofile - improve diff by deserializing the fetched ``kc.user.profile.config``
and serialize it only when sending back (https://github.com/ansible-collections/community.general/pull/8940).
- lxd_container - fix bug introduced in previous commit (https://github.com/ansible-collections/community.general/pull/8895,
https://github.com/ansible-collections/community.general/issues/8888).
- one_service - fix service creation after it was deleted with ``unique``
parameter (https://github.com/ansible-collections/community.general/issues/3137,
https://github.com/ansible-collections/community.general/pull/8887).
- pam_limits - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
https://github.com/ansible-collections/community.general/pull/8925).
- python_runner module utils - parameter ``path_prefix`` was being handled
as string when it should be a list (https://github.com/ansible-collections/community.general/pull/8944).
- udm_user - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4690,
https://github.com/ansible-collections/community.general/pull/8987).
deprecated_features:
- hipchat - the hipchat service has been discontinued and the self-hosted
variant has been End of Life since 2020. The module is therefore deprecated
and will be removed from community.general 11.0.0 if nobody provides compelling
reasons to still keep it (https://github.com/ansible-collections/community.general/pull/8919).
minor_changes:
- dig lookup plugin - add ``port`` option to specify DNS server port (https://github.com/ansible-collections/community.general/pull/8966).
- flatpak - improve the parsing of Flatpak application IDs based on official
guidelines (https://github.com/ansible-collections/community.general/pull/8909).
- gio_mime - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8855).
- gitlab_deploy_key - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_group - add many new parameters (https://github.com/ansible-collections/community.general/pull/8908).
- gitlab_group - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_issue - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_merge_request - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- gitlab_runner - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- icinga2_host - replace loop with dict comprehension (https://github.com/ansible-collections/community.general/pull/8876).
- jira - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8856).
- keycloak_client - add ``client-x509`` choice to ``client_authenticator_type``
(https://github.com/ansible-collections/community.general/pull/8973).
- keycloak_user_federation - add the user federation config parameter ``referral``
to the module arguments (https://github.com/ansible-collections/community.general/pull/8954).
- memset_dns_reload - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_memstore_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_server_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone_domain - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- memset_zone_record - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
- nmcli - add ``conn_enable`` param to reload connection (https://github.com/ansible-collections/community.general/issues/3752,
https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/pull/8897).
- nmcli - add ``state=up`` and ``state=down`` to enable/disable connections
(https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704,
https://github.com/ansible-collections/community.general/issues/7152, https://github.com/ansible-collections/community.general/pull/8897).
- nmcli - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- npm - add ``force`` parameter to allow ``--force`` (https://github.com/ansible-collections/community.general/pull/8885).
- one_image - add option ``persistent`` to manage image persistence (https://github.com/ansible-collections/community.general/issues/3578,
https://github.com/ansible-collections/community.general/pull/8889).
- one_image - extend xsd scheme to make it return a lot more info about image
(https://github.com/ansible-collections/community.general/pull/8889).
- one_image - refactor code to make it more similar to ``one_template`` and
``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
- one_image_info - extend xsd scheme to make it return a lot more info about
image (https://github.com/ansible-collections/community.general/pull/8889).
- one_image_info - refactor code to make it more similar to ``one_template``
and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
- open_iscsi - allow login to a portal with multiple targets without specifying
any of them (https://github.com/ansible-collections/community.general/pull/8719).
- opennebula.py - add VM ``id`` and VM ``host`` to inventory host data (https://github.com/ansible-collections/community.general/pull/8532).
- passwordstore lookup plugin - add subkey creation/update support (https://github.com/ansible-collections/community.general/pull/8952).
- proxmox inventory plugin - clean up authentication code (https://github.com/ansible-collections/community.general/pull/8917).
- redfish_command - add handling of the ``PasswordChangeRequired`` message
from services in the ``UpdateUserPassword`` command to directly modify the
user's password if the requested user is the one invoking the operation
(https://github.com/ansible-collections/community.general/issues/8652, https://github.com/ansible-collections/community.general/pull/8653).
- redfish_confg - remove ``CapacityBytes`` from required paramaters of the
``CreateVolume`` command (https://github.com/ansible-collections/community.general/pull/8956).
- redfish_config - add parameter ``storage_none_volume_deletion`` to ``CreateVolume``
command in order to control the automatic deletion of non-RAID volumes (https://github.com/ansible-collections/community.general/pull/8990).
- redfish_info - adds ``RedfishURI`` and ``StorageId`` to Disk inventory (https://github.com/ansible-collections/community.general/pull/8937).
- scaleway_container - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_info - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_namespace - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_namespace_info - replace Python 2.6 construct with dict
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_registry - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_container_registry_info - replace Python 2.6 construct with dict
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_info - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_namespace - replace Python 2.6 construct with dict comprehensions
(https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_function_namespace_info - replace Python 2.6 construct with dict
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
- scaleway_user_data - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
- udm_dns_record - replace loop with ``dict.update()`` (https://github.com/ansible-collections/community.general/pull/8876).
release_summary: 'Regular bugfix and feature release.
Please note that this is the last feature release for community.general 9.x.y.
From now on, new features will only go into community.general 10.x.y.'
fragments:
- 8532-expand-opennuebula-inventory-data.yml
- 8652-Redfish-Password-Change-Required.yml
- 8679-fix-cloudflare-srv.yml
- 8719-openiscsi-add-multiple-targets.yaml
- 8785-keycloak_user_federation-set-krbPrincipalAttribute-to-empty-string-if-missing.yaml
- 8812-keycloak-user-federation-remove-lastSync-param-from-kc-responses.yml
- 8855-gio_mime_vardict.yml
- 8856-jira_vardict.yml
- 8858-dict-comprehension.yml
- 8876-dict-items-loop.yml
- 8877-keycloak_realm-sort-lists-before-change-detection.yaml
- 8885-add-force-flag-for-nmp.yml
- 8887-fix-one_service-unique.yml
- 8889-refactor-one-image-modules.yml
- 8895-fix-comprehension.yaml
- 8897-nmcli-add-reload-and-up-down.yml
- 8898-add-arg-to-exclude-bind-credential-from-change-check.yaml
- 8900-ipa-hostgroup-fix-states.yml
- 8908-add-gitlab-group-params.yml
- 8909-flatpak-improve-name-parsing.yaml
- 8917-proxmox-clean-auth.yml
- 8920-ipa-host-fix-state.yml
- 8923-keycloak_userprofile-fix-empty-response-when-fetching-userprofile.yml
- 8925-atomic.yml
- 8929-cmd_runner-bugfix.yml
- 8937-add-StorageId-RedfishURI-to-disk-facts.yml
- 8940-keycloak_userprofile-improve-diff.yml
- 8944-django-command-fix.yml
- 8952-password-store-lookup-create-subkey-support.yml
- 8954-keycloak-user-federation-add-referral-parameter.yml
- 8956-remove-capacitybytes-from-the-required-parameters_list.yml
- 8966-dig-add-port-option.yml
- 8970-fix-dig-multi-nameservers.yml
- 8973-keycloak_client-add-x509-auth.yml
- 8987-legacycrypt.yml
- 8990.yml
- 9.5.0.yml
- deprecate-hipchat.yml
modules:
- description: Manage keytab file in FreeIPA.
name: ipa_getkeytab
namespace: ''
release_date: '2024-10-07'

View File

@@ -68,20 +68,27 @@ This is meant to be done once, then every time you need to execute the command y
with runner("version") as ctx:
dummy, stdout, dummy = ctx.run()
# passes arg 'data' to AnsibleModule.run_command()
with runner("type name", data=stdin_data) as ctx:
dummy, stdout, dummy = ctx.run()
# Another way of expressing it
dummy, stdout, dummy = runner("version").run()
Note that you can pass values for the arguments when calling ``run()``,
otherwise ``CmdRunner`` uses the module options with the exact same names to
provide values for the runner arguments. If no value is passed and no module option
is found for the name specified, then an exception is raised, unless the
argument is using ``cmd_runner_fmt.as_fixed`` as format function like the
``version`` in the example above. See more about it below.
Note that you can pass values for the arguments when calling ``run()``, otherwise ``CmdRunner``
uses the module options with the exact same names to provide values for the runner arguments.
If no value is passed and no module option is found for the name specified, then an exception is raised, unless
the argument is using ``cmd_runner_fmt.as_fixed`` as format function like the ``version`` in the example above.
See more about it below.
In the first example, values of ``type``, ``force``, ``no_deps`` and others
are taken straight from the module, whilst ``galaxy_cmd`` and ``upgrade`` are
passed explicitly.
.. note::
It is not possible to automatically retrieve values of suboptions.
That generates a resulting command line similar to (example taken from the
output of an integration test):
@@ -110,7 +117,7 @@ into something formatted for the command line.
Argument format function
""""""""""""""""""""""""
An ``arg_format`` function should be of the form:
An ``arg_format`` function is defined in the form similar to:
.. code-block:: python
@@ -155,7 +162,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_list()``
- Example:
- Examples:
+----------------------+---------------------+
| Value | Outcome |
+======================+=====================+
@@ -167,12 +174,11 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- ``cmd_runner_fmt.as_bool()``
This method receives two different parameters: ``args_true`` and ``args_false``, latter being optional.
If the boolean evaluation of ``value`` is ``True``, the format function returns ``args_true``.
If the boolean evaluation is ``False``, then the function returns ``args_false``
if it was provided, or ``[]`` otherwise.
If the boolean evaluation is ``False``, then the function returns ``args_false`` if it was provided, or ``[]`` otherwise.
- Creation:
- Creation (one arg):
``cmd_runner_fmt.as_bool("--force")``
- Example:
- Examples:
+------------+--------------------+
| Value | Outcome |
+============+====================+
@@ -180,6 +186,30 @@ In these descriptions ``value`` refers to the single parameter passed to the for
+------------+--------------------+
| ``False`` | ``[]`` |
+------------+--------------------+
- Creation (two args, ``None`` treated as ``False``):
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it")``
- Examples:
+------------+----------------------+
| Value | Outcome |
+============+======================+
| ``True`` | ``["--relax"]`` |
+------------+----------------------+
| ``False`` | ``["--dont-do-it"]`` |
+------------+----------------------+
| | ``["--dont-do-it"]`` |
+------------+----------------------+
- Creation (two args, ``None`` is ignored):
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it", ignore_none=True)``
- Examples:
+------------+----------------------+
| Value | Outcome |
+============+======================+
| ``True`` | ``["--relax"]`` |
+------------+----------------------+
| ``False`` | ``["--dont-do-it"]`` |
+------------+----------------------+
| | ``[]`` |
+------------+----------------------+
- ``cmd_runner_fmt.as_bool_not()``
This method receives one parameter, which is returned by the function when the boolean evaluation
@@ -187,7 +217,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_bool_not("--no-deps")``
- Example:
- Examples:
+-------------+---------------------+
| Value | Outcome |
+=============+=====================+
@@ -202,7 +232,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_optval("-i")``
- Example:
- Examples:
+---------------+---------------------+
| Value | Outcome |
+===============+=====================+
@@ -216,7 +246,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_opt_val("--name")``
- Example:
- Examples:
+--------------+--------------------------+
| Value | Outcome |
+==============+==========================+
@@ -229,7 +259,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_opt_eq_val("--num-cpus")``
- Example:
- Examples:
+------------+-------------------------+
| Value | Outcome |
+============+=========================+
@@ -243,7 +273,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_fixed("--version")``
- Example:
- Examples:
+---------+-----------------------+
| Value | Outcome |
+=========+=======================+
@@ -265,7 +295,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_map(dict(a=1, b=2, c=3), default=42)``
- Example:
- Examples:
+---------------------+---------------+
| Value | Outcome |
+=====================+===============+
@@ -359,6 +389,8 @@ Settings that can be passed to the ``CmdRunner`` constructor are:
Command to be executed. It can be a single string, the executable name, or a list
of strings containing the executable name as the first element and, optionally, fixed parameters.
Those parameters are used in all executions of the runner.
The *executable* pointed by this parameter (whether itself when ``str`` or its first element when ``list``) is
processed using ``AnsibleModule.get_bin_path()`` *unless* it is an absolute path or contains the character ``/``.
- ``arg_formats: dict``
Mapping of argument names to formatting functions.
- ``default_args_order: str``
@@ -394,6 +426,10 @@ When creating a context, the additional settings that can be passed to the call
Defaults to ``False``.
- ``check_mode_return: any``
If ``check_mode_skip=True``, then return this value instead.
- valid named arguments to ``AnsibleModule.run_command()``
Other than ``args``, any valid argument to ``run_command()`` can be passed when setting up the run context.
For example, ``data`` can be used to send information to the command's standard input.
Or ``cwd`` can be used to run the command inside a specific working directory.
Additionally, any other valid parameters for ``AnsibleModule.run_command()`` may be passed, but unexpected behavior
might occur if redefining options already present in the runner or its context creation. Use with caution.

View File

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

View File

@@ -75,6 +75,10 @@ plugin_routing:
deprecation:
removal_version: 10.0.0
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
hipchat:
deprecation:
removal_version: 11.0.0
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
rax_cbs_attachments:
tombstone:
removal_version: 9.0.0

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Alexei Znamensky <russoz@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
class ModuleDocFragment(object):
DOCUMENTATION = r'''
options:
global:
description:
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
type: bool
default: false
executable:
description:
- Path to the C(pipx) installed in the system.
- >
If not specified, the module will use C(python -m pipx) to run the tool,
using the same Python interpreter as ansible itself.
type: path
notes:
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
- Please note that C(pipx) requires Python 3.6 or above.
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
- >
This module will honor C(pipx) environment variables such as but not limited to E(PIPX_HOME) and E(PIPX_BIN_DIR)
passed using the R(environment Ansible keyword, playbooks_environment).
seealso:
- name: C(pipx) command manual page
description: Manual page for the command.
link: https://pipx.pypa.io/latest/docs/
'''

View File

@@ -199,6 +199,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
continue
server['name'] = vm.NAME
server['id'] = vm.ID
if hasattr(vm.HISTORY_RECORDS, 'HISTORY') and vm.HISTORY_RECORDS.HISTORY:
server['host'] = vm.HISTORY_RECORDS.HISTORY[-1].HOSTNAME
server['LABELS'] = labels
server['v4_first_ip'] = self._get_vm_ipv4(vm)
server['v6_first_ip'] = self._get_vm_ipv6(vm)

View File

@@ -275,11 +275,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return self.session
def _get_auth(self):
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
if self.proxmox_password:
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password})
a = self._get_session()

View File

@@ -75,6 +75,11 @@ DOCUMENTATION = '''
default: false
type: bool
version_added: 7.5.0
port:
description: Use port as target port when looking up DNS records.
default: 53
type: int
version_added: 9.5.0
notes:
- ALL is not a record per-se, merely the listed fields are available for any record results you retrieve in the form of a dictionary.
- While the 'dig' lookup plugin supports anything which dnspython supports out of the box, only a subset can be converted into a dictionary.
@@ -330,11 +335,13 @@ class LookupModule(LookupBase):
myres.use_edns(0, ednsflags=dns.flags.DO, payload=edns_size)
domains = []
nameservers = []
qtype = self.get_option('qtype')
flat = self.get_option('flat')
fail_on_error = self.get_option('fail_on_error')
real_empty = self.get_option('real_empty')
tcp = self.get_option('tcp')
port = self.get_option('port')
try:
rdclass = dns.rdataclass.from_text(self.get_option('class'))
except Exception as e:
@@ -345,7 +352,6 @@ class LookupModule(LookupBase):
if t.startswith('@'): # e.g. "@10.0.1.2,192.0.2.1" is ok.
nsset = t[1:].split(',')
for ns in nsset:
nameservers = []
# Check if we have a valid IP address. If so, use that, otherwise
# try to resolve name to address using system's resolver. If that
# fails we bail out.
@@ -358,7 +364,6 @@ class LookupModule(LookupBase):
nameservers.append(nsaddr)
except Exception as e:
raise AnsibleError("dns lookup NS: %s" % to_native(e))
myres.nameservers = nameservers
continue
if '=' in t:
try:
@@ -397,6 +402,11 @@ class LookupModule(LookupBase):
# print "--- domain = {0} qtype={1} rdclass={2}".format(domain, qtype, rdclass)
if port:
myres.port = port
if len(nameservers) > 0:
myres.nameservers = nameservers
if qtype.upper() == 'PTR':
reversed_domains = []
for domain in domains:

View File

@@ -14,7 +14,7 @@ DOCUMENTATION = '''
short_description: manage passwords with passwordstore.org's pass utility
description:
- Enables Ansible to retrieve, create or update passwords from the passwordstore.org pass utility.
It also retrieves YAML style keys stored as multilines in the passwordfile.
It can also retrieve, create or update YAML style keys stored as multilines in the passwordfile.
- To avoid problems when accessing multiple secrets at once, add C(auto-expand-secmem) to
C(~/.gnupg/gpg-agent.conf). Where this is not possible, consider using O(lock=readwrite) instead.
options:
@@ -33,11 +33,11 @@ DOCUMENTATION = '''
env:
- name: PASSWORD_STORE_DIR
create:
description: Create the password if it does not already exist. Takes precedence over O(missing).
description: Create the password or the subkey if it does not already exist. Takes precedence over O(missing).
type: bool
default: false
overwrite:
description: Overwrite the password if it does already exist.
description: Overwrite the password or the subkey if it does already exist.
type: bool
default: false
umask:
@@ -53,7 +53,9 @@ DOCUMENTATION = '''
type: bool
default: false
subkey:
description: Return a specific subkey of the password. When set to V(password), always returns the first line.
description:
- By default return a specific subkey of the password. When set to V(password), always returns the first line.
- With O(overwrite=true), it will create the subkey and return it.
type: str
default: password
userpass:
@@ -64,7 +66,7 @@ DOCUMENTATION = '''
type: integer
default: 16
backup:
description: Used with O(overwrite=true). Backup the previous password in a subkey.
description: Used with O(overwrite=true). Backup the previous password or subkey in a subkey.
type: bool
default: false
nosymbols:
@@ -189,6 +191,17 @@ tasks.yml: |
vars:
mypassword: "{{ lookup('community.general.passwordstore', 'example/test', missing='create')}}"
- name: >-
Create a random 16 character password in a subkey. If the password file already exists, just add the subkey in it.
If the subkey exists, returns it
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, subkey='foo') }}"
- name: >-
Create a random 16 character password in a subkey. Overwrite if it already exists and backup the old one.
ansible.builtin.debug:
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, subkey='user', overwrite=true, backup=true) }}"
- name: Prints 'abc' if example/test does not exist, just give the password otherwise
ansible.builtin.debug:
var: mypassword
@@ -411,15 +424,48 @@ class LookupModule(LookupBase):
def update_password(self):
# generate new password, insert old lines from current result and return new password
# if the target is a subkey, only modify the subkey
newpass = self.get_newpass()
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
msg = newpass
if self.paramvals['preserve'] or self.paramvals['timestamp']:
msg += '\n'
if self.paramvals['preserve'] and self.passoutput[1:]:
msg += '\n'.join(self.passoutput[1:]) + '\n'
if self.paramvals['timestamp'] and self.paramvals['backup']:
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
subkey = self.paramvals["subkey"]
if subkey != "password":
msg_lines = []
subkey_exists = False
subkey_line = "{0}: {1}".format(subkey, newpass)
oldpass = None
for line in self.passoutput:
if line.startswith("{0}: ".format(subkey)):
oldpass = self.passdict[subkey]
line = subkey_line
subkey_exists = True
msg_lines.append(line)
if not subkey_exists:
msg_lines.insert(2, subkey_line)
if self.paramvals["timestamp"] and self.paramvals["backup"] and oldpass and oldpass != newpass:
msg_lines.append(
"lookup_pass: old subkey '{0}' password was {1} (Updated on {2})\n".format(
subkey, oldpass, datetime
)
)
msg = os.linesep.join(msg_lines)
else:
msg = newpass
if self.paramvals['preserve'] or self.paramvals['timestamp']:
msg += '\n'
if self.paramvals['preserve'] and self.passoutput[1:]:
msg += '\n'.join(self.passoutput[1:]) + '\n'
if self.paramvals['timestamp'] and self.paramvals['backup']:
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
try:
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
except (subprocess.CalledProcessError) as e:
@@ -431,13 +477,21 @@ class LookupModule(LookupBase):
# use pwgen to generate the password and insert values with pass -m
newpass = self.get_newpass()
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
msg = newpass
subkey = self.paramvals["subkey"]
if subkey != "password":
msg = "\n\n{0}: {1}".format(subkey, newpass)
else:
msg = newpass
if self.paramvals['timestamp']:
msg += '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
try:
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
except (subprocess.CalledProcessError) as e:
raise AnsibleError('exit code {0} while running {1}. Error output: {2}'.format(e.returncode, e.cmd, e.output))
return newpass
def get_passresult(self):
@@ -525,7 +579,10 @@ class LookupModule(LookupBase):
self.parse_params(term) # parse the input into paramvals
with self.opt_lock('readwrite'):
if self.check_pass(): # password exists
if self.paramvals['overwrite'] and self.paramvals['subkey'] == 'password':
if self.paramvals['overwrite']:
with self.opt_lock('write'):
result.append(self.update_password())
elif self.paramvals["subkey"] != "password" and not self.passdict.get(self.paramvals['subkey']): # password exists but not the subkey
with self.opt_lock('write'):
result.append(self.update_password())
else:

View File

@@ -239,7 +239,7 @@ class CmdRunner(object):
self.check_rc = check_rc
if force_lang == "auto":
try:
self.force_lang = get_best_parsable_locale()
self.force_lang = get_best_parsable_locale(module)
except RuntimeWarning:
self.force_lang = "C"
else:

View File

@@ -16,6 +16,7 @@ from ansible.module_utils.six import string_types
from ansible.module_utils.basic import AnsibleModule
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
HAS_PYONE = True
try:
@@ -347,3 +348,90 @@ class OpenNebulaModule:
result: the Ansible result
"""
raise NotImplementedError("Method requires implementation")
def get_image_list_id(self, image, element):
"""
This is a helper function for get_image_info to iterate over a simple list of objects
"""
list_of_id = []
if element == 'VMS':
image_list = image.VMS
if element == 'CLONES':
image_list = image.CLONES
if element == 'APP_CLONES':
image_list = image.APP_CLONES
for iter in image_list.ID:
list_of_id.append(
# These are optional so firstly check for presence
getattr(iter, 'ID', 'Null'),
)
return list_of_id
def get_image_snapshots_list(self, image):
"""
This is a helper function for get_image_info to iterate over a dictionary
"""
list_of_snapshots = []
for iter in image.SNAPSHOTS.SNAPSHOT:
list_of_snapshots.append({
'date': iter['DATE'],
'parent': iter['PARENT'],
'size': iter['SIZE'],
# These are optional so firstly check for presence
'allow_orhans': getattr(image.SNAPSHOTS, 'ALLOW_ORPHANS', 'Null'),
'children': getattr(iter, 'CHILDREN', 'Null'),
'active': getattr(iter, 'ACTIVE', 'Null'),
'name': getattr(iter, 'NAME', 'Null'),
})
return list_of_snapshots
def get_image_info(self, image):
"""
This method is used by one_image and one_image_info modules to retrieve
information from XSD scheme of an image
Returns: a copy of the parameters that includes the resolved parameters.
"""
info = {
'id': image.ID,
'name': image.NAME,
'state': IMAGE_STATES[image.STATE],
'running_vms': image.RUNNING_VMS,
'used': bool(image.RUNNING_VMS),
'user_name': image.UNAME,
'user_id': image.UID,
'group_name': image.GNAME,
'group_id': image.GID,
'permissions': {
'owner_u': image.PERMISSIONS.OWNER_U,
'owner_m': image.PERMISSIONS.OWNER_M,
'owner_a': image.PERMISSIONS.OWNER_A,
'group_u': image.PERMISSIONS.GROUP_U,
'group_m': image.PERMISSIONS.GROUP_M,
'group_a': image.PERMISSIONS.GROUP_A,
'other_u': image.PERMISSIONS.OTHER_U,
'other_m': image.PERMISSIONS.OTHER_M,
'other_a': image.PERMISSIONS.OTHER_A
},
'type': image.TYPE,
'disk_type': image.DISK_TYPE,
'persistent': image.PERSISTENT,
'regtime': image.REGTIME,
'source': image.SOURCE,
'path': image.PATH,
'fstype': getattr(image, 'FSTYPE', 'Null'),
'size': image.SIZE,
'cloning_ops': image.CLONING_OPS,
'cloning_id': image.CLONING_ID,
'target_snapshot': image.TARGET_SNAPSHOT,
'datastore_id': image.DATASTORE_ID,
'datastore': image.DATASTORE,
'vms': self.get_image_list_id(image, 'VMS'),
'clones': self.get_image_list_id(image, 'CLONES'),
'app_clones': self.get_image_list_id(image, 'APP_CLONES'),
'snapshots': self.get_image_snapshots_list(image),
'template': image.TEMPLATE,
}
return info

View File

@@ -9,6 +9,12 @@ __metaclass__ = type
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
pipx_common_argspec = {
"global": dict(type='bool', default=False),
"executable": dict(type='path'),
}
_state_map = dict(
install='install',
install_all='install-all',

View File

@@ -22,10 +22,12 @@ class PythonRunner(CmdRunner):
if (os.path.isabs(python) or '/' in python):
self.python = python
elif self.has_venv:
path_prefix = os.path.join(venv, "bin")
if path_prefix is None:
path_prefix = []
path_prefix.append(os.path.join(venv, "bin"))
if environ_update is None:
environ_update = {}
environ_update["PATH"] = "%s:%s" % (path_prefix, os.environ["PATH"])
environ_update["PATH"] = "%s:%s" % (":".join(path_prefix), os.environ["PATH"])
environ_update["VIRTUAL_ENV"] = venv
python_cmd = [self.python] + _ensure_list(command)

View File

@@ -165,11 +165,11 @@ class RedfishUtils(object):
if not allow_no_resp:
raise
except HTTPError as e:
msg = self._get_extended_message(e)
msg, data = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on GET request to '%s', extended message: '%s'"
% (e.code, uri, msg),
'status': e.code}
'status': e.code, 'data': data}
except URLError as e:
return {'ret': False, 'msg': "URL Error on GET request to '%s': '%s'"
% (uri, e.reason)}
@@ -208,11 +208,11 @@ class RedfishUtils(object):
data = None
headers = {k.lower(): v for (k, v) in resp.info().items()}
except HTTPError as e:
msg = self._get_extended_message(e)
msg, data = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on POST request to '%s', extended message: '%s'"
% (e.code, uri, msg),
'status': e.code}
'status': e.code, 'data': data}
except URLError as e:
return {'ret': False, 'msg': "URL Error on POST request to '%s': '%s'"
% (uri, e.reason)}
@@ -256,11 +256,11 @@ class RedfishUtils(object):
follow_redirects='all',
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
except HTTPError as e:
msg = self._get_extended_message(e)
msg, data = self._get_extended_message(e)
return {'ret': False, 'changed': False,
'msg': "HTTP Error %s on PATCH request to '%s', extended message: '%s'"
% (e.code, uri, msg),
'status': e.code}
'status': e.code, 'data': data}
except URLError as e:
return {'ret': False, 'changed': False,
'msg': "URL Error on PATCH request to '%s': '%s'" % (uri, e.reason)}
@@ -291,11 +291,11 @@ class RedfishUtils(object):
follow_redirects='all',
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
except HTTPError as e:
msg = self._get_extended_message(e)
msg, data = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on PUT request to '%s', extended message: '%s'"
% (e.code, uri, msg),
'status': e.code}
'status': e.code, 'data': data}
except URLError as e:
return {'ret': False, 'msg': "URL Error on PUT request to '%s': '%s'"
% (uri, e.reason)}
@@ -317,11 +317,11 @@ class RedfishUtils(object):
follow_redirects='all',
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
except HTTPError as e:
msg = self._get_extended_message(e)
msg, data = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on DELETE request to '%s', extended message: '%s'"
% (e.code, uri, msg),
'status': e.code}
'status': e.code, 'data': data}
except URLError as e:
return {'ret': False, 'msg': "URL Error on DELETE request to '%s': '%s'"
% (uri, e.reason)}
@@ -391,8 +391,10 @@ class RedfishUtils(object):
:param error: an HTTPError exception
:type error: HTTPError
:return: the ExtendedInfo message if present, else standard HTTP error
:return: the JSON data of the response if present
"""
msg = http_client.responses.get(error.code, '')
data = None
if error.code >= 400:
try:
body = error.read().decode('utf-8')
@@ -406,7 +408,7 @@ class RedfishUtils(object):
msg = str(data['error']['@Message.ExtendedInfo'])
except Exception:
pass
return msg
return msg, data
def _init_session(self):
pass
@@ -864,6 +866,7 @@ class RedfishUtils(object):
return response
data = response['data']
controller_name = 'Controller 1'
storage_id = data['Id']
if 'Controllers' in data:
controllers_uri = data['Controllers'][u'@odata.id']
@@ -898,6 +901,7 @@ class RedfishUtils(object):
data = response['data']
drive_result = {}
drive_result['RedfishURI'] = data['@odata.id']
for property in properties:
if property in data:
if data[property] is not None:
@@ -909,6 +913,7 @@ class RedfishUtils(object):
drive_result[property] = data[property]
drive_results.append(drive_result)
drives = {'Controller': controller_name,
'StorageId': storage_id,
'Drives': drive_results}
result["entries"].append(drives)
@@ -1245,32 +1250,49 @@ class RedfishUtils(object):
return response
return {'ret': True, 'changed': True}
def _find_account_uri(self, username=None, acct_id=None):
def _find_account_uri(self, username=None, acct_id=None, password_change_uri=None):
if not any((username, acct_id)):
return {'ret': False, 'msg':
'Must provide either account_id or account_username'}
response = self.get_request(self.root_uri + self.accounts_uri)
if response['ret'] is False:
return response
data = response['data']
uris = [a.get('@odata.id') for a in data.get('Members', []) if
a.get('@odata.id')]
for uri in uris:
response = self.get_request(self.root_uri + uri)
if password_change_uri:
# Password change required; go directly to the specified URI
response = self.get_request(self.root_uri + password_change_uri)
if response['ret'] is False:
continue
return response
data = response['data']
headers = response['headers']
if username:
if username == data.get('UserName'):
return {'ret': True, 'data': data,
'headers': headers, 'uri': uri}
'headers': headers, 'uri': password_change_uri}
if acct_id:
if acct_id == data.get('Id'):
return {'ret': True, 'data': data,
'headers': headers, 'uri': uri}
'headers': headers, 'uri': password_change_uri}
else:
# Walk the accounts collection to find the desired user
response = self.get_request(self.root_uri + self.accounts_uri)
if response['ret'] is False:
return response
data = response['data']
uris = [a.get('@odata.id') for a in data.get('Members', []) if
a.get('@odata.id')]
for uri in uris:
response = self.get_request(self.root_uri + uri)
if response['ret'] is False:
continue
data = response['data']
headers = response['headers']
if username:
if username == data.get('UserName'):
return {'ret': True, 'data': data,
'headers': headers, 'uri': uri}
if acct_id:
if acct_id == data.get('Id'):
return {'ret': True, 'data': data,
'headers': headers, 'uri': uri}
return {'ret': False, 'no_match': True, 'msg':
'No account with the given account_id or account_username found'}
@@ -1491,7 +1513,8 @@ class RedfishUtils(object):
'Must provide account_password for UpdateUserPassword command'}
response = self._find_account_uri(username=user.get('account_username'),
acct_id=user.get('account_id'))
acct_id=user.get('account_id'),
password_change_uri=user.get('account_passwordchangerequired'))
if not response['ret']:
return response
@@ -1534,6 +1557,31 @@ class RedfishUtils(object):
resp['msg'] = 'Modified account service'
return resp
def check_password_change_required(self, return_data):
"""
Checks a response if a user needs to change their password
:param return_data: The return data for a failed request
:return: None or the URI of the account to update
"""
uri = None
if 'data' in return_data:
# Find the extended messages in the response payload
extended_messages = return_data['data'].get('error', {}).get('@Message.ExtendedInfo', [])
if len(extended_messages) == 0:
extended_messages = return_data['data'].get('@Message.ExtendedInfo', [])
# Go through each message and look for Base.1.X.PasswordChangeRequired
for message in extended_messages:
message_id = message.get('MessageId')
if message_id is None:
# While this is invalid, treat the lack of a MessageId as "no message"
continue
if message_id.startswith('Base.1.') and message_id.endswith('.PasswordChangeRequired'):
# Password change required; get the URI of the user account
uri = message['MessageArgs'][0]
break
return uri
def get_sessions(self):
result = {}
# listing all users has always been slower than other operations, why?
@@ -3694,7 +3742,7 @@ class RedfishUtils(object):
return {'ret': True, 'changed': True,
'msg': "The following volumes were deleted: %s" % str(volume_ids)}
def create_volume(self, volume_details, storage_subsystem_id):
def create_volume(self, volume_details, storage_subsystem_id, storage_none_volume_deletion=False):
# Find the Storage resource from the requested ComputerSystem resource
response = self.get_request(self.root_uri + self.systems_uri)
if response['ret'] is False:
@@ -3729,8 +3777,8 @@ class RedfishUtils(object):
'msg': "Provided Storage Subsystem ID %s does not exist on the server" % storage_subsystem_id}
# Validate input parameters
required_parameters = ['RAIDType', 'Drives', 'CapacityBytes']
allowed_parameters = ['DisplayName', 'InitializeMethod', 'MediaSpanCount',
required_parameters = ['RAIDType', 'Drives']
allowed_parameters = ['CapacityBytes', 'DisplayName', 'InitializeMethod', 'MediaSpanCount',
'Name', 'ReadCachePolicy', 'StripSizeBytes', 'VolumeUsage', 'WriteCachePolicy']
for parameter in required_parameters:
@@ -3746,22 +3794,23 @@ class RedfishUtils(object):
data = response['data']
# Deleting any volumes of RAIDType None present on the Storage Subsystem
response = self.get_request(self.root_uri + data['Volumes']['@odata.id'])
if response['ret'] is False:
return response
volume_data = response['data']
if storage_none_volume_deletion:
response = self.get_request(self.root_uri + data['Volumes']['@odata.id'])
if response['ret'] is False:
return response
volume_data = response['data']
if "Members" in volume_data:
for member in volume_data["Members"]:
response = self.get_request(self.root_uri + member['@odata.id'])
if response['ret'] is False:
return response
member_data = response['data']
if member_data["RAIDType"] == "None":
response = self.delete_request(self.root_uri + member['@odata.id'])
if "Members" in volume_data:
for member in volume_data["Members"]:
response = self.get_request(self.root_uri + member['@odata.id'])
if response['ret'] is False:
return response
member_data = response['data']
if member_data["RAIDType"] == "None":
response = self.delete_request(self.root_uri + member['@odata.id'])
if response['ret'] is False:
return response
# Construct payload and issue POST command to create volume
volume_details["Links"] = {}

View File

@@ -9,23 +9,29 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
---
module: ansible_galaxy_install
author:
- "Alexei Znamensky (@russoz)"
- "Alexei Znamensky (@russoz)"
short_description: Install Ansible roles or collections using ansible-galaxy
version_added: 3.5.0
description:
- This module allows the installation of Ansible collections or roles using C(ansible-galaxy).
- This module allows the installation of Ansible collections or roles using C(ansible-galaxy).
notes:
- Support for B(Ansible 2.9/2.10) was removed in community.general 8.0.0.
- >
The module will try and run using the C(C.UTF-8) locale.
If that fails, it will try C(en_US.UTF-8).
If that one also fails, the module will fail.
- Support for B(Ansible 2.9/2.10) was removed in community.general 8.0.0.
- >
The module will try and run using the C(C.UTF-8) locale.
If that fails, it will try C(en_US.UTF-8).
If that one also fails, the module will fail.
seealso:
- name: C(ansible-galaxy) command manual page
description: Manual page for the command.
link: https://docs.ansible.com/ansible/latest/cli/ansible-galaxy.html
requirements:
- ansible-core 2.11 or newer
- ansible-core 2.11 or newer
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
support: none
@@ -34,62 +40,63 @@ attributes:
options:
state:
description:
- >
If O(state=present) then the collection or role will be installed.
Note that the collections and roles are not updated with this option.
- >
Currently the O(state=latest) is ignored unless O(type=collection), and it will
ensure the collection is installed and updated to the latest available version.
- Please note that O(force=true) can be used to perform upgrade regardless of O(type).
- >
If O(state=present) then the collection or role will be installed.
Note that the collections and roles are not updated with this option.
- >
Currently the O(state=latest) is ignored unless O(type=collection), and it will
ensure the collection is installed and updated to the latest available version.
- Please note that O(force=true) can be used to perform upgrade regardless of O(type).
type: str
choices: [ present, latest ]
choices: [present, latest]
default: present
version_added: 9.1.0
type:
description:
- The type of installation performed by C(ansible-galaxy).
- If O(type=both), then O(requirements_file) must be passed and it may contain both roles and collections.
- "Note however that the opposite is not true: if using a O(requirements_file), then O(type) can be any of the three choices."
- The type of installation performed by C(ansible-galaxy).
- If O(type=both), then O(requirements_file) must be passed and it may contain both roles and collections.
- "Note however that the opposite is not true: if using a O(requirements_file), then O(type) can be any of the three choices."
type: str
choices: [collection, role, both]
required: true
name:
description:
- Name of the collection or role being installed.
- >
Versions can be specified with C(ansible-galaxy) usual formats.
For example, the collection V(community.docker:1.6.1) or the role V(ansistrano.deploy,3.8.0).
- O(name) and O(requirements_file) are mutually exclusive.
- Name of the collection or role being installed.
- >
Versions can be specified with C(ansible-galaxy) usual formats.
For example, the collection V(community.docker:1.6.1) or the role V(ansistrano.deploy,3.8.0).
- O(name) and O(requirements_file) are mutually exclusive.
type: str
requirements_file:
description:
- Path to a file containing a list of requirements to be installed.
- It works for O(type) equals to V(collection) and V(role).
- O(name) and O(requirements_file) are mutually exclusive.
- Path to a file containing a list of requirements to be installed.
- It works for O(type) equals to V(collection) and V(role).
- O(name) and O(requirements_file) are mutually exclusive.
type: path
dest:
description:
- The path to the directory containing your collections or roles, according to the value of O(type).
- >
Please notice that C(ansible-galaxy) will not install collections with O(type=both), when O(requirements_file)
contains both roles and collections and O(dest) is specified.
- The path to the directory containing your collections or roles, according to the value of O(type).
- >
Please notice that C(ansible-galaxy) will not install collections with O(type=both), when O(requirements_file)
contains both roles and collections and O(dest) is specified.
type: path
no_deps:
description:
- Refrain from installing dependencies.
- Refrain from installing dependencies.
version_added: 4.5.0
type: bool
default: false
force:
description:
- Force overwriting existing roles and/or collections.
- It can be used for upgrading, but the module output will always report C(changed=true).
- Using O(force=true) is mandatory when downgrading.
- Force overwriting existing roles and/or collections.
- It can be used for upgrading, but the module output will always report C(changed=true).
- Using O(force=true) is mandatory when downgrading.
type: bool
default: false
"""
EXAMPLES = """
---
- name: Install collection community.network
community.general.ansible_galaxy_install:
type: collection
@@ -111,76 +118,76 @@ EXAMPLES = """
type: collection
name: community.network:3.0.2
force: true
"""
RETURN = """
type:
description: The value of the O(type) parameter.
type: str
returned: always
name:
description: The value of the O(name) parameter.
type: str
returned: always
dest:
description: The value of the O(dest) parameter.
type: str
returned: always
requirements_file:
description: The value of the O(requirements_file) parameter.
type: str
returned: always
force:
description: The value of the O(force) parameter.
type: bool
returned: always
installed_roles:
description:
- If O(requirements_file) is specified instead, returns dictionary with all the roles installed per path.
- If O(name) is specified, returns that role name and the version installed per path.
type: dict
returned: always when installing roles
contains:
"<path>":
description: Roles and versions for that path.
type: dict
sample:
/home/user42/.ansible/roles:
ansistrano.deploy: 3.9.0
baztian.xfce: v0.0.3
/custom/ansible/roles:
ansistrano.deploy: 3.8.0
installed_collections:
description:
- If O(requirements_file) is specified instead, returns dictionary with all the collections installed per path.
- If O(name) is specified, returns that collection name and the version installed per path.
type: dict
returned: always when installing collections
contains:
"<path>":
description: Collections and versions for that path
type: dict
sample:
/home/az/.ansible/collections/ansible_collections:
community.docker: 1.6.0
community.general: 3.0.2
/custom/ansible/ansible_collections:
community.general: 3.1.0
new_collections:
description: New collections installed by this module.
returned: success
type: dict
sample:
community.general: 3.1.0
community.docker: 1.6.1
new_roles:
description: New roles installed by this module.
returned: success
type: dict
sample:
ansistrano.deploy: 3.8.0
---
type:
description: The value of the O(type) parameter.
type: str
returned: always
name:
description: The value of the O(name) parameter.
type: str
returned: always
dest:
description: The value of the O(dest) parameter.
type: str
returned: always
requirements_file:
description: The value of the O(requirements_file) parameter.
type: str
returned: always
force:
description: The value of the O(force) parameter.
type: bool
returned: always
installed_roles:
description:
- If O(requirements_file) is specified instead, returns dictionary with all the roles installed per path.
- If O(name) is specified, returns that role name and the version installed per path.
type: dict
returned: always when installing roles
contains:
"<path>":
description: Roles and versions for that path.
type: dict
sample:
/home/user42/.ansible/roles:
ansistrano.deploy: 3.9.0
baztian.xfce: v0.0.3
/custom/ansible/roles:
ansistrano.deploy: 3.8.0
installed_collections:
description:
- If O(requirements_file) is specified instead, returns dictionary with all the collections installed per path.
- If O(name) is specified, returns that collection name and the version installed per path.
type: dict
returned: always when installing collections
contains:
"<path>":
description: Collections and versions for that path
type: dict
sample:
/home/az/.ansible/collections/ansible_collections:
community.docker: 1.6.0
community.general: 3.0.2
/custom/ansible/ansible_collections:
community.general: 3.1.0
new_collections:
description: New collections installed by this module.
returned: success
type: dict
sample:
community.general: 3.1.0
community.docker: 1.6.1
new_roles:
description: New roles installed by this module.
returned: success
type: dict
sample:
ansistrano.deploy: 3.8.0
baztian.xfce: v0.0.3
"""
import re

View File

@@ -102,40 +102,40 @@ EXAMPLES = r'''
- name: Create a @home subvolume under the root subvolume
community.general.btrfs_subvolume:
name: /@home
device: /dev/vda2
filesystem_device: /dev/vda2
- name: Remove the @home subvolume if it exists
community.general.btrfs_subvolume:
name: /@home
state: absent
device: /dev/vda2
filesystem_device: /dev/vda2
- name: Create a snapshot of the root subvolume named @
community.general.btrfs_subvolume:
name: /@
snapshot_source: /
device: /dev/vda2
filesystem_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
filesystem_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
filesystem_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
filesystem_device: /dev/vda2
'''

View File

@@ -716,12 +716,14 @@ class CloudflareAPI(object):
"port": params['port'],
"weight": params['weight'],
"priority": params['priority'],
"name": params['record'],
"proto": params['proto'],
"service": params['service']
}
new_record = {"type": params['type'], "ttl": params['ttl'], 'data': srv_data}
new_record = {
"type": params['type'],
"name": params['service'] + '.' + params['proto'] + '.' + params['record'],
"ttl": params['ttl'],
'data': srv_data,
}
search_value = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
search_record = params['service'] + '.' + params['proto'] + '.' + params['record']

View File

@@ -10,14 +10,14 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: cpanm
short_description: Manages Perl library dependencies
description:
- Manage Perl library dependencies using cpanminus.
- Manage Perl library dependencies using cpanminus.
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
support: none
@@ -27,76 +27,82 @@ options:
name:
type: str
description:
- The Perl library to install. Valid values change according to the O(mode), see notes for more details.
- Note that for installing from a local path the parameter O(from_path) should be used.
- The Perl library to install. Valid values change according to the O(mode), see notes for more details.
- Note that for installing from a local path the parameter O(from_path) should be used.
aliases: [pkg]
from_path:
type: path
description:
- The local directory or C(tar.gz) file to install from.
- The local directory or C(tar.gz) file to install from.
notest:
description:
- Do not run unit tests.
- Do not run unit tests.
type: bool
default: false
locallib:
description:
- Specify the install base to install modules.
- Specify the install base to install modules.
type: path
mirror:
description:
- Specifies the base URL for the CPAN mirror to use.
- Specifies the base URL for the CPAN mirror to use.
type: str
mirror_only:
description:
- Use the mirror's index file instead of the CPAN Meta DB.
- Use the mirror's index file instead of the CPAN Meta DB.
type: bool
default: false
installdeps:
description:
- Only install dependencies.
- Only install dependencies.
type: bool
default: false
version:
description:
- Version specification for the perl module. When O(mode) is V(new), C(cpanm) version operators are accepted.
- Version specification for the perl module. When O(mode) is V(new), C(cpanm) version operators are accepted.
type: str
executable:
description:
- Override the path to the cpanm executable.
- Override the path to the cpanm executable.
type: path
mode:
description:
- Controls the module behavior. See notes below for more details.
- The default changed from V(compatibility) to V(new) in community.general 9.0.0.
- Controls the module behavior. See notes below for more details.
- The default changed from V(compatibility) to V(new) in community.general 9.0.0.
type: str
choices: [compatibility, new]
default: new
version_added: 3.0.0
name_check:
description:
- When O(mode=new), this parameter can be used to check if there is a module O(name) installed (at O(version), when specified).
- When O(mode=new), this parameter can be used to check if there is a module O(name) installed (at O(version), when specified).
type: str
version_added: 3.0.0
notes:
- Please note that U(http://search.cpan.org/dist/App-cpanminus/bin/cpanm, cpanm) must be installed on the remote host.
- "This module now comes with a choice of execution O(mode): V(compatibility) or V(new)."
- >
O(mode=compatibility): When using V(compatibility) mode, the module will keep backward compatibility.
This was the default mode before community.general 9.0.0.
O(name) must be either a module name or a distribution file. If the perl module given by O(name) is installed (at the exact O(version)
when specified), then nothing happens. Otherwise, it will be installed using the C(cpanm) executable. O(name) cannot be an URL, or a git URL.
C(cpanm) version specifiers do not work in this mode.
- >
O(mode=new): When using V(new) mode, the module will behave differently. The O(name) parameter may refer to a module name, a distribution file,
a HTTP URL or a git repository URL as described in C(cpanminus) documentation. C(cpanm) version specifiers are recognized.
This is the default mode from community.general 9.0.0 onwards.
author:
- "Franck Cuny (@fcuny)"
- "Alexei Znamensky (@russoz)"
'''
- Please note that U(http://search.cpan.org/dist/App-cpanminus/bin/cpanm, cpanm) must be installed on the remote host.
- "This module now comes with a choice of execution O(mode): V(compatibility) or V(new)."
- >
O(mode=compatibility): When using V(compatibility) mode, the module will keep backward compatibility.
This was the default mode before community.general 9.0.0.
O(name) must be either a module name or a distribution file. If the perl module given by O(name) is installed (at the exact O(version)
when specified), then nothing happens. Otherwise, it will be installed using the C(cpanm) executable. O(name) cannot be an URL, or a git URL.
C(cpanm) version specifiers do not work in this mode.
- >
O(mode=new): When using V(new) mode, the module will behave differently. The O(name) parameter may refer to a module name, a distribution file,
a HTTP URL or a git repository URL as described in C(cpanminus) documentation. C(cpanm) version specifiers are recognized.
This is the default mode from community.general 9.0.0 onwards.
EXAMPLES = '''
seealso:
- name: C(cpanm) command manual page
description: Manual page for the command.
link: https://metacpan.org/dist/App-cpanminus/view/bin/cpanm
author:
- "Franck Cuny (@fcuny)"
- "Alexei Znamensky (@russoz)"
"""
EXAMPLES = """
---
- name: Install Dancer perl package
community.general.cpanm:
name: Dancer
@@ -134,7 +140,7 @@ EXAMPLES = '''
community.general.cpanm:
name: Dancer
version: '1.0'
'''
"""
import os

View File

@@ -57,6 +57,8 @@ run_info:
returned: success and O(verbosity) >= 3
"""
import shlex
from ansible_collections.community.general.plugins.module_utils.django import DjangoModuleHelper
from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt
@@ -74,6 +76,9 @@ class DjangoCommand(DjangoModuleHelper):
)
django_admin_arg_order = "extra_args"
def __init_module__(self):
self.vars.command = shlex.split(self.vars.command)
def main():
DjangoCommand.execute()

View File

@@ -329,13 +329,39 @@ def _match_flat_using_flatpak_column_feature(module, binary, parsed_name, method
return row.split()[0]
def _is_flatpak_id(part):
# For guidelines on application IDs, refer to the following resources:
# Flatpak:
# https://docs.flatpak.org/en/latest/conventions.html#application-ids
# Flathub:
# https://docs.flathub.org/docs/for-app-authors/requirements#application-id
if '.' not in part:
return False
sections = part.split('.')
if len(sections) < 2:
return False
domain = sections[0]
if not domain.islower():
return False
for section in sections[1:]:
if not section.isalnum():
return False
return True
def _parse_flatpak_name(name):
if name.startswith('http://') or name.startswith('https://'):
file_name = urlparse(name).path.split('/')[-1]
file_name_without_extension = file_name.split('.')[0:-1]
common_name = ".".join(file_name_without_extension)
else:
common_name = name
parts = name.split('/')
for part in parts:
if _is_flatpak_id(part):
common_name = part
break
else:
common_name = name
return common_name

View File

@@ -9,16 +9,21 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: gconftool2
author:
- Kenneth D. Evensen (@kevensen)
- Kenneth D. Evensen (@kevensen)
short_description: Edit GNOME Configurations
description:
- This module allows for the manipulation of GNOME 2 Configuration via
gconftool-2. Please see the gconftool-2(1) man pages for more details.
- This module allows for the manipulation of GNOME 2 Configuration via gconftool-2. Please see the gconftool-2(1) man pages for more details.
seealso:
- name: C(gconftool-2) command manual page
description: Manual page for the command.
link: https://help.gnome.org/admin//system-admin-guide/2.32/gconf-6.html.en
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
support: full
@@ -28,42 +33,36 @@ options:
key:
type: str
description:
- A GConf preference key is an element in the GConf repository
that corresponds to an application preference. See man gconftool-2(1).
- A GConf preference key is an element in the GConf repository that corresponds to an application preference.
required: true
value:
type: str
description:
- Preference keys typically have simple values such as strings,
integers, or lists of strings and integers.
This is ignored unless O(state=present). See man gconftool-2(1).
- Preference keys typically have simple values such as strings, integers, or lists of strings and integers. This is ignored unless O(state=present).
value_type:
type: str
description:
- The type of value being set.
This is ignored unless O(state=present). See man gconftool-2(1).
choices: [ bool, float, int, string ]
- The type of value being set. This is ignored unless O(state=present).
choices: [bool, float, int, string]
state:
type: str
description:
- The action to take upon the key/value.
required: true
choices: [ absent, present ]
choices: [absent, present]
config_source:
type: str
description:
- Specify a configuration source to use rather than the default path.
See man gconftool-2(1).
direct:
description:
- Access the config database directly, bypassing server. If O(direct) is
specified then the O(config_source) must be specified as well.
See man gconftool-2(1).
- Access the config database directly, bypassing server. If O(direct) is specified then the O(config_source) must be specified as well.
type: bool
default: false
'''
"""
EXAMPLES = """
---
- name: Change the widget font to "Serif 12"
community.general.gconftool2:
key: "/desktop/gnome/interface/font_name"
@@ -71,33 +70,33 @@ EXAMPLES = """
value: "Serif 12"
"""
RETURN = '''
key:
description: The key specified in the module parameters.
returned: success
type: str
sample: /desktop/gnome/interface/font_name
value_type:
description: The type of the value that was changed.
returned: success
type: str
sample: string
value:
description:
- The value of the preference key after executing the module or V(null) if key is removed.
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
returned: success
type: str
sample: "Serif 12"
previous_value:
description:
- The value of the preference key before executing the module.
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
returned: success
type: str
sample: "Serif 12"
...
'''
RETURN = """
---
key:
description: The key specified in the module parameters.
returned: success
type: str
sample: /desktop/gnome/interface/font_name
value_type:
description: The type of the value that was changed.
returned: success
type: str
sample: string
value:
description:
- The value of the preference key after executing the module or V(null) if key is removed.
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
returned: success
type: str
sample: "Serif 12"
previous_value:
description:
- The value of the preference key before executing the module.
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
returned: success
type: str
sample: "Serif 12"
"""
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
from ansible_collections.community.general.plugins.module_utils.gconftool2 import gconftool2_runner

View File

@@ -7,46 +7,50 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: gconftool2_info
author:
- "Alexei Znamensky (@russoz)"
- "Alexei Znamensky (@russoz)"
short_description: Retrieve GConf configurations
version_added: 5.1.0
description:
- This module allows retrieving application preferences from the GConf database, with the help of C(gconftool-2).
- This module allows retrieving application preferences from the GConf database, with the help of C(gconftool-2).
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
- community.general.attributes
- community.general.attributes.info_module
options:
key:
description:
- The key name for an element in the GConf database.
type: str
required: true
notes:
- See man gconftool-2(1) for more details.
seealso:
- name: gconf repository (archived)
description: Git repository for the project. It is an archived project, so the repository is read-only.
link: https://gitlab.gnome.org/Archive/gconf
'''
- name: C(gconftool-2) command manual page
description: Manual page for the command.
link: https://help.gnome.org/admin//system-admin-guide/2.32/gconf-6.html.en
- name: gconf repository (archived)
description: Git repository for the project. It is an archived project, so the repository is read-only.
link: https://gitlab.gnome.org/Archive/gconf
"""
EXAMPLES = """
---
- name: Get value for a certain key in the database.
community.general.gconftool2_info:
key: /desktop/gnome/background/picture_filename
register: result
"""
RETURN = '''
value:
description:
- The value of the property.
returned: success
type: str
sample: Monospace 10
'''
RETURN = """
---
value:
description:
- The value of the property.
returned: success
type: str
sample: Monospace 10
"""
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
from ansible_collections.community.general.plugins.module_utils.gconftool2 import gconftool2_runner

View File

@@ -7,16 +7,17 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: gio_mime
author:
- "Alexei Znamensky (@russoz)"
- "Alexei Znamensky (@russoz)"
short_description: Set default handler for MIME type, for applications using Gnome GIO
version_added: 7.5.0
description:
- This module allows configuring the default handler for a specific MIME type, to be used by applications built with th Gnome GIO API.
- This module allows configuring the default handler for a specific MIME type, to be used by applications built with th Gnome GIO API.
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
support: full
@@ -25,24 +26,28 @@ attributes:
options:
mime_type:
description:
- MIME type for which a default handler will be set.
- MIME type for which a default handler will be set.
type: str
required: true
handler:
description:
- Default handler will be set for the MIME type.
- Default handler will be set for the MIME type.
type: str
required: true
notes:
- This module is a thin wrapper around the C(gio mime) command (and subcommand).
- See man gio(1) for more details.
- This module is a thin wrapper around the C(gio mime) command (and subcommand).
- See man gio(1) for more details.
seealso:
- name: GIO Documentation
description: Reference documentation for the GIO API..
link: https://docs.gtk.org/gio/
'''
- name: C(gio) command manual page
description: Manual page for the command.
link: https://man.archlinux.org/man/gio.1
- name: GIO Documentation
description: Reference documentation for the GIO API..
link: https://docs.gtk.org/gio/
"""
EXAMPLES = """
---
- name: Set chrome as the default handler for https
community.general.gio_mime:
mime_type: x-scheme-handler/https
@@ -50,26 +55,27 @@ EXAMPLES = """
register: result
"""
RETURN = '''
handler:
description:
- The handler set as default.
returned: success
type: str
sample: google-chrome.desktop
stdout:
description:
- The output of the C(gio) command.
returned: success
type: str
sample: Set google-chrome.desktop as the default for x-scheme-handler/https
stderr:
description:
- The error output of the C(gio) command.
returned: failure
type: str
sample: 'gio: Failed to load info for handler "never-existed.desktop"'
'''
RETURN = """
---
handler:
description:
- The handler set as default.
returned: success
type: str
sample: google-chrome.desktop
stdout:
description:
- The output of the C(gio) command.
returned: success
type: str
sample: Set google-chrome.desktop as the default for x-scheme-handler/https
stderr:
description:
- The error output of the C(gio) command.
returned: failure
type: str
sample: 'gio: Failed to load info for handler "never-existed.desktop"'
"""
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
from ansible_collections.community.general.plugins.module_utils.gio_mime import gio_mime_runner, gio_mime_get
@@ -84,7 +90,7 @@ class GioMime(ModuleHelper):
),
supports_check_mode=True,
)
mute_vardict_deprecation = True
use_old_vardict = False
def __init_module__(self):
self.runner = gio_mime_runner(self.module, check_rc=True)
@@ -92,7 +98,7 @@ class GioMime(ModuleHelper):
def __run__(self):
check_mode_return = (0, 'Module executed in check mode', '')
if self.vars.has_changed("handler"):
if self.vars.has_changed:
with self.runner.context(args_order=["mime_type", "handler"], check_mode_skip=True, check_mode_return=check_mode_return) as ctx:
rc, out, err = ctx.run()
self.vars.stdout = out

View File

@@ -196,9 +196,9 @@ class GitLabDeployKey(object):
changed = False
for arg_key, arg_value in arguments.items():
if arguments[arg_key] is not None:
if getattr(deploy_key, arg_key) != arguments[arg_key]:
setattr(deploy_key, arg_key, arguments[arg_key])
if arg_value is not None:
if getattr(deploy_key, arg_key) != arg_value:
setattr(deploy_key, arg_key, arg_value)
changed = True
return (changed, deploy_key)

View File

@@ -33,66 +33,35 @@ attributes:
support: none
options:
name:
description:
- Name of the group you want to create.
required: true
type: str
path:
description:
- The path of the group you want to create, this will be api_url/group_path
- If not supplied, the group_name will be used.
type: str
description:
description:
- A description for the group.
type: str
state:
description:
- create or delete group.
- Possible values are present and absent.
default: present
type: str
choices: ["present", "absent"]
parent:
description:
- Allow to create subgroups
- Id or Full path of parent group in the form of group/name
type: str
visibility:
description:
- Default visibility of the group
choices: ["private", "internal", "public"]
default: private
type: str
project_creation_level:
description:
- Determine if developers can create projects in the group.
choices: ["developer", "maintainer", "noone"]
type: str
version_added: 3.7.0
auto_devops_enabled:
description:
- Default to Auto DevOps pipeline for all projects within this group.
type: bool
version_added: 3.7.0
subgroup_creation_level:
description:
- Allowed to create subgroups.
choices: ["maintainer", "owner"]
type: str
version_added: 3.7.0
require_two_factor_authentication:
description:
- Require all users in this group to setup two-factor authentication.
type: bool
version_added: 3.7.0
avatar_path:
description:
- Absolute path image to configure avatar. File size should not exceed 200 kb.
- This option is only used on creation, not for updates.
type: path
version_added: 4.2.0
default_branch:
description:
- All merge requests and commits are made against this branch unless you specify a different one.
type: str
version_added: 9.5.0
description:
description:
- A description for the group.
type: str
enabled_git_access_protocol:
description:
- V(all) means SSH and HTTP(S) is enabled.
- V(ssh) means only SSH is enabled.
- V(http) means only HTTP(S) is enabled.
- Only available for top level groups.
choices: ["all", "ssh", "http"]
type: str
version_added: 9.5.0
force_delete:
description:
- Force delete group even if projects in it.
@@ -100,6 +69,113 @@ options:
type: bool
default: false
version_added: 7.5.0
lfs_enabled:
description:
- Projects in this group can use Git LFS.
type: bool
version_added: 9.5.0
lock_duo_features_enabled:
description:
- Enforce GitLab Duo features for all subgroups.
- Only available for top level groups.
type: bool
version_added: 9.5.0
membership_lock:
description:
- Users cannot be added to projects in this group.
type: bool
version_added: 9.5.0
mentions_disabled:
description:
- Group mentions are disabled.
type: bool
version_added: 9.5.0
name:
description:
- Name of the group you want to create.
required: true
type: str
parent:
description:
- Allow to create subgroups
- Id or Full path of parent group in the form of group/name
type: str
path:
description:
- The path of the group you want to create, this will be api_url/group_path
- If not supplied, the group_name will be used.
type: str
prevent_forking_outside_group:
description:
- Prevent forking outside of the group.
type: bool
version_added: 9.5.0
prevent_sharing_groups_outside_hierarchy:
description:
- Members cannot invite groups outside of this group and its subgroups.
- Only available for top level groups.
type: bool
version_added: 9.5.0
project_creation_level:
description:
- Determine if developers can create projects in the group.
choices: ["developer", "maintainer", "noone"]
type: str
version_added: 3.7.0
request_access_enabled:
description:
- Users can request access (if visibility is public or internal).
type: bool
version_added: 9.5.0
service_access_tokens_expiration_enforced:
description:
- Service account token expiration.
- Changes will not affect existing token expiration dates.
- Only available for top level groups.
type: bool
version_added: 9.5.0
share_with_group_lock:
description:
- Projects cannot be shared with other groups.
type: bool
version_added: 9.5.0
require_two_factor_authentication:
description:
- Require all users in this group to setup two-factor authentication.
type: bool
version_added: 3.7.0
state:
description:
- create or delete group.
- Possible values are present and absent.
default: present
type: str
choices: ["present", "absent"]
subgroup_creation_level:
description:
- Allowed to create subgroups.
choices: ["maintainer", "owner"]
type: str
version_added: 3.7.0
two_factor_grace_period:
description:
- Delay 2FA enforcement (hours).
type: str
version_added: 9.5.0
visibility:
description:
- Default visibility of the group
choices: ["private", "internal", "public"]
default: private
type: str
wiki_access_level:
description:
- V(enabled) means everyone can access the wiki.
- V(private) means only members of this group can access the wiki.
- V(disabled) means group-level wiki is disabled.
choices: ["enabled", "private", "disabled"]
type: str
version_added: 9.5.0
'''
EXAMPLES = '''
@@ -202,23 +278,38 @@ class GitLabGroup(object):
def create_or_update_group(self, name, parent, options):
changed = False
payload = {
'auto_devops_enabled': options['auto_devops_enabled'],
'default_branch': options['default_branch'],
'description': options['description'],
'lfs_enabled': options['lfs_enabled'],
'membership_lock': options['membership_lock'],
'mentions_disabled': options['mentions_disabled'],
'name': name,
'path': options['path'],
'prevent_forking_outside_group': options['prevent_forking_outside_group'],
'project_creation_level': options['project_creation_level'],
'request_access_enabled': options['request_access_enabled'],
'require_two_factor_authentication': options['require_two_factor_authentication'],
'share_with_group_lock': options['share_with_group_lock'],
'subgroup_creation_level': options['subgroup_creation_level'],
'visibility': options['visibility'],
'wiki_access_level': options['wiki_access_level'],
}
if options.get('enabled_git_access_protocol') and parent is None:
payload['enabled_git_access_protocol'] = options['enabled_git_access_protocol']
if options.get('lock_duo_features_enabled') and parent is None:
payload['lock_duo_features_enabled'] = options['lock_duo_features_enabled']
if options.get('prevent_sharing_groups_outside_hierarchy') and parent is None:
payload['prevent_sharing_groups_outside_hierarchy'] = options['prevent_sharing_groups_outside_hierarchy']
if options.get('service_access_tokens_expiration_enforced') and parent is None:
payload['service_access_tokens_expiration_enforced'] = options['service_access_tokens_expiration_enforced']
if options.get('two_factor_grace_period'):
payload['two_factor_grace_period'] = int(options['two_factor_grace_period'])
# Because we have already call userExists in main()
if self.group_object is None:
parent_id = self.get_group_id(parent)
payload = {
'name': name,
'path': options['path'],
'parent_id': parent_id,
'visibility': options['visibility'],
'project_creation_level': options['project_creation_level'],
'auto_devops_enabled': options['auto_devops_enabled'],
'subgroup_creation_level': options['subgroup_creation_level'],
}
if options.get('description'):
payload['description'] = options['description']
if options.get('require_two_factor_authentication'):
payload['require_two_factor_authentication'] = options['require_two_factor_authentication']
payload['parent_id'] = self.get_group_id(parent)
group = self.create_group(payload)
# add avatar to group
@@ -229,15 +320,7 @@ class GitLabGroup(object):
self._module.fail_json(msg='Cannot open {0}: {1}'.format(options['avatar_path'], e))
changed = True
else:
changed, group = self.update_group(self.group_object, {
'name': name,
'description': options['description'],
'visibility': options['visibility'],
'project_creation_level': options['project_creation_level'],
'auto_devops_enabled': options['auto_devops_enabled'],
'subgroup_creation_level': options['subgroup_creation_level'],
'require_two_factor_authentication': options['require_two_factor_authentication'],
})
changed, group = self.update_group(self.group_object, payload)
self.group_object = group
if changed:
@@ -277,9 +360,9 @@ class GitLabGroup(object):
changed = False
for arg_key, arg_value in arguments.items():
if arguments[arg_key] is not None:
if getattr(group, arg_key) != arguments[arg_key]:
setattr(group, arg_key, arguments[arg_key])
if arg_value is not None:
if getattr(group, arg_key) != arg_value:
setattr(group, arg_key, arg_value)
changed = True
return (changed, group)
@@ -322,28 +405,41 @@ def main():
argument_spec = basic_auth_argument_spec()
argument_spec.update(auth_argument_spec())
argument_spec.update(dict(
name=dict(type='str', required=True),
path=dict(type='str'),
description=dict(type='str'),
state=dict(type='str', default="present", choices=["absent", "present"]),
parent=dict(type='str'),
visibility=dict(type='str', default="private", choices=["internal", "private", "public"]),
project_creation_level=dict(type='str', choices=['developer', 'maintainer', 'noone']),
auto_devops_enabled=dict(type='bool'),
subgroup_creation_level=dict(type='str', choices=['maintainer', 'owner']),
require_two_factor_authentication=dict(type='bool'),
avatar_path=dict(type='path'),
default_branch=dict(type='str'),
description=dict(type='str'),
enabled_git_access_protocol=dict(type='str', choices=['all', 'ssh', 'http']),
force_delete=dict(type='bool', default=False),
lfs_enabled=dict(type='bool'),
lock_duo_features_enabled=dict(type='bool'),
membership_lock=dict(type='bool'),
mentions_disabled=dict(type='bool'),
name=dict(type='str', required=True),
parent=dict(type='str'),
path=dict(type='str'),
prevent_forking_outside_group=dict(type='bool'),
prevent_sharing_groups_outside_hierarchy=dict(type='bool'),
project_creation_level=dict(type='str', choices=['developer', 'maintainer', 'noone']),
request_access_enabled=dict(type='bool'),
require_two_factor_authentication=dict(type='bool'),
service_access_tokens_expiration_enforced=dict(type='bool'),
share_with_group_lock=dict(type='bool'),
state=dict(type='str', default="present", choices=["absent", "present"]),
subgroup_creation_level=dict(type='str', choices=['maintainer', 'owner']),
two_factor_grace_period=dict(type='str'),
visibility=dict(type='str', default="private", choices=["internal", "private", "public"]),
wiki_access_level=dict(type='str', choices=['enabled', 'private', 'disabled']),
))
module = AnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=[
['api_username', 'api_token'],
['api_username', 'api_oauth_token'],
['api_username', 'api_job_token'],
['api_token', 'api_oauth_token'],
['api_token', 'api_job_token'],
['api_token', 'api_oauth_token'],
['api_username', 'api_job_token'],
['api_username', 'api_oauth_token'],
['api_username', 'api_token'],
],
required_together=[
['api_username', 'api_password'],
@@ -357,18 +453,31 @@ def main():
# check prerequisites and connect to gitlab server
gitlab_instance = gitlab_authentication(module)
auto_devops_enabled = module.params['auto_devops_enabled']
avatar_path = module.params['avatar_path']
default_branch = module.params['default_branch']
description = module.params['description']
enabled_git_access_protocol = module.params['enabled_git_access_protocol']
force_delete = module.params['force_delete']
group_name = module.params['name']
group_path = module.params['path']
description = module.params['description']
state = module.params['state']
parent_identifier = module.params['parent']
group_visibility = module.params['visibility']
lfs_enabled = module.params['lfs_enabled']
lock_duo_features_enabled = module.params['lock_duo_features_enabled']
membership_lock = module.params['membership_lock']
mentions_disabled = module.params['mentions_disabled']
parent_identifier = module.params['parent']
prevent_forking_outside_group = module.params['prevent_forking_outside_group']
prevent_sharing_groups_outside_hierarchy = module.params['prevent_sharing_groups_outside_hierarchy']
project_creation_level = module.params['project_creation_level']
auto_devops_enabled = module.params['auto_devops_enabled']
subgroup_creation_level = module.params['subgroup_creation_level']
request_access_enabled = module.params['request_access_enabled']
require_two_factor_authentication = module.params['require_two_factor_authentication']
avatar_path = module.params['avatar_path']
force_delete = module.params['force_delete']
service_access_tokens_expiration_enforced = module.params['service_access_tokens_expiration_enforced']
share_with_group_lock = module.params['share_with_group_lock']
state = module.params['state']
subgroup_creation_level = module.params['subgroup_creation_level']
two_factor_grace_period = module.params['two_factor_grace_period']
wiki_access_level = module.params['wiki_access_level']
# Define default group_path based on group_name
if group_path is None:
@@ -380,7 +489,7 @@ def main():
if parent_identifier:
parent_group = find_group(gitlab_instance, parent_identifier)
if not parent_group:
module.fail_json(msg="Failed create GitLab group: Parent group doesn't exists")
module.fail_json(msg="Failed to create GitLab group: Parent group doesn't exist")
group_exists = gitlab_group.exists_group(parent_group.full_path + '/' + group_path)
else:
@@ -391,18 +500,31 @@ def main():
gitlab_group.delete_group(force=force_delete)
module.exit_json(changed=True, msg="Successfully deleted group %s" % group_name)
else:
module.exit_json(changed=False, msg="Group deleted or does not exists")
module.exit_json(changed=False, msg="Group deleted or does not exist")
if state == 'present':
if gitlab_group.create_or_update_group(group_name, parent_group, {
"path": group_path,
"description": description,
"visibility": group_visibility,
"project_creation_level": project_creation_level,
"auto_devops_enabled": auto_devops_enabled,
"subgroup_creation_level": subgroup_creation_level,
"require_two_factor_authentication": require_two_factor_authentication,
"avatar_path": avatar_path,
"default_branch": default_branch,
"description": description,
"enabled_git_access_protocol": enabled_git_access_protocol,
"lfs_enabled": lfs_enabled,
"lock_duo_features_enabled": lock_duo_features_enabled,
"membership_lock": membership_lock,
"mentions_disabled": mentions_disabled,
"path": group_path,
"prevent_forking_outside_group": prevent_forking_outside_group,
"prevent_sharing_groups_outside_hierarchy": prevent_sharing_groups_outside_hierarchy,
"project_creation_level": project_creation_level,
"request_access_enabled": request_access_enabled,
"require_two_factor_authentication": require_two_factor_authentication,
"service_access_tokens_expiration_enforced": service_access_tokens_expiration_enforced,
"share_with_group_lock": share_with_group_lock,
"subgroup_creation_level": subgroup_creation_level,
"two_factor_grace_period": two_factor_grace_period,
"visibility": group_visibility,
"wiki_access_level": wiki_access_level,
}):
module.exit_json(changed=True, msg="Successfully created or updated the group %s" % group_name, group=gitlab_group.group_object._attrs)
else:

View File

@@ -264,14 +264,14 @@ class GitlabIssue(object):
if key == 'milestone_id':
old_milestone = getattr(issue, 'milestone')['id'] if getattr(issue, 'milestone') else ""
if options[key] != old_milestone:
if value != old_milestone:
return True
elif key == 'assignee_ids':
if options[key] != sorted([user["id"] for user in getattr(issue, 'assignees')]):
if value != sorted([user["id"] for user in getattr(issue, 'assignees')]):
return True
elif key == 'labels':
if options[key] != sorted(getattr(issue, key)):
if value != sorted(getattr(issue, key)):
return True
elif getattr(issue, key) != value:

View File

@@ -263,15 +263,15 @@ class GitlabMergeRequest(object):
key = 'force_remove_source_branch'
if key == 'assignee_ids':
if options[key] != sorted([user["id"] for user in getattr(mr, 'assignees')]):
if value != sorted([user["id"] for user in getattr(mr, 'assignees')]):
return True
elif key == 'reviewer_ids':
if options[key] != sorted([user["id"] for user in getattr(mr, 'reviewers')]):
if value != sorted([user["id"] for user in getattr(mr, 'reviewers')]):
return True
elif key == 'labels':
if options[key] != sorted(getattr(mr, key)):
if value != sorted(getattr(mr, key)):
return True
elif getattr(mr, key) != value:

View File

@@ -15,7 +15,7 @@ module: gitlab_project
short_description: Creates/updates/deletes GitLab Projects
description:
- When the project does not exist in GitLab, it will be created.
- When the project does exists and O(state=absent), the project will be deleted.
- When the project does exist and O(state=absent), the project will be deleted.
- When changes are made to the project, the project will be updated.
author:
- Werner Dijkerman (@dj-wasabi)
@@ -716,7 +716,7 @@ def main():
if group_identifier:
group = find_group(gitlab_instance, group_identifier)
if group is None:
module.fail_json(msg="Failed to create project: group %s doesn't exists" % group_identifier)
module.fail_json(msg="Failed to create project: group %s doesn't exist" % group_identifier)
namespace_id = group.id
else:

View File

@@ -33,7 +33,10 @@ author:
- Samy Coenen (@SamyCoenen)
- Guillaume Martinez (@Lunik)
requirements:
- python-gitlab >= 1.5.0
- python-gitlab >= 1.5.0 for legacy runner registration workflow
(runner registration token - U(https://docs.gitlab.com/runner/register/#register-with-a-runner-registration-token-deprecated))
- python-gitlab >= 4.0.0 for new runner registration workflow
(runner authentication token - U(https://docs.gitlab.com/runner/register/#register-with-a-runner-authentication-token))
extends_documentation_fragment:
- community.general.auth_basic
- community.general.gitlab
@@ -365,18 +368,18 @@ class GitLabRunner(object):
changed = False
for arg_key, arg_value in arguments.items():
if arguments[arg_key] is not None:
if isinstance(arguments[arg_key], list):
if arg_value is not None:
if isinstance(arg_value, list):
list1 = getattr(runner, arg_key)
list1.sort()
list2 = arguments[arg_key]
list2 = arg_value
list2.sort()
if list1 != list2:
setattr(runner, arg_key, arguments[arg_key])
setattr(runner, arg_key, arg_value)
changed = True
else:
if getattr(runner, arg_key) != arguments[arg_key]:
setattr(runner, arg_key, arguments[arg_key])
if getattr(runner, arg_key) != arg_value:
setattr(runner, arg_key, arg_value)
changed = True
return (changed, runner)

View File

@@ -17,6 +17,10 @@ description:
- Send a message to a Hipchat room, with options to control the formatting.
extends_documentation_fragment:
- community.general.attributes
deprecated:
removed_in: 11.0.0
why: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
alternative: There is none.
attributes:
check_mode:
support: full

View File

@@ -61,17 +61,17 @@ EXAMPLES = """
state: present
- name: Start the foo service (equivalent to `brew services start foo`)
community.general.homebrew_service:
community.general.homebrew_services:
name: foo
state: present
- name: Restart the foo service (equivalent to `brew services restart foo`)
community.general.homebrew_service:
community.general.homebrew_services:
name: foo
state: restarted
- name: Remove the foo service (equivalent to `brew services stop foo`)
community.general.homebrew_service:
community.general.homebrew_services:
name: foo
service_state: absent
"""

View File

@@ -18,11 +18,11 @@ version_added: 4.4.0
description:
- Manages a user's home directory managed by systemd-homed.
notes:
- This module does B(not) work with Python 3.13 or newer. It uses the deprecated L(crypt Python module,
https://docs.python.org/3.12/library/crypt.html) from the Python standard library, which was removed
from Python 3.13.
- This module requires the deprecated L(crypt Python module,
https://docs.python.org/3.12/library/crypt.html) library which was removed from Python 3.13.
For Python 3.13 or newer, you need to install L(legacycrypt, https://pypi.org/project/legacycrypt/).
requirements:
- Python 3.12 or earlier
- legacycrypt (on Python 3.13 or newer)
extends_documentation_fragment:
- community.general.attributes
attributes:
@@ -284,6 +284,17 @@ else:
HAS_CRYPT = True
CRYPT_IMPORT_ERROR = None
try:
import legacycrypt
if not HAS_CRYPT:
crypt = legacycrypt
except ImportError:
HAS_LEGACYCRYPT = False
LEGACYCRYPT_IMPORT_ERROR = traceback.format_exc()
else:
HAS_LEGACYCRYPT = True
LEGACYCRYPT_IMPORT_ERROR = None
class Homectl(object):
'''#TODO DOC STRINGS'''
@@ -606,9 +617,9 @@ def main():
]
)
if not HAS_CRYPT:
if not HAS_CRYPT and not HAS_LEGACYCRYPT:
module.fail_json(
msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
msg=missing_required_lib('crypt (part of standard library up to Python 3.12) or legacycrypt (PyPI)'),
exception=CRYPT_IMPORT_ERROR,
)

View File

@@ -282,9 +282,7 @@ def main():
'vars.made_by': "ansible"
}
}
for key, value in variables.items():
data['attrs']['vars.' + key] = value
data['attrs'].update({'vars.' + key: value for key, value in variables.items()})
changed = False
if icinga.exists(name):

View File

@@ -569,7 +569,7 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
module.fail_json(msg="Unable to create temporary file %s", traceback=traceback.format_exc())
try:
module.atomic_move(tmpfile, target_filename)
module.atomic_move(tmpfile, os.path.abspath(target_filename))
except IOError:
module.ansible.fail_json(msg='Unable to move temporary \
file %s to %s, IOError' % (tmpfile, target_filename), traceback=traceback.format_exc())

View File

@@ -0,0 +1,247 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2024 Alexander Bakanovskii <skottttt228@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 = r'''
---
module: ipa_getkeytab
short_description: Manage keytab file in FreeIPA
version_added: 9.5.0
description:
- Manage keytab file with C(ipa-getkeytab) utility.
- See U(https://manpages.ubuntu.com/manpages/jammy/man1/ipa-getkeytab.1.html) for reference.
author: "Alexander Bakanovskii (@abakanovskii)"
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
path:
description:
- The base path where to put generated keytab file.
type: path
aliases: ["keytab"]
required: true
principal:
description:
- The non-realm part of the full principal name.
type: str
required: true
ipa_host:
description:
- The IPA server to retrieve the keytab from (FQDN).
type: str
ldap_uri:
description:
- LDAP URI. If V(ldap://) is specified, STARTTLS is initiated by default.
- Can not be used with the O(ipa_host) option.
type: str
bind_dn:
description:
- The LDAP DN to bind as when retrieving a keytab without Kerberos credentials.
- Generally used with the O(bind_pw) option.
type: str
bind_pw:
description:
- The LDAP password to use when not binding with Kerberos.
type: str
password:
description:
- Use this password for the key instead of one randomly generated.
type: str
ca_cert:
description:
- The path to the IPA CA certificate used to validate LDAPS/STARTTLS connections.
type: path
sasl_mech:
description:
- SASL mechanism to use if O(bind_dn) and O(bind_pw) are not specified.
choices: ["GSSAPI", "EXTERNAL"]
type: str
retrieve_mode:
description:
- Retrieve an existing key from the server instead of generating a new one.
- This is incompatible with the O(password), and will work only against a IPA server more recent than version 3.3.
- The user requesting the keytab must have access to the keys for this operation to succeed.
- Be aware that if set V(true), a new keytab will be generated.
- This invalidates all previously retrieved keytabs for this service principal.
type: bool
encryption_types:
description:
- The list of encryption types to use to generate keys.
- It will use local client defaults if not provided.
- Valid values depend on the Kerberos library version and configuration.
type: str
state:
description:
- The state of the keytab file.
- V(present) only check for existence of a file, if you want to recreate keytab with other parameters you should set O(force=true).
type: str
default: present
choices: ["present", "absent"]
force:
description:
- Force recreation if exists already.
type: bool
requirements:
- freeipa-client
- Managed host is FreeIPA client
extends_documentation_fragment:
- community.general.attributes
'''
EXAMPLES = r'''
- name: Get kerberos ticket
ansible.builtin.shell: kinit admin
args:
stdin: "{{ aldpro_admin_password }}"
changed_when: true
- name: Create keytab
community.general.ipa_getkeytab:
path: /etc/ipa/test.keytab
principal: HTTP/freeipa-dc02.ipa.test
ipa_host: freeipa-dc01.ipa.test
- name: Retrieve already existing keytab
community.general.ipa_getkeytab:
path: /etc/ipa/test.keytab
principal: HTTP/freeipa-dc02.ipa.test
ipa_host: freeipa-dc01.ipa.test
retrieve_mode: true
- name: Force keytab recreation
community.general.ipa_getkeytab:
path: /etc/ipa/test.keytab
principal: HTTP/freeipa-dc02.ipa.test
ipa_host: freeipa-dc01.ipa.test
force: true
'''
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
class IPAKeytab(object):
def __init__(self, module, **kwargs):
self.module = module
self.path = kwargs['path']
self.state = kwargs['state']
self.principal = kwargs['principal']
self.ipa_host = kwargs['ipa_host']
self.ldap_uri = kwargs['ldap_uri']
self.bind_dn = kwargs['bind_dn']
self.bind_pw = kwargs['bind_pw']
self.password = kwargs['password']
self.ca_cert = kwargs['ca_cert']
self.sasl_mech = kwargs['sasl_mech']
self.retrieve_mode = kwargs['retrieve_mode']
self.encryption_types = kwargs['encryption_types']
self.runner = CmdRunner(
module,
command='ipa-getkeytab',
arg_formats=dict(
retrieve_mode=cmd_runner_fmt.as_bool('--retrieve'),
path=cmd_runner_fmt.as_opt_val('--keytab'),
ipa_host=cmd_runner_fmt.as_opt_val('--server'),
principal=cmd_runner_fmt.as_opt_val('--principal'),
ldap_uri=cmd_runner_fmt.as_opt_val('--ldapuri'),
bind_dn=cmd_runner_fmt.as_opt_val('--binddn'),
bind_pw=cmd_runner_fmt.as_opt_val('--bindpw'),
password=cmd_runner_fmt.as_opt_val('--password'),
ca_cert=cmd_runner_fmt.as_opt_val('--cacert'),
sasl_mech=cmd_runner_fmt.as_opt_val('--mech'),
encryption_types=cmd_runner_fmt.as_opt_val('--enctypes'),
)
)
def _exec(self, check_rc=True):
with self.runner(
"retrieve_mode path ipa_host principal ldap_uri bind_dn bind_pw password ca_cert sasl_mech encryption_types",
check_rc=check_rc
) as ctx:
rc, out, err = ctx.run()
return out
def main():
arg_spec = dict(
path=dict(type='path', required=True, aliases=["keytab"]),
state=dict(default='present', choices=['present', 'absent']),
principal=dict(type='str', required=True),
ipa_host=dict(type='str'),
ldap_uri=dict(type='str'),
bind_dn=dict(type='str'),
bind_pw=dict(type='str'),
password=dict(type='str', no_log=True),
ca_cert=dict(type='path'),
sasl_mech=dict(type='str', choices=["GSSAPI", "EXTERNAL"]),
retrieve_mode=dict(type='bool'),
encryption_types=dict(type='str'),
force=dict(type='bool'),
)
module = AnsibleModule(
argument_spec=arg_spec,
mutually_exclusive=[('ipa_host', 'ldap_uri'), ('retrieve_mode', 'password')],
supports_check_mode=True,
)
path = module.params['path']
state = module.params['state']
force = module.params['force']
keytab = IPAKeytab(module,
path=path,
state=state,
principal=module.params['principal'],
ipa_host=module.params['ipa_host'],
ldap_uri=module.params['ldap_uri'],
bind_dn=module.params['bind_dn'],
bind_pw=module.params['bind_pw'],
password=module.params['password'],
ca_cert=module.params['ca_cert'],
sasl_mech=module.params['sasl_mech'],
retrieve_mode=module.params['retrieve_mode'],
encryption_types=module.params['encryption_types'],
)
changed = False
if state == 'present':
if os.path.exists(path):
if force and not module.check_mode:
try:
os.remove(path)
except OSError as e:
module.fail_json(msg="Error deleting: %s - %s." % (e.filename, e.strerror))
keytab._exec()
changed = True
if force and module.check_mode:
changed = True
else:
changed = True
keytab._exec()
if state == 'absent':
if os.path.exists(path):
changed = True
if not module.check_mode:
try:
os.remove(path)
except OSError as e:
module.fail_json(msg="Error deleting: %s - %s." % (e.filename, e.strerror))
module.exit_json(changed=changed)
if __name__ == '__main__':
main()

View File

@@ -74,10 +74,17 @@ options:
type: list
elements: str
state:
description: State to ensure.
description:
- State to ensure.
default: present
choices: ["absent", "disabled", "enabled", "present"]
type: str
force_creation:
description:
- Create host if O(state=disabled) or O(state=enabled) but not present.
default: true
type: bool
version_added: 9.5.0
update_dns:
description:
- If set V(true) with O(state=absent), then removes DNS records of the host managed by FreeIPA DNS.
@@ -233,26 +240,31 @@ def get_host_diff(client, ipa_host, module_host):
def ensure(module, client):
name = module.params['fqdn']
state = module.params['state']
force_creation = module.params['force_creation']
ipa_host = client.host_find(name=name)
module_host = get_host_dict(description=module.params['description'],
force=module.params['force'], ip_address=module.params['ip_address'],
force=module.params['force'],
ip_address=module.params['ip_address'],
ns_host_location=module.params['ns_host_location'],
ns_hardware_platform=module.params['ns_hardware_platform'],
ns_os_version=module.params['ns_os_version'],
user_certificate=module.params['user_certificate'],
mac_address=module.params['mac_address'],
random_password=module.params.get('random_password'),
random_password=module.params['random_password'],
)
changed = False
if state in ['present', 'enabled', 'disabled']:
if not ipa_host:
if not ipa_host and (force_creation or state == 'present'):
changed = True
if not module.check_mode:
# OTP password generated by FreeIPA is visible only for host_add command
# so, return directly from here.
return changed, client.host_add(name=name, host=module_host)
else:
if state in ['disabled', 'enabled']:
module.fail_json(msg="No host with name " + ipa_host + " found")
diff = get_host_diff(client, ipa_host, module_host)
if len(diff) > 0:
changed = True
@@ -261,11 +273,10 @@ def ensure(module, client):
for key in diff:
data[key] = module_host.get(key)
ipa_host_show = client.host_show(name=name)
if ipa_host_show.get('has_keytab', False) and module.params.get('random_password'):
if ipa_host_show.get('has_keytab', True) and (state == 'disabled' or module.params.get('random_password')):
client.host_disable(name=name)
return changed, client.host_mod(name=name, host=data)
else:
elif state == 'absent':
if ipa_host:
changed = True
update_dns = module.params.get('update_dns', False)
@@ -288,7 +299,8 @@ def main():
mac_address=dict(type='list', aliases=['macaddress'], elements='str'),
update_dns=dict(type='bool'),
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']),
random_password=dict(type='bool', no_log=False),)
random_password=dict(type='bool', no_log=False),
force_creation=dict(type='bool', default=True),)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)

View File

@@ -57,13 +57,14 @@ options:
state:
description:
- State to ensure.
- V("absent") and V("disabled") give the same results.
- V("present") and V("enabled") give the same results.
default: "present"
choices: ["absent", "disabled", "enabled", "present"]
type: str
extends_documentation_fragment:
- community.general.ipa.documentation
- community.general.attributes
'''
EXAMPLES = r'''
@@ -160,7 +161,7 @@ def ensure(module, client):
module_hostgroup = get_hostgroup_dict(description=module.params['description'])
changed = False
if state == 'present':
if state in ['present', 'enabled']:
if not ipa_hostgroup:
changed = True
if not module.check_mode:

View File

@@ -150,13 +150,11 @@ EXAMPLES = '''
name: example
certificate: |
-----BEGIN CERTIFICATE-----
h19dUZ2co2fI/ibYiwxWk4aeNE6KWvCaTQOMQ8t6Uo2XKhpL/xnjoAgh1uCQN/69
MG+34+RhUWzCfdZH7T8/qDxJw2kEPKluaYh7KnMsba+5jHjmtzix5QIDAQABo4IB
h19dUZ2co2f...
-----END CERTIFICATE-----
private_key: |
-----BEGIN RSA PRIVATE KEY-----
DBVFTEVDVFJJQ0lURSBERSBGUkFOQ0UxFzAVBgNVBAsMDjAwMDIgNTUyMDgxMzE3
GLlDNMw/uHyME7gHFsqJA7O11VY6O5WQ4IDP3m/s5ZV6s+Nn6Lerz17VZ99
DBVFTEVDVFJ...
-----END RSA PRIVATE KEY-----
password: changeit
dest: /etc/security/keystore.jks
@@ -472,7 +470,7 @@ class JavaKeystore:
if self.keystore_type == 'pkcs12':
# Preserve properties of the destination file, if any.
self.module.atomic_move(keystore_p12_path, self.keystore_path)
self.module.atomic_move(os.path.abspath(keystore_p12_path), os.path.abspath(self.keystore_path))
self.update_permissions()
self.result['changed'] = True
return self.result

View File

@@ -685,7 +685,7 @@ class JenkinsPlugin(object):
# Move the updates file to the right place if we could read it
if tmp_updates_file != updates_file:
self.module.atomic_move(tmp_updates_file, updates_file)
self.module.atomic_move(os.path.abspath(tmp_updates_file), os.path.abspath(updates_file))
# Check if we have the plugin data available
if not data.get('plugins', {}).get(self.params['name']):
@@ -718,7 +718,7 @@ class JenkinsPlugin(object):
details=to_native(e))
# Move the file onto the right place
self.module.atomic_move(tmp_f, f)
self.module.atomic_move(os.path.abspath(tmp_f), os.path.abspath(f))
def uninstall(self):
changed = False

View File

@@ -531,7 +531,7 @@ class JIRA(StateModuleHelper):
),
supports_check_mode=False
)
mute_vardict_deprecation = True
use_old_vardict = False
state_param = 'operation'
def __init_module__(self):

View File

@@ -214,7 +214,7 @@ def run_module(module, tmpdir, kwriteconfig):
if module.params['backup'] and os.path.exists(b_path):
result['backup_file'] = module.backup_local(result['path'])
try:
module.atomic_move(b_tmpfile, b_path)
module.atomic_move(b_tmpfile, os.path.abspath(b_path))
except IOError:
module.ansible.fail_json(msg='Unable to move temporary file %s to %s, IOError' % (tmpfile, result['path']), traceback=traceback.format_exc())

View File

@@ -108,13 +108,14 @@ options:
client_authenticator_type:
description:
- How do clients authenticate with the auth server? Either V(client-secret) or
V(client-jwt) can be chosen. When using V(client-secret), the module parameter
O(secret) can set it, while for V(client-jwt), you can use the keys C(use.jwks.url),
- How do clients authenticate with the auth server? Either V(client-secret),
V(client-jwt), or V(client-x509) can be chosen. When using V(client-secret), the module parameter
O(secret) can set it, for V(client-jwt), you can use the keys C(use.jwks.url),
C(jwks.url), and C(jwt.credential.certificate) in the O(attributes) module parameter
to configure its behavior.
to configure its behavior. For V(client-x509) you can use the keys C(x509.allow.regex.pattern.comparison)
and C(x509.subjectdn) in the O(attributes) module parameter to configure which certificate(s) to accept.
- This is 'clientAuthenticatorType' in the Keycloak REST API.
choices: ['client-secret', 'client-jwt']
choices: ['client-secret', 'client-jwt', 'client-x509']
aliases:
- clientAuthenticatorType
type: str
@@ -533,7 +534,6 @@ options:
description:
- SAML Redirect Binding URL for the client's assertion consumer service (login responses).
saml_force_name_id_format:
description:
- For SAML clients, Boolean specifying whether to ignore requested NameID subject format and using the configured one instead.
@@ -581,6 +581,18 @@ options:
- For OpenID-Connect clients, client certificate for validating JWT issued by
client and signed by its key, base64-encoded.
x509.subjectdn:
description:
- For OpenID-Connect clients, subject which will be used to authenticate the client.
type: str
version_added: 9.5.0
x509.allow.regex.pattern.comparison:
description:
- For OpenID-Connect clients, boolean specifying whether to allow C(x509.subjectdn) as regular expression.
type: bool
version_added: 9.5.0
extends_documentation_fragment:
- community.general.keycloak
- community.general.attributes
@@ -624,6 +636,22 @@ EXAMPLES = '''
delegate_to: localhost
- name: Create or update a Keycloak client (minimal example), with x509 authentication
community.general.keycloak_client:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
realm: master
state: present
client_id: test
client_authenticator_type: client-x509
attributes:
x509.subjectdn: "CN=client"
x509.allow.regex.pattern.comparison: false
- name: Create or update a Keycloak client (with all the bells and whistles)
community.general.keycloak_client:
auth_client_id: admin-cli
@@ -913,7 +941,7 @@ def main():
base_url=dict(type='str', aliases=['baseUrl']),
surrogate_auth_required=dict(type='bool', aliases=['surrogateAuthRequired']),
enabled=dict(type='bool'),
client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt'], aliases=['clientAuthenticatorType']),
client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt', 'client-x509'], aliases=['clientAuthenticatorType']),
secret=dict(type='str', no_log=True),
registration_access_token=dict(type='str', aliases=['registrationAccessToken'], no_log=True),
default_roles=dict(type='list', elements='str', aliases=['defaultRoles']),

View File

@@ -803,7 +803,7 @@ def main():
if module._diff:
result['diff'] = dict(before=sanitize_cr(before_norm),
after=sanitize_cr(desired_norm))
result['changed'] = (before_realm != desired_realm)
result['changed'] = (before_norm != desired_norm)
module.exit_json(**result)

View File

@@ -93,6 +93,24 @@ options:
default: true
version_added: 9.4.0
bind_credential_update_mode:
description:
- The value of the config parameter O(config.bindCredential) is redacted in the Keycloak responses.
Comparing the redacted value with the desired value always evaluates to not equal. This means
the before and desired states are never equal if the parameter is set.
- Set to V(always) to include O(config.bindCredential) in the comparison of before and desired state.
Because of the redacted value returned by Keycloak the module will always detect a change
and make an update if a O(config.bindCredential) value is set.
- Set to V(only_indirect) to exclude O(config.bindCredential) when comparing the before state with the
desired state. The value of O(config.bindCredential) will only be updated if there are other changes
to the user federation that require an update.
type: str
default: always
choices:
- always
- only_indirect
version_added: 9.5.0
config:
description:
- Dict specifying the configuration options for the provider; the contents differ depending on
@@ -442,6 +460,17 @@ options:
- Max lifespan of cache entry in milliseconds.
type: int
referral:
description:
- Specifies if LDAP referrals should be followed or ignored. Please note that enabling
referrals can slow down authentication as it allows the LDAP server to decide which other
LDAP servers to use. This could potentially include untrusted servers.
type: str
choices:
- ignore
- follow
version_added: 9.5.0
mappers:
description:
- A list of dicts defining mappers associated with this Identity Provider.
@@ -721,15 +750,23 @@ from ansible.module_utils.six.moves.urllib.parse import urlencode
from copy import deepcopy
def normalize_kc_comp(comp):
if 'config' in comp:
# kc completely removes the parameter `krbPrincipalAttribute` if it is set to `''`; the unset kc parameter is equivalent to `''`;
# to make change detection and diff more accurate we set it again in the kc responses
if 'krbPrincipalAttribute' not in comp['config']:
comp['config']['krbPrincipalAttribute'] = ['']
# kc stores a timestamp of the last sync in `lastSync` to time the periodic sync, it is removed to minimize diff/changes
comp['config'].pop('lastSync', None)
def sanitize(comp):
compcopy = deepcopy(comp)
if 'config' in compcopy:
compcopy['config'] = {k: v[0] for k, v in compcopy['config'].items()}
if 'bindCredential' in compcopy['config']:
compcopy['config']['bindCredential'] = '**********'
# an empty string is valid for krbPrincipalAttribute but is filtered out in diff
if 'krbPrincipalAttribute' not in compcopy['config']:
compcopy['config']['krbPrincipalAttribute'] = ''
if 'mappers' in compcopy:
for mapper in compcopy['mappers']:
if 'config' in mapper:
@@ -780,6 +817,7 @@ def main():
priority=dict(type='int', default=0),
rdnLDAPAttribute=dict(type='str'),
readTimeout=dict(type='int'),
referral=dict(type='str', choices=['ignore', 'follow']),
searchScope=dict(type='str', choices=['1', '2'], default='1'),
serverPrincipal=dict(type='str'),
krbPrincipalAttribute=dict(type='str'),
@@ -817,6 +855,7 @@ def main():
provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'),
parent_id=dict(type='str', aliases=['parentId']),
remove_unspecified_mappers=dict(type='bool', default=True),
bind_credential_update_mode=dict(type='str', default='always', choices=['always', 'only_indirect']),
mappers=dict(type='list', elements='dict', options=mapper_spec),
)
@@ -864,8 +903,9 @@ def main():
# Filter and map the parameters names that apply
comp_params = [x for x in module.params
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers', 'remove_unspecified_mappers'] and
module.params.get(x) is not None]
if x not in list(keycloak_argument_spec().keys())
+ ['state', 'realm', 'mappers', 'remove_unspecified_mappers', 'bind_credential_update_mode']
and module.params.get(x) is not None]
# See if it already exists in Keycloak
if cid is None:
@@ -885,6 +925,8 @@ def main():
if cid is not None and before_comp:
before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '')
normalize_kc_comp(before_comp)
# Build a proposed changeset from parameters given to this module
changeset = {}
@@ -994,6 +1036,7 @@ def main():
kc.delete_component(default_mapper['id'], realm)
after_comp['mappers'] = kc.get_components(urlencode(dict(parent=cid)), realm)
normalize_kc_comp(after_comp)
if module._diff:
result['diff'] = dict(before='', after=sanitize(after_comp))
result['end_state'] = sanitize(after_comp)
@@ -1004,8 +1047,15 @@ def main():
if state == 'present':
# Process an update
desired_copy = deepcopy(desired_comp)
before_copy = deepcopy(before_comp)
# exclude bindCredential when checking wether an update is required, therefore
# updating it only if there are other changes
if module.params['bind_credential_update_mode'] == 'only_indirect':
desired_copy.get('config', []).pop('bindCredential', None)
before_copy.get('config', []).pop('bindCredential', None)
# no changes
if desired_comp == before_comp:
if desired_copy == before_copy:
result['changed'] = False
result['end_state'] = sanitize(desired_comp)
result['msg'] = "No changes required to user federation {id}.".format(id=cid)
@@ -1041,6 +1091,7 @@ def main():
after_comp = kc.get_component(cid, realm)
after_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '')
normalize_kc_comp(after_comp)
after_comp_sanitized = sanitize(after_comp)
before_comp_sanitized = sanitize(before_comp)
result['end_state'] = after_comp_sanitized

View File

@@ -612,9 +612,7 @@ def main():
attribute['validations']['person-name-prohibited-characters'] = (
attribute['validations'].pop('personNameProhibitedCharacters')
)
# special JSON parsing for kc_user_profile_config
value = json.dumps(kc_user_profile_config[0])
changeset[camel(component_param)][config_param].append(value)
changeset[camel(component_param)][config_param].append(kc_user_profile_config[0])
# usual camelCase parameters
else:
changeset[camel(component_param)][camel(config_param)] = []
@@ -641,7 +639,7 @@ def main():
changeset_copy = deepcopy(changeset)
# Get a list of all Keycloak components that are of userprofile provider type.
realm_userprofiles = kc.get_components(urlencode(dict(type=provider_type, parent=parent_id)), parent_id)
realm_userprofiles = kc.get_components(urlencode(dict(type=provider_type)), parent_id)
# If this component is present get its userprofile ID. Confusingly the userprofile ID is
# also known as the Provider ID.
@@ -662,6 +660,10 @@ def main():
changeset['id'] = userprofile_id
changeset_copy['id'] = userprofile_id
# keycloak returns kc.user.profile.config as a single JSON formatted string, so we have to deserialize it
if 'config' in userprofile and 'kc.user.profile.config' in userprofile['config']:
userprofile['config']['kc.user.profile.config'][0] = json.loads(userprofile['config']['kc.user.profile.config'][0])
# Compare top-level parameters
for param, value in changeset.items():
before_realm_userprofile[param] = userprofile[param]
@@ -680,6 +682,10 @@ def main():
# Check all the possible states of the resource and do what is needed to
# converge current state with desired state (create, update or delete
# the userprofile).
# keycloak expects kc.user.profile.config as a single JSON formatted string, so we have to serialize it
if 'config' in changeset and 'kc.user.profile.config' in changeset['config']:
changeset['config']['kc.user.profile.config'][0] = json.dumps(changeset['config']['kc.user.profile.config'][0])
if userprofile_id and state == 'present':
if result['changed']:
if module._diff:

View File

@@ -618,7 +618,7 @@ class LXDContainerManagement(object):
data = (self._get_instance_state_json() or {}).get('metadata', None) or {}
network = {
k: v
for k, v in data.get('network', {}).items()
for k, v in (data.get('network') or {}).items()
if k not in ignore_devices
}
addresses = {
@@ -768,7 +768,7 @@ class LXDContainerManagement(object):
self.old_instance_json = self._get_instance_json()
self.old_sections = {
section: adjust_content(content)
for section, content in self.old_instance_json.get('metadata', {}).items()
for section, content in (self.old_instance_json.get('metadata') or {}).items()
if section in set(CONFIG_PARAMS) - set(CONFIG_CREATION_PARAMS)
}

View File

@@ -304,22 +304,7 @@ EXAMPLES = '''
security_protocol: 'ssl-with-validation-custom-ca'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
metrics:
auth_key: 'topSecret'
@@ -330,22 +315,7 @@ EXAMPLES = '''
security_protocol: 'ssl-with-validation-custom-ca'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
manageiq_connection:
url: 'https://127.0.0.1:80'
@@ -367,22 +337,7 @@ EXAMPLES = '''
security_protocol: 'ssl-with-validation-custom-ca'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
metrics:
auth_key: 'topSecret'
@@ -392,22 +347,7 @@ EXAMPLES = '''
security_protocol: 'ssl-with-validation-custom-ca'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
manageiq_connection:
url: 'https://127.0.0.1'
@@ -455,22 +395,7 @@ EXAMPLES = '''
validate_certs: true
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
metrics:
hostname: 'metrics.example.com'
@@ -480,22 +405,7 @@ EXAMPLES = '''
validate_certs: true
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
manageiq_connection:
url: 'https://127.0.0.1'
@@ -551,22 +461,7 @@ EXAMPLES = '''
validate_certs: 'true'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
ssh_keypair:
hostname: director.example.com
@@ -590,22 +485,7 @@ EXAMPLES = '''
validate_certs: 'true'
certificate_authority: |
-----BEGIN CERTIFICATE-----
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
FAKECERTsdKgAwI...
-----END CERTIFICATE-----
metrics:
role: amqp

View File

@@ -178,9 +178,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
retvals = reload_dns(args)

View File

@@ -163,9 +163,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
retvals = get_facts(args)

View File

@@ -288,9 +288,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
retvals = get_facts(args)

View File

@@ -300,9 +300,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
args['check_mode'] = module.check_mode
# validate some API-specific limitations.

View File

@@ -244,9 +244,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
args['check_mode'] = module.check_mode
# validate some API-specific limitations.

View File

@@ -374,9 +374,7 @@ def main():
)
# populate the dict with the user-provided vars.
args = dict()
for key, arg in module.params.items():
args[key] = arg
args = dict(module.params)
args['check_mode'] = module.check_mode
# perform some Memset API-specific validation

View File

@@ -10,15 +10,20 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
author: Kairo Araujo (@kairoaraujo)
module: mksysb
short_description: Generates AIX mksysb rootvg backups
description:
- This module manages a basic AIX mksysb (image) of rootvg.
- This module manages a basic AIX mksysb (image) of rootvg.
seealso:
- name: C(mksysb) command manual page
description: Manual page for the command.
link: https://www.ibm.com/docs/en/aix/7.3?topic=m-mksysb-command
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
support: full
@@ -27,72 +32,73 @@ attributes:
options:
backup_crypt_files:
description:
- Backup encrypted files.
- Backup encrypted files.
type: bool
default: true
backup_dmapi_fs:
description:
- Back up DMAPI filesystem files.
- Back up DMAPI filesystem files.
type: bool
default: true
create_map_files:
description:
- Creates a new MAP files.
- Creates a new MAP files.
type: bool
default: false
exclude_files:
description:
- Excludes files using C(/etc/rootvg.exclude).
- Excludes files using C(/etc/rootvg.exclude).
type: bool
default: false
exclude_wpar_files:
description:
- Excludes WPAR files.
- Excludes WPAR files.
type: bool
default: false
extended_attrs:
description:
- Backup extended attributes.
- Backup extended attributes.
type: bool
default: true
name:
type: str
description:
- Backup name
- Backup name
required: true
new_image_data:
description:
- Creates a new file data.
- Creates a new file data.
type: bool
default: true
software_packing:
description:
- Exclude files from packing option listed in
C(/etc/exclude_packing.rootvg).
- Exclude files from packing option listed in C(/etc/exclude_packing.rootvg).
type: bool
default: false
storage_path:
type: str
description:
- Storage path where the mksysb will stored.
- Storage path where the mksysb will stored.
required: true
use_snapshot:
description:
- Creates backup using snapshots.
- Creates backup using snapshots.
type: bool
default: false
'''
"""
EXAMPLES = '''
EXAMPLES = """
---
- name: Running a backup image mksysb
community.general.mksysb:
name: myserver
storage_path: /repository/images
exclude_files: true
exclude_wpar_files: true
'''
"""
RETURN = '''
RETURN = """
---
changed:
description: Return changed for mksysb actions as true or false.
returned: always
@@ -101,7 +107,7 @@ msg:
description: Return message regarding the action.
returned: always
type: str
'''
"""
import os

View File

@@ -34,9 +34,11 @@ options:
state:
description:
- Whether the device should exist or not, taking action if the state is different from what is stated.
- Using O(state=present) to create connection will automatically bring connection up.
- Using O(state=up) and O(state=down) will not modify connection with other parameters. These states have been added in community.general 9.5.0.
type: str
required: true
choices: [ absent, present ]
choices: [ absent, present, up, down ]
autoconnect:
description:
- Whether the connection should start on boot.
@@ -48,6 +50,13 @@ options:
- The name used to call the connection. Pattern is <type>[-<ifname>][-<num>].
type: str
required: true
conn_reload:
description:
- Whether the connection should be reloaded if it was modified.
type: bool
required: false
default: false
version_added: 9.5.0
ifname:
description:
- The interface to bind the connection to.
@@ -1309,6 +1318,25 @@ EXAMPLES = r'''
type: ethernet
state: present
- name: Change the property of a setting e.g. MTU and reload connection
community.general.nmcli:
conn_name: my-eth1
mtu: 1500
type: ethernet
state: present
conn_reload: true
- name: Disable connection
community.general.nmcli:
conn_name: my-eth1
state: down
- name: Reload and enable connection
community.general.nmcli:
conn_name: my-eth1
state: up
reload: true
- name: Add second ip4 address
community.general.nmcli:
conn_name: my-eth1
@@ -1581,6 +1609,7 @@ class Nmcli(object):
self.ignore_unsupported_suboptions = module.params['ignore_unsupported_suboptions']
self.autoconnect = module.params['autoconnect']
self.conn_name = module.params['conn_name']
self.conn_reload = module.params['conn_reload']
self.slave_type = module.params['slave_type']
self.master = module.params['master']
self.ifname = module.params['ifname']
@@ -1944,7 +1973,7 @@ class Nmcli(object):
convert_func = self.list_to_string
if callable(convert_func):
options[setting] = convert_func(options[setting])
options[setting] = convert_func(value)
return options
@@ -2165,6 +2194,10 @@ class Nmcli(object):
cmd = [self.nmcli_bin, 'con', 'up', self.conn_name]
return self.execute_command(cmd)
def reload_connection(self):
cmd = [self.nmcli_bin, 'con', 'reload']
return self.execute_command(cmd)
def connection_update(self, nmcli_command):
if nmcli_command == 'create':
cmd = [self.nmcli_bin, 'con', 'add', 'type']
@@ -2431,8 +2464,9 @@ def main():
argument_spec=dict(
ignore_unsupported_suboptions=dict(type='bool', default=False),
autoconnect=dict(type='bool', default=True),
state=dict(type='str', required=True, choices=['absent', 'present']),
state=dict(type='str', required=True, choices=['absent', 'present', 'up', 'down']),
conn_name=dict(type='str', required=True),
conn_reload=dict(type='bool', default=False),
master=dict(type='str'),
slave_type=dict(type='str', choices=['bond', 'bridge', 'team', 'ovs-port']),
ifname=dict(type='str'),
@@ -2639,6 +2673,8 @@ def main():
if module.check_mode:
module.exit_json(changed=True, **result)
(rc, out, err) = nmcli.modify_connection()
if nmcli.conn_reload:
(rc, out, err) = nmcli.reload_connection()
else:
result['Exists'] = 'Connections already exist and no changes made'
if module.check_mode:
@@ -2650,6 +2686,27 @@ def main():
(rc, out, err) = nmcli.create_connection()
if rc is not None and rc != 0:
module.fail_json(name=nmcli.conn_name, msg=err, rc=rc)
elif nmcli.state == 'up':
if nmcli.connection_exists():
if module.check_mode:
module.exit_json(changed=True)
if nmcli.conn_reload:
(rc, out, err) = nmcli.reload_connection()
(rc, out, err) = nmcli.up_connection()
if rc != 0:
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
elif nmcli.state == 'down':
if nmcli.connection_exists():
if module.check_mode:
module.exit_json(changed=True)
if nmcli.conn_reload:
(rc, out, err) = nmcli.reload_connection()
(rc, out, err) = nmcli.down_connection()
if rc != 0:
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
except NmcliModuleError as e:
module.fail_json(name=nmcli.conn_name, msg=str(e))

View File

@@ -96,6 +96,12 @@ options:
type: bool
default: false
version_added: 2.5.0
force:
description:
- Use the C(--force) flag when installing.
type: bool
default: false
version_added: 9.5.0
requirements:
- npm installed in bin path (recommended /usr/local/bin)
'''
@@ -117,6 +123,11 @@ EXAMPLES = r'''
name: coffee-script
global: true
- name: Force Install "coffee-script" node.js package.
community.general.npm:
name: coffee-script
force: true
- name: Remove the globally package "coffee-script".
community.general.npm:
name: coffee-script
@@ -167,6 +178,7 @@ class Npm(object):
self.state = kwargs['state']
self.no_optional = kwargs['no_optional']
self.no_bin_links = kwargs['no_bin_links']
self.force = kwargs['force']
if kwargs['executable']:
self.executable = kwargs['executable'].split(' ')
@@ -191,6 +203,7 @@ class Npm(object):
registry=cmd_runner_fmt.as_opt_val('--registry'),
no_optional=cmd_runner_fmt.as_bool('--no-optional'),
no_bin_links=cmd_runner_fmt.as_bool('--no-bin-links'),
force=cmd_runner_fmt.as_bool('--force'),
)
)
@@ -212,7 +225,7 @@ class Npm(object):
params['name_version'] = self.name_version if add_package_name else None
with self.runner(
"exec_args global_ production ignore_scripts unsafe_perm name_version registry no_optional no_bin_links",
"exec_args global_ production ignore_scripts unsafe_perm name_version registry no_optional no_bin_links force",
check_rc=check_rc, cwd=cwd
) as ctx:
rc, out, err = ctx.run(**params)
@@ -289,6 +302,7 @@ def main():
ci=dict(default=False, type='bool'),
no_optional=dict(default=False, type='bool'),
no_bin_links=dict(default=False, type='bool'),
force=dict(default=False, type='bool'),
)
arg_spec['global'] = dict(default=False, type='bool')
module = AnsibleModule(
@@ -318,7 +332,8 @@ def main():
unsafe_perm=module.params['unsafe_perm'],
state=state,
no_optional=module.params['no_optional'],
no_bin_links=module.params['no_bin_links'])
no_bin_links=module.params['no_bin_links'],
force=module.params['force'])
changed = False
if module.params['ci']:

View File

@@ -17,6 +17,7 @@ description:
requirements:
- pyone
extends_documentation_fragment:
- community.general.opennebula
- community.general.attributes
attributes:
check_mode:
@@ -24,23 +25,6 @@ attributes:
diff_mode:
support: none
options:
api_url:
description:
- URL of the OpenNebula RPC server.
- It is recommended to use HTTPS so that the username/password are not
- transferred over the network unencrypted.
- If not set then the value of the E(ONE_URL) environment variable is used.
type: str
api_username:
description:
- Name of the user to login into the OpenNebula RPC server. If not set
- then the value of the E(ONE_USERNAME) environment variable is used.
type: str
api_password:
description:
- Password of the user to login into OpenNebula RPC server. If not set
- then the value of the E(ONE_PASSWORD) environment variable is used.
type: str
id:
description:
- A O(id) of the image you would like to manage.
@@ -67,6 +51,11 @@ options:
- A name that will be assigned to the existing or new image.
- In the case of cloning, by default O(new_name) will take the name of the origin image with the prefix 'Copy of'.
type: str
persistent:
description:
- Whether the image should be persistent or non-persistent.
type: bool
version_added: 9.5.0
author:
- "Milan Ilic (@ilicmilan)"
'''
@@ -92,6 +81,11 @@ EXAMPLES = '''
id: 37
enabled: false
- name: Make the IMAGE persistent
community.general.one_image:
id: 37
persistent: true
- name: Enable the IMAGE by name
community.general.one_image:
name: bar-image
@@ -114,300 +108,448 @@ RETURN = '''
id:
description: image id
type: int
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: 153
name:
description: image name
type: str
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: app1
group_id:
description: image's group id
type: int
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: 1
group_name:
description: image's group name
type: str
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: one-users
owner_id:
description: image's owner id
type: int
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: 143
owner_name:
description: image's owner name
type: str
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: ansible-test
state:
description: state of image instance
type: str
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: READY
used:
description: is image in use
type: bool
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: true
running_vms:
description: count of running vms that use this image
type: int
returned: success
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample: 7
permissions:
description: The image's permissions.
type: dict
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
contains:
owner_u:
description: The image's owner USAGE permissions.
type: str
sample: 1
owner_m:
description: The image's owner MANAGE permissions.
type: str
sample: 0
owner_a:
description: The image's owner ADMIN permissions.
type: str
sample: 0
group_u:
description: The image's group USAGE permissions.
type: str
sample: 0
group_m:
description: The image's group MANAGE permissions.
type: str
sample: 0
group_a:
description: The image's group ADMIN permissions.
type: str
sample: 0
other_u:
description: The image's other users USAGE permissions.
type: str
sample: 0
other_m:
description: The image's other users MANAGE permissions.
type: str
sample: 0
other_a:
description: The image's other users ADMIN permissions
type: str
sample: 0
sample:
owner_u: 1
owner_m: 0
owner_a: 0
group_u: 0
group_m: 0
group_a: 0
other_u: 0
other_m: 0
other_a: 0
type:
description: The image's type.
type: str
sample: 0
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
disk_type:
description: The image's format type.
type: str
sample: 0
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
persistent:
description: The image's persistence status (1 means true, 0 means false).
type: int
sample: 1
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
source:
description: The image's source.
type: str
sample: /var/lib/one//datastores/100/somerandomstringxd
returned: when O(state=present), O(state=cloned), or O(state=renamed)
path:
description: The image's filesystem path.
type: str
sample: /var/tmp/hello.qcow2
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
fstype:
description: The image's filesystem type.
type: str
sample: ext4
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
size:
description: The image's size in MegaBytes.
type: int
sample: 10000
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
cloning_ops:
description: The image's cloning operations per second.
type: int
sample: 0
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
cloning_id:
description: The image's cloning ID.
type: int
sample: -1
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
target_snapshot:
description: The image's target snapshot.
type: int
sample: 1
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
datastore_id:
description: The image's datastore ID.
type: int
sample: 100
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
datastore:
description: The image's datastore name.
type: int
sample: image_datastore
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
vms:
description: The image's list of vm ID's.
type: list
elements: int
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample:
- 1
- 2
- 3
version_added: 9.5.0
clones:
description: The image's list of clones ID's.
type: list
elements: int
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample:
- 1
- 2
- 3
version_added: 9.5.0
app_clones:
description: The image's list of app_clones ID's.
type: list
elements: int
returned: when O(state=present), O(state=cloned), or O(state=renamed)
sample:
- 1
- 2
- 3
version_added: 9.5.0
snapshots:
description: The image's list of snapshots.
type: list
returned: when O(state=present), O(state=cloned), or O(state=renamed)
version_added: 9.5.0
sample:
- date: 123123
parent: 1
size: 10228
allow_orphans: 1
children: 0
active: 1
name: SampleName
'''
try:
import pyone
HAS_PYONE = True
except ImportError:
HAS_PYONE = False
from ansible.module_utils.basic import AnsibleModule
import os
def get_image(module, client, predicate):
# Filter -2 means fetch all images user can Use
pool = client.imagepool.info(-2, -1, -1, -1)
for image in pool.IMAGE:
if predicate(image):
return image
return None
def get_image_by_name(module, client, image_name):
return get_image(module, client, lambda image: (image.NAME == image_name))
def get_image_by_id(module, client, image_id):
return get_image(module, client, lambda image: (image.ID == image_id))
def get_image_instance(module, client, requested_id, requested_name):
if requested_id:
return get_image_by_id(module, client, requested_id)
else:
return get_image_by_name(module, client, requested_name)
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
def get_image_info(image):
info = {
'id': image.ID,
'name': image.NAME,
'state': IMAGE_STATES[image.STATE],
'running_vms': image.RUNNING_VMS,
'used': bool(image.RUNNING_VMS),
'user_name': image.UNAME,
'user_id': image.UID,
'group_name': image.GNAME,
'group_id': image.GID,
}
class ImageModule(OpenNebulaModule):
def __init__(self):
argument_spec = dict(
id=dict(type='int', required=False),
name=dict(type='str', required=False),
state=dict(type='str', choices=['present', 'absent', 'cloned', 'renamed'], default='present'),
enabled=dict(type='bool', required=False),
new_name=dict(type='str', required=False),
persistent=dict(type='bool', required=False),
)
required_if = [
['state', 'renamed', ['id']]
]
mutually_exclusive = [
['id', 'name'],
]
return info
OpenNebulaModule.__init__(self,
argument_spec,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
required_if=required_if)
def run(self, one, module, result):
params = module.params
id = params.get('id')
name = params.get('name')
desired_state = params.get('state')
enabled = params.get('enabled')
new_name = params.get('new_name')
persistent = params.get('persistent')
def wait_for_state(module, client, image_id, wait_timeout, state_predicate):
import time
start_time = time.time()
self.result = {}
image = self.get_image_instance(id, name)
if not image and desired_state != 'absent':
# Using 'if id:' doesn't work properly when id=0
if id is not None:
module.fail_json(msg="There is no image with id=" + str(id))
elif name is not None:
module.fail_json(msg="There is no image with name=" + name)
if desired_state == 'absent':
self.result = self.delete_image(image)
else:
if persistent is not None:
self.result = self.change_persistence(image, persistent)
if enabled is not None:
self.result = self.enable_image(image, enabled)
if desired_state == "cloned":
self.result = self.clone_image(image, new_name)
elif desired_state == "renamed":
self.result = self.rename_image(image, new_name)
self.exit()
def get_image(self, predicate):
# Filter -2 means fetch all images user can Use
pool = self.one.imagepool.info(-2, -1, -1, -1)
for image in pool.IMAGE:
if predicate(image):
return image
return None
def get_image_by_name(self, image_name):
return self.get_image(lambda image: (image.NAME == image_name))
def get_image_by_id(self, image_id):
return self.get_image(lambda image: (image.ID == image_id))
def get_image_instance(self, requested_id, requested_name):
# Using 'if requested_id:' doesn't work properly when requested_id=0
if requested_id is not None:
return self.get_image_by_id(requested_id)
else:
return self.get_image_by_name(requested_name)
def wait_for_ready(self, image_id, wait_timeout=60):
import time
start_time = time.time()
while (time.time() - start_time) < wait_timeout:
image = self.one.image.info(image_id)
state = image.STATE
if state in [IMAGE_STATES.index('ERROR')]:
self.module.fail_json(msg="Got an ERROR state: " + image.TEMPLATE['ERROR'])
if state in [IMAGE_STATES.index('READY')]:
return True
time.sleep(1)
self.module.fail_json(msg="Wait timeout has expired!")
def wait_for_delete(self, image_id, wait_timeout=60):
import time
start_time = time.time()
while (time.time() - start_time) < wait_timeout:
# It might be already deleted by the time this function is called
try:
image = self.one.image.info(image_id)
except Exception:
check_image = self.get_image_instance(image_id)
if not check_image:
return True
state = image.STATE
if state in [IMAGE_STATES.index('DELETE')]:
return True
time.sleep(1)
self.module.fail_json(msg="Wait timeout has expired!")
def enable_image(self, image, enable):
image = self.one.image.info(image.ID)
changed = False
while (time.time() - start_time) < wait_timeout:
image = client.image.info(image_id)
state = image.STATE
if state_predicate(state):
return image
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
if enable:
self.module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
else:
self.module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
time.sleep(1)
if ((enable and state != IMAGE_STATES.index('READY')) or
(not enable and state != IMAGE_STATES.index('DISABLED'))):
changed = True
module.fail_json(msg="Wait timeout has expired!")
if changed and not self.module.check_mode:
self.one.image.enable(image.ID, enable)
result = OpenNebulaModule.get_image_info(image)
result['changed'] = changed
def wait_for_ready(module, client, image_id, wait_timeout=60):
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('READY')]))
def wait_for_delete(module, client, image_id, wait_timeout=60):
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('DELETE')]))
def enable_image(module, client, image, enable):
image = client.image.info(image.ID)
changed = False
state = image.STATE
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
if enable:
module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
else:
module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
if ((enable and state != IMAGE_STATES.index('READY')) or
(not enable and state != IMAGE_STATES.index('DISABLED'))):
changed = True
if changed and not module.check_mode:
client.image.enable(image.ID, enable)
result = get_image_info(image)
result['changed'] = changed
return result
def clone_image(module, client, image, new_name):
if new_name is None:
new_name = "Copy of " + image.NAME
tmp_image = get_image_by_name(module, client, new_name)
if tmp_image:
result = get_image_info(tmp_image)
result['changed'] = False
return result
if image.STATE == IMAGE_STATES.index('DISABLED'):
module.fail_json(msg="Cannot clone DISABLED image")
def change_persistence(self, image, enable):
image = self.one.image.info(image.ID)
changed = False
if not module.check_mode:
new_id = client.image.clone(image.ID, new_name)
wait_for_ready(module, client, new_id)
image = client.image.info(new_id)
state = image.STATE
result = get_image_info(image)
result['changed'] = True
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
if enable:
self.module.fail_json(msg="Cannot enable persistence for " + IMAGE_STATES[state] + " image!")
else:
self.module.fail_json(msg="Cannot disable persistence for " + IMAGE_STATES[state] + " image!")
return result
if ((enable and state != IMAGE_STATES.index('READY')) or
(not enable and state != IMAGE_STATES.index('DISABLED'))):
changed = True
if changed and not self.module.check_mode:
self.one.image.persistent(image.ID, enable)
def rename_image(module, client, image, new_name):
if new_name is None:
module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
result = OpenNebulaModule.get_image_info(image)
result['changed'] = changed
if new_name == image.NAME:
result = get_image_info(image)
result['changed'] = False
return result
tmp_image = get_image_by_name(module, client, new_name)
if tmp_image:
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
def clone_image(self, image, new_name):
if new_name is None:
new_name = "Copy of " + image.NAME
if not module.check_mode:
client.image.rename(image.ID, new_name)
tmp_image = self.get_image_by_name(new_name)
if tmp_image:
result = OpenNebulaModule.get_image_info(tmp_image)
result['changed'] = False
return result
result = get_image_info(image)
result['changed'] = True
return result
if image.STATE == IMAGE_STATES.index('DISABLED'):
self.module.fail_json(msg="Cannot clone DISABLED image")
if not self.module.check_mode:
new_id = self.one.image.clone(image.ID, new_name)
self.wait_for_ready(new_id)
image = self.one.image.info(new_id)
def delete_image(module, client, image):
result = OpenNebulaModule.get_image_info(image)
result['changed'] = True
if not image:
return {'changed': False}
return result
if image.RUNNING_VMS > 0:
module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
def rename_image(self, image, new_name):
if new_name is None:
self.module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
if not module.check_mode:
client.image.delete(image.ID)
wait_for_delete(module, client, image.ID)
if new_name == image.NAME:
result = OpenNebulaModule.get_image_info(image)
result['changed'] = False
return result
return {'changed': True}
tmp_image = self.get_image_by_name(new_name)
if tmp_image:
self.module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
if not self.module.check_mode:
self.one.image.rename(image.ID, new_name)
def get_connection_info(module):
result = OpenNebulaModule.get_image_info(image)
result['changed'] = True
return result
url = module.params.get('api_url')
username = module.params.get('api_username')
password = module.params.get('api_password')
def delete_image(self, image):
if not image:
return {'changed': False}
if not url:
url = os.environ.get('ONE_URL')
if image.RUNNING_VMS > 0:
self.module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
if not username:
username = os.environ.get('ONE_USERNAME')
if not self.module.check_mode:
self.one.image.delete(image.ID)
self.wait_for_delete(image.ID)
if not password:
password = os.environ.get('ONE_PASSWORD')
if not (url and username and password):
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
from collections import namedtuple
auth_params = namedtuple('auth', ('url', 'username', 'password'))
return auth_params(url=url, username=username, password=password)
return {'changed': True}
def main():
fields = {
"api_url": {"required": False, "type": "str"},
"api_username": {"required": False, "type": "str"},
"api_password": {"required": False, "type": "str", "no_log": True},
"id": {"required": False, "type": "int"},
"name": {"required": False, "type": "str"},
"state": {
"default": "present",
"choices": ['present', 'absent', 'cloned', 'renamed'],
"type": "str"
},
"enabled": {"required": False, "type": "bool"},
"new_name": {"required": False, "type": "str"},
}
module = AnsibleModule(argument_spec=fields,
mutually_exclusive=[['id', 'name']],
supports_check_mode=True)
if not HAS_PYONE:
module.fail_json(msg='This module requires pyone to work!')
auth = get_connection_info(module)
params = module.params
id = params.get('id')
name = params.get('name')
state = params.get('state')
enabled = params.get('enabled')
new_name = params.get('new_name')
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
result = {}
if not id and state == 'renamed':
module.fail_json(msg="Option 'id' is required when the state is 'renamed'")
image = get_image_instance(module, client, id, name)
if not image and state != 'absent':
if id:
module.fail_json(msg="There is no image with id=" + str(id))
else:
module.fail_json(msg="There is no image with name=" + name)
if state == 'absent':
result = delete_image(module, client, image)
else:
result = get_image_info(image)
changed = False
result['changed'] = False
if enabled is not None:
result = enable_image(module, client, image, enabled)
if state == "cloned":
result = clone_image(module, client, image, new_name)
elif state == "renamed":
result = rename_image(module, client, image, new_name)
changed = changed or result['changed']
result['changed'] = changed
module.exit_json(**result)
ImageModule().run_module()
if __name__ == '__main__':

View File

@@ -17,29 +17,14 @@ description:
requirements:
- pyone
extends_documentation_fragment:
- community.general.opennebula
- community.general.attributes
- community.general.attributes.info_module
options:
api_url:
description:
- URL of the OpenNebula RPC server.
- It is recommended to use HTTPS so that the username/password are not
- transferred over the network unencrypted.
- If not set then the value of the E(ONE_URL) environment variable is used.
type: str
api_username:
description:
- Name of the user to login into the OpenNebula RPC server. If not set
- then the value of the E(ONE_USERNAME) environment variable is used.
type: str
api_password:
description:
- Password of the user to login into OpenNebula RPC server. If not set
- then the value of the E(ONE_PASSWORD) environment variable is used.
type: str
ids:
description:
- A list of images ids whose facts you want to gather.
- Module can use integers too.
aliases: ['id']
type: list
elements: str
@@ -66,9 +51,16 @@ EXAMPLES = '''
msg: result
- name: Gather facts about an image using ID
community.general.one_image_info:
ids: 123
- name: Gather facts about an image using list of ID
community.general.one_image_info:
ids:
- 123
- 456
- 789
- 0
- name: Gather facts about an image using the name
community.general.one_image_info:
@@ -93,182 +85,285 @@ images:
returned: success
contains:
id:
description: image id
description: The image's id.
type: int
sample: 153
name:
description: image name
description: The image's name.
type: str
sample: app1
group_id:
description: image's group id
description: The image's group id
type: int
sample: 1
group_name:
description: image's group name
description: The image's group name.
type: str
sample: one-users
owner_id:
description: image's owner id
description: The image's owner id.
type: int
sample: 143
owner_name:
description: image's owner name
description: The image's owner name.
type: str
sample: ansible-test
state:
description: state of image instance
description: The image's state.
type: str
sample: READY
used:
description: is image in use
description: The image's usage status.
type: bool
sample: true
running_vms:
description: count of running vms that use this image
description: The image's count of running vms that use this image.
type: int
sample: 7
permissions:
description: The image's permissions.
type: dict
version_added: 9.5.0
contains:
owner_u:
description: The image's owner USAGE permissions.
type: str
sample: 1
owner_m:
description: The image's owner MANAGE permissions.
type: str
sample: 0
owner_a:
description: The image's owner ADMIN permissions.
type: str
sample: 0
group_u:
description: The image's group USAGE permissions.
type: str
sample: 0
group_m:
description: The image's group MANAGE permissions.
type: str
sample: 0
group_a:
description: The image's group ADMIN permissions.
type: str
sample: 0
other_u:
description: The image's other users USAGE permissions.
type: str
sample: 0
other_m:
description: The image's other users MANAGE permissions.
type: str
sample: 0
other_a:
description: The image's other users ADMIN permissions
type: str
sample: 0
sample:
owner_u: 1
owner_m: 0
owner_a: 0
group_u: 0
group_m: 0
group_a: 0
other_u: 0
other_m: 0
other_a: 0
type:
description: The image's type.
type: int
sample: 0
version_added: 9.5.0
disk_type:
description: The image's format type.
type: int
sample: 0
version_added: 9.5.0
persistent:
description: The image's persistence status (1 means true, 0 means false).
type: int
sample: 1
version_added: 9.5.0
source:
description: The image's source.
type: str
sample: /var/lib/one//datastores/100/somerandomstringxd
version_added: 9.5.0
path:
description: The image's filesystem path.
type: str
sample: /var/tmp/hello.qcow2
version_added: 9.5.0
fstype:
description: The image's filesystem type.
type: str
sample: ext4
version_added: 9.5.0
size:
description: The image's size in MegaBytes.
type: int
sample: 10000
version_added: 9.5.0
cloning_ops:
description: The image's cloning operations per second.
type: int
sample: 0
version_added: 9.5.0
cloning_id:
description: The image's cloning ID.
type: int
sample: -1
version_added: 9.5.0
target_snapshot:
description: The image's target snapshot.
type: int
sample: 1
version_added: 9.5.0
datastore_id:
description: The image's datastore ID.
type: int
sample: 100
version_added: 9.5.0
datastore:
description: The image's datastore name.
type: int
sample: image_datastore
version_added: 9.5.0
vms:
description: The image's list of vm ID's.
type: list
elements: int
version_added: 9.5.0
sample:
- 1
- 2
- 3
clones:
description: The image's list of clones ID's.
type: list
elements: int
version_added: 9.5.0
sample:
- 1
- 2
- 3
app_clones:
description: The image's list of app_clones ID's.
type: list
elements: int
version_added: 9.5.0
sample:
- 1
- 2
- 3
snapshots:
description: The image's list of snapshots.
type: list
version_added: 9.5.0
sample:
- date: 123123
parent: 1
size: 10228
allow_orphans: 1
children: 0
active: 1
name: SampleName
'''
try:
import pyone
HAS_PYONE = True
except ImportError:
HAS_PYONE = False
from ansible.module_utils.basic import AnsibleModule
import os
def get_all_images(client):
pool = client.imagepool.info(-2, -1, -1, -1)
# Filter -2 means fetch all images user can Use
return pool
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
def get_image_info(image):
info = {
'id': image.ID,
'name': image.NAME,
'state': IMAGE_STATES[image.STATE],
'running_vms': image.RUNNING_VMS,
'used': bool(image.RUNNING_VMS),
'user_name': image.UNAME,
'user_id': image.UID,
'group_name': image.GNAME,
'group_id': image.GID,
}
return info
class ImageInfoModule(OpenNebulaModule):
def __init__(self):
argument_spec = dict(
ids=dict(type='list', aliases=['id'], elements='str', required=False),
name=dict(type='str', required=False),
)
mutually_exclusive = [
['ids', 'name'],
]
OpenNebulaModule.__init__(self,
argument_spec,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive)
def get_images_by_ids(module, client, ids):
images = []
pool = get_all_images(client)
def run(self, one, module, result):
params = module.params
ids = params.get('ids')
name = params.get('name')
for image in pool.IMAGE:
if str(image.ID) in ids:
images.append(image)
ids.remove(str(image.ID))
if len(ids) == 0:
if ids:
images = self.get_images_by_ids(ids)
elif name:
images = self.get_images_by_name(name)
else:
images = self.get_all_images().IMAGE
self.result = {
'images': [OpenNebulaModule.get_image_info(image) for image in images]
}
self.exit()
def get_all_images(self):
pool = self.one.imagepool.info(-2, -1, -1, -1)
# Filter -2 means fetch all images user can Use
return pool
def get_images_by_ids(self, ids):
images = []
pool = self.get_all_images()
for image in pool.IMAGE:
if str(image.ID) in ids:
images.append(image)
ids.remove(str(image.ID))
if len(ids) == 0:
break
if len(ids) > 0:
self.module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
return images
def get_images_by_name(self, name_pattern):
images = []
pattern = None
pool = self.get_all_images()
if name_pattern.startswith('~'):
import re
if name_pattern[1] == '*':
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
else:
pattern = re.compile(name_pattern[1:])
for image in pool.IMAGE:
if pattern is not None:
if pattern.match(image.NAME):
images.append(image)
elif name_pattern == image.NAME:
images.append(image)
break
if len(ids) > 0:
module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
# if the specific name is indicated
if pattern is None and len(images) == 0:
self.module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
return images
def get_images_by_name(module, client, name_pattern):
images = []
pattern = None
pool = get_all_images(client)
if name_pattern.startswith('~'):
import re
if name_pattern[1] == '*':
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
else:
pattern = re.compile(name_pattern[1:])
for image in pool.IMAGE:
if pattern is not None:
if pattern.match(image.NAME):
images.append(image)
elif name_pattern == image.NAME:
images.append(image)
break
# if the specific name is indicated
if pattern is None and len(images) == 0:
module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
return images
def get_connection_info(module):
url = module.params.get('api_url')
username = module.params.get('api_username')
password = module.params.get('api_password')
if not url:
url = os.environ.get('ONE_URL')
if not username:
username = os.environ.get('ONE_USERNAME')
if not password:
password = os.environ.get('ONE_PASSWORD')
if not (url and username and password):
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
from collections import namedtuple
auth_params = namedtuple('auth', ('url', 'username', 'password'))
return auth_params(url=url, username=username, password=password)
return images
def main():
fields = {
"api_url": {"required": False, "type": "str"},
"api_username": {"required": False, "type": "str"},
"api_password": {"required": False, "type": "str", "no_log": True},
"ids": {"required": False, "aliases": ['id'], "type": "list", "elements": "str"},
"name": {"required": False, "type": "str"},
}
module = AnsibleModule(argument_spec=fields,
mutually_exclusive=[['ids', 'name']],
supports_check_mode=True)
if not HAS_PYONE:
module.fail_json(msg='This module requires pyone to work!')
auth = get_connection_info(module)
params = module.params
ids = params.get('ids')
name = params.get('name')
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
if ids:
images = get_images_by_ids(module, client, ids)
elif name:
images = get_images_by_name(module, client, name)
else:
images = get_all_images(client).IMAGE
result = {
'images': [get_image_info(image) for image in images],
}
module.exit_json(**result)
ImageInfoModule().run_module()
if __name__ == '__main__':

View File

@@ -522,7 +522,7 @@ def create_service_and_operation(module, auth, template_id, service_name, owner_
if unique:
service = get_service_by_name(module, auth, service_name)
if not service:
if not service or service["TEMPLATE"]["BODY"]["state"] == "DONE":
if not module.check_mode:
service = create_service(module, auth, template_id, service_name, custom_attrs, unique, wait, wait_timeout)
changed = True
@@ -637,7 +637,6 @@ def get_service_id_by_name(module, auth, service_name):
def get_connection_info(module):
url = module.params.get('api_url')
username = module.params.get('api_username')
password = module.params.get('api_password')

View File

@@ -45,6 +45,7 @@ options:
login:
description:
- Whether the target node should be connected.
- When O(target) is omitted, will login to all available.
type: bool
aliases: [ state ]
node_auth:
@@ -101,7 +102,6 @@ options:
type: bool
default: false
version_added: 4.1.0
'''
EXAMPLES = r'''
@@ -117,8 +117,7 @@ EXAMPLES = r'''
discover: true
ip: 10.1.2.3
# NOTE: Only works if exactly one target is exported to the initiator
- name: Discover targets on portal and login to the one available
- name: Discover targets on portal and login to the ones available
community.general.open_iscsi:
portal: '{{ iscsi_target }}'
login: true
@@ -227,7 +226,7 @@ def target_loggedon(module, target, portal=None, port=None):
module.fail_json(cmd=cmd, rc=rc, msg=err)
def target_login(module, target, portal=None, port=None):
def target_login(module, target, check_rc, portal=None, port=None):
node_auth = module.params['node_auth']
node_user = module.params['node_user']
node_pass = module.params['node_pass']
@@ -240,21 +239,22 @@ def target_login(module, target, portal=None, port=None):
('node.session.auth.password', node_pass)]
for (name, value) in params:
cmd = [iscsiadm_cmd, '--mode', 'node', '--targetname', target, '--op=update', '--name', name, '--value', value]
module.run_command(cmd, check_rc=True)
module.run_command(cmd, check_rc=check_rc)
if node_user_in:
params = [('node.session.auth.username_in', node_user_in),
('node.session.auth.password_in', node_pass_in)]
for (name, value) in params:
cmd = '%s --mode node --targetname %s --op=update --name %s --value %s' % (iscsiadm_cmd, target, name, value)
module.run_command(cmd, check_rc=True)
module.run_command(cmd, check_rc=check_rc)
cmd = [iscsiadm_cmd, '--mode', 'node', '--targetname', target, '--login']
if portal is not None and port is not None:
cmd.append('--portal')
cmd.append('%s:%s' % (portal, port))
module.run_command(cmd, check_rc=True)
rc, out, err = module.run_command(cmd, check_rc=check_rc)
return rc
def target_logout(module, target):
@@ -339,7 +339,10 @@ def main():
),
required_together=[['node_user', 'node_pass'], ['node_user_in', 'node_pass_in']],
required_if=[('discover', True, ['portal'])],
required_if=[
('discover', True, ['portal']),
('auto_node_startup', True, ['target']),
('auto_portal_startup', True, ['target'])],
supports_check_mode=True,
)
@@ -369,6 +372,8 @@ def main():
# return json dict
result = {'changed': False}
login_to_all_nodes = False
check_rc = True
if discover:
if check:
@@ -385,9 +390,10 @@ def main():
if login is not None or automatic is not None:
if target is None:
if len(nodes) > 1:
module.fail_json(msg="Need to specify a target")
else:
target = nodes[0]
# Disable strict return code checking if there are multiple targets
# That will allow to skip target where we have no rights to login
login_to_all_nodes = True
check_rc = False
else:
# check given target is in cache
check_target = False
@@ -402,26 +408,54 @@ def main():
result['nodes'] = nodes
if login is not None:
loggedon = target_loggedon(module, target, portal, port)
if (login and loggedon) or (not login and not loggedon):
result['changed'] |= False
if login:
result['devicenodes'] = target_device_node(target)
elif not check:
if login:
target_login(module, target, portal, port)
# give udev some time
time.sleep(1)
result['devicenodes'] = target_device_node(target)
else:
target_logout(module, target)
result['changed'] |= True
result['connection_changed'] = True
if login_to_all_nodes:
result['devicenodes'] = []
for index_target in nodes:
loggedon = target_loggedon(module, index_target, portal, port)
if (login and loggedon) or (not login and not loggedon):
result['changed'] |= False
if login:
result['devicenodes'] += target_device_node(index_target)
elif not check:
if login:
login_result = target_login(module, index_target, check_rc, portal, port)
# give udev some time
time.sleep(1)
result['devicenodes'] += target_device_node(index_target)
else:
target_logout(module, index_target)
# Check if there are multiple targets on a single portal and
# do not mark the task changed if host could not login to one of them
if len(nodes) > 1 and login_result == 24:
result['changed'] |= False
result['connection_changed'] = False
else:
result['changed'] |= True
result['connection_changed'] = True
else:
result['changed'] |= True
result['connection_changed'] = True
else:
result['changed'] |= True
result['connection_changed'] = True
loggedon = target_loggedon(module, target, portal, port)
if (login and loggedon) or (not login and not loggedon):
result['changed'] |= False
if login:
result['devicenodes'] = target_device_node(target)
elif not check:
if login:
target_login(module, target, portal, port)
# give udev some time
time.sleep(1)
result['devicenodes'] = target_device_node(target)
else:
target_logout(module, target)
result['changed'] |= True
result['connection_changed'] = True
else:
result['changed'] |= True
result['connection_changed'] = True
if automatic is not None:
if automatic is not None and not login_to_all_nodes:
isauto = target_isauto(module, target)
if (automatic and isauto) or (not automatic and not isauto):
result['changed'] |= False
@@ -437,7 +471,7 @@ def main():
result['changed'] |= True
result['automatic_changed'] = True
if automatic_portal is not None:
if automatic_portal is not None and not login_to_all_nodes:
isauto = target_isauto(module, target, portal, port)
if (automatic_portal and isauto) or (not automatic_portal and not isauto):
result['changed'] |= False

View File

@@ -339,7 +339,7 @@ def main():
pass
# Move tempfile to newfile
module.atomic_move(nf.name, limits_conf)
module.atomic_move(os.path.abspath(nf.name), os.path.abspath(limits_conf))
try:
nf.close()

View File

@@ -9,169 +9,150 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: pipx
short_description: Manages applications installed with pipx
version_added: 3.8.0
description:
- Manage Python applications installed in isolated virtualenvs using pipx.
- Manage Python applications installed in isolated virtualenvs using pipx.
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
- community.general.pipx
attributes:
check_mode:
support: full
diff_mode:
support: full
check_mode:
support: full
diff_mode:
support: full
options:
state:
type: str
choices:
- present
- absent
- install
- install_all
- uninstall
- uninstall_all
- inject
- uninject
- upgrade
- upgrade_shared
- upgrade_all
- reinstall
- reinstall_all
- latest
- pin
- unpin
default: install
description:
- Desired state for the application.
- The states V(present) and V(absent) are aliases to V(install) and V(uninstall), respectively.
- The state V(latest) is equivalent to executing the task twice, with state V(install) and then V(upgrade).
It was added in community.general 5.5.0.
- The states V(install_all), V(uninject), V(upgrade_shared), V(pin) and V(unpin) are only available in C(pipx>=1.6.0),
make sure to have a compatible version when using this option. These states have been added in community.general 9.4.0.
name:
type: str
description:
- >
The name of the application to be installed. It must to be a simple package name.
For passing package specifications or installing from URLs or directories,
please use the O(source) option.
source:
type: str
description:
- >
If the application source, such as a package with version specifier, or an URL,
directory or any other accepted specification. See C(pipx) documentation for more details.
- When specified, the C(pipx) command will use O(source) instead of O(name).
install_apps:
description:
- Add apps from the injected packages.
- Only used when O(state=inject).
type: bool
default: false
version_added: 6.5.0
install_deps:
description:
- Include applications of dependent packages.
- Only used when O(state=install), O(state=latest), or O(state=inject).
type: bool
default: false
inject_packages:
description:
- Packages to be injected into an existing virtual environment.
- Only used when O(state=inject).
type: list
elements: str
force:
description:
- Force modification of the application's virtual environment. See C(pipx) for details.
- Only used when O(state=install), O(state=upgrade), O(state=upgrade_all), O(state=latest), or O(state=inject).
type: bool
default: false
include_injected:
description:
- Upgrade the injected packages along with the application.
- Only used when O(state=upgrade), O(state=upgrade_all), or O(state=latest).
- This is used with O(state=upgrade) and O(state=latest) since community.general 6.6.0.
type: bool
default: false
index_url:
description:
- Base URL of Python Package Index.
- Only used when O(state=install), O(state=upgrade), O(state=latest), or O(state=inject).
type: str
python:
description:
- Python version to be used when creating the application virtual environment. Must be 3.6+.
- Only used when O(state=install), O(state=latest), O(state=reinstall), or O(state=reinstall_all).
type: str
system_site_packages:
description:
- Give application virtual environment access to the system site-packages directory.
- Only used when O(state=install) or O(state=latest).
type: bool
default: false
version_added: 6.6.0
executable:
description:
- Path to the C(pipx) installed in the system.
- >
If not specified, the module will use C(python -m pipx) to run the tool,
using the same Python interpreter as ansible itself.
type: path
editable:
description:
- Install the project in editable mode.
type: bool
default: false
version_added: 4.6.0
pip_args:
description:
- Arbitrary arguments to pass directly to C(pip).
type: str
version_added: 4.6.0
suffix:
description:
- Optional suffix for virtual environment and executable names.
- "B(Warning:) C(pipx) documentation states this is an B(experimental) feature subject to change."
type: str
version_added: 9.3.0
global:
description:
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
type: bool
default: false
version_added: 9.4.0
spec_metadata:
description:
- Spec metadata file for O(state=install_all).
- This content of the file is usually generated with C(pipx list --json), and it can be obtained with M(community.general.pipx_info)
with O(community.general.pipx_info#module:include_raw=true) and obtaining the content from the RV(community.general.pipx_info#module:raw_output).
type: path
version_added: 9.4.0
state:
type: str
choices:
- present
- absent
- install
- install_all
- uninstall
- uninstall_all
- inject
- uninject
- upgrade
- upgrade_shared
- upgrade_all
- reinstall
- reinstall_all
- latest
- pin
- unpin
default: install
description:
- Desired state for the application.
- The states V(present) and V(absent) are aliases to V(install) and V(uninstall), respectively.
- The state V(latest) is equivalent to executing the task twice, with state V(install) and then V(upgrade). It was added in community.general
5.5.0.
- The states V(install_all), V(uninject), V(upgrade_shared), V(pin) and V(unpin) are only available in C(pipx>=1.6.0), make sure to have a
compatible version when using this option. These states have been added in community.general 9.4.0.
name:
type: str
description:
- The name of the application. In C(pipx) documentation it is also referred to as the name of the virtual environment where the application
will be installed.
- If O(name) is a simple package name without version specifiers, then that name is used as the Python package name to be installed.
- Use O(source) for passing package specifications or installing from URLs or directories.
source:
type: str
description:
- Source for the package. This option is used when O(state=install) or O(state=latest), and it is ignored with other states.
- Use O(source) when installing a Python package with version specifier, or from a local path, from a VCS URL or compressed file.
- The value of this option is passed as-is to C(pipx).
- O(name) is still required when using O(source) to establish the application name without fetching the package from a remote source.
install_apps:
description:
- Add apps from the injected packages.
- Only used when O(state=inject).
type: bool
default: false
version_added: 6.5.0
install_deps:
description:
- Include applications of dependent packages.
- Only used when O(state=install), O(state=latest), or O(state=inject).
type: bool
default: false
inject_packages:
description:
- Packages to be injected into an existing virtual environment.
- Only used when O(state=inject).
type: list
elements: str
force:
description:
- Force modification of the application's virtual environment. See C(pipx) for details.
- Only used when O(state=install), O(state=upgrade), O(state=upgrade_all), O(state=latest), or O(state=inject).
type: bool
default: false
include_injected:
description:
- Upgrade the injected packages along with the application.
- Only used when O(state=upgrade), O(state=upgrade_all), or O(state=latest).
- This is used with O(state=upgrade) and O(state=latest) since community.general 6.6.0.
type: bool
default: false
index_url:
description:
- Base URL of Python Package Index.
- Only used when O(state=install), O(state=upgrade), O(state=latest), or O(state=inject).
type: str
python:
description:
- Python version to be used when creating the application virtual environment. Must be 3.6+.
- Only used when O(state=install), O(state=latest), O(state=reinstall), or O(state=reinstall_all).
type: str
system_site_packages:
description:
- Give application virtual environment access to the system site-packages directory.
- Only used when O(state=install) or O(state=latest).
type: bool
default: false
version_added: 6.6.0
editable:
description:
- Install the project in editable mode.
type: bool
default: false
version_added: 4.6.0
pip_args:
description:
- Arbitrary arguments to pass directly to C(pip).
type: str
version_added: 4.6.0
suffix:
description:
- Optional suffix for virtual environment and executable names.
- "B(Warning:) C(pipx) documentation states this is an B(experimental) feature subject to change."
type: str
version_added: 9.3.0
global:
version_added: 9.4.0
spec_metadata:
description:
- Spec metadata file for O(state=install_all).
- This content of the file is usually generated with C(pipx list --json), and it can be obtained with M(community.general.pipx_info) with
O(community.general.pipx_info#module:include_raw=true) and obtaining the content from the RV(community.general.pipx_info#module:raw_output).
type: path
version_added: 9.4.0
notes:
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
- Please note that C(pipx) requires Python 3.6 or above.
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
- >
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 first implementation does not verify whether a specified version constraint has been installed or not.
Hence, when using version operators, C(pipx) module will always try to execute the operation,
even when the application was previously installed.
This feature will be added in the future.
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
- >
This first implementation does not verify whether a specified version constraint has been installed or not.
Hence, when using version operators, C(pipx) module will always try to execute the operation,
even when the application was previously installed.
This feature will be added in the future.
author:
- "Alexei Znamensky (@russoz)"
'''
- "Alexei Znamensky (@russoz)"
"""
EXAMPLES = '''
EXAMPLES = """
---
- name: Install tox
community.general.pipx:
name: tox
@@ -200,20 +181,20 @@ EXAMPLES = '''
- name: Install multiple packages from list
vars:
pipx_packages:
- pycowsay
- black
- tox
- pycowsay
- black
- tox
community.general.pipx:
name: "{{ item }}"
state: latest
with_items: "{{ pipx_packages }}"
'''
"""
import json
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner, pipx_common_argspec
from ansible.module_utils.facts.compat import ansible_facts
@@ -240,13 +221,12 @@ class PipX(StateModuleHelper):
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'),
suffix=dict(type='str'),
spec_metadata=dict(type='path'),
)
argument_spec["global"] = dict(type='bool', default=False)
argument_spec.update(pipx_common_argspec)
module = dict(
argument_spec=argument_spec,
@@ -328,7 +308,7 @@ class PipX(StateModuleHelper):
def state_install_all(self):
self.changed = True
with self.runner('state global index_url force python system_site_packages editable pip_args spec_metadata', check_mode_skip=True) as ctx:
ctx.run(name_source=[self.vars.name, self.vars.source])
ctx.run()
self._capture_results(ctx)
def state_upgrade(self):

View File

@@ -9,66 +9,46 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: pipx_info
short_description: Rretrieves information about applications installed with pipx
version_added: 5.6.0
description:
- Retrieve details about Python applications installed in isolated virtualenvs using pipx.
- Retrieve details about Python applications installed in isolated virtualenvs using pipx.
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
- community.general.attributes
- community.general.attributes.info_module
- community.general.pipx
options:
name:
description:
- Name of an application installed with C(pipx).
type: str
include_deps:
description:
- Include dependent packages in the output.
type: bool
default: false
include_injected:
description:
- Include injected packages in the output.
type: bool
default: false
include_raw:
description:
- Returns the raw output of C(pipx list --json).
- The raw output is not affected by O(include_deps) or O(include_injected).
type: bool
default: false
executable:
description:
- Path to the C(pipx) installed in the system.
- >
If not specified, the module will use C(python -m pipx) to run the tool,
using the same Python interpreter as ansible itself.
type: path
global:
description:
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
type: bool
default: false
version_added: 9.3.0
notes:
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
- Please note that C(pipx) requires Python 3.6 or above.
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
- >
This module will honor C(pipx) environment variables such as but not limited to E(PIPX_HOME) and E(PIPX_BIN_DIR)
passed using the R(environment Ansible keyword, playbooks_environment).
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
name:
description:
- Name of an application installed with C(pipx).
type: str
include_deps:
description:
- Include dependent packages in the output.
type: bool
default: false
include_injected:
description:
- Include injected packages in the output.
type: bool
default: false
include_raw:
description:
- Returns the raw output of C(pipx list --json).
- The raw output is not affected by O(include_deps) or O(include_injected).
type: bool
default: false
global:
version_added: 9.3.0
author:
- "Alexei Znamensky (@russoz)"
'''
- "Alexei Znamensky (@russoz)"
"""
EXAMPLES = '''
EXAMPLES = """
---
- name: retrieve all installed applications
community.general.pipx_info: {}
@@ -86,9 +66,10 @@ EXAMPLES = '''
community.general.pipx_info:
name: ansible-lint
include_deps: true
'''
"""
RETURN = '''
RETURN = """
---
application:
description: The list of installed applications
returned: success
@@ -128,20 +109,13 @@ cmd:
returned: success
type: list
elements: str
sample: [
"/usr/bin/python3.10",
"-m",
"pipx",
"list",
"--include-injected",
"--json"
]
'''
sample: ["/usr/bin/python3.10", "-m", "pipx", "list", "--include-injected", "--json"]
"""
import json
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner, pipx_common_argspec
from ansible.module_utils.facts.compat import ansible_facts
@@ -153,9 +127,8 @@ class PipXInfo(ModuleHelper):
include_deps=dict(type='bool', default=False),
include_injected=dict(type='bool', default=False),
include_raw=dict(type='bool', default=False),
executable=dict(type='path'),
)
argument_spec["global"] = dict(type='bool', default=False)
argument_spec.update(pipx_common_argspec)
module = dict(
argument_spec=argument_spec,
supports_check_mode=True,

View File

@@ -911,6 +911,7 @@ def main():
'account_oemaccounttypes': module.params['oem_account_types'],
'account_updatename': module.params['update_username'],
'account_properties': module.params['account_properties'],
'account_passwordchangerequired': None,
}
# timeout
@@ -983,10 +984,16 @@ def main():
# execute only if we find an Account service resource
result = rf_utils._find_accountservice_resource()
if result['ret'] is False:
module.fail_json(msg=to_native(result['msg']))
for command in command_list:
result = ACCOUNTS_COMMANDS[command](user)
# If a password change is required and the user is attempting to
# modify their password, try to proceed.
user['account_passwordchangerequired'] = rf_utils.check_password_change_required(result)
if len(command_list) == 1 and command_list[0] == "UpdateUserPassword" and user['account_passwordchangerequired']:
result = rf_utils.update_user_password(user)
else:
module.fail_json(msg=to_native(result['msg']))
else:
for command in command_list:
result = ACCOUNTS_COMMANDS[command](user)
elif category == "Systems":
# execute only if we find a System resource

View File

@@ -145,6 +145,13 @@ options:
type: str
default: ''
version_added: '7.3.0'
storage_none_volume_deletion:
required: false
description:
- Indicates if all non-RAID volumes are automatically deleted prior to creating the new volume.
type: bool
default: false
version_added: '9.5.0'
volume_ids:
required: false
description:
@@ -164,6 +171,9 @@ options:
required: false
description:
- Setting dict of volume to be created.
- If C(CapacityBytes) key is not specified in this dictionary, the size of
the volume will be determined by the Redfish service. It is possible the
size will not be the maximum available size.
type: dict
default: {}
version_added: '7.5.0'
@@ -415,6 +425,7 @@ def main():
hostinterface_id=dict(),
sessions_config=dict(type='dict', default={}),
storage_subsystem_id=dict(type='str', default=''),
storage_none_volume_deletion=dict(type='bool', default=False),
volume_ids=dict(type='list', default=[], elements='str'),
secure_boot_enable=dict(type='bool', default=True),
volume_details=dict(type='dict', default={}),
@@ -481,6 +492,7 @@ def main():
# Volume creation options
volume_details = module.params['volume_details']
storage_subsystem_id = module.params['storage_subsystem_id']
storage_none_volume_deletion = module.params['storage_none_volume_deletion']
# ciphers
ciphers = module.params['ciphers']
@@ -524,7 +536,7 @@ def main():
elif command == "DeleteVolumes":
result = rf_utils.delete_volumes(storage_subsystem_id, volume_ids)
elif command == "CreateVolume":
result = rf_utils.create_volume(volume_details, storage_subsystem_id)
result = rf_utils.create_volume(volume_details, storage_subsystem_id, storage_none_volume_deletion)
elif category == "Manager":
# execute only if we find a Manager service resource

View File

@@ -260,8 +260,7 @@ def absent_strategy(api, wished_cn):
changed = False
cn_list = api.fetch_all_resources("containers")
cn_lookup = dict((cn["name"], cn)
for cn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
if wished_cn["name"] not in cn_lookup:
return changed, {}
@@ -285,8 +284,7 @@ def present_strategy(api, wished_cn):
changed = False
cn_list = api.fetch_all_resources("containers")
cn_lookup = dict((cn["name"], cn)
for cn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
payload_cn = payload_from_wished_cn(wished_cn)

View File

@@ -97,8 +97,7 @@ from ansible.module_utils.basic import AnsibleModule
def info_strategy(api, wished_cn):
cn_list = api.fetch_all_resources("containers")
cn_lookup = dict((fn["name"], fn)
for fn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
if wished_cn["name"] not in cn_lookup:
msg = "Error during container lookup: Unable to find container named '%s' in namespace '%s'" % (wished_cn["name"],

View File

@@ -167,8 +167,7 @@ def absent_strategy(api, wished_cn):
changed = False
cn_list = api.fetch_all_resources("namespaces")
cn_lookup = dict((cn["name"], cn)
for cn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
if wished_cn["name"] not in cn_lookup:
return changed, {}
@@ -192,8 +191,7 @@ def present_strategy(api, wished_cn):
changed = False
cn_list = api.fetch_all_resources("namespaces")
cn_lookup = dict((cn["name"], cn)
for cn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
payload_cn = payload_from_wished_cn(wished_cn)

View File

@@ -88,8 +88,7 @@ from ansible.module_utils.basic import AnsibleModule
def info_strategy(api, wished_cn):
cn_list = api.fetch_all_resources("namespaces")
cn_lookup = dict((fn["name"], fn)
for fn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
if wished_cn["name"] not in cn_lookup:
msg = "Error during container namespace lookup: Unable to find container namespace named '%s' in project '%s'" % (wished_cn["name"],

View File

@@ -150,8 +150,7 @@ def absent_strategy(api, wished_cr):
changed = False
cr_list = api.fetch_all_resources("namespaces")
cr_lookup = dict((cr["name"], cr)
for cr in cr_list)
cr_lookup = {cr["name"]: cr for cr in cr_list}
if wished_cr["name"] not in cr_lookup:
return changed, {}
@@ -175,8 +174,7 @@ def present_strategy(api, wished_cr):
changed = False
cr_list = api.fetch_all_resources("namespaces")
cr_lookup = dict((cr["name"], cr)
for cr in cr_list)
cr_lookup = {cr["name"]: cr for cr in cr_list}
payload_cr = payload_from_wished_cr(wished_cr)

View File

@@ -87,8 +87,7 @@ from ansible.module_utils.basic import AnsibleModule
def info_strategy(api, wished_cn):
cn_list = api.fetch_all_resources("namespaces")
cn_lookup = dict((fn["name"], fn)
for fn in cn_list)
cn_lookup = {cn["name"]: cn for cn in cn_list}
if wished_cn["name"] not in cn_lookup:
msg = "Error during container registries lookup: Unable to find container registry named '%s' in project '%s'" % (wished_cn["name"],

View File

@@ -245,8 +245,7 @@ def absent_strategy(api, wished_fn):
changed = False
fn_list = api.fetch_all_resources("functions")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
if wished_fn["name"] not in fn_lookup:
return changed, {}
@@ -270,8 +269,7 @@ def present_strategy(api, wished_fn):
changed = False
fn_list = api.fetch_all_resources("functions")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
payload_fn = payload_from_wished_fn(wished_fn)

View File

@@ -96,8 +96,7 @@ from ansible.module_utils.basic import AnsibleModule
def info_strategy(api, wished_fn):
fn_list = api.fetch_all_resources("functions")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
if wished_fn["name"] not in fn_lookup:
msg = "Error during function lookup: Unable to find function named '%s' in namespace '%s'" % (wished_fn["name"],

View File

@@ -168,8 +168,7 @@ def absent_strategy(api, wished_fn):
changed = False
fn_list = api.fetch_all_resources("namespaces")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
if wished_fn["name"] not in fn_lookup:
return changed, {}
@@ -193,8 +192,7 @@ def present_strategy(api, wished_fn):
changed = False
fn_list = api.fetch_all_resources("namespaces")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
payload_fn = payload_from_wished_fn(wished_fn)

View File

@@ -88,8 +88,7 @@ from ansible.module_utils.basic import AnsibleModule
def info_strategy(api, wished_fn):
fn_list = api.fetch_all_resources("namespaces")
fn_lookup = dict((fn["name"], fn)
for fn in fn_list)
fn_lookup = {fn["name"]: fn for fn in fn_list}
if wished_fn["name"] not in fn_lookup:
msg = "Error during function namespace lookup: Unable to find function namespace named '%s' in project '%s'" % (wished_fn["name"],

View File

@@ -149,7 +149,7 @@ def core(module):
# Then we patch keys that are different
for key, value in user_data.items():
if key not in present_user_data or user_data[key] != present_user_data[key]:
if key not in present_user_data or value != present_user_data[key]:
changed = True
if compute_api.module.check_mode:

View File

@@ -196,8 +196,7 @@ def main():
else:
obj['name'] = name
for k, v in data.items():
obj[k] = v
obj.update(data)
diff = obj.diff()
changed = obj.diff() != []
if not module.check_mode:

View File

@@ -21,11 +21,11 @@ description:
server (UCS).
It uses the python API of the UCS to create a new object or edit it."
notes:
- This module does B(not) work with Python 3.13 or newer. It uses the deprecated L(crypt Python module,
https://docs.python.org/3.12/library/crypt.html) from the Python standard library, which was removed
from Python 3.13.
- This module requires the deprecated L(crypt Python module,
https://docs.python.org/3.12/library/crypt.html) library which was removed from Python 3.13.
For Python 3.13 or newer, you need to install L(legacycrypt, https://pypi.org/project/legacycrypt/).
requirements:
- Python 3.12 or earlier
- legacycrypt (on Python 3.13 or newer)
extends_documentation_fragment:
- community.general.attributes
attributes:
@@ -350,6 +350,17 @@ else:
HAS_CRYPT = True
CRYPT_IMPORT_ERROR = None
try:
import legacycrypt
if not HAS_CRYPT:
crypt = legacycrypt
except ImportError:
HAS_LEGACYCRYPT = False
LEGACYCRYPT_IMPORT_ERROR = traceback.format_exc()
else:
HAS_LEGACYCRYPT = True
LEGACYCRYPT_IMPORT_ERROR = None
def main():
expiry = date.strftime(date.today() + timedelta(days=365), "%Y-%m-%d")
@@ -467,10 +478,10 @@ def main():
])
)
if not HAS_CRYPT:
if not HAS_CRYPT and not HAS_LEGACYCRYPT:
module.fail_json(
msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
exception=CRYPT_IMPORT_ERROR,
msg=missing_required_lib('crypt (part of standard library up to Python 3.12) or legacycrypt (PyPI)'),
exception=LEGACYCRYPT_IMPORT_ERROR,
)
username = module.params['username']

View File

@@ -8,26 +8,26 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: xfconf
author:
- "Joseph Benden (@jbenden)"
- "Alexei Znamensky (@russoz)"
- "Joseph Benden (@jbenden)"
- "Alexei Znamensky (@russoz)"
short_description: Edit XFCE4 Configurations
description:
- This module allows for the manipulation of Xfce 4 Configuration with the help of
xfconf-query. Please see the xfconf-query(1) man page for more details.
- This module allows for the manipulation of Xfce 4 Configuration with the help of C(xfconf-query).
seealso:
- name: xfconf-query(1) man page
description: Manual page of the C(xfconf-query) tool at the XFCE documentation site.
link: 'https://docs.xfce.org/xfce/xfconf/xfconf-query'
- name: xfconf-query(1) man page
description: Manual page of the C(xfconf-query) tool at the XFCE documentation site.
link: 'https://docs.xfce.org/xfce/xfconf/xfconf-query'
- name: xfconf - Configuration Storage System
description: XFCE documentation for the Xfconf configuration system.
link: 'https://docs.xfce.org/xfce/xfconf/start'
- name: xfconf - Configuration Storage System
description: XFCE documentation for the Xfconf configuration system.
link: 'https://docs.xfce.org/xfce/xfconf/start'
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes
attributes:
check_mode:
@@ -38,55 +38,50 @@ attributes:
options:
channel:
description:
- A Xfconf preference channel is a top-level tree key, inside of the
Xfconf repository that corresponds to the location for which all
application properties/keys are stored. See man xfconf-query(1).
- A Xfconf preference channel is a top-level tree key, inside of the Xfconf repository that corresponds to the location for which all application
properties/keys are stored. See man xfconf-query(1).
required: true
type: str
property:
description:
- A Xfce preference key is an element in the Xfconf repository
that corresponds to an application preference. See man xfconf-query(1).
- A Xfce preference key is an element in the Xfconf repository that corresponds to an application preference. See man xfconf-query(1).
required: true
type: str
value:
description:
- Preference properties typically have simple values such as strings,
integers, or lists of strings and integers. See man xfconf-query(1).
- Preference properties typically have simple values such as strings, integers, or lists of strings and integers. See man xfconf-query(1).
type: list
elements: raw
value_type:
description:
- The type of value being set.
- When providing more than one O(value_type), the length of the list must
be equal to the length of O(value).
- If only one O(value_type) is provided, but O(value) contains more than
on element, that O(value_type) will be applied to all elements of O(value).
- If the O(property) being set is an array and it can possibly have only one
element in the array, then O(force_array=true) must be used to ensure
that C(xfconf-query) will interpret the value as an array rather than a
scalar.
- Support for V(uchar), V(char), V(uint64), and V(int64) has been added in community.general 4.8.0.
- The type of value being set.
- When providing more than one O(value_type), the length of the list must be equal to the length of O(value).
- If only one O(value_type) is provided, but O(value) contains more than on element, that O(value_type) will be applied to all elements of
O(value).
- If the O(property) being set is an array and it can possibly have only one element in the array, then O(force_array=true) must be used to
ensure that C(xfconf-query) will interpret the value as an array rather than a scalar.
- Support for V(uchar), V(char), V(uint64), and V(int64) has been added in community.general 4.8.0.
type: list
elements: str
choices: [ string, int, double, bool, uint, uchar, char, uint64, int64, float ]
choices: [string, int, double, bool, uint, uchar, char, uint64, int64, float]
state:
type: str
description:
- The action to take upon the property/value.
- The state V(get) has been removed in community.general 5.0.0. Please use the module M(community.general.xfconf_info) instead.
choices: [ present, absent ]
- The action to take upon the property/value.
- The state V(get) has been removed in community.general 5.0.0. Please use the module M(community.general.xfconf_info) instead.
choices: [present, absent]
default: "present"
force_array:
description:
- Force array even if only one element.
- Force array even if only one element.
type: bool
default: false
aliases: ['array']
version_added: 1.0.0
'''
"""
EXAMPLES = """
---
- name: Change the DPI to "192"
xfconf:
channel: "xsettings"
@@ -110,60 +105,58 @@ EXAMPLES = """
force_array: true
"""
RETURN = '''
channel:
description: The channel specified in the module parameters
returned: success
type: str
sample: "xsettings"
property:
description: The property specified in the module parameters
returned: success
type: str
sample: "/Xft/DPI"
value_type:
description:
- The type of the value that was changed (V(none) for O(state=reset)).
Either a single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"int" or ["str", "str", "str"]'
value:
description:
- The value of the preference key after executing the module. Either a
single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"192" or ["orange", "yellow", "violet"]'
previous_value:
description:
- The value of the preference key before executing the module.
Either a single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"96" or ["red", "blue", "green"]'
cmd:
description:
- A list with the resulting C(xfconf-query) command executed by the module.
returned: success
type: list
elements: str
version_added: 5.4.0
sample:
- /usr/bin/xfconf-query
- --channel
- xfce4-panel
- --property
- /plugins/plugin-19/timezone
- --create
- --type
- string
- --set
- Pacific/Auckland
'''
RETURN = """
---
channel:
description: The channel specified in the module parameters
returned: success
type: str
sample: "xsettings"
property:
description: The property specified in the module parameters
returned: success
type: str
sample: "/Xft/DPI"
value_type:
description:
- The type of the value that was changed (V(none) for O(state=reset)). Either a single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"int" or ["str", "str", "str"]'
value:
description:
- The value of the preference key after executing the module. Either a single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"192" or ["orange", "yellow", "violet"]'
previous_value:
description:
- The value of the preference key before executing the module. Either a single string value or a list of strings for array types.
- This is a string or a list of strings.
returned: success
type: any
sample: '"96" or ["red", "blue", "green"]'
cmd:
description:
- A list with the resulting C(xfconf-query) command executed by the module.
returned: success
type: list
elements: str
version_added: 5.4.0
sample:
- /usr/bin/xfconf-query
- --channel
- xfce4-panel
- --property
- /plugins/plugin-19/timezone
- --create
- --type
- string
- --set
- Pacific/Auckland
"""
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
from ansible_collections.community.general.plugins.module_utils.xfconf import xfconf_runner

View File

@@ -7,17 +7,18 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = """
---
module: xfconf_info
author:
- "Alexei Znamensky (@russoz)"
- "Alexei Znamensky (@russoz)"
short_description: Retrieve XFCE4 configurations
version_added: 3.5.0
description:
- This module allows retrieving Xfce 4 configurations with the help of C(xfconf-query).
- This module allows retrieving Xfce 4 configurations with the help of C(xfconf-query).
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
- community.general.attributes
- community.general.attributes.info_module
attributes:
check_mode:
version_added: 3.3.0
@@ -40,10 +41,11 @@ options:
- If not provided and a O(channel) is provided, then the module will list all available properties in that O(channel).
type: str
notes:
- See man xfconf-query(1) for more details.
'''
- See man xfconf-query(1) for more details.
"""
EXAMPLES = """
---
- name: Get list of all available channels
community.general.xfconf_info: {}
register: result
@@ -66,63 +68,64 @@ EXAMPLES = """
register: result
"""
RETURN = '''
channels:
description:
- List of available channels.
- Returned when the module receives no parameter at all.
returned: success
type: list
elements: str
sample:
- xfce4-desktop
- displays
- xsettings
- xfwm4
properties:
description:
- List of available properties for a specific channel.
- Returned by passing only the O(channel) parameter to the module.
returned: success
type: list
elements: str
sample:
- /Gdk/WindowScalingFactor
- /Gtk/ButtonImages
- /Gtk/CursorThemeSize
- /Gtk/DecorationLayout
- /Gtk/FontName
- /Gtk/MenuImages
- /Gtk/MonospaceFontName
- /Net/DoubleClickTime
- /Net/IconThemeName
- /Net/ThemeName
- /Xft/Antialias
- /Xft/Hinting
- /Xft/HintStyle
- /Xft/RGBA
is_array:
description:
- Flag indicating whether the property is an array or not.
returned: success
type: bool
value:
description:
- The value of the property. Empty if the property is of array type.
returned: success
type: str
sample: Monospace 10
value_array:
description:
- The array value of the property. Empty if the property is not of array type.
returned: success
type: list
elements: str
sample:
- Main
- Work
- Tmp
'''
RETURN = """
---
channels:
description:
- List of available channels.
- Returned when the module receives no parameter at all.
returned: success
type: list
elements: str
sample:
- xfce4-desktop
- displays
- xsettings
- xfwm4
properties:
description:
- List of available properties for a specific channel.
- Returned by passing only the O(channel) parameter to the module.
returned: success
type: list
elements: str
sample:
- /Gdk/WindowScalingFactor
- /Gtk/ButtonImages
- /Gtk/CursorThemeSize
- /Gtk/DecorationLayout
- /Gtk/FontName
- /Gtk/MenuImages
- /Gtk/MonospaceFontName
- /Net/DoubleClickTime
- /Net/IconThemeName
- /Net/ThemeName
- /Xft/Antialias
- /Xft/Hinting
- /Xft/HintStyle
- /Xft/RGBA
is_array:
description:
- Flag indicating whether the property is an array or not.
returned: success
type: bool
value:
description:
- The value of the property. Empty if the property is of array type.
returned: success
type: str
sample: Monospace 10
value_array:
description:
- The array value of the property. Empty if the property is not of array type.
returned: success
type: list
elements: str
sample:
- Main
- Work
- Tmp
"""
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
from ansible_collections.community.general.plugins.module_utils.xfconf import xfconf_runner

View File

@@ -0,0 +1,21 @@
# 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
azp/posix/2
skip/python2
skip/freebsd
skip/macos
skip/osx
skip/rhel8.2
skip/rhel8.3
skip/rhel8.4
skip/rhel8.5
skip/rhel8.6
skip/rhel8.7
skip/rhel8.8
skip/rhel9.0
skip/rhel9.1
skip/rhel9.2
skip/rhel9.3
skip/rhel9.4

View File

@@ -0,0 +1,6 @@
# 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
# single_app_project/core/settings.py
SECRET_KEY = 'testtesttesttesttest'

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python
# 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
# single_app_project/manage.py
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'single_app_project.core.settings')
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python
# 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
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'p1.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,133 @@
# -*- 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
"""
Django settings for p1 project.
Generated by 'django-admin startproj' using Django 3.1.5.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '%g@gyhl*q@@g(_ab@t^76dao^#b9-v8mw^50)x_bv6wpl+mukj'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'p1.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'p1.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/tmp/django-static'
if "DJANGO_ANSIBLE_RAISE" in os.environ:
raise ValueError("DJANGO_ANSIBLE_RAISE={0}".format(os.environ["DJANGO_ANSIBLE_RAISE"]))

View File

@@ -0,0 +1,28 @@
# 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
"""p1 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]

View File

@@ -0,0 +1,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
dependencies:
- setup_pkg_mgr
- setup_os_pkg_name

View File

@@ -0,0 +1,91 @@
# Test code for django_command module
#
# Copyright (c) 2020, Alexei Znamensky <russoz@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
- name: Create temporary test directory
tempfile:
state: directory
suffix: .django_command
register: tmp_django_root
- name: Install OS package virtualenv
package:
name: "{{ os_package_name.virtualenv }}"
state: present
- name: Ensure virtualenv is created
command: >-
virtualenv {{ tmp_django_root.path }}/venv
- name: Update python package pip
pip:
name: pip
state: latest
virtualenv: "{{ tmp_django_root.path }}/venv"
- name: Install python package django
pip:
name: django
state: present
virtualenv: "{{ tmp_django_root.path }}/venv"
- name: Copy files
copy:
src: base_test/
dest: "{{ tmp_django_root.path }}"
mode: preserve
- name: Create project
command:
chdir: "{{ tmp_django_root.path }}/startproj"
cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startproject test_django_command_1"
- name: Create app
command:
chdir: "{{ tmp_django_root.path }}/startproj"
cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startapp app1"
- name: Check
community.general.django_command:
pythonpath: "{{ tmp_django_root.path }}/startproj/test_django_command_1"
settings: test_django_command_1.settings
command: check
venv: "{{ tmp_django_root.path }}/venv"
- name: Check simple_project
community.general.django_command:
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
settings: p1.settings
command: check
venv: "{{ tmp_django_root.path }}/venv"
- name: Check custom project
community.general.django_command:
pythonpath: "{{ tmp_django_root.path }}/1045-single-app-project/single_app_project"
settings: core.settings
command: check
venv: "{{ tmp_django_root.path }}/venv"
- name: Run collectstatic --noinput on simple project
community.general.django_command:
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
settings: p1.settings
command: collectstatic --noinput
venv: "{{ tmp_django_root.path }}/venv"
- name: Trigger exception with environment variable
community.general.django_command:
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
settings: p1.settings
command: collectstatic --noinput
venv: "{{ tmp_django_root.path }}/venv"
environment:
DJANGO_ANSIBLE_RAISE: blah
ignore_errors: true
register: env_raise
- name: Check env variable reached manage.py
ansible.builtin.assert:
that:
- "'ValueError: DJANGO_ANSIBLE_RAISE=blah' in env_raise.msg"

View File

@@ -15,6 +15,11 @@
ignore_errors: true
- block:
- name: Install legacycrypt on Python 3.13+
pip:
name: legacycrypt
when: ansible_python_version is version("3.13", ">=")
- name: Check and start systemd-homed service
service:
name: systemd-homed.service

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
azp/generic/1
cloud/opennebula
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!

View File

@@ -0,0 +1,210 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# 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
# Checks for existence
- name: Make sure image is present by ID
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: present
register: result
- name: Assert that image is present
assert:
that:
- result is not changed
- name: Make sure image is present by ID
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
name: my_image
state: present
register: result
- name: Assert that image is present
assert:
that:
- result is not changed
# Updating an image
- name: Clone image without name
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: cloned
register: result
- name: Assert that image is cloned
assert:
that:
- result is changed
- name: Clone image with name
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: renamed
new_name: new_image
register: result
- name: Assert that image is cloned
assert:
that:
- result is changed
- name: Disable image
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
enabled: false
register: result
- name: Assert that network is disabled
assert:
that:
- result is changed
- name: Enable image
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
enabled: true
register: result
- name: Assert that network is enabled
assert:
that:
- result is changed
- name: Make image persistent
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
persistent: true
register: result
- name: Assert that network is persistent
assert:
that:
- result is changed
- name: Make image non-persistent
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
persistent: false
register: result
- name: Assert that network is non-persistent
assert:
that:
- result is changed
# Testing idempotence using the same tasks
- name: Make image non-persistent
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
persistent: false
enabled: true
register: result
- name: Assert that network not changed
assert:
that:
- result is not changed
# Delete images
- name: Deleting non-existing image
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 228
state: absent
register: result
- name: Assert that network not changed
assert:
that:
- result is not changed
- name: Delete an existing image
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: absent
register: result
- name: Assert that image was deleted
assert:
that:
- result is changed
# Trying to run with wrong arguments
- name: Try to use name and ID at the same time
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
name: name
register: result
ignore_errors: true
- name: Assert that task failed
assert:
that:
- result is failed
- name: Try to rename image without specifying new name
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: rename
register: result
ignore_errors: true
- name: Assert that task failed
assert:
that:
- result is failed
- name: Try to rename image without specifying new name
one_image:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: rename
register: result
ignore_errors: true

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
azp/generic/1
cloud/opennebula
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!

View File

@@ -0,0 +1,192 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# 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
# Checks for existence
- name: Get info by ID
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
register: result
- name: Assert that image is present
assert:
that:
- result is not changed
- name: Get info by list of ID
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
ids:
- 2
- 2
- 8
register: result
- name: Assert that image is present
assert:
that:
- result is not changed
- name: Get info by list of ID
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
name: somename
register: result
- name: Assert that image is present
assert:
that:
- result is not changed
- name: Gather all info
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
register: result
- name: Assert that images are present
assert:
that:
- result is not changed
- name: Gather info by regex
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
name: '~my_image-[0-9].*'
register: result
- name: Assert that images are present
assert:
that:
- result is not changed
- name: Gather info by regex and ignore upper/lower cases
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
name: '~*my_image-[0-9].*'
register: result
- name: Assert that images are present
assert:
that:
- result is not changed
# Updating an image
- name: Clone image without name
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: cloned
register: result
- name: Assert that image is cloned
assert:
that:
- result is changed
- name: Clone image with name
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
state: renamed
new_name: new_image
register: result
- name: Assert that image is cloned
assert:
that:
- result is changed
- name: Disable image
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
enabled: false
register: result
- name: Assert that network is disabled
assert:
that:
- result is changed
- name: Enable image
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
enabled: true
register: result
- name: Assert that network is enabled
assert:
that:
- result is changed
- name: Make image persistent
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
persistent: true
register: result
- name: Assert that network is persistent
assert:
that:
- result is changed
- name: Make image non-persistent
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
persistent: false
register: result
- name: Assert that network is non-persistent
assert:
that:
- result is changed
# Testing errors
- name: Try to use name and ID a the same time
one_image_info:
api_url: "{{ opennebula_url }}"
api_username: "{{ opennebula_username }}"
api_password: "{{ opennebula_password }}"
id: 0
name: somename
register: result
ignore_errors: true
- name: Assert that network not changed
assert:
that:
- result is failed

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