summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/general')
-rw-r--r--ansible_collections/community/general/.azure-pipelines/azure-pipelines.yml4
-rw-r--r--ansible_collections/community/general/.github/BOTMETA.yml22
-rw-r--r--ansible_collections/community/general/CHANGELOG.md110
-rw-r--r--ansible_collections/community/general/CHANGELOG.rst67
-rw-r--r--ansible_collections/community/general/FILES.json460
-rw-r--r--ansible_collections/community/general/MANIFEST.json4
-rw-r--r--ansible_collections/community/general/changelogs/changelog.yaml108
-rw-r--r--ansible_collections/community/general/docs/docsite/extra-docs.yml4
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-common.yml16
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml16
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml16
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008.yml2
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml9
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml3
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009.yml14
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/default-common.yml12
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/list3.yml6
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples.yml56
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples_all.rst.j24
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/extra-vars.yml7
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j244
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/list3.out.j22
-rw-r--r--ansible_collections/community/general/docs/docsite/helper/lists_mergeby/playbook.yml10
-rw-r--r--ansible_collections/community/general/docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst248
-rw-r--r--ansible_collections/community/general/docs/docsite/rst/guide_deps.rst74
-rw-r--r--ansible_collections/community/general/docs/docsite/rst/guide_vardict.rst176
-rw-r--r--ansible_collections/community/general/meta/runtime.yml2
-rw-r--r--ansible_collections/community/general/plugins/callback/opentelemetry.py18
-rw-r--r--ansible_collections/community/general/plugins/doc_fragments/django.py9
-rw-r--r--ansible_collections/community/general/plugins/doc_fragments/proxmox.py7
-rw-r--r--ansible_collections/community/general/plugins/filter/keep_keys.py138
-rw-r--r--ansible_collections/community/general/plugins/filter/lists_mergeby.py212
-rw-r--r--ansible_collections/community/general/plugins/filter/remove_keys.py138
-rw-r--r--ansible_collections/community/general/plugins/filter/replace_keys.py180
-rw-r--r--ansible_collections/community/general/plugins/inventory/opennebula.py3
-rw-r--r--ansible_collections/community/general/plugins/module_utils/cmd_runner.py53
-rw-r--r--ansible_collections/community/general/plugins/module_utils/consul.py67
-rw-r--r--ansible_collections/community/general/plugins/module_utils/django.py39
-rw-r--r--ansible_collections/community/general/plugins/module_utils/proxmox.py8
-rw-r--r--ansible_collections/community/general/plugins/module_utils/redfish_utils.py56
-rw-r--r--ansible_collections/community/general/plugins/modules/ansible_galaxy_install.py27
-rw-r--r--ansible_collections/community/general/plugins/modules/cargo.py64
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_agent_check.py254
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_agent_service.py289
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_auth_method.py2
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_binding_rule.py2
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_policy.py2
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_role.py2
-rw-r--r--ansible_collections/community/general/plugins/modules/consul_token.py4
-rw-r--r--ansible_collections/community/general/plugins/modules/django_check.py113
-rw-r--r--ansible_collections/community/general/plugins/modules/django_createcachetable.py67
-rw-r--r--ansible_collections/community/general/plugins/modules/git_config.py41
-rw-r--r--ansible_collections/community/general/plugins/modules/homectl.py25
-rw-r--r--ansible_collections/community/general/plugins/modules/ipa_dnsrecord.py27
-rw-r--r--ansible_collections/community/general/plugins/modules/keycloak_client.py111
-rw-r--r--ansible_collections/community/general/plugins/modules/keycloak_clientscope.py38
-rw-r--r--ansible_collections/community/general/plugins/modules/launchd.py3
-rw-r--r--ansible_collections/community/general/plugins/modules/openbsd_pkg.py26
-rw-r--r--ansible_collections/community/general/plugins/modules/pacman.py5
-rw-r--r--ansible_collections/community/general/plugins/modules/proxmox_kvm.py1
-rw-r--r--ansible_collections/community/general/plugins/modules/proxmox_vm_info.py33
-rw-r--r--ansible_collections/community/general/plugins/modules/redfish_command.py30
-rw-r--r--ansible_collections/community/general/plugins/modules/redfish_info.py22
-rw-r--r--ansible_collections/community/general/plugins/modules/redis_info.py48
-rw-r--r--ansible_collections/community/general/plugins/modules/udm_user.py26
-rw-r--r--ansible_collections/community/general/plugins/plugin_utils/keys_filter.py141
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml55
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_directory.yml122
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_check.yml114
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_service.yml89
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_keep_keys/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml79
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_keep_keys/vars/main.yml33
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_remove_keys/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/remove_keys.yml79
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_remove_keys/vars/main.yml33
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_replace_keys/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/fn-test-replace_keys.yml21
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/replace_keys.yml56
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_replace_keys/vars/main.yml58
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml24
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml128
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py5
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.py13
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.yaml27
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.py13
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.yaml15
112 files changed, 4376 insertions, 543 deletions
diff --git a/ansible_collections/community/general/.azure-pipelines/azure-pipelines.yml b/ansible_collections/community/general/.azure-pipelines/azure-pipelines.yml
index 7dc438ad3..3f9293ac1 100644
--- a/ansible_collections/community/general/.azure-pipelines/azure-pipelines.yml
+++ b/ansible_collections/community/general/.azure-pipelines/azure-pipelines.yml
@@ -53,7 +53,7 @@ variables:
resources:
containers:
- container: default
- image: quay.io/ansible/azure-pipelines-test-container:4.0.1
+ image: quay.io/ansible/azure-pipelines-test-container:6.0.0
pool: Standard
@@ -127,6 +127,7 @@ stages:
- test: '3.10'
- test: '3.11'
- test: '3.12'
+ - test: '3.13'
- stage: Units_2_17
displayName: Units 2.17
dependsOn: []
@@ -354,6 +355,7 @@ stages:
targets:
- test: '3.8'
- test: '3.11'
+ - test: '3.13'
- stage: Generic_2_17
displayName: Generic 2.17
dependsOn: []
diff --git a/ansible_collections/community/general/.github/BOTMETA.yml b/ansible_collections/community/general/.github/BOTMETA.yml
index add324935..faedb4260 100644
--- a/ansible_collections/community/general/.github/BOTMETA.yml
+++ b/ansible_collections/community/general/.github/BOTMETA.yml
@@ -157,6 +157,8 @@ files:
$filters/jc.py:
maintainers: kellyjonbrazil
$filters/json_query.py: {}
+ $filters/keep_keys.py:
+ maintainers: vbotka
$filters/lists.py:
maintainers: cfiehe
$filters/lists_difference.yml:
@@ -170,6 +172,10 @@ files:
$filters/lists_union.yml:
maintainers: cfiehe
$filters/random_mac.py: {}
+ $filters/remove_keys.py:
+ maintainers: vbotka
+ $filters/replace_keys.py:
+ maintainers: vbotka
$filters/time.py:
maintainers: resmo
$filters/to_days.yml:
@@ -502,12 +508,16 @@ files:
maintainers: tintoy
$modules/discord.py:
maintainers: cwollinger
+ $modules/django_check.py:
+ maintainers: russoz
+ $modules/django_command.py:
+ maintainers: russoz
+ $modules/django_createcachetable.py:
+ maintainers: russoz
$modules/django_manage.py:
ignore: scottanderson42 tastychutney
labels: django_manage
maintainers: russoz
- $modules/django_command.py:
- maintainers: russoz
$modules/dnf_versionlock.py:
maintainers: moreda
$modules/dnf_config_manager.py:
@@ -1415,6 +1425,8 @@ files:
ignore: matze
labels: zypper
maintainers: $team_suse
+ $plugin_utils/keys_filter.py:
+ maintainers: vbotka
$plugin_utils/unsafe.py:
maintainers: felixfontein
$tests/a_module.py:
@@ -1454,6 +1466,10 @@ files:
maintainers: baldwinSPC nurfet-becirevic t0mk teebes
docs/docsite/rst/guide_scaleway.rst:
maintainers: $team_scaleway
+ docs/docsite/rst/guide_deps.rst:
+ maintainers: russoz
+ docs/docsite/rst/guide_vardict.rst:
+ maintainers: russoz
docs/docsite/rst/test_guide.rst:
maintainers: felixfontein
#########################
@@ -1485,7 +1501,7 @@ macros:
team_ansible_core:
team_aix: MorrisA bcoca d-little flynn1973 gforster kairoaraujo marvin-sinister mator molekuul ramooncamacho wtcross
team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo
- team_consul: sgargan apollo13
+ team_consul: sgargan apollo13 Ilgmi
team_cyberark_conjur: jvanderhoof ryanprior
team_e_spirit: MatrixCrawler getjack
team_flatpak: JayKayy oolongbrothers
diff --git a/ansible_collections/community/general/CHANGELOG.md b/ansible_collections/community/general/CHANGELOG.md
index 91922fb7a..7a752cb6a 100644
--- a/ansible_collections/community/general/CHANGELOG.md
+++ b/ansible_collections/community/general/CHANGELOG.md
@@ -2,42 +2,118 @@
**Topics**
-- <a href="#v9-0-1">v9\.0\.1</a>
+- <a href="#v9-1-0">v9\.1\.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="#v9-0-0">v9\.0\.0</a>
+ - <a href="#known-issues">Known Issues</a>
+ - <a href="#new-plugins">New Plugins</a>
+ - <a href="#filter">Filter</a>
+ - <a href="#new-modules">New Modules</a>
+- <a href="#v9-0-1">v9\.0\.1</a>
- <a href="#release-summary-1">Release Summary</a>
- <a href="#minor-changes-1">Minor Changes</a>
+ - <a href="#bugfixes-1">Bugfixes</a>
+- <a href="#v9-0-0">v9\.0\.0</a>
+ - <a href="#release-summary-2">Release Summary</a>
+ - <a href="#minor-changes-2">Minor Changes</a>
- <a href="#breaking-changes--porting-guide">Breaking Changes / Porting Guide</a>
- - <a href="#deprecated-features">Deprecated Features</a>
+ - <a href="#deprecated-features-1">Deprecated Features</a>
- <a href="#removed-features-previously-deprecated">Removed Features \(previously deprecated\)</a>
- <a href="#security-fixes">Security Fixes</a>
- - <a href="#bugfixes-1">Bugfixes</a>
- - <a href="#new-plugins">New Plugins</a>
+ - <a href="#bugfixes-2">Bugfixes</a>
+ - <a href="#new-plugins-1">New Plugins</a>
- <a href="#become">Become</a>
- <a href="#callback">Callback</a>
- <a href="#connection">Connection</a>
- - <a href="#filter">Filter</a>
+ - <a href="#filter-1">Filter</a>
- <a href="#lookup">Lookup</a>
- <a href="#test">Test</a>
- - <a href="#new-modules">New Modules</a>
+ - <a href="#new-modules-1">New Modules</a>
This changelog describes changes after version 8\.0\.0\.
+<a id="v9-1-0"></a>
+## v9\.1\.0
+
+<a id="release-summary"></a>
+### Release Summary
+
+Regular feature and bugfix release\.
+
+<a id="minor-changes"></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)\)\.
+* ansible\_galaxy\_install \- add upgrade feature \([https\://github\.com/ansible\-collections/community\.general/pull/8431](https\://github\.com/ansible\-collections/community\.general/pull/8431)\, [https\://github\.com/ansible\-collections/community\.general/issues/8351](https\://github\.com/ansible\-collections/community\.general/issues/8351)\)\.
+* cargo \- add option <code>directory</code>\, which allows source directory to be specified \([https\://github\.com/ansible\-collections/community\.general/pull/8480](https\://github\.com/ansible\-collections/community\.general/pull/8480)\)\.
+* cmd\_runner module utils \- add decorator <code>cmd\_runner\_fmt\.stack</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8415](https\://github\.com/ansible\-collections/community\.general/pull/8415)\)\.
+* cmd\_runner\_fmt module utils \- simplify implementation of <code>cmd\_runner\_fmt\.as\_bool\_not\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8512](https\://github\.com/ansible\-collections/community\.general/pull/8512)\)\.
+* ipa\_dnsrecord \- adds <code>SSHFP</code> record type for managing SSH fingerprints in FreeIPA DNS \([https\://github\.com/ansible\-collections/community\.general/pull/8404](https\://github\.com/ansible\-collections/community\.general/pull/8404)\)\.
+* keycloak\_client \- assign auth flow by name \([https\://github\.com/ansible\-collections/community\.general/pull/8428](https\://github\.com/ansible\-collections/community\.general/pull/8428)\)\.
+* openbsd\_pkg \- adds diff support to show changes in installed package list\. This does not yet work for check mode \([https\://github\.com/ansible\-collections/community\.general/pull/8402](https\://github\.com/ansible\-collections/community\.general/pull/8402)\)\.
+* proxmox \- allow specification of the API port when using proxmox\_\* \([https\://github\.com/ansible\-collections/community\.general/issues/8440](https\://github\.com/ansible\-collections/community\.general/issues/8440)\, [https\://github\.com/ansible\-collections/community\.general/pull/8441](https\://github\.com/ansible\-collections/community\.general/pull/8441)\)\.
+* proxmox\_vm\_info \- add <code>network</code> option to retrieve current network information \([https\://github\.com/ansible\-collections/community\.general/pull/8471](https\://github\.com/ansible\-collections/community\.general/pull/8471)\)\.
+* redfish\_command \- add <code>wait</code> and <code>wait\_timeout</code> options to allow a user to block a command until a service is accessible after performing the requested command \([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)\)\.
+* 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"></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"></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)\)\.
+* keycloak\_realm \- add normalizations for <code>attributes</code> and <code>protocol\_mappers</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8496](https\://github\.com/ansible\-collections/community\.general/pull/8496)\)\.
+* launched \- correctly report changed status in check mode \([https\://github\.com/ansible\-collections/community\.general/pull/8406](https\://github\.com/ansible\-collections/community\.general/pull/8406)\)\.
+* opennebula inventory plugin \- fix invalid reference to IP when inventory runs against NICs with no IPv4 address \([https\://github\.com/ansible\-collections/community\.general/pull/8489](https\://github\.com/ansible\-collections/community\.general/pull/8489)\)\.
+* opentelemetry callback \- do not save the JSON response when using the <code>ansible\.builtin\.uri</code> module \([https\://github\.com/ansible\-collections/community\.general/pull/8430](https\://github\.com/ansible\-collections/community\.general/pull/8430)\)\.
+* opentelemetry callback \- do not save the content response when using the <code>ansible\.builtin\.slurp</code> module \([https\://github\.com/ansible\-collections/community\.general/pull/8430](https\://github\.com/ansible\-collections/community\.general/pull/8430)\)\.
+* paman \- do not fail if an empty list of packages has been provided and there is nothing to do \([https\://github\.com/ansible\-collections/community\.general/pull/8514](https\://github\.com/ansible\-collections/community\.general/pull/8514)\)\.
+
+<a id="known-issues"></a>
+### Known Issues
+
+* homectl \- the module does not work under Python 3\.13 or newer\, since it relies on the removed <code>crypt</code> standard library module \([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/8497](https\://github\.com/ansible\-collections/community\.general/pull/8497)\)\.
+* udm\_user \- the module does not work under Python 3\.13 or newer\, since it relies on the removed <code>crypt</code> standard library module \([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/8497](https\://github\.com/ansible\-collections/community\.general/pull/8497)\)\.
+
+<a id="new-plugins"></a>
+### New Plugins
+
+<a id="filter"></a>
+#### Filter
+
+* community\.general\.keep\_keys \- Keep specific keys from dictionaries in a list\.
+* 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"></a>
+### New Modules
+
+* community\.general\.consul\_agent\_check \- Add\, modify\, and delete checks within a consul cluster\.
+* community\.general\.consul\_agent\_service \- Add\, modify and delete services within a consul cluster\.
+* community\.general\.django\_check \- Wrapper for C\(django\-admin check\)\.
+* community\.general\.django\_createcachetable \- Wrapper for C\(django\-admin createcachetable\)\.
+
<a id="v9-0-1"></a>
## v9\.0\.1
-<a id="release-summary"></a>
+<a id="release-summary-1"></a>
### Release Summary
Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
-<a id="minor-changes"></a>
+<a id="minor-changes-1"></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"></a>
+<a id="bugfixes-1"></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)\)\.
@@ -56,12 +132,12 @@ Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
<a id="v9-0-0"></a>
## v9\.0\.0
-<a id="release-summary-1"></a>
+<a id="release-summary-2"></a>
### Release Summary
This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-05\-20\.
-<a id="minor-changes-1"></a>
+<a id="minor-changes-2"></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)\)\.
@@ -190,7 +266,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"></a>
+<a id="deprecated-features-1"></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)\)\.
@@ -231,7 +307,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-1"></a>
+<a id="bugfixes-2"></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)\)\.
@@ -306,7 +382,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
* to\_ini filter plugin \- disabling interpolation of <code>ConfigParser</code> to allow converting values with a <code>\%</code> sign \([https\://github\.com/ansible\-collections/community\.general/issues/8183](https\://github\.com/ansible\-collections/community\.general/issues/8183)\, [https\://github\.com/ansible\-collections/community\.general/pull/8185](https\://github\.com/ansible\-collections/community\.general/pull/8185)\)\.
* xml \- make module work with lxml 5\.1\.1\, which removed some internals that the module was relying on \([https\://github\.com/ansible\-collections/community\.general/pull/8169](https\://github\.com/ansible\-collections/community\.general/pull/8169)\)\.
-<a id="new-plugins"></a>
+<a id="new-plugins-1"></a>
### New Plugins
<a id="become"></a>
@@ -325,7 +401,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
* community\.general\.incus \- Run tasks in Incus instances via the Incus CLI\.
-<a id="filter"></a>
+<a id="filter-1"></a>
#### Filter
* community\.general\.from\_ini \- Converts INI text input into a dictionary\.
@@ -346,7 +422,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"></a>
+<a id="new-modules-1"></a>
### New Modules
* community\.general\.consul\_acl\_bootstrap \- Bootstrap ACLs in Consul\.
diff --git a/ansible_collections/community/general/CHANGELOG.rst b/ansible_collections/community/general/CHANGELOG.rst
index 384bee747..523acb975 100644
--- a/ansible_collections/community/general/CHANGELOG.rst
+++ b/ansible_collections/community/general/CHANGELOG.rst
@@ -6,6 +6,73 @@ Community General Release Notes
This changelog describes changes after version 8.0.0.
+v9.1.0
+======
+
+Release Summary
+---------------
+
+Regular feature and bugfix release.
+
+Minor Changes
+-------------
+
+- CmdRunner module util - argument formats can be specified as plain functions without calling ``cmd_runner_fmt.as_func()`` (https://github.com/ansible-collections/community.general/pull/8479).
+- ansible_galaxy_install - add upgrade feature (https://github.com/ansible-collections/community.general/pull/8431, https://github.com/ansible-collections/community.general/issues/8351).
+- cargo - add option ``directory``, which allows source directory to be specified (https://github.com/ansible-collections/community.general/pull/8480).
+- cmd_runner module utils - add decorator ``cmd_runner_fmt.stack`` (https://github.com/ansible-collections/community.general/pull/8415).
+- cmd_runner_fmt module utils - simplify implementation of ``cmd_runner_fmt.as_bool_not()`` (https://github.com/ansible-collections/community.general/pull/8512).
+- ipa_dnsrecord - adds ``SSHFP`` record type for managing SSH fingerprints in FreeIPA DNS (https://github.com/ansible-collections/community.general/pull/8404).
+- keycloak_client - assign auth flow by name (https://github.com/ansible-collections/community.general/pull/8428).
+- openbsd_pkg - adds diff support to show changes in installed package list. This does not yet work for check mode (https://github.com/ansible-collections/community.general/pull/8402).
+- proxmox - allow specification of the API port when using proxmox_* (https://github.com/ansible-collections/community.general/issues/8440, https://github.com/ansible-collections/community.general/pull/8441).
+- proxmox_vm_info - add ``network`` option to retrieve current network information (https://github.com/ansible-collections/community.general/pull/8471).
+- redfish_command - add ``wait`` and ``wait_timeout`` options to allow a user to block a command until a service is accessible after performing the requested command (https://github.com/ansible-collections/community.general/issues/8051, https://github.com/ansible-collections/community.general/pull/8434).
+- redfish_info - add command ``CheckAvailability`` to check if a service is accessible (https://github.com/ansible-collections/community.general/issues/8051, 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).
+
+Deprecated Features
+-------------------
+
+- CmdRunner module util - setting the value of the ``ignore_none`` parameter within a ``CmdRunner`` context is deprecated and that feature should be removed in community.general 12.0.0 (https://github.com/ansible-collections/community.general/pull/8479).
+- git_config - the ``list_all`` option has been deprecated and will be removed in community.general 11.0.0. Use the ``community.general.git_config_info`` module instead (https://github.com/ansible-collections/community.general/pull/8453).
+- git_config - using ``state=present`` without providing ``value`` is deprecated and will be disallowed in community.general 11.0.0. Use the ``community.general.git_config_info`` module instead to read a value (https://github.com/ansible-collections/community.general/pull/8453).
+
+Bugfixes
+--------
+
+- git_config - fix behavior of ``state=absent`` if ``value`` is present (https://github.com/ansible-collections/community.general/issues/8436, https://github.com/ansible-collections/community.general/pull/8452).
+- keycloak_realm - add normalizations for ``attributes`` and ``protocol_mappers`` (https://github.com/ansible-collections/community.general/pull/8496).
+- launched - correctly report changed status in check mode (https://github.com/ansible-collections/community.general/pull/8406).
+- opennebula inventory plugin - fix invalid reference to IP when inventory runs against NICs with no IPv4 address (https://github.com/ansible-collections/community.general/pull/8489).
+- opentelemetry callback - do not save the JSON response when using the ``ansible.builtin.uri`` module (https://github.com/ansible-collections/community.general/pull/8430).
+- opentelemetry callback - do not save the content response when using the ``ansible.builtin.slurp`` module (https://github.com/ansible-collections/community.general/pull/8430).
+- paman - do not fail if an empty list of packages has been provided and there is nothing to do (https://github.com/ansible-collections/community.general/pull/8514).
+
+Known Issues
+------------
+
+- homectl - the module does not work under Python 3.13 or newer, since it relies on the removed ``crypt`` standard library module (https://github.com/ansible-collections/community.general/issues/4691, https://github.com/ansible-collections/community.general/pull/8497).
+- udm_user - the module does not work under Python 3.13 or newer, since it relies on the removed ``crypt`` standard library module (https://github.com/ansible-collections/community.general/issues/4690, https://github.com/ansible-collections/community.general/pull/8497).
+
+New Plugins
+-----------
+
+Filter
+~~~~~~
+
+- community.general.keep_keys - Keep specific keys from dictionaries in a list.
+- community.general.remove_keys - Remove specific keys from dictionaries in a list.
+- community.general.replace_keys - Replace specific keys in a list of dictionaries.
+
+New Modules
+-----------
+
+- community.general.consul_agent_check - Add, modify, and delete checks within a consul cluster.
+- community.general.consul_agent_service - Add, modify and delete services within a consul cluster.
+- community.general.django_check - Wrapper for C(django-admin check).
+- community.general.django_createcachetable - Wrapper for C(django-admin createcachetable).
+
v9.0.1
======
diff --git a/ansible_collections/community/general/FILES.json b/ansible_collections/community/general/FILES.json
index 87fdd5c9a..41f20623d 100644
--- a/ansible_collections/community/general/FILES.json
+++ b/ansible_collections/community/general/FILES.json
@@ -109,7 +109,7 @@
"name": ".azure-pipelines/azure-pipelines.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "48eb26e372a37363159b6a17ba403c830d01c2cb29c106bfa8e9748ad534ff50",
+ "chksum_sha256": "a79320cab46fce92c3205300c9f17c58bc8dacbb21868bebe83636d4a730e99d",
"format": 1
},
{
@@ -193,7 +193,7 @@
"name": ".github/BOTMETA.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "95b932042b339875673fcbe218094b962576d3900511a041b9b7fb91a858e68c",
+ "chksum_sha256": "cc8b862bd68049694fb8b89ff204b657855e0a8f80ea78a643647d19ed4997f3",
"format": 1
},
{
@@ -312,7 +312,7 @@
"name": "changelogs/changelog.yaml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0014fa74832def0e1d47b994b7aee9fbe393a30553ac051acc1f9a2ac886be6a",
+ "chksum_sha256": "7079dd86212c9c1ba677b86a9b240c210f0f9d591eba846bd2b871050032cbf0",
"format": 1
},
{
@@ -368,14 +368,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "bd2eef902441d9c71b84d997508e78803e648c534e75c8b12323e199eeca81d6",
+ "chksum_sha256": "1cf297e6880eb27f8fd1da74208b089d70647a972fb8357f613e58bf40334fcd",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "663a3b0429cd096b10b06038a561f01872a34bc0e611f535abc9e626a474b6a9",
+ "chksum_sha256": "3cdee8eb1544ce35baf81583ab7e7c722dbe81e5643c3013d2739f22919581c0",
"format": 1
},
{
@@ -389,14 +389,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "bd2eef902441d9c71b84d997508e78803e648c534e75c8b12323e199eeca81d6",
+ "chksum_sha256": "1cf297e6880eb27f8fd1da74208b089d70647a972fb8357f613e58bf40334fcd",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "852a8518e33c1d8a2144f1835f6a10b0c17fcc13850cf45475ce538a4312171e",
+ "chksum_sha256": "39da0ce92bf01050b04f3aafecad1870fba5b6970e7301a618f9445d885ddf94",
"format": 1
},
{
@@ -410,14 +410,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "73003165935630df144177b3cbb78954f7eeaccc3031d694a1e1c2f8b365d99d",
+ "chksum_sha256": "2f7de68a5c297dc9bec3127737002cf214052069d709b77412df2c42eb03d3bb",
"format": 1
},
{
@@ -431,14 +431,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "1b04d243241c56ad51cdee8c34a6475af76482801a74a55a269a296122c3be44",
+ "chksum_sha256": "a260b433df2d9d689bf5cd61141e91a40f86677c944f8ad75e85da0c1fb02a31",
"format": 1
},
{
@@ -452,14 +452,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d4541eb704de6d64c4c34c12cc22328e19016c66811e99af22b4be27053d9526",
+ "chksum_sha256": "2a198e5156b90d540962bf0adfffdb445294449e815cf2c0471efcafdfd3a996",
"format": 1
},
{
@@ -473,14 +473,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "205959e977ba30d49bdfd879f4b7bb2f50b50762361d89b2360cbb419a6af931",
+ "chksum_sha256": "577f0bd0c766630aa359f0453691007d9a9d3a0f4535b9b9b161ce407bff2d6d",
"format": 1
},
{
@@ -494,14 +494,14 @@
"name": "docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f5517facd2643e52b06c5181df7768b096b2916ac411f7403484f0ad1b7d8ee1",
+ "chksum_sha256": "41b95615fb4eca9d5beab31ea436ca9875a2b2d109dc036919cd68388fc64b17",
"format": 1
},
{
@@ -515,119 +515,154 @@
"name": "docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a833536f106aebd7a5f737743f96c791302260e72f9393be53bdda0a86a10c9a",
+ "chksum_sha256": "9b2d102869f86a54b1ad7c5b38bdd6515aa3b7e444747069dce98d32fa12f640",
+ "format": 1
+ },
+ {
+ "name": "docs/docsite/helper/lists_mergeby/example-009_vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/docsite/helper/lists_mergeby/example-009_vars/default-common.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1cf297e6880eb27f8fd1da74208b089d70647a972fb8357f613e58bf40334fcd",
+ "format": 1
+ },
+ {
+ "name": "docs/docsite/helper/lists_mergeby/example-009_vars/list3.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4523d397880b12fc1b8ec3c85403c30cf59e95fdfafc9b40e4e3279856f3fa5a",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/default-common.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "bd2eef902441d9c71b84d997508e78803e648c534e75c8b12323e199eeca81d6",
+ "chksum_sha256": "1cf297e6880eb27f8fd1da74208b089d70647a972fb8357f613e58bf40334fcd",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/default-recursive-true.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce747f67c1635a6dfd887c7ebf3ee02c2014b9eced5773b08d747a11fd916a95",
+ "chksum_sha256": "40233cee2ac468835816e1e05b898c6c2444c89dafeb81f5aac16a26dc417734",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-001.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ec664dbed63f2d8f9d7173a44f6f53b8aac3917e4c152082157ae06b2b64b717",
+ "chksum_sha256": "b7f83ae4eda65288a5c7b1e448d3f379ac69f5c40c0bbb2b180d0ad71b33ac3b",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-002.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "174db79b357280e60b4b37b96e77708148894d4d81571fa10d65d9846bbcf365",
+ "chksum_sha256": "c5abd9609513167ba5a0df49223fb9269e288d3ecd597004a6ae066179c6d368",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-003.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5fac7cdbb3fc0b3addddb5ffaa58fcbf241df07195b41ad011f2550df1281fc7",
+ "chksum_sha256": "211edd2c682dcee996f4211f8c832896401ef0dc76f38f6e91aef6c5789d7721",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-004.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c7781daf8df6f6e6403fc8bd8ba40a6a5515e24b1ffa96f85b4d3cb2e23d926e",
+ "chksum_sha256": "05090716aaf46cb3ed48035d83482aec11ff0b3c30cc850f30fb5320844c8877",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-005.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c02a2a83fc72b27668c5ce96e0c3feb466ea89047f5fa8bc961260bce0aa97b5",
+ "chksum_sha256": "6dc6b4160e81b1d49936db28762428d182da87441fa66817146daa4b747998b2",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-006.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9eda979168ded0b39e43a4ae0e02c38cdccecc236604b61cbf80069869e762e3",
+ "chksum_sha256": "03adfa30300af062e28fb963bc2472cabf4102dce03e088470727ee35df488db",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-007.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "78a002ab23ee4a16c60322ce988af490a5d900131fb621ada85541afee932fdd",
+ "chksum_sha256": "c5ab5b8f580cd9bca71c93f2e0581593e170357d4a677fafefa10023524f6bd5",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/example-008.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "11c87a5bd327951a21baaf28e47d11c0c07e8a12cdcc5326fcd7f8b0d5217d56",
+ "chksum_sha256": "ac56a41a9e1bcdef1c80c590484f9081ec7f095106df18777a56e506a3a26970",
+ "format": 1
+ },
+ {
+ "name": "docs/docsite/helper/lists_mergeby/example-009.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1cb469396a08823b54768d987f12bf94414e82f218c3923d9af1c421833c09e1",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/examples.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "fe278ca276168ebfc2167cf5ad2e2d4b3cae0d6cdd246326b11620db259e2eb3",
+ "chksum_sha256": "f22163a6554b36a7df5d626e0061f3c4b8d83d4fe7f6c64b3f3ff3373598bb92",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/examples_all.rst.j2",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "31f8a0a79c3aebf801b74e0cc7587ea7486fc221a7ab948701211f0178310ace",
+ "chksum_sha256": "55874c71532504a32b927c036756f8e32a8dc6695f34c4451e91defa6c62b316",
+ "format": 1
+ },
+ {
+ "name": "docs/docsite/helper/lists_mergeby/extra-vars.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3cc1cfbd48140a9368dc9583683da172197ce124e3e1e44a75bb50e524945729",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "27fd7341d931b081d0caa6d9dbeacee7cd788bb5418019fb70e5794293f15582",
+ "chksum_sha256": "1f30ab3058307c531437e39abb3aaa7b0258b8a1537adcb82844f9b51d5bc1fc",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/list3.out.j2",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d228735e576466950b3a90b1de44b6a503de957521b3db41a492d742695d8d71",
+ "chksum_sha256": "e3a21ec55be646c12180396c2311c85d341ab5f75bf8a16ea09fe3c214b47da9",
"format": 1
},
{
"name": "docs/docsite/helper/lists_mergeby/playbook.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "825f6e9f15549cd02a8fa1cf5a6b6bdbc1dc39ff80ce59f5014af8c0358bee58",
+ "chksum_sha256": "840405c1b7fc7cd852404e9ee35337626f97cd6843457cc7ad5a4de50902a707",
"format": 1
},
{
@@ -683,7 +718,7 @@
"name": "docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a0a81486d2b415efa2e2f8576b3aadb98125e74b10b2113f13f6dcf1e652faed",
+ "chksum_sha256": "6b7a3ec73c58b87695919903c13805db6bac8c2b44d0a916f432651ac1e4ee58",
"format": 1
},
{
@@ -743,6 +778,13 @@
"format": 1
},
{
+ "name": "docs/docsite/rst/guide_deps.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "df07337b3596ae5246294cceecf12ca46287436ad2e381a7e7d25a84ed33e9bd",
+ "format": 1
+ },
+ {
"name": "docs/docsite/rst/guide_online.rst",
"ftype": "file",
"chksum_type": "sha256",
@@ -764,6 +806,13 @@
"format": 1
},
{
+ "name": "docs/docsite/rst/guide_vardict.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eabc2712be99330fba6016b38ef1410427b115631ff10bad98922c1c0dc1705a",
+ "format": 1
+ },
+ {
"name": "docs/docsite/rst/test_guide.rst",
"ftype": "file",
"chksum_type": "sha256",
@@ -781,7 +830,7 @@
"name": "docs/docsite/extra-docs.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f3f9cbc513d494da6c34cf6db49319f34645a8dd1ee79736360c0e48643e76af",
+ "chksum_sha256": "3099ca216ce7a424bef49f96819691f5c0411bf838ebeb814f3fd2c0ef8c8eeb",
"format": 1
},
{
@@ -802,7 +851,7 @@
"name": "meta/runtime.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "bdd7473e27902ea009dac03f604dd957a93413ee0275b561aff956ff26caf391",
+ "chksum_sha256": "6934d562c2c37a3ffb229276c9bfbaf2f869a1d9e0538072b004885e09c0b13f",
"format": 1
},
{
@@ -1075,7 +1124,7 @@
"name": "plugins/callback/opentelemetry.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6eb005c02c46afa1b07d7d7861b54496d30ced27113f10b156719b3f151b3014",
+ "chksum_sha256": "f53258ebf00c73a1ac7908a1e963b100673c586e1f68da40592caf0af78bdb10",
"format": 1
},
{
@@ -1278,7 +1327,7 @@
"name": "plugins/doc_fragments/django.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0836d2f746a8b62a1408455fdd54bcbd8f41342eb2f07a681b378b0efef5f3c8",
+ "chksum_sha256": "e30b30e859e4c2861828eee9c1b35b54c535ef2aa0ac77ba12cfcf1611ab9bb4",
"format": 1
},
{
@@ -1453,7 +1502,7 @@
"name": "plugins/doc_fragments/proxmox.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "daa7d15e8a1ffc6e4c539712099537ab9c0f667684d2bcf4ba89e196caf9e460",
+ "chksum_sha256": "b64edf0d5addde754b70fd42e65b304913c0ff2e45004853d04d0b2bd5adbbca",
"format": 1
},
{
@@ -1604,6 +1653,13 @@
"format": 1
},
{
+ "name": "plugins/filter/keep_keys.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f4474076e83cfe8850482fbe29663644d0ca4b15f179a0a2646b9b92bbe7b824",
+ "format": 1
+ },
+ {
"name": "plugins/filter/lists.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -1628,7 +1684,7 @@
"name": "plugins/filter/lists_mergeby.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4163df17512c4388bf020417cdd3f9919db9c6f1c23b2a57852bd10523a0abbe",
+ "chksum_sha256": "6ab23ff362b7940d0ec340eda1ef9a0d3262dc6894a9139fa1357ffdbb2674da",
"format": 1
},
{
@@ -1653,6 +1709,20 @@
"format": 1
},
{
+ "name": "plugins/filter/remove_keys.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d8c3d19a4129c1007a675dbb97f9f593aa410c35887891dfeb556e68a5aec62a",
+ "format": 1
+ },
+ {
+ "name": "plugins/filter/replace_keys.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c5ddedca5a0fa34413b894a9e40991aa91ba6cb112269fe3e4bf311c86dc99bf",
+ "format": 1
+ },
+ {
"name": "plugins/filter/time.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -1803,7 +1873,7 @@
"name": "plugins/inventory/opennebula.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "954bb22612557fbe3be3510d3ebc7e230164cb3dcb689345a287270b85234970",
+ "chksum_sha256": "e3fd2fdd5abb43a0690b6b97daa79d45bfa334268e9d1051f11a5806b87956e4",
"format": 1
},
{
@@ -2342,14 +2412,14 @@
"name": "plugins/module_utils/cmd_runner.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9b9f5d0e0ed818b1bdc80e8a6a4a858ba23f92bcaf6d5b224073b4e4a5de1e4c",
+ "chksum_sha256": "d627300d236348a1df761fcb8624c5d55f72c33938a08250097c7e28259294d8",
"format": 1
},
{
"name": "plugins/module_utils/consul.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9e572ae8233e2f4a72bbdaba9d39f0e0d631ff4ccb3684a32f853d0b7b6581bd",
+ "chksum_sha256": "3f55f60552f8187b7db9c4ce3bc915cf6e278594f21c0e30c5024af736aff172",
"format": 1
},
{
@@ -2391,7 +2461,7 @@
"name": "plugins/module_utils/django.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "78e63b0a1b6304d42171f878bcb50bfd1ff96bfead0ba41220c0b43b6ad1f7cb",
+ "chksum_sha256": "46b9d866dcc4432d3c7bf67b4e31037d1fdd064bf421359a1c1660bd4331bc32",
"format": 1
},
{
@@ -2587,7 +2657,7 @@
"name": "plugins/module_utils/proxmox.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "756e2b829ba10cf0f2d177b8729b92007ef23edaf1a708c064b1cbdb9b96b02c",
+ "chksum_sha256": "235bc199d79987aaa2d49984ad2b8140fbb58f3fa536109e82d660381316d57b",
"format": 1
},
{
@@ -2615,7 +2685,7 @@
"name": "plugins/module_utils/redfish_utils.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce4679e15df51fc1a102f5f6f8ef1cb8f8153cfb5a896786c5531829d76ce0e2",
+ "chksum_sha256": "43fc29e5b41bc436a7c1d2face5334cc3a8a79861defbcece2dc5156b78c0081",
"format": 1
},
{
@@ -2811,7 +2881,7 @@
"name": "plugins/modules/ansible_galaxy_install.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7d6e02e1a7306d048c97e5e9f2e5553fe8ece50ded61a39f014af2fecd51da2a",
+ "chksum_sha256": "a482de0224539003f97a7cc019a7ec499c7fb3b40a95964f421c47f19d397935",
"format": 1
},
{
@@ -2986,7 +3056,7 @@
"name": "plugins/modules/cargo.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "1f937d2d6b2eb1d54baaaddc8aad6e45ff219c1762ea07d73e028b1030b47601",
+ "chksum_sha256": "59179736f71cff627a2c85788a5cd93bc0885347bfeb5037bfd1d4921ba20382",
"format": 1
},
{
@@ -3137,17 +3207,31 @@
"format": 1
},
{
+ "name": "plugins/modules/consul_agent_check.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4b47d0ab359a40730c169d01873d0eb1023ade6c0c42529910cb047ec0fb4be1",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/consul_agent_service.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc493896df6b2b2754a68c557e7902214acfc9a34fcdaea25dd754e90a17c6e0",
+ "format": 1
+ },
+ {
"name": "plugins/modules/consul_auth_method.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "776c407945f239f40ea36e24f42606d20141316ffa7f36e95cb3f00efa4c6bcb",
+ "chksum_sha256": "0ee3b66b9b873472ad25479c8809b34ea9da645e942efd602b9f0ff7fe6ccafd",
"format": 1
},
{
"name": "plugins/modules/consul_binding_rule.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "bfc78b6b53d3ad705f099e2ed04eaff75ab0d1ef3d823b3de58d6881efe2178d",
+ "chksum_sha256": "78859326d12520ddec8e4f3b324f6e7b8150e8252a0babd4dc7932ae29dad096",
"format": 1
},
{
@@ -3161,14 +3245,14 @@
"name": "plugins/modules/consul_policy.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "05fcd6bd9085bb781d7f9bdc8a808f1e63f493d018299ce3dcb6a8c7f89ae729",
+ "chksum_sha256": "3757983feca43c6da4a0cf08fc6dbbf71f6095bf7e16cf6f3698d47fe171cb5f",
"format": 1
},
{
"name": "plugins/modules/consul_role.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f18b68eab0c232b18fefe5d66164dfa49e4e9c11396616200e23b0774c98c5ae",
+ "chksum_sha256": "33f6b5f02c216241fef4dd98371c08737c1cb8094f058f4a08f71828d4eeed0b",
"format": 1
},
{
@@ -3182,7 +3266,7 @@
"name": "plugins/modules/consul_token.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8572e2337ce15303534844ed997859fc6e26b3b5537ea80fbea280f1bfa17419",
+ "chksum_sha256": "34b3c9e7bed0b8566edb79ccdd46f49c466f7c261e0b69c1b1d748dcbbe6ab86",
"format": 1
},
{
@@ -3270,6 +3354,13 @@
"format": 1
},
{
+ "name": "plugins/modules/django_check.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e028e8dd895fef7c8002e310ec5ac92c183c4993fc8d061c80fa0de916c057c",
+ "format": 1
+ },
+ {
"name": "plugins/modules/django_command.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -3277,6 +3368,13 @@
"format": 1
},
{
+ "name": "plugins/modules/django_createcachetable.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "66f18cc86d806a4447888afd48e3e7d008933f0ee93b109c0fea62b5b8e1ec4a",
+ "format": 1
+ },
+ {
"name": "plugins/modules/django_manage.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -3441,7 +3539,7 @@
"name": "plugins/modules/git_config.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3764f124422fcd3be0c60b955a6286da22e458fff5023c57ba73d0e90d3842a7",
+ "chksum_sha256": "92c164fb3a497d2d0887b0f239657c66d82f1ecd22cf2950a01c2f2b5b35e174",
"format": 1
},
{
@@ -3707,7 +3805,7 @@
"name": "plugins/modules/homectl.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f5af3533ea90dda0925e64715ea23b8aea4418020c0fb66115fa0595d452d749",
+ "chksum_sha256": "c8dac7831a418cc0385c4616484aefea40eddc91eef77f85404e58b3a6e47633",
"format": 1
},
{
@@ -4022,7 +4120,7 @@
"name": "plugins/modules/ipa_dnsrecord.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c8ae9aa977adb6ec60404eb8bf40adbaa7b1ce897e6707769d5e90230b40f16c",
+ "chksum_sha256": "2f3e8e8a7e64541c19873a1fb534afb8295f163d9c2204e6687031f68675ef31",
"format": 1
},
{
@@ -4351,7 +4449,7 @@
"name": "plugins/modules/keycloak_client.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7870103accbfbd0204f1b7adeee0e20b9a6b8cddad0edda0bd31ec252572f186",
+ "chksum_sha256": "1200158abd819a25429fc2ad8c563623c117c668e4740dc9ffa68b22b1660abc",
"format": 1
},
{
@@ -4372,7 +4470,7 @@
"name": "plugins/modules/keycloak_clientscope.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c712e8e6d6b9d27297f7fbed371d7338aea6c92c6fc940fea1cf9e8c4b897c71",
+ "chksum_sha256": "3bf4a3c511dca811693ec895233193c633ead77585e3f3710844e7f3f6cb3510",
"format": 1
},
{
@@ -4505,7 +4603,7 @@
"name": "plugins/modules/launchd.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "239bdd1ceebfc51da46babbede85732efd5aca73719214225bd505c202cc4915",
+ "chksum_sha256": "0008d09fc7fb13a3bd959dbbc7f0089a22a8d6c5720fdf0fb69acee361cddd62",
"format": 1
},
{
@@ -5233,7 +5331,7 @@
"name": "plugins/modules/openbsd_pkg.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "aa5f9a76a91826cfcf340da018cee24dec149b5d858e9161023cf94e5867ac09",
+ "chksum_sha256": "eea55d4a493ae779edcf4fa9d6d9d401c502b9cf0acc000c9c4c85fafe78215a",
"format": 1
},
{
@@ -5338,7 +5436,7 @@
"name": "plugins/modules/pacman.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3e92ff37a394cf3ee414ca9e55e928fae7d2875992630b8e80e7fad1c5d1273f",
+ "chksum_sha256": "77db65d60288cc9d30ab7fa148a1f94f969d61d6f5c23a34bf76fda8306fd478",
"format": 1
},
{
@@ -5597,7 +5695,7 @@
"name": "plugins/modules/proxmox_kvm.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "030cd0ca64eb1eac59e15bd62ce58239e6466b01391a6abc314feff1c8a32614",
+ "chksum_sha256": "afdba49a542d59df6afba627eb4a3ca187f9033070193b1db599667427102cfc",
"format": 1
},
{
@@ -5674,7 +5772,7 @@
"name": "plugins/modules/proxmox_vm_info.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9bb1491ad5da16bd483f659726e5d99a29ac784a23f615244165ad4d9bde0120",
+ "chksum_sha256": "84b5e54194e9b6f67c49b01b5e20b795bd67f3cd82adcece514d8256827bd2d0",
"format": 1
},
{
@@ -5730,7 +5828,7 @@
"name": "plugins/modules/redfish_command.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3a06abd918e13f440dcd7bd66a1eae9d77a414aa80dc231eba9ddad2c3fc16d7",
+ "chksum_sha256": "c3beab5266697dca3d00e13de6760bbcb8ca48dc39d1425ab1da361ba39eb051",
"format": 1
},
{
@@ -5744,7 +5842,7 @@
"name": "plugins/modules/redfish_info.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3f14d5645eb692b829ab83dac5e38eac9ea9fc7bae38f87ffd532ad2e8d49de5",
+ "chksum_sha256": "7a423fb3fd03d467af475d4823658c2ff3cba2f2c2a72ac63be4bca9292e5466",
"format": 1
},
{
@@ -5786,7 +5884,7 @@
"name": "plugins/modules/redis_info.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5c544a02b0415efa329c84abc4119e0039991bfadcbdae32d14d484c7443e47a",
+ "chksum_sha256": "ace8770d46241e343ba03a55a42f56d674acc9f88e4f12e06fe130c81bb1da41",
"format": 1
},
{
@@ -6444,7 +6542,7 @@
"name": "plugins/modules/udm_user.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "108d20d909305308b8e5e7a6bf5e323f154db4c473c02e47734e8fb0c8bc7b07",
+ "chksum_sha256": "b061e35e6e8ea1970710888fe431a17dd3739b40e59e6342846e50dbdd9154ef",
"format": 1
},
{
@@ -6798,6 +6896,13 @@
"format": 1
},
{
+ "name": "plugins/plugin_utils/keys_filter.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "950a97d3f8fc8d57b0495b08dc40be2ad47e4436e65c2ac1384a8b656c07c2b0",
+ "format": 1
+ },
+ {
"name": "plugins/plugin_utils/unsafe.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -7130,7 +7235,7 @@
"name": "tests/integration/targets/ansible_galaxy_install/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "db8a8fd9d62a035862276fb8ce6bae558ccfe9ee6a03a498c561272236301307",
+ "chksum_sha256": "f6896e1d1f1b563728905f203d6623fcc9ced037e88132af50457c368d750fb8",
"format": 1
},
{
@@ -7662,7 +7767,7 @@
"name": "tests/integration/targets/cargo/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e5f66835ea6bd23ef85f61c95211e57f257d3f2ecab06d669afe311ba64045b3",
+ "chksum_sha256": "9370d274dfb0bec0b81f998ccc03441f09cbb731d9647e1810043a5857ff5c52",
"format": 1
},
{
@@ -7673,6 +7778,13 @@
"format": 1
},
{
+ "name": "tests/integration/targets/cargo/tasks/test_directory.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3aed8a73a3b83b455c377054f1198be385d5cfb5e055368be7222a70fc0f40a7",
+ "format": 1
+ },
+ {
"name": "tests/integration/targets/cargo/tasks/test_general.yml",
"ftype": "file",
"chksum_type": "sha256",
@@ -8051,6 +8163,20 @@
"format": 1
},
{
+ "name": "tests/integration/targets/consul/tasks/consul_agent_check.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ac1e5b0cf2a5483ba484c99016b34975759a6a8060a503603146d7c9be552f75",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/consul/tasks/consul_agent_service.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4f7deacb2c709ea660887c954c8005acc6f8163958cc71e9a5f5cec547784d33",
+ "format": 1
+ },
+ {
"name": "tests/integration/targets/consul/tasks/consul_auth_method.yml",
"ftype": "file",
"chksum_type": "sha256",
@@ -8110,7 +8236,7 @@
"name": "tests/integration/targets/consul/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6bafc431979010557c06259d8b5cccc03f49d1ec2097a26118dd3a2091fd4cc7",
+ "chksum_sha256": "76dec6dcfe653795428e6eaa8f0eb4c8eccd6d04d7c3896b99362c386ac93ffc",
"format": 1
},
{
@@ -9297,6 +9423,55 @@
"format": 1
},
{
+ "name": "tests/integration/targets/filter_keep_keys",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1ece426f8edfd07e80705b0925b97d70c0b020dc7ed6c2bd02dc7ed69834a85f",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "928ea7662292fac8633465e8405edf98c5e71c0c9de8b81d6cf1b33e7aabfe4c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "da6753430b6dea343d2a605d0a7af54e6ac66640bdca97159417c54c75696be0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_keep_keys/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "96646c5ab7118c53b8b722f6abe91a7ed4d3eed9be401faefb399d6a8f427c2e",
+ "format": 1
+ },
+ {
"name": "tests/integration/targets/filter_lists",
"ftype": "dir",
"chksum_type": null,
@@ -9465,6 +9640,111 @@
"format": 1
},
{
+ "name": "tests/integration/targets/filter_remove_keys",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3815b9d04a798cc01c090d0494ca27544bec4a4a08bd1995fa8ff586e1201c40",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/tasks/remove_keys.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "430024a06df2b362cd3d5d658102a8389ddacc780613dde834d95c01cd5cff21",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "19b18860ad324c02cfb996c048e2516e53e7d6b84f13059abeff602e9802120c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_remove_keys/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "96646c5ab7118c53b8b722f6abe91a7ed4d3eed9be401faefb399d6a8f427c2e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/tasks/fn-test-replace_keys.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b62bfcb4f9b61bc818b033993b111eb88196dbd8ddf6fa0942f93badf3274338",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1efdb33269dba21292e7b4525611ca41360277f60aaed0d476d3115b6b683c74",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/tasks/replace_keys.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4f92e48603760a77ca302371847e004bec26b1599f863fe7719d6bcfa37d8e33",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "12fb80e1cc0fed908d3bb9ae1bb3b1e1bbebeb7f2e259476f16cf4a75173cbc6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/filter_replace_keys/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "96646c5ab7118c53b8b722f6abe91a7ed4d3eed9be401faefb399d6a8f427c2e",
+ "format": 1
+ },
+ {
"name": "tests/integration/targets/filter_time",
"ftype": "dir",
"chksum_type": null,
@@ -10028,7 +10308,7 @@
"name": "tests/integration/targets/git_config/tasks/unset_value.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e31339e0f76aecbacc8839d3755b7fd21fa1882a8189d5cc79890e45d65e559c",
+ "chksum_sha256": "a8836b380c222836aea5c2aee38187580c031cbb7aec8d70fa55588f7a162f75",
"format": 1
},
{
@@ -13010,7 +13290,7 @@
"name": "tests/integration/targets/keycloak_client/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "10f83e1b6754b9dbc932e5bc5e6dfd85d028dffb2f85c5f9023f197211ea85bc",
+ "chksum_sha256": "dac1226601e47b2dc8127eca264d249e57054828048d590f6e9a260ef9b675ec",
"format": 1
},
{
@@ -23237,7 +23517,7 @@
"name": "tests/unit/plugins/module_utils/test_cmd_runner.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5e44ed79ba57b534227340b6e97849f84d20811597aaed62aa697d09ab6243f6",
+ "chksum_sha256": "98b8cc91992ff261d7bddcafc08f106a0ff38a17d6f45240f3387c4c019eecff",
"format": 1
},
{
@@ -26538,6 +26818,20 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/modules/test_django_check.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "71fb52b56b5957aa54aebeb786ff74f396902effb388a202a86627e0cf0256d5",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_django_check.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7f3e625f4f6aec1e810913ecd137e8340c8d3c1f97dc2dc81bffa0c1600eed1d",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/modules/test_django_command.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -26552,6 +26846,20 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/modules/test_django_createcachetable.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b12c8d85f2466e732a6f05e80485a6105d26fdd5c67477e4e46031c43f46086b",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_django_createcachetable.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4eaa1b63defefb1bf8e4be46b98ce23ac97c49a01b06225bf1da803b3ebcce63",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/modules/test_dnf_config_manager.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -27654,7 +27962,7 @@
"name": "CHANGELOG.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2fc9c5d7b462624c77da1320ae1acaa13e7e4f765f0fe3393278146104281693",
+ "chksum_sha256": "d4edd4ccef5e7c4b176e19e06f7deb76de1ce252c72ad839644ee37f0a181476",
"format": 1
},
{
@@ -27668,7 +27976,7 @@
"name": "CHANGELOG.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "28d86e5061f86ee8a49776681a2f660ee38c29d057b9c23c6d2ab473c0f218a5",
+ "chksum_sha256": "781bf561f0bec0521238cbf4b7481f2887d01fef494ed6ff450fae4daa870616",
"format": 1
},
{
diff --git a/ansible_collections/community/general/MANIFEST.json b/ansible_collections/community/general/MANIFEST.json
index ffbae1e0e..26506ad50 100644
--- a/ansible_collections/community/general/MANIFEST.json
+++ b/ansible_collections/community/general/MANIFEST.json
@@ -2,7 +2,7 @@
"collection_info": {
"namespace": "community",
"name": "general",
- "version": "9.0.1",
+ "version": "9.1.0",
"authors": [
"Ansible (https://github.com/ansible)"
],
@@ -23,7 +23,7 @@
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "be2f78923401a69f1bb6105804257106a4092634b2fa6a02058fea1e14de1bff",
+ "chksum_sha256": "98bfdff0974e81d3a63af8d9b29886515483d7536197a6f78e3c706272aba8f8",
"format": 1
},
"format": 1
diff --git a/ansible_collections/community/general/changelogs/changelog.yaml b/ansible_collections/community/general/changelogs/changelog.yaml
index 6215a89ce..609989343 100644
--- a/ansible_collections/community/general/changelogs/changelog.yaml
+++ b/ansible_collections/community/general/changelogs/changelog.yaml
@@ -794,3 +794,111 @@ releases:
- 8413-galaxy-refactor.yml
- 9.0.1.yml
release_date: '2024-05-27'
+ 9.1.0:
+ changes:
+ bugfixes:
+ - git_config - fix behavior of ``state=absent`` if ``value`` is present (https://github.com/ansible-collections/community.general/issues/8436,
+ https://github.com/ansible-collections/community.general/pull/8452).
+ - keycloak_realm - add normalizations for ``attributes`` and ``protocol_mappers``
+ (https://github.com/ansible-collections/community.general/pull/8496).
+ - launched - correctly report changed status in check mode (https://github.com/ansible-collections/community.general/pull/8406).
+ - opennebula inventory plugin - fix invalid reference to IP when inventory runs
+ against NICs with no IPv4 address (https://github.com/ansible-collections/community.general/pull/8489).
+ - opentelemetry callback - do not save the JSON response when using the ``ansible.builtin.uri``
+ module (https://github.com/ansible-collections/community.general/pull/8430).
+ - opentelemetry callback - do not save the content response when using the ``ansible.builtin.slurp``
+ module (https://github.com/ansible-collections/community.general/pull/8430).
+ - paman - do not fail if an empty list of packages has been provided and there
+ is nothing to do (https://github.com/ansible-collections/community.general/pull/8514).
+ deprecated_features:
+ - CmdRunner module util - setting the value of the ``ignore_none`` parameter
+ within a ``CmdRunner`` context is deprecated and that feature should be removed
+ in community.general 12.0.0 (https://github.com/ansible-collections/community.general/pull/8479).
+ - git_config - the ``list_all`` option has been deprecated and will be removed
+ in community.general 11.0.0. Use the ``community.general.git_config_info``
+ module instead (https://github.com/ansible-collections/community.general/pull/8453).
+ - git_config - using ``state=present`` without providing ``value`` is deprecated
+ and will be disallowed in community.general 11.0.0. Use the ``community.general.git_config_info``
+ module instead to read a value (https://github.com/ansible-collections/community.general/pull/8453).
+ known_issues:
+ - homectl - the module does not work under Python 3.13 or newer, since it relies
+ on the removed ``crypt`` standard library module (https://github.com/ansible-collections/community.general/issues/4691,
+ https://github.com/ansible-collections/community.general/pull/8497).
+ - udm_user - the module does not work under Python 3.13 or newer, since it relies
+ on the removed ``crypt`` standard library module (https://github.com/ansible-collections/community.general/issues/4690,
+ https://github.com/ansible-collections/community.general/pull/8497).
+ minor_changes:
+ - CmdRunner module util - argument formats can be specified as plain functions
+ without calling ``cmd_runner_fmt.as_func()`` (https://github.com/ansible-collections/community.general/pull/8479).
+ - ansible_galaxy_install - add upgrade feature (https://github.com/ansible-collections/community.general/pull/8431,
+ https://github.com/ansible-collections/community.general/issues/8351).
+ - cargo - add option ``directory``, which allows source directory to be specified
+ (https://github.com/ansible-collections/community.general/pull/8480).
+ - cmd_runner module utils - add decorator ``cmd_runner_fmt.stack`` (https://github.com/ansible-collections/community.general/pull/8415).
+ - cmd_runner_fmt module utils - simplify implementation of ``cmd_runner_fmt.as_bool_not()``
+ (https://github.com/ansible-collections/community.general/pull/8512).
+ - ipa_dnsrecord - adds ``SSHFP`` record type for managing SSH fingerprints in
+ FreeIPA DNS (https://github.com/ansible-collections/community.general/pull/8404).
+ - keycloak_client - assign auth flow by name (https://github.com/ansible-collections/community.general/pull/8428).
+ - openbsd_pkg - adds diff support to show changes in installed package list.
+ This does not yet work for check mode (https://github.com/ansible-collections/community.general/pull/8402).
+ - proxmox - allow specification of the API port when using proxmox_* (https://github.com/ansible-collections/community.general/issues/8440,
+ https://github.com/ansible-collections/community.general/pull/8441).
+ - proxmox_vm_info - add ``network`` option to retrieve current network information
+ (https://github.com/ansible-collections/community.general/pull/8471).
+ - redfish_command - add ``wait`` and ``wait_timeout`` options to allow a user
+ to block a command until a service is accessible after performing the requested
+ command (https://github.com/ansible-collections/community.general/issues/8051,
+ https://github.com/ansible-collections/community.general/pull/8434).
+ - redfish_info - add command ``CheckAvailability`` to check if a service is
+ accessible (https://github.com/ansible-collections/community.general/issues/8051,
+ 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).
+ release_summary: Regular feature and bugfix release.
+ fragments:
+ - 8051-Redfish-Wait-For-Service.yml
+ - 8402-add-diif-mode-openbsd-pkg.yml
+ - 8404-ipa_dnsrecord_sshfp.yml
+ - 8415-cmd-runner-stack.yml
+ - 8428-assign-auth-flow-by-name-keycloak-client.yaml
+ - 8430-fix-opentelemetry-when-using-logs-with-uri-or-slurp-tasks.yaml
+ - 8431-galaxy-upgrade.yml
+ - 8440-allow-api-port-specification.yaml
+ - 8452-git_config-absent.yml
+ - 8453-git_config-deprecate-read.yml
+ - 8464-redis-add-cluster-info.yml
+ - 8471-proxmox-vm-info-network.yml
+ - 8476-launchd-check-mode-changed.yaml
+ - 8479-cmdrunner-improvements.yml
+ - 8480-directory-feature-cargo.yml
+ - 8489-fix-opennebula-inventory-crash-when-nic-has-no-ip.yml
+ - 8496-keycloak_clientscope-add-normalizations.yaml
+ - 8497-crypt.yml
+ - 8512-as-bool-not.yml
+ - 8514-pacman-empty.yml
+ - 9.1.0.yml
+ modules:
+ - description: Add, modify, and delete checks within a consul cluster.
+ name: consul_agent_check
+ namespace: ''
+ - description: Add, modify and delete services within a consul cluster.
+ name: consul_agent_service
+ namespace: ''
+ - description: Wrapper for C(django-admin check).
+ name: django_check
+ namespace: ''
+ - description: Wrapper for C(django-admin createcachetable).
+ name: django_createcachetable
+ namespace: ''
+ plugins:
+ filter:
+ - description: Keep specific keys from dictionaries in a list.
+ name: keep_keys
+ namespace: null
+ - description: Remove specific keys from dictionaries in a list.
+ name: remove_keys
+ namespace: null
+ - description: Replace specific keys in a list of dictionaries.
+ name: replace_keys
+ namespace: null
+ release_date: '2024-06-17'
diff --git a/ansible_collections/community/general/docs/docsite/extra-docs.yml b/ansible_collections/community/general/docs/docsite/extra-docs.yml
index 529573606..3bed9e35f 100644
--- a/ansible_collections/community/general/docs/docsite/extra-docs.yml
+++ b/ansible_collections/community/general/docs/docsite/extra-docs.yml
@@ -14,3 +14,7 @@ sections:
- guide_online
- guide_packet
- guide_scaleway
+ - title: Developer Guides
+ toctree:
+ - guide_deps
+ - guide_vardict
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-common.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-common.yml
index fd874e5c9..4431fe27d 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-common.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-common.yml
@@ -2,17 +2,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- - name: foo
- extra: true
- - name: bar
- extra: false
- - name: meh
- extra: true
+ - {name: foo, extra: true}
+ - {name: bar, extra: false}
+ - {name: meh, extra: true}
list2:
- - name: foo
- path: /foo
- - name: baz
- path: /baz
+ - {name: foo, path: /foo}
+ - {name: baz, path: /baz}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001.yml
index 0cf6a9b8a..c27b019e5 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001.yml
@@ -8,7 +8,7 @@
dir: example-001_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-001.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml
index fd874e5c9..4431fe27d 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/default-common.yml
@@ -2,17 +2,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- - name: foo
- extra: true
- - name: bar
- extra: false
- - name: meh
- extra: true
+ - {name: foo, extra: true}
+ - {name: bar, extra: false}
+ - {name: meh, extra: true}
list2:
- - name: foo
- path: /foo
- - name: baz
- path: /baz
+ - {name: foo, path: /foo}
+ - {name: baz, path: /baz}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml
index 0604feccb..8bd8bc8f2 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-001_vars/list3.yml
@@ -2,6 +2,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
-list3: "{{ list1|
+list3: "{{ list1 |
community.general.lists_mergeby(list2, 'name') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002.yml
index 5e6e0315d..e164db125 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002.yml
@@ -8,7 +8,7 @@
dir: example-002_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-002.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml
index fd874e5c9..4431fe27d 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/default-common.yml
@@ -2,17 +2,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- - name: foo
- extra: true
- - name: bar
- extra: false
- - name: meh
- extra: true
+ - {name: foo, extra: true}
+ - {name: bar, extra: false}
+ - {name: meh, extra: true}
list2:
- - name: foo
- path: /foo
- - name: baz
- path: /baz
+ - {name: foo, path: /foo}
+ - {name: baz, path: /baz}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml
index 8ad752407..be6cfcbf3 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-002_vars/list3.yml
@@ -2,6 +2,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003.yml
index 2f93ab8a2..cbc5e43a5 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003.yml
@@ -8,7 +8,7 @@
dir: example-003_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-003.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml
index d5374eece..2eff5df41 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-003_vars/list3.yml
@@ -2,7 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true) }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004.yml
index 3ef067faf..68e77dea8 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004.yml
@@ -8,7 +8,7 @@
dir: example-004_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-004.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml
index a054ea1e7..94c8ceed3 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-004_vars/list3.yml
@@ -2,8 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='keep') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005.yml
index 57e7a779d..b7b81de29 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005.yml
@@ -8,7 +8,7 @@
dir: example-005_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-005.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml
index 3480bf658..f0d7751f2 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-005_vars/list3.yml
@@ -2,8 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='append') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006.yml
index 41fc88e49..1be3becbc 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006.yml
@@ -8,7 +8,7 @@
dir: example-006_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-006.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml
index 97513b559..f555c8dcb 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-006_vars/list3.yml
@@ -2,8 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='prepend') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007.yml
index 3de715844..8a596ea68 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007.yml
@@ -8,7 +8,7 @@
dir: example-007_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug|d(false) | bool
- template:
src: list3.out.j2
dest: example-007.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml
index cb51653b4..d8ad16cf4 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-007_vars/list3.yml
@@ -2,8 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='append_rp') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008.yml
index e33828bf9..6d5c03bc6 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008.yml
@@ -8,7 +8,7 @@
dir: example-008_vars
- debug:
var: list3
- when: debug|d(false)|bool
+ when: debug | d(false) | bool
- template:
src: list3.out.j2
dest: example-008.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml
index 133c8f2ae..eb83ea82e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/default-recursive-true.yml
@@ -2,14 +2,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
list1:
- name: myname01
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -18,7 +16,6 @@ list2:
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml
index af7001fc4..b2051376e 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-008_vars/list3.yml
@@ -2,8 +2,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
-
-list3: "{{ [list1, list2]|
+list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='prepend_rp') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009.yml
new file mode 100644
index 000000000..beef5d356
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009.yml
@@ -0,0 +1,14 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: 9. Merge single list by common attribute 'name'
+ include_vars:
+ dir: example-009_vars
+- debug:
+ var: list3
+ when: debug | d(false) | bool
+- template:
+ src: list3.out.j2
+ dest: example-009.out
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/default-common.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/default-common.yml
new file mode 100644
index 000000000..4431fe27d
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/default-common.yml
@@ -0,0 +1,12 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+list1:
+ - {name: foo, extra: true}
+ - {name: bar, extra: false}
+ - {name: meh, extra: true}
+
+list2:
+ - {name: foo, path: /foo}
+ - {name: baz, path: /baz}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/list3.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/list3.yml
new file mode 100644
index 000000000..1708e3baf
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/example-009_vars/list3.yml
@@ -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
+list3: "{{ [list1 + list2, []] |
+ community.general.lists_mergeby('name') }}"
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples.yml
index 83b985084..34ad2d155 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples.yml
@@ -4,51 +4,75 @@
# SPDX-License-Identifier: GPL-3.0-or-later
examples:
- - label: 'In the example below the lists are merged by the attribute ``name``:'
+ - title: Two lists
+ description: 'In the example below the lists are merged by the attribute ``name``:'
file: example-001_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-001.out
lang: 'yaml'
- - label: 'It is possible to use a list of lists as an input of the filter:'
+ - title: List of two lists
+ description: 'It is possible to use a list of lists as an input of the filter:'
file: example-002_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces the same result as in the previous example:'
+ - title:
+ description: 'This produces the same result as in the previous example:'
file: example-002.out
lang: 'yaml'
- - label: 'Example ``list_merge=replace`` (default):'
+ - title: Single list
+ description: 'It is possible to merge single list:'
+ file: example-009_vars/list3.yml
+ lang: 'yaml+jinja'
+ - title:
+ description: 'This produces the same result as in the previous example:'
+ file: example-009.out
+ lang: 'yaml'
+ - title: list_merge=replace (default)
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=replace` (default):'
file: example-003_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-003.out
lang: 'yaml'
- - label: 'Example ``list_merge=keep``:'
+ - title: list_merge=keep
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=keep`:'
file: example-004_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-004.out
lang: 'yaml'
- - label: 'Example ``list_merge=append``:'
+ - title: list_merge=append
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=append`:'
file: example-005_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-005.out
lang: 'yaml'
- - label: 'Example ``list_merge=prepend``:'
+ - title: list_merge=prepend
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=prepend`:'
file: example-006_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-006.out
lang: 'yaml'
- - label: 'Example ``list_merge=append_rp``:'
+ - title: list_merge=append_rp
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=append_rp`:'
file: example-007_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-007.out
lang: 'yaml'
- - label: 'Example ``list_merge=prepend_rp``:'
+ - title: list_merge=prepend_rp
+ description: 'Example :ansopt:`community.general.lists_mergeby#filter:list_merge=prepend_rp`:'
file: example-008_vars/list3.yml
lang: 'yaml+jinja'
- - label: 'This produces:'
+ - title:
+ description: 'This produces:'
file: example-008.out
lang: 'yaml'
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples_all.rst.j2 b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples_all.rst.j2
index 95a0fafdd..88098683b 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples_all.rst.j2
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/examples_all.rst.j2
@@ -4,10 +4,10 @@
SPDX-License-Identifier: GPL-3.0-or-later
{% for i in examples %}
-{{ i.label }}
+{{ i.description }}
.. code-block:: {{ i.lang }}
- {{ lookup('file', i.file)|indent(2) }}
+ {{ lookup('file', i.file) | split('\n') | reject('match', '^(#|---)') | join ('\n') | indent(2) }}
{% endfor %}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/extra-vars.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/extra-vars.yml
new file mode 100644
index 000000000..0482c7ff2
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/extra-vars.yml
@@ -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
+examples_one: true
+examples_all: true
+merging_lists_of_dictionaries: true
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2 b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2
index 71d0d5da6..ad74161dc 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2
@@ -6,57 +6,69 @@
Merging lists of dictionaries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If you have two or more lists of dictionaries and want to combine them into a list of merged dictionaries, where the dictionaries are merged by an attribute, you can use the ``lists_mergeby`` filter.
+If you have two or more lists of dictionaries and want to combine them into a list of merged dictionaries, where the dictionaries are merged by an attribute, you can use the :ansplugin:`community.general.lists_mergeby <community.general.lists_mergeby#filter>` filter.
-.. note:: The output of the examples in this section use the YAML callback plugin. Quoting: "Ansible output that can be quite a bit easier to read than the default JSON formatting." See :ref:`the documentation for the community.general.yaml callback plugin <ansible_collections.community.general.yaml_callback>`.
+.. note:: The output of the examples in this section use the YAML callback plugin. Quoting: "Ansible output that can be quite a bit easier to read than the default JSON formatting." See the documentation for the :ansplugin:`community.general.yaml callback plugin <community.general.yaml#callback>`.
Let us use the lists below in the following examples:
.. code-block:: yaml
- {{ lookup('file', 'default-common.yml')|indent(2) }}
+ {{ lookup('file', 'default-common.yml') | split('\n') | reject('match', '^(#|---)') | join ('\n') | indent(2) }}
{% for i in examples[0:2] %}
-{{ i.label }}
+{% if i.title | d('', true) | length > 0 %}
+{{ i.title }}
+{{ "%s" % ('"' * i.title|length) }}
+{% endif %}
+{{ i.description }}
.. code-block:: {{ i.lang }}
- {{ lookup('file', i.file)|indent(2) }}
+ {{ lookup('file', i.file) | split('\n') | reject('match', '^(#|---)') | join ('\n') | indent(2) }}
{% endfor %}
.. versionadded:: 2.0.0
-{% for i in examples[2:4] %}
-{{ i.label }}
+{% for i in examples[2:6] %}
+{% if i.title | d('', true) | length > 0 %}
+{{ i.title }}
+{{ "%s" % ('"' * i.title|length) }}
+{% endif %}
+{{ i.description }}
.. code-block:: {{ i.lang }}
- {{ lookup('file', i.file)|indent(2) }}
+ {{ lookup('file', i.file) | split('\n') | reject('match', '^(#|---)') | join ('\n') | indent(2) }}
{% endfor %}
-The filter also accepts two optional parameters: ``recursive`` and ``list_merge``. These parameters are only supported when used with ansible-base 2.10 or ansible-core, but not with Ansible 2.9. This is available since community.general 4.4.0.
+The filter also accepts two optional parameters: :ansopt:`community.general.lists_mergeby#filter:recursive` and :ansopt:`community.general.lists_mergeby#filter:list_merge`. This is available since community.general 4.4.0.
**recursive**
- Is a boolean, default to ``False``. Should the ``community.general.lists_mergeby`` recursively merge nested hashes. Note: It does not depend on the value of the ``hash_behaviour`` setting in ``ansible.cfg``.
+ Is a boolean, default to ``false``. Should the :ansplugin:`community.general.lists_mergeby#filter` filter recursively merge nested hashes. Note: It does not depend on the value of the ``hash_behaviour`` setting in ``ansible.cfg``.
**list_merge**
- Is a string, its possible values are ``replace`` (default), ``keep``, ``append``, ``prepend``, ``append_rp`` or ``prepend_rp``. It modifies the behaviour of ``community.general.lists_mergeby`` when the hashes to merge contain arrays/lists.
+ Is a string, its possible values are :ansval:`replace` (default), :ansval:`keep`, :ansval:`append`, :ansval:`prepend`, :ansval:`append_rp` or :ansval:`prepend_rp`. It modifies the behaviour of :ansplugin:`community.general.lists_mergeby#filter` when the hashes to merge contain arrays/lists.
-The examples below set ``recursive=true`` and display the differences among all six options of ``list_merge``. Functionality of the parameters is exactly the same as in the filter ``combine``. See :ref:`Combining hashes/dictionaries <combine_filter>` to learn details about these options.
+The examples below set :ansopt:`community.general.lists_mergeby#filter:recursive=true` and display the differences among all six options of :ansopt:`community.general.lists_mergeby#filter:list_merge`. Functionality of the parameters is exactly the same as in the filter :ansplugin:`ansible.builtin.combine#filter`. See :ref:`Combining hashes/dictionaries <combine_filter>` to learn details about these options.
Let us use the lists below in the following examples
.. code-block:: yaml
- {{ lookup('file', 'default-recursive-true.yml')|indent(2) }}
+ {{ lookup('file', 'default-recursive-true.yml') | split('\n') | reject('match', '^(#|---)') | join ('\n') |indent(2) }}
-{% for i in examples[4:16] %}
-{{ i.label }}
+{% for i in examples[6:] %}
+{% if i.title | d('', true) | length > 0 %}
+{{ i.title }}
+{{ "%s" % ('"' * i.title|length) }}
+{% endif %}
+{{ i.description }}
.. code-block:: {{ i.lang }}
- {{ lookup('file', i.file)|indent(2) }}
+ {{ lookup('file', i.file) | split('\n') | reject('match', '^(#|---)') | join ('\n') |indent(2) }}
{% endfor %}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/list3.out.j2 b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/list3.out.j2
index b51f6b868..a30a5c4ab 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/list3.out.j2
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/list3.out.j2
@@ -4,4 +4,4 @@ GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://w
SPDX-License-Identifier: GPL-3.0-or-later
#}
list3:
-{{ list3|to_nice_yaml(indent=0) }}
+ {{ list3 | to_yaml(indent=2, sort_keys=false) | indent(2) }}
diff --git a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/playbook.yml b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/playbook.yml
index 793d23348..ab389fa12 100644
--- a/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/playbook.yml
+++ b/ansible_collections/community/general/docs/docsite/helper/lists_mergeby/playbook.yml
@@ -5,7 +5,7 @@
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 1) Run all examples and create example-XXX.out
-# shell> ansible-playbook playbook.yml -e examples=true
+# shell> ansible-playbook playbook.yml -e examples_one=true
#
# 2) Optionally, for testing, create examples_all.rst
# shell> ansible-playbook playbook.yml -e examples_all=true
@@ -45,18 +45,20 @@
tags: t007
- import_tasks: example-008.yml
tags: t008
- when: examples|d(false)|bool
+ - import_tasks: example-009.yml
+ tags: t009
+ when: examples_one | d(false) | bool
- block:
- include_vars: examples.yml
- template:
src: examples_all.rst.j2
dest: examples_all.rst
- when: examples_all|d(false)|bool
+ when: examples_all | d(false) | bool
- block:
- include_vars: examples.yml
- template:
src: filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2
dest: filter_guide_abstract_informations_merging_lists_of_dictionaries.rst
- when: merging_lists_of_dictionaries|d(false)|bool
+ when: merging_lists_of_dictionaries | d(false) | bool
diff --git a/ansible_collections/community/general/docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst b/ansible_collections/community/general/docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst
index 06fa79d16..cafe04e5c 100644
--- a/ansible_collections/community/general/docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst
+++ b/ansible_collections/community/general/docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst
@@ -6,33 +6,30 @@
Merging lists of dictionaries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If you have two or more lists of dictionaries and want to combine them into a list of merged dictionaries, where the dictionaries are merged by an attribute, you can use the :ansplugin:`community.general.lists_mergeby filter <community.general.lists_mergeby#filter>`.
+If you have two or more lists of dictionaries and want to combine them into a list of merged dictionaries, where the dictionaries are merged by an attribute, you can use the :ansplugin:`community.general.lists_mergeby <community.general.lists_mergeby#filter>` filter.
-.. note:: The output of the examples in this section use the YAML callback plugin. Quoting: "Ansible output that can be quite a bit easier to read than the default JSON formatting." See :ref:`the documentation for the community.general.yaml callback plugin <ansible_collections.community.general.yaml_callback>`.
+.. note:: The output of the examples in this section use the YAML callback plugin. Quoting: "Ansible output that can be quite a bit easier to read than the default JSON formatting." See the documentation for the :ansplugin:`community.general.yaml callback plugin <community.general.yaml#callback>`.
Let us use the lists below in the following examples:
.. code-block:: yaml
list1:
- - name: foo
- extra: true
- - name: bar
- extra: false
- - name: meh
- extra: true
+ - {name: foo, extra: true}
+ - {name: bar, extra: false}
+ - {name: meh, extra: true}
list2:
- - name: foo
- path: /foo
- - name: baz
- path: /baz
+ - {name: foo, path: /foo}
+ - {name: baz, path: /baz}
+Two lists
+"""""""""
In the example below the lists are merged by the attribute ``name``:
.. code-block:: yaml+jinja
- list3: "{{ list1|
+ list3: "{{ list1 |
community.general.lists_mergeby(list2, 'name') }}"
This produces:
@@ -40,24 +37,21 @@ This produces:
.. code-block:: yaml
list3:
- - extra: false
- name: bar
- - name: baz
- path: /baz
- - extra: true
- name: foo
- path: /foo
- - extra: true
- name: meh
+ - {name: bar, extra: false}
+ - {name: baz, path: /baz}
+ - {name: foo, extra: true, path: /foo}
+ - {name: meh, extra: true}
.. versionadded:: 2.0.0
+List of two lists
+"""""""""""""""""
It is possible to use a list of lists as an input of the filter:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name') }}"
This produces the same result as in the previous example:
@@ -65,15 +59,29 @@ This produces the same result as in the previous example:
.. code-block:: yaml
list3:
- - extra: false
- name: bar
- - name: baz
- path: /baz
- - extra: true
- name: foo
- path: /foo
- - extra: true
- name: meh
+ - {name: bar, extra: false}
+ - {name: baz, path: /baz}
+ - {name: foo, extra: true, path: /foo}
+ - {name: meh, extra: true}
+
+Single list
+"""""""""""
+It is possible to merge single list:
+
+.. code-block:: yaml+jinja
+
+ list3: "{{ [list1 + list2, []] |
+ community.general.lists_mergeby('name') }}"
+
+This produces the same result as in the previous example:
+
+.. code-block:: yaml
+
+ list3:
+ - {name: bar, extra: false}
+ - {name: baz, path: /baz}
+ - {name: foo, extra: true, path: /foo}
+ - {name: meh, extra: true}
The filter also accepts two optional parameters: :ansopt:`community.general.lists_mergeby#filter:recursive` and :ansopt:`community.general.lists_mergeby#filter:list_merge`. This is available since community.general 4.4.0.
@@ -95,8 +103,7 @@ Let us use the lists below in the following examples
param01:
x: default_value
y: default_value
- list:
- - default_value
+ list: [default_value]
- name: myname02
param01: [1, 1, 2, 3]
@@ -105,16 +112,17 @@ Let us use the lists below in the following examples
param01:
y: patch_value
z: patch_value
- list:
- - patch_value
+ list: [patch_value]
- name: myname02
- param01: [3, 4, 4, {key: value}]
+ param01: [3, 4, 4]
+list_merge=replace (default)
+""""""""""""""""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=replace` (default):
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true) }}"
@@ -123,25 +131,22 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - patch_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 3
- - 4
- - 4
- - key: value
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [patch_value]
+ z: patch_value
+ - name: myname02
+ param01: [3, 4, 4]
+list_merge=keep
+"""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=keep`:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='keep') }}"
@@ -151,25 +156,22 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - default_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 1
- - 1
- - 2
- - 3
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [default_value]
+ z: patch_value
+ - name: myname02
+ param01: [1, 1, 2, 3]
+list_merge=append
+"""""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=append`:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='append') }}"
@@ -179,30 +181,22 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - default_value
- - patch_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 1
- - 1
- - 2
- - 3
- - 3
- - 4
- - 4
- - key: value
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [default_value, patch_value]
+ z: patch_value
+ - name: myname02
+ param01: [1, 1, 2, 3, 3, 4, 4]
+list_merge=prepend
+""""""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=prepend`:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='prepend') }}"
@@ -212,30 +206,22 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - patch_value
- - default_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 3
- - 4
- - 4
- - key: value
- - 1
- - 1
- - 2
- - 3
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [patch_value, default_value]
+ z: patch_value
+ - name: myname02
+ param01: [3, 4, 4, 1, 1, 2, 3]
+list_merge=append_rp
+""""""""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=append_rp`:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='append_rp') }}"
@@ -245,29 +231,22 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - default_value
- - patch_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 1
- - 1
- - 2
- - 3
- - 4
- - 4
- - key: value
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [default_value, patch_value]
+ z: patch_value
+ - name: myname02
+ param01: [1, 1, 2, 3, 4, 4]
+list_merge=prepend_rp
+"""""""""""""""""""""
Example :ansopt:`community.general.lists_mergeby#filter:list_merge=prepend_rp`:
.. code-block:: yaml+jinja
- list3: "{{ [list1, list2]|
+ list3: "{{ [list1, list2] |
community.general.lists_mergeby('name',
recursive=true,
list_merge='prepend_rp') }}"
@@ -277,21 +256,12 @@ This produces:
.. code-block:: yaml
list3:
- - name: myname01
- param01:
- list:
- - patch_value
- - default_value
- x: default_value
- y: patch_value
- z: patch_value
- - name: myname02
- param01:
- - 3
- - 4
- - 4
- - key: value
- - 1
- - 1
- - 2
+ - name: myname01
+ param01:
+ x: default_value
+ y: patch_value
+ list: [patch_value, default_value]
+ z: patch_value
+ - name: myname02
+ param01: [3, 4, 4, 1, 1, 2]
diff --git a/ansible_collections/community/general/docs/docsite/rst/guide_deps.rst b/ansible_collections/community/general/docs/docsite/rst/guide_deps.rst
new file mode 100644
index 000000000..4c0c4687a
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/rst/guide_deps.rst
@@ -0,0 +1,74 @@
+..
+ 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
+
+.. _ansible_collections.community.general.docsite.guide_deps:
+
+``deps`` Guide
+==============
+
+
+Using ``deps``
+^^^^^^^^^^^^^^
+
+The ``ansible_collections.community.general.plugins.module_utils.deps`` module util simplifies
+the importing of code as described in :ref:`Importing and using shared code <shared_code>`.
+Please notice that ``deps`` is meant to be used specifically with Ansible modules, and not other types of plugins.
+
+The same example from the Developer Guide would become:
+
+.. code-block:: python
+
+ from ansible_collections.community.general.plugins.module_utils import deps
+
+ with deps.declare("foo"):
+ import foo
+
+Then in ``main()``, just after the argspec (or anywhere in the code, for that matter), do
+
+.. code-block:: python
+
+ deps.validate(module) # assuming module is a valid AnsibleModule instance
+
+By default, ``deps`` will rely on ``ansible.module_utils.basic.missing_required_lib`` to generate
+a message about a failing import. That function accepts parameters ``reason`` and ``url``, and
+and so does ``deps```:
+
+.. code-block:: python
+
+ with deps.declare("foo", reason="foo is needed to properly bar", url="https://foo.bar.io"):
+ import foo
+
+If you would rather write a custom message instead of using ``missing_required_lib`` then do:
+
+.. code-block:: python
+
+ with deps.declare("foo", msg="Custom msg explaining why foo is needed"):
+ import foo
+
+``deps`` allows for multiple dependencies to be declared:
+
+.. code-block:: python
+
+ with deps.declare("foo"):
+ import foo
+
+ with deps.declare("bar"):
+ import bar
+
+ with deps.declare("doe"):
+ import doe
+
+By default, ``deps.validate()`` will check on all the declared dependencies, but if so desired,
+they can be validated selectively by doing:
+
+.. code-block:: python
+
+ deps.validate(module, "foo") # only validates the "foo" dependency
+
+ deps.validate(module, "doe:bar") # only validates the "doe" and "bar" dependencies
+
+ deps.validate(module, "-doe:bar") # validates all dependencies except "doe" and "bar"
+
+.. versionadded:: 6.1.0
diff --git a/ansible_collections/community/general/docs/docsite/rst/guide_vardict.rst b/ansible_collections/community/general/docs/docsite/rst/guide_vardict.rst
new file mode 100644
index 000000000..f65b09055
--- /dev/null
+++ b/ansible_collections/community/general/docs/docsite/rst/guide_vardict.rst
@@ -0,0 +1,176 @@
+..
+ 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
+
+.. _ansible_collections.community.general.docsite.guide_vardict:
+
+VarDict Guide
+=============
+
+Introduction
+^^^^^^^^^^^^
+
+The ``ansible_collections.community.general.plugins.module_utils.vardict`` module util provides the
+``VarDict`` class to help manage the module variables. That class is a container for module variables,
+especially the ones for which the module must keep track of state changes, and the ones that should
+be published as return values.
+
+Each variable has extra behaviors controlled by associated metadata, simplifying the generation of
+output values from the module.
+
+Quickstart
+""""""""""
+
+The simplest way of using ``VarDict`` is:
+
+.. code-block:: python
+
+ from ansible_collections.community.general.plugins.module_utils.vardict import VarDict
+
+Then in ``main()``, or any other function called from there:
+
+.. code-block:: python
+
+ vars = VarDict()
+
+ # Next 3 statements are equivalent
+ vars.abc = 123
+ vars["abc"] = 123
+ vars.set("abc", 123)
+
+ vars.xyz = "bananas"
+ vars.ghi = False
+
+And by the time the module is about to exit:
+
+.. code-block:: python
+
+ results = vars.output()
+ module.exit_json(**results)
+
+That makes the return value of the module:
+
+.. code-block:: javascript
+
+ {
+ "abc": 123,
+ "xyz": "bananas",
+ "ghi": false
+ }
+
+Metadata
+""""""""
+
+The metadata values associated with each variable are:
+
+- ``output: bool`` - marks the variable for module output as a module return value.
+- ``fact: bool`` - marks the variable for module output as an Ansible fact.
+- ``verbosity: int`` - sets the minimum level of verbosity for which the variable will be included in the output.
+- ``change: bool`` - controls the detection of changes in the variable value.
+- ``initial_value: any`` - when using ``change`` and need to forcefully set an intial value to the variable.
+- ``diff: bool`` - used along with ``change``, this generates an Ansible-style diff ``dict``.
+
+See the sections below for more details on how to use the metadata.
+
+
+Using VarDict
+^^^^^^^^^^^^^
+
+Basic Usage
+"""""""""""
+
+As shown above, variables can be accessed using the ``[]`` operator, as in a ``dict`` object,
+and also as an object attribute, such as ``vars.abc``. The form using the ``set()``
+method is special in the sense that you can use it to set metadata values:
+
+.. code-block:: python
+
+ vars.set("abc", 123, output=False)
+ vars.set("abc", 123, output=True, change=True)
+
+Another way to set metadata after the variables have been created is:
+
+.. code-block:: python
+
+ vars.set_meta("abc", output=False)
+ vars.set_meta("abc", output=True, change=True, diff=True)
+
+You can use either operator and attribute forms to access the value of the variable. Other ways to
+access its value and its metadata are:
+
+.. code-block:: python
+
+ print("abc value = {0}".format(vars.var("abc")["value"])) # get the value
+ print("abc output? {0}".format(vars.get_meta("abc")["output"])) # get the metadata like this
+
+The names of methods, such as ``set``, ``get_meta``, ``output`` amongst others, are reserved and
+cannot be used as variable names. If you try to use a reserved name a ``ValueError`` exception
+is raised with the message "Name <var> is reserved".
+
+Generating output
+"""""""""""""""""
+
+By default, every variable create will be enable for output with minimum verbosity set to zero, in
+other words, they will always be in the output by default.
+
+You can control that when creating the variable for the first time or later in the code:
+
+.. code-block:: python
+
+ vars.set("internal", x + 4, output=False)
+ vars.set_meta("internal", output=False)
+
+You can also set the verbosity of some variable, like:
+
+.. code-block:: python
+
+ vars.set("abc", x + 4)
+ vars.set("debug_x", x, verbosity=3)
+
+ results = vars.output(module._verbosity)
+ module.exit_json(**results)
+
+If the module was invoked with verbosity lower than 3, then the output will only contain
+the variable ``abc``. If running at higher verbosity, as in ``ansible-playbook -vvv``,
+then the output will also contain ``debug_x``.
+
+Generating facts is very similar to regular output, but variables are not marked as facts by default.
+
+.. code-block:: python
+
+ vars.set("modulefact", x + 4, fact=True)
+ vars.set("debugfact", x, fact=True, verbosity=3)
+
+ results = vars.output(module._verbosity)
+ results["ansible_facts"] = {"module_name": vars.facts(module._verbosity)}
+ module.exit_json(**results)
+
+Handling change
+"""""""""""""""
+
+You can use ``VarDict`` to determine whether variables have had their values changed.
+
+.. code-block:: python
+
+ vars.set("abc", 42, change=True)
+ vars.abc = 90
+
+ results = vars.output()
+ results["changed"] = vars.has_changed
+ module.exit_json(**results)
+
+If tracking changes in variables, you may want to present the difference between the initial and the final
+values of it. For that, you want to use:
+
+.. code-block:: python
+
+ vars.set("abc", 42, change=True, diff=True)
+ vars.abc = 90
+
+ results = vars.output()
+ results["changed"] = vars.has_changed
+ results["diff"] = vars.diff()
+ module.exit_json(**results)
+
+.. versionadded:: 7.1.0
diff --git a/ansible_collections/community/general/meta/runtime.yml b/ansible_collections/community/general/meta/runtime.yml
index edeb53005..4f5007b4a 100644
--- a/ansible_collections/community/general/meta/runtime.yml
+++ b/ansible_collections/community/general/meta/runtime.yml
@@ -6,6 +6,8 @@
requires_ansible: '>=2.13.0'
action_groups:
consul:
+ - consul_agent_check
+ - consul_agent_service
- consul_auth_method
- consul_binding_rule
- consul_policy
diff --git a/ansible_collections/community/general/plugins/callback/opentelemetry.py b/ansible_collections/community/general/plugins/callback/opentelemetry.py
index 58cfa057b..c6e8a87c1 100644
--- a/ansible_collections/community/general/plugins/callback/opentelemetry.py
+++ b/ansible_collections/community/general/plugins/callback/opentelemetry.py
@@ -556,11 +556,19 @@ class CallbackModule(CallbackBase):
self.otel_exporter_otlp_traces_protocol = self.get_option('otel_exporter_otlp_traces_protocol')
- def dump_results(self, result):
+ def dump_results(self, task, result):
""" dump the results if disable_logs is not enabled """
if self.disable_logs:
return ""
- return self._dump_results(result._result)
+ # ansible.builtin.uri contains the response in the json field
+ save = dict(result._result)
+
+ if "json" in save and task.action in ("ansible.builtin.uri", "ansible.legacy.uri", "uri"):
+ save.pop("json")
+ # ansible.builtin.slurp contains the response in the content field
+ if "content" in save and task.action in ("ansible.builtin.slurp", "ansible.legacy.slurp", "slurp"):
+ save.pop("content")
+ return self._dump_results(save)
def v2_playbook_on_start(self, playbook):
self.ansible_playbook = basename(playbook._file_name)
@@ -611,7 +619,7 @@ class CallbackModule(CallbackBase):
self.tasks_data,
status,
result,
- self.dump_results(result)
+ self.dump_results(self.tasks_data[result._task._uuid], result)
)
def v2_runner_on_ok(self, result):
@@ -619,7 +627,7 @@ class CallbackModule(CallbackBase):
self.tasks_data,
'ok',
result,
- self.dump_results(result)
+ self.dump_results(self.tasks_data[result._task._uuid], result)
)
def v2_runner_on_skipped(self, result):
@@ -627,7 +635,7 @@ class CallbackModule(CallbackBase):
self.tasks_data,
'skipped',
result,
- self.dump_results(result)
+ self.dump_results(self.tasks_data[result._task._uuid], result)
)
def v2_playbook_on_include(self, included_file):
diff --git a/ansible_collections/community/general/plugins/doc_fragments/django.py b/ansible_collections/community/general/plugins/doc_fragments/django.py
index d92799937..f89ec9144 100644
--- a/ansible_collections/community/general/plugins/doc_fragments/django.py
+++ b/ansible_collections/community/general/plugins/doc_fragments/django.py
@@ -51,3 +51,12 @@ seealso:
Please make sure that you select the right version of Django in the version selector on that page.
link: https://docs.djangoproject.com/en/5.0/ref/django-admin/
'''
+
+ DATABASE = r'''
+options:
+ database:
+ description:
+ - Specify the database to be used.
+ type: str
+ default: default
+'''
diff --git a/ansible_collections/community/general/plugins/doc_fragments/proxmox.py b/ansible_collections/community/general/plugins/doc_fragments/proxmox.py
index cb533fefa..239dba06d 100644
--- a/ansible_collections/community/general/plugins/doc_fragments/proxmox.py
+++ b/ansible_collections/community/general/plugins/doc_fragments/proxmox.py
@@ -16,6 +16,13 @@ options:
- Specify the target host of the Proxmox VE cluster.
type: str
required: true
+ api_port:
+ description:
+ - Specify the target port of the Proxmox VE cluster.
+ - Uses the E(PROXMOX_PORT) environment variable if not specified.
+ type: int
+ required: false
+ version_added: 9.1.0
api_user:
description:
- Specify the user to authenticate with.
diff --git a/ansible_collections/community/general/plugins/filter/keep_keys.py b/ansible_collections/community/general/plugins/filter/keep_keys.py
new file mode 100644
index 000000000..dffccba35
--- /dev/null
+++ b/ansible_collections/community/general/plugins/filter/keep_keys.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com>
+# Copyright (c) 2024 Felix Fontein <felix@fontein.de>
+# 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 = '''
+ name: keep_keys
+ short_description: Keep specific keys from dictionaries in a list
+ version_added: "9.1.0"
+ author:
+ - Vladimir Botka (@vbotka)
+ - Felix Fontein (@felixfontein)
+ description: This filter keeps only specified keys from a provided list of dictionaries.
+ options:
+ _input:
+ description:
+ - A list of dictionaries.
+ - Top level keys must be strings.
+ type: list
+ elements: dictionary
+ required: true
+ target:
+ description:
+ - A single key or key pattern to keep, or a list of keys or keys patterns to keep.
+ - If O(matching_parameter=regex) there must be exactly one pattern provided.
+ type: raw
+ required: true
+ matching_parameter:
+ description: Specify the matching option of target keys.
+ type: str
+ default: equal
+ choices:
+ equal: Matches keys of exactly one of the O(target) items.
+ starts_with: Matches keys that start with one of the O(target) items.
+ ends_with: Matches keys that end with one of the O(target) items.
+ regex:
+ - Matches keys that match the regular expresion provided in O(target).
+ - In this case, O(target) must be a regex string or a list with single regex string.
+'''
+
+EXAMPLES = '''
+ l:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+ # 1) By default match keys that equal any of the items in the target.
+ t: [k0_x0, k1_x1]
+ r: "{{ l | community.general.keep_keys(target=t) }}"
+
+ # 2) Match keys that start with any of the items in the target.
+ t: [k0, k1]
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='starts_with') }}"
+
+ # 3) Match keys that end with any of the items in target.
+ t: [x0, x1]
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='ends_with') }}"
+
+ # 4) Match keys by the regex.
+ t: ['^.*[01]_x.*$']
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='regex') }}"
+
+ # 5) Match keys by the regex.
+ t: '^.*[01]_x.*$'
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='regex') }}"
+
+ # The results of above examples 1-5 are all the same.
+ r:
+ - {k0_x0: A0, k1_x1: B0}
+ - {k0_x0: A1, k1_x1: B1}
+
+ # 6) By default match keys that equal the target.
+ t: k0_x0
+ r: "{{ l | community.general.keep_keys(target=t) }}"
+
+ # 7) Match keys that start with the target.
+ t: k0
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='starts_with') }}"
+
+ # 8) Match keys that end with the target.
+ t: x0
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='ends_with') }}"
+
+ # 9) Match keys by the regex.
+ t: '^.*0_x.*$'
+ r: "{{ l | community.general.keep_keys(target=t, matching_parameter='regex') }}"
+
+ # The results of above examples 6-9 are all the same.
+ r:
+ - {k0_x0: A0}
+ - {k0_x0: A1}
+'''
+
+RETURN = '''
+ _value:
+ description: The list of dictionaries with selected keys.
+ type: list
+ elements: dictionary
+'''
+
+from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
+ _keys_filter_params,
+ _keys_filter_target_str)
+
+
+def keep_keys(data, target=None, matching_parameter='equal'):
+ """keep specific keys from dictionaries in a list"""
+
+ # test parameters
+ _keys_filter_params(data, matching_parameter)
+ # test and transform target
+ tt = _keys_filter_target_str(target, matching_parameter)
+
+ if matching_parameter == 'equal':
+ def keep_key(key):
+ return key in tt
+ elif matching_parameter == 'starts_with':
+ def keep_key(key):
+ return key.startswith(tt)
+ elif matching_parameter == 'ends_with':
+ def keep_key(key):
+ return key.endswith(tt)
+ elif matching_parameter == 'regex':
+ def keep_key(key):
+ return tt.match(key) is not None
+
+ return [dict((k, v) for k, v in d.items() if keep_key(k)) for d in data]
+
+
+class FilterModule(object):
+
+ def filters(self):
+ return {
+ 'keep_keys': keep_keys,
+ }
diff --git a/ansible_collections/community/general/plugins/filter/lists_mergeby.py b/ansible_collections/community/general/plugins/filter/lists_mergeby.py
index caf183492..0e47d5017 100644
--- a/ansible_collections/community/general/plugins/filter/lists_mergeby.py
+++ b/ansible_collections/community/general/plugins/filter/lists_mergeby.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2020-2022, Vladimir Botka <vbotka@gmail.com>
+# Copyright (c) 2020-2024, Vladimir Botka <vbotka@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
@@ -12,22 +12,32 @@ DOCUMENTATION = '''
version_added: 2.0.0
author: Vladimir Botka (@vbotka)
description:
- - Merge two or more lists by attribute O(index). Optional parameters O(recursive) and O(list_merge)
- control the merging of the lists in values. The function merge_hash from ansible.utils.vars
- is used. To learn details on how to use the parameters O(recursive) and O(list_merge) see
- Ansible User's Guide chapter "Using filters to manipulate data" section "Combining
- hashes/dictionaries".
+ - Merge two or more lists by attribute O(index). Optional
+ parameters O(recursive) and O(list_merge) control the merging of
+ the nested dictionaries and lists.
+ - The function C(merge_hash) from C(ansible.utils.vars) is used.
+ - To learn details on how to use the parameters O(recursive) and
+ O(list_merge) see Ansible User's Guide chapter "Using filters to
+ manipulate data" section R(Combining hashes/dictionaries, combine_filter) or the
+ filter P(ansible.builtin.combine#filter).
+
positional: another_list, index
options:
_input:
- description: A list of dictionaries.
+ description:
+ - A list of dictionaries, or a list of lists of dictionaries.
+ - The required type of the C(elements) is set to C(raw)
+ because all elements of O(_input) can be either dictionaries
+ or lists.
type: list
- elements: dictionary
+ elements: raw
required: true
another_list:
- description: Another list of dictionaries. This parameter can be specified multiple times.
+ description:
+ - Another list of dictionaries, or a list of lists of dictionaries.
+ - This parameter can be specified multiple times.
type: list
- elements: dictionary
+ elements: raw
index:
description:
- The dictionary key that must be present in every dictionary in every list that is used to
@@ -55,40 +65,134 @@ DOCUMENTATION = '''
'''
EXAMPLES = '''
-- name: Merge two lists
+# Some results below are manually formatted for better readability. The
+# dictionaries' keys will be sorted alphabetically in real output.
+
+- name: Example 1. Merge two lists. The results r1 and r2 are the same.
+ ansible.builtin.debug:
+ msg: |
+ r1: {{ r1 }}
+ r2: {{ r2 }}
+ vars:
+ list1:
+ - {index: a, value: 123}
+ - {index: b, value: 4}
+ list2:
+ - {index: a, foo: bar}
+ - {index: c, foo: baz}
+ r1: "{{ list1 | community.general.lists_mergeby(list2, 'index') }}"
+ r2: "{{ [list1, list2] | community.general.lists_mergeby('index') }}"
+
+# r1:
+# - {index: a, foo: bar, value: 123}
+# - {index: b, value: 4}
+# - {index: c, foo: baz}
+# r2:
+# - {index: a, foo: bar, value: 123}
+# - {index: b, value: 4}
+# - {index: c, foo: baz}
+
+- name: Example 2. Merge three lists
+ ansible.builtin.debug:
+ var: r
+ vars:
+ list1:
+ - {index: a, value: 123}
+ - {index: b, value: 4}
+ list2:
+ - {index: a, foo: bar}
+ - {index: c, foo: baz}
+ list3:
+ - {index: d, foo: qux}
+ r: "{{ [list1, list2, list3] | community.general.lists_mergeby('index') }}"
+
+# r:
+# - {index: a, foo: bar, value: 123}
+# - {index: b, value: 4}
+# - {index: c, foo: baz}
+# - {index: d, foo: qux}
+
+- name: Example 3. Merge single list. The result is the same as 2.
+ ansible.builtin.debug:
+ var: r
+ vars:
+ list1:
+ - {index: a, value: 123}
+ - {index: b, value: 4}
+ - {index: a, foo: bar}
+ - {index: c, foo: baz}
+ - {index: d, foo: qux}
+ r: "{{ [list1, []] | community.general.lists_mergeby('index') }}"
+
+# r:
+# - {index: a, foo: bar, value: 123}
+# - {index: b, value: 4}
+# - {index: c, foo: baz}
+# - {index: d, foo: qux}
+
+- name: Example 4. Merge two lists. By default, replace nested lists.
+ ansible.builtin.debug:
+ var: r
+ vars:
+ list1:
+ - {index: a, foo: [X1, X2]}
+ - {index: b, foo: [X1, X2]}
+ list2:
+ - {index: a, foo: [Y1, Y2]}
+ - {index: b, foo: [Y1, Y2]}
+ r: "{{ [list1, list2] | community.general.lists_mergeby('index') }}"
+
+# r:
+# - {index: a, foo: [Y1, Y2]}
+# - {index: b, foo: [Y1, Y2]}
+
+- name: Example 5. Merge two lists. Append nested lists.
+ ansible.builtin.debug:
+ var: r
+ vars:
+ list1:
+ - {index: a, foo: [X1, X2]}
+ - {index: b, foo: [X1, X2]}
+ list2:
+ - {index: a, foo: [Y1, Y2]}
+ - {index: b, foo: [Y1, Y2]}
+ r: "{{ [list1, list2] | community.general.lists_mergeby('index', list_merge='append') }}"
+
+# r:
+# - {index: a, foo: [X1, X2, Y1, Y2]}
+# - {index: b, foo: [X1, X2, Y1, Y2]}
+
+- name: Example 6. Merge two lists. By default, do not merge nested dictionaries.
+ ansible.builtin.debug:
+ var: r
+ vars:
+ list1:
+ - {index: a, foo: {x: 1, y: 2}}
+ - {index: b, foo: [X1, X2]}
+ list2:
+ - {index: a, foo: {y: 3, z: 4}}
+ - {index: b, foo: [Y1, Y2]}
+ r: "{{ [list1, list2] | community.general.lists_mergeby('index') }}"
+
+# r:
+# - {index: a, foo: {y: 3, z: 4}}
+# - {index: b, foo: [Y1, Y2]}
+
+- name: Example 7. Merge two lists. Merge nested dictionaries too.
ansible.builtin.debug:
- msg: >-
- {{ list1 | community.general.lists_mergeby(
- list2,
- 'index',
- recursive=True,
- list_merge='append'
- ) }}"
+ var: r
vars:
list1:
- - index: a
- value: 123
- - index: b
- value: 42
+ - {index: a, foo: {x: 1, y: 2}}
+ - {index: b, foo: [X1, X2]}
list2:
- - index: a
- foo: bar
- - index: c
- foo: baz
- # Produces the following list of dictionaries:
- # {
- # "index": "a",
- # "foo": "bar",
- # "value": 123
- # },
- # {
- # "index": "b",
- # "value": 42
- # },
- # {
- # "index": "c",
- # "foo": "baz"
- # }
+ - {index: a, foo: {y: 3, z: 4}}
+ - {index: b, foo: [Y1, Y2]}
+ r: "{{ [list1, list2] | community.general.lists_mergeby('index', recursive=true) }}"
+
+# r:
+# - {index: a, foo: {x:1, y: 3, z: 4}}
+# - {index: b, foo: [Y1, Y2]}
'''
RETURN = '''
@@ -108,13 +212,14 @@ from operator import itemgetter
def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
- ''' Merge 2 lists by attribute 'index'. The function merge_hash from ansible.utils.vars is used.
- This function is used by the function lists_mergeby.
+ '''Merge 2 lists by attribute 'index'. The function 'merge_hash'
+ from ansible.utils.vars is used. This function is used by the
+ function lists_mergeby.
'''
d = defaultdict(dict)
- for l in (x, y):
- for elem in l:
+ for lst in (x, y):
+ for elem in lst:
if not isinstance(elem, Mapping):
msg = "Elements of list arguments for lists_mergeby must be dictionaries. %s is %s"
raise AnsibleFilterError(msg % (elem, type(elem)))
@@ -124,20 +229,9 @@ def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
def lists_mergeby(*terms, **kwargs):
- ''' Merge 2 or more lists by attribute 'index'. Optional parameters 'recursive' and 'list_merge'
- control the merging of the lists in values. The function merge_hash from ansible.utils.vars
- is used. To learn details on how to use the parameters 'recursive' and 'list_merge' see
- Ansible User's Guide chapter "Using filters to manipulate data" section "Combining
- hashes/dictionaries".
-
- Example:
- - debug:
- msg: "{{ list1|
- community.general.lists_mergeby(list2,
- 'index',
- recursive=True,
- list_merge='append')|
- list }}"
+ '''Merge 2 or more lists by attribute 'index'. To learn details
+ on how to use the parameters 'recursive' and 'list_merge' see
+ the filter ansible.builtin.combine.
'''
recursive = kwargs.pop('recursive', False)
@@ -155,7 +249,7 @@ def lists_mergeby(*terms, **kwargs):
"must be lists. %s is %s")
raise AnsibleFilterError(msg % (sublist, type(sublist)))
if len(sublist) > 0:
- if all(isinstance(l, Sequence) for l in sublist):
+ if all(isinstance(lst, Sequence) for lst in sublist):
for item in sublist:
flat_list.append(item)
else:
diff --git a/ansible_collections/community/general/plugins/filter/remove_keys.py b/ansible_collections/community/general/plugins/filter/remove_keys.py
new file mode 100644
index 000000000..cabce1468
--- /dev/null
+++ b/ansible_collections/community/general/plugins/filter/remove_keys.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com>
+# Copyright (c) 2024 Felix Fontein <felix@fontein.de>
+# 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 = '''
+ name: remove_keys
+ short_description: Remove specific keys from dictionaries in a list
+ version_added: "9.1.0"
+ author:
+ - Vladimir Botka (@vbotka)
+ - Felix Fontein (@felixfontein)
+ description: This filter removes only specified keys from a provided list of dictionaries.
+ options:
+ _input:
+ description:
+ - A list of dictionaries.
+ - Top level keys must be strings.
+ type: list
+ elements: dictionary
+ required: true
+ target:
+ description:
+ - A single key or key pattern to remove, or a list of keys or keys patterns to remove.
+ - If O(matching_parameter=regex) there must be exactly one pattern provided.
+ type: raw
+ required: true
+ matching_parameter:
+ description: Specify the matching option of target keys.
+ type: str
+ default: equal
+ choices:
+ equal: Matches keys of exactly one of the O(target) items.
+ starts_with: Matches keys that start with one of the O(target) items.
+ ends_with: Matches keys that end with one of the O(target) items.
+ regex:
+ - Matches keys that match the regular expresion provided in O(target).
+ - In this case, O(target) must be a regex string or a list with single regex string.
+'''
+
+EXAMPLES = '''
+ l:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+ # 1) By default match keys that equal any of the items in the target.
+ t: [k0_x0, k1_x1]
+ r: "{{ l | community.general.remove_keys(target=t) }}"
+
+ # 2) Match keys that start with any of the items in the target.
+ t: [k0, k1]
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='starts_with') }}"
+
+ # 3) Match keys that end with any of the items in target.
+ t: [x0, x1]
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='ends_with') }}"
+
+ # 4) Match keys by the regex.
+ t: ['^.*[01]_x.*$']
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='regex') }}"
+
+ # 5) Match keys by the regex.
+ t: '^.*[01]_x.*$'
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='regex') }}"
+
+ # The results of above examples 1-5 are all the same.
+ r:
+ - {k2_x2: [C0], k3_x3: foo}
+ - {k2_x2: [C1], k3_x3: bar}
+
+ # 6) By default match keys that equal the target.
+ t: k0_x0
+ r: "{{ l | community.general.remove_keys(target=t) }}"
+
+ # 7) Match keys that start with the target.
+ t: k0
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='starts_with') }}"
+
+ # 8) Match keys that end with the target.
+ t: x0
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='ends_with') }}"
+
+ # 9) Match keys by the regex.
+ t: '^.*0_x.*$'
+ r: "{{ l | community.general.remove_keys(target=t, matching_parameter='regex') }}"
+
+ # The results of above examples 6-9 are all the same.
+ r:
+ - {k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+'''
+
+RETURN = '''
+ _value:
+ description: The list of dictionaries with selected keys removed.
+ type: list
+ elements: dictionary
+'''
+
+from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
+ _keys_filter_params,
+ _keys_filter_target_str)
+
+
+def remove_keys(data, target=None, matching_parameter='equal'):
+ """remove specific keys from dictionaries in a list"""
+
+ # test parameters
+ _keys_filter_params(data, matching_parameter)
+ # test and transform target
+ tt = _keys_filter_target_str(target, matching_parameter)
+
+ if matching_parameter == 'equal':
+ def keep_key(key):
+ return key not in tt
+ elif matching_parameter == 'starts_with':
+ def keep_key(key):
+ return not key.startswith(tt)
+ elif matching_parameter == 'ends_with':
+ def keep_key(key):
+ return not key.endswith(tt)
+ elif matching_parameter == 'regex':
+ def keep_key(key):
+ return tt.match(key) is None
+
+ return [dict((k, v) for k, v in d.items() if keep_key(k)) for d in data]
+
+
+class FilterModule(object):
+
+ def filters(self):
+ return {
+ 'remove_keys': remove_keys,
+ }
diff --git a/ansible_collections/community/general/plugins/filter/replace_keys.py b/ansible_collections/community/general/plugins/filter/replace_keys.py
new file mode 100644
index 000000000..d3b12c05d
--- /dev/null
+++ b/ansible_collections/community/general/plugins/filter/replace_keys.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com>
+# Copyright (c) 2024 Felix Fontein <felix@fontein.de>
+# 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 = '''
+ name: replace_keys
+ short_description: Replace specific keys in a list of dictionaries
+ version_added: "9.1.0"
+ author:
+ - Vladimir Botka (@vbotka)
+ - Felix Fontein (@felixfontein)
+ description: This filter replaces specified keys in a provided list of dictionaries.
+ options:
+ _input:
+ description:
+ - A list of dictionaries.
+ - Top level keys must be strings.
+ type: list
+ elements: dictionary
+ required: true
+ target:
+ description:
+ - A list of dictionaries with attributes C(before) and C(after).
+ - The value of O(target[].after) replaces key matching O(target[].before).
+ type: list
+ elements: dictionary
+ required: true
+ suboptions:
+ before:
+ description:
+ - A key or key pattern to change.
+ - The interpretation of O(target[].before) depends on O(matching_parameter).
+ - For a key that matches multiple O(target[].before)s, the B(first) matching O(target[].after) will be used.
+ type: str
+ after:
+ description: A matching key change to.
+ type: str
+ matching_parameter:
+ description: Specify the matching option of target keys.
+ type: str
+ default: equal
+ choices:
+ equal: Matches keys of exactly one of the O(target[].before) items.
+ starts_with: Matches keys that start with one of the O(target[].before) items.
+ ends_with: Matches keys that end with one of the O(target[].before) items.
+ regex: Matches keys that match one of the regular expressions provided in O(target[].before).
+'''
+
+EXAMPLES = '''
+ l:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+ # 1) By default, replace keys that are equal any of the attributes before.
+ t:
+ - {before: k0_x0, after: a0}
+ - {before: k1_x1, after: a1}
+ r: "{{ l | community.general.replace_keys(target=t) }}"
+
+ # 2) Replace keys that starts with any of the attributes before.
+ t:
+ - {before: k0, after: a0}
+ - {before: k1, after: a1}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='starts_with') }}"
+
+ # 3) Replace keys that ends with any of the attributes before.
+ t:
+ - {before: x0, after: a0}
+ - {before: x1, after: a1}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='ends_with') }}"
+
+ # 4) Replace keys that match any regex of the attributes before.
+ t:
+ - {before: "^.*0_x.*$", after: a0}
+ - {before: "^.*1_x.*$", after: a1}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='regex') }}"
+
+ # The results of above examples 1-4 are all the same.
+ r:
+ - {a0: A0, a1: B0, k2_x2: [C0], k3_x3: foo}
+ - {a0: A1, a1: B1, k2_x2: [C1], k3_x3: bar}
+
+ # 5) If more keys match the same attribute before the last one will be used.
+ t:
+ - {before: "^.*_x.*$", after: X}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='regex') }}"
+
+ # gives
+
+ r:
+ - X: foo
+ - X: bar
+
+ # 6) If there are items with equal attribute before the first one will be used.
+ t:
+ - {before: "^.*_x.*$", after: X}
+ - {before: "^.*_x.*$", after: Y}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='regex') }}"
+
+ # gives
+
+ r:
+ - X: foo
+ - X: bar
+
+ # 7) If there are more matches for a key the first one will be used.
+ l:
+ - {aaa1: A, bbb1: B, ccc1: C}
+ - {aaa2: D, bbb2: E, ccc2: F}
+ t:
+ - {before: a, after: X}
+ - {before: aa, after: Y}
+ r: "{{ l | community.general.replace_keys(target=t, matching_parameter='starts_with') }}"
+
+ # gives
+
+ r:
+ - {X: A, bbb1: B, ccc1: C}
+ - {X: D, bbb2: E, ccc2: F}
+'''
+
+RETURN = '''
+ _value:
+ description: The list of dictionaries with replaced keys.
+ type: list
+ elements: dictionary
+'''
+
+from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
+ _keys_filter_params,
+ _keys_filter_target_dict)
+
+
+def replace_keys(data, target=None, matching_parameter='equal'):
+ """replace specific keys in a list of dictionaries"""
+
+ # test parameters
+ _keys_filter_params(data, matching_parameter)
+ # test and transform target
+ tz = _keys_filter_target_dict(target, matching_parameter)
+
+ if matching_parameter == 'equal':
+ def replace_key(key):
+ for b, a in tz:
+ if key == b:
+ return a
+ return key
+ elif matching_parameter == 'starts_with':
+ def replace_key(key):
+ for b, a in tz:
+ if key.startswith(b):
+ return a
+ return key
+ elif matching_parameter == 'ends_with':
+ def replace_key(key):
+ for b, a in tz:
+ if key.endswith(b):
+ return a
+ return key
+ elif matching_parameter == 'regex':
+ def replace_key(key):
+ for b, a in tz:
+ if b.match(key):
+ return a
+ return key
+
+ return [dict((replace_key(k), v) for k, v in d.items()) for d in data]
+
+
+class FilterModule(object):
+
+ def filters(self):
+ return {
+ 'replace_keys': replace_keys,
+ }
diff --git a/ansible_collections/community/general/plugins/inventory/opennebula.py b/ansible_collections/community/general/plugins/inventory/opennebula.py
index b097307c3..bf81758ef 100644
--- a/ansible_collections/community/general/plugins/inventory/opennebula.py
+++ b/ansible_collections/community/general/plugins/inventory/opennebula.py
@@ -143,7 +143,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
nic = [nic]
for net in nic:
- return net['IP']
+ if net.get('IP'):
+ return net['IP']
return False
diff --git a/ansible_collections/community/general/plugins/module_utils/cmd_runner.py b/ansible_collections/community/general/plugins/module_utils/cmd_runner.py
index 2bf2b32e8..da4f1b6fc 100644
--- a/ansible_collections/community/general/plugins/module_utils/cmd_runner.py
+++ b/ansible_collections/community/general/plugins/module_utils/cmd_runner.py
@@ -89,18 +89,31 @@ class FormatError(CmdRunnerException):
class _ArgFormat(object):
+ # DEPRECATION: set default value for ignore_none to True in community.general 12.0.0
def __init__(self, func, ignore_none=None, ignore_missing_value=False):
self.func = func
self.ignore_none = ignore_none
self.ignore_missing_value = ignore_missing_value
- def __call__(self, value, ctx_ignore_none):
+ # DEPRECATION: remove parameter ctx_ignore_none in community.general 12.0.0
+ def __call__(self, value, ctx_ignore_none=True):
+ # DEPRECATION: replace ctx_ignore_none with True in community.general 12.0.0
ignore_none = self.ignore_none if self.ignore_none is not None else ctx_ignore_none
if value is None and ignore_none:
return []
f = self.func
return [str(x) for x in f(value)]
+ def __str__(self):
+ return "<ArgFormat: func={0}, ignore_none={1}, ignore_missing_value={2}>".format(
+ self.func,
+ self.ignore_none,
+ self.ignore_missing_value,
+ )
+
+ def __repr__(self):
+ return str(self)
+
class _Format(object):
@staticmethod
@@ -114,7 +127,7 @@ class _Format(object):
@staticmethod
def as_bool_not(args):
- return _ArgFormat(lambda value: [] if value else _ensure_list(args), ignore_none=False)
+ return _Format.as_bool([], args, ignore_none=False)
@staticmethod
def as_optval(arg, ignore_none=None):
@@ -184,6 +197,19 @@ class _Format(object):
return func(**v)
return wrapper
+ @staticmethod
+ def stack(fmt):
+ @wraps(fmt)
+ def wrapper(*args, **kwargs):
+ new_func = fmt(ignore_none=True, *args, **kwargs)
+
+ def stacking(value):
+ stack = [new_func(v) for v in value if v]
+ stack = [x for args in stack for x in args]
+ return stack
+ return _ArgFormat(stacking, ignore_none=True)
+ return wrapper
+
class CmdRunner(object):
"""
@@ -204,7 +230,11 @@ class CmdRunner(object):
self.default_args_order = self._prepare_args_order(default_args_order)
if arg_formats is None:
arg_formats = {}
- self.arg_formats = dict(arg_formats)
+ self.arg_formats = {}
+ for fmt_name, fmt in arg_formats.items():
+ if not isinstance(fmt, _ArgFormat):
+ fmt = _Format.as_func(func=fmt, ignore_none=True)
+ self.arg_formats[fmt_name] = fmt
self.check_rc = check_rc
self.force_lang = force_lang
self.path_prefix = path_prefix
@@ -223,7 +253,16 @@ class CmdRunner(object):
def binary(self):
return self.command[0]
- def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs):
+ # remove parameter ignore_value_none in community.general 12.0.0
+ def __call__(self, args_order=None, output_process=None, ignore_value_none=None, check_mode_skip=False, check_mode_return=None, **kwargs):
+ if ignore_value_none is None:
+ ignore_value_none = True
+ else:
+ self.module.deprecate(
+ "Using ignore_value_none when creating the runner context is now deprecated, "
+ "and the parameter will be removed in community.general 12.0.0. ",
+ version="12.0.0", collection_name="community.general"
+ )
if output_process is None:
output_process = _process_as_is
if args_order is None:
@@ -235,7 +274,7 @@ class CmdRunner(object):
return _CmdRunnerContext(runner=self,
args_order=args_order,
output_process=output_process,
- ignore_value_none=ignore_value_none,
+ ignore_value_none=ignore_value_none, # DEPRECATION: remove in community.general 12.0.0
check_mode_skip=check_mode_skip,
check_mode_return=check_mode_return, **kwargs)
@@ -251,6 +290,7 @@ class _CmdRunnerContext(object):
self.runner = runner
self.args_order = tuple(args_order)
self.output_process = output_process
+ # DEPRECATION: parameter ignore_value_none at the context level is deprecated and will be removed in community.general 12.0.0
self.ignore_value_none = ignore_value_none
self.check_mode_skip = check_mode_skip
self.check_mode_return = check_mode_return
@@ -290,6 +330,7 @@ class _CmdRunnerContext(object):
value = named_args[arg_name]
elif not runner.arg_formats[arg_name].ignore_missing_value:
raise MissingArgumentValue(self.args_order, arg_name)
+ # DEPRECATION: remove parameter ctx_ignore_none in 12.0.0
self.cmd.extend(runner.arg_formats[arg_name](value, ctx_ignore_none=self.ignore_value_none))
except MissingArgumentValue:
raise
@@ -306,7 +347,7 @@ class _CmdRunnerContext(object):
@property
def run_info(self):
return dict(
- ignore_value_none=self.ignore_value_none,
+ ignore_value_none=self.ignore_value_none, # DEPRECATION: remove in community.general 12.0.0
check_rc=self.check_rc,
environ_update=self.environ_update,
args_order=self.args_order,
diff --git a/ansible_collections/community/general/plugins/module_utils/consul.py b/ansible_collections/community/general/plugins/module_utils/consul.py
index 68c1a130b..cd54a105f 100644
--- a/ansible_collections/community/general/plugins/module_utils/consul.py
+++ b/ansible_collections/community/general/plugins/module_utils/consul.py
@@ -10,6 +10,7 @@ __metaclass__ = type
import copy
import json
+import re
from ansible.module_utils.six.moves.urllib import error as urllib_error
from ansible.module_utils.six.moves.urllib.parse import urlencode
@@ -68,6 +69,25 @@ def camel_case_key(key):
return "".join(parts)
+def validate_check(check):
+ validate_duration_keys = ['Interval', 'Ttl', 'Timeout']
+ validate_tcp_regex = r"(?P<host>.*):(?P<port>(?:[0-9]+))$"
+ if check.get('Tcp') is not None:
+ match = re.match(validate_tcp_regex, check['Tcp'])
+ if not match:
+ raise Exception('tcp check must be in host:port format')
+ for duration in validate_duration_keys:
+ if duration in check and check[duration] is not None:
+ check[duration] = validate_duration(check[duration])
+
+
+def validate_duration(duration):
+ if duration:
+ if not re.search(r"\d+(?:ns|us|ms|s|m|h)", duration):
+ duration = "{0}s".format(duration)
+ return duration
+
+
STATE_PARAMETER = "state"
STATE_PRESENT = "present"
STATE_ABSENT = "absent"
@@ -81,7 +101,7 @@ OPERATION_DELETE = "remove"
def _normalize_params(params, arg_spec):
final_params = {}
for k, v in params.items():
- if k not in arg_spec: # Alias
+ if k not in arg_spec or v is None: # Alias
continue
spec = arg_spec[k]
if (
@@ -105,9 +125,10 @@ class _ConsulModule:
"""
api_endpoint = None # type: str
- unique_identifier = None # type: str
+ unique_identifiers = None # type: list
result_key = None # type: str
create_only_fields = set()
+ operational_attributes = set()
params = {}
def __init__(self, module):
@@ -119,6 +140,8 @@ class _ConsulModule:
if k not in STATE_PARAMETER and k not in AUTH_ARGUMENTS_SPEC
}
+ self.operational_attributes.update({"CreateIndex", "CreateTime", "Hash", "ModifyIndex"})
+
def execute(self):
obj = self.read_object()
@@ -203,14 +226,24 @@ class _ConsulModule:
return False
def prepare_object(self, existing, obj):
- operational_attributes = {"CreateIndex", "CreateTime", "Hash", "ModifyIndex"}
existing = {
- k: v for k, v in existing.items() if k not in operational_attributes
+ k: v for k, v in existing.items() if k not in self.operational_attributes
}
for k, v in obj.items():
existing[k] = v
return existing
+ def id_from_obj(self, obj, camel_case=False):
+ def key_func(key):
+ return camel_case_key(key) if camel_case else key
+
+ if self.unique_identifiers:
+ for identifier in self.unique_identifiers:
+ identifier = key_func(identifier)
+ if identifier in obj:
+ return obj[identifier]
+ return None
+
def endpoint_url(self, operation, identifier=None):
if operation == OPERATION_CREATE:
return self.api_endpoint
@@ -219,7 +252,8 @@ class _ConsulModule:
raise RuntimeError("invalid arguments passed")
def read_object(self):
- url = self.endpoint_url(OPERATION_READ, self.params.get(self.unique_identifier))
+ identifier = self.id_from_obj(self.params)
+ url = self.endpoint_url(OPERATION_READ, identifier)
try:
return self.get(url)
except RequestError as e:
@@ -233,25 +267,28 @@ class _ConsulModule:
if self._module.check_mode:
return obj
else:
- return self.put(self.api_endpoint, data=self.prepare_object({}, obj))
+ url = self.endpoint_url(OPERATION_CREATE)
+ created_obj = self.put(url, data=self.prepare_object({}, obj))
+ if created_obj is None:
+ created_obj = self.read_object()
+ return created_obj
def update_object(self, existing, obj):
- url = self.endpoint_url(
- OPERATION_UPDATE, existing.get(camel_case_key(self.unique_identifier))
- )
merged_object = self.prepare_object(existing, obj)
if self._module.check_mode:
return merged_object
else:
- return self.put(url, data=merged_object)
+ url = self.endpoint_url(OPERATION_UPDATE, self.id_from_obj(existing, camel_case=True))
+ updated_obj = self.put(url, data=merged_object)
+ if updated_obj is None:
+ updated_obj = self.read_object()
+ return updated_obj
def delete_object(self, obj):
if self._module.check_mode:
return {}
else:
- url = self.endpoint_url(
- OPERATION_DELETE, obj.get(camel_case_key(self.unique_identifier))
- )
+ url = self.endpoint_url(OPERATION_DELETE, self.id_from_obj(obj, camel_case=True))
return self.delete(url)
def _request(self, method, url_parts, data=None, params=None):
@@ -309,7 +346,9 @@ class _ConsulModule:
if 400 <= status < 600:
raise RequestError(status, response_data)
- return json.loads(response_data)
+ if response_data:
+ return json.loads(response_data)
+ return None
def get(self, url_parts, **kwargs):
return self._request("GET", url_parts, **kwargs)
diff --git a/ansible_collections/community/general/plugins/module_utils/django.py b/ansible_collections/community/general/plugins/module_utils/django.py
index fbaf840db..5fb375c6f 100644
--- a/ansible_collections/community/general/plugins/module_utils/django.py
+++ b/ansible_collections/community/general/plugins/module_utils/django.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
+from ansible.module_utils.common.dict_transformations import dict_merge
from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt
from ansible_collections.community.general.plugins.module_utils.python_runner import PythonRunner
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
@@ -33,6 +34,18 @@ _django_std_arg_fmts = dict(
skip_checks=cmd_runner_fmt.as_bool("--skip-checks"),
)
+_django_database_args = dict(
+ database=dict(type="str", default="default"),
+)
+
+_args_menu = dict(
+ std=(django_std_args, _django_std_arg_fmts),
+ database=(_django_database_args, {"database": cmd_runner_fmt.as_opt_eq_val("--database")}),
+ noinput=({}, {"noinput": cmd_runner_fmt.as_fixed("--noinput")}),
+ dry_run=({}, {"dry_run": cmd_runner_fmt.as_bool("--dry-run")}),
+ check=({}, {"check": cmd_runner_fmt.as_bool("--check")}),
+)
+
class _DjangoRunner(PythonRunner):
def __init__(self, module, arg_formats=None, **kwargs):
@@ -55,15 +68,30 @@ class DjangoModuleHelper(ModuleHelper):
arg_formats = {}
django_admin_arg_order = ()
use_old_vardict = False
+ _django_args = []
+ _check_mode_arg = ""
def __init__(self):
- argument_spec = dict(django_std_args)
- argument_spec.update(self.module.get("argument_spec", {}))
- self.module["argument_spec"] = argument_spec
+ self.module["argument_spec"], self.arg_formats = self._build_args(self.module.get("argument_spec", {}),
+ self.arg_formats,
+ *(["std"] + self._django_args))
super(DjangoModuleHelper, self).__init__(self.module)
if self.django_admin_cmd is not None:
self.vars.command = self.django_admin_cmd
+ @staticmethod
+ def _build_args(arg_spec, arg_format, *names):
+ res_arg_spec = {}
+ res_arg_fmts = {}
+ for name in names:
+ args, fmts = _args_menu[name]
+ res_arg_spec = dict_merge(res_arg_spec, args)
+ res_arg_fmts = dict_merge(res_arg_fmts, fmts)
+ res_arg_spec = dict_merge(res_arg_spec, arg_spec)
+ res_arg_fmts = dict_merge(res_arg_fmts, arg_format)
+
+ return res_arg_spec, res_arg_fmts
+
def __run__(self):
runner = _DjangoRunner(self.module,
default_args_order=self.django_admin_arg_order,
@@ -71,7 +99,10 @@ class DjangoModuleHelper(ModuleHelper):
venv=self.vars.venv,
check_rc=True)
with runner() as ctx:
- results = ctx.run()
+ run_params = self.vars.as_dict()
+ if self._check_mode_arg:
+ run_params.update({self._check_mode_arg: self.check_mode})
+ results = ctx.run(**run_params)
self.vars.stdout = ctx.results_out
self.vars.stderr = ctx.results_err
self.vars.cmd = ctx.cmd
diff --git a/ansible_collections/community/general/plugins/module_utils/proxmox.py b/ansible_collections/community/general/plugins/module_utils/proxmox.py
index 5fd783d65..05bf1874b 100644
--- a/ansible_collections/community/general/plugins/module_utils/proxmox.py
+++ b/ansible_collections/community/general/plugins/module_utils/proxmox.py
@@ -29,6 +29,9 @@ def proxmox_auth_argument_spec():
required=True,
fallback=(env_fallback, ['PROXMOX_HOST'])
),
+ api_port=dict(type='int',
+ fallback=(env_fallback, ['PROXMOX_PORT'])
+ ),
api_user=dict(type='str',
required=True,
fallback=(env_fallback, ['PROXMOX_USER'])
@@ -82,6 +85,7 @@ class ProxmoxAnsible(object):
def _connect(self):
api_host = self.module.params['api_host']
+ api_port = self.module.params['api_port']
api_user = self.module.params['api_user']
api_password = self.module.params['api_password']
api_token_id = self.module.params['api_token_id']
@@ -89,6 +93,10 @@ class ProxmoxAnsible(object):
validate_certs = self.module.params['validate_certs']
auth_args = {'user': api_user}
+
+ if api_port:
+ auth_args['port'] = api_port
+
if api_password:
auth_args['password'] = api_password
else:
diff --git a/ansible_collections/community/general/plugins/module_utils/redfish_utils.py b/ansible_collections/community/general/plugins/module_utils/redfish_utils.py
index 6935573d0..139628bd9 100644
--- a/ansible_collections/community/general/plugins/module_utils/redfish_utils.py
+++ b/ansible_collections/community/general/plugins/module_utils/redfish_utils.py
@@ -11,6 +11,7 @@ import os
import random
import string
import gzip
+import time
from io import BytesIO
from ansible.module_utils.urls import open_url
from ansible.module_utils.common.text.converters import to_native
@@ -132,11 +133,13 @@ class RedfishUtils(object):
return resp
# The following functions are to send GET/POST/PATCH/DELETE requests
- def get_request(self, uri, override_headers=None, allow_no_resp=False):
+ def get_request(self, uri, override_headers=None, allow_no_resp=False, timeout=None):
req_headers = dict(GET_HEADERS)
if override_headers:
req_headers.update(override_headers)
username, password, basic_auth = self._auth_params(req_headers)
+ if timeout is None:
+ timeout = self.timeout
try:
# Service root is an unauthenticated resource; remove credentials
# in case the caller will be using sessions later.
@@ -146,7 +149,7 @@ class RedfishUtils(object):
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
follow_redirects='all',
- use_proxy=True, timeout=self.timeout)
+ use_proxy=True, timeout=timeout)
headers = dict((k.lower(), v) for (k, v) in resp.info().items())
try:
if headers.get('content-encoding') == 'gzip' and LooseVersion(ansible_version) < LooseVersion('2.14'):
@@ -624,6 +627,24 @@ class RedfishUtils(object):
allowable_values = default_values
return allowable_values
+ def check_service_availability(self):
+ """
+ Checks if the service is accessible.
+
+ :return: dict containing the status of the service
+ """
+
+ # Get the service root
+ # Override the timeout since the service root is expected to be readily
+ # available.
+ service_root = self.get_request(self.root_uri + self.service_root, timeout=10)
+ if service_root['ret'] is False:
+ # Failed, either due to a timeout or HTTP error; not available
+ return {'ret': True, 'available': False}
+
+ # Successfully accessed the service root; available
+ return {'ret': True, 'available': True}
+
def get_logs(self):
log_svcs_uri_list = []
list_of_logs = []
@@ -1083,11 +1104,12 @@ class RedfishUtils(object):
return self.manage_power(command, self.systems_uri,
'#ComputerSystem.Reset')
- def manage_manager_power(self, command):
+ def manage_manager_power(self, command, wait=False, wait_timeout=120):
return self.manage_power(command, self.manager_uri,
- '#Manager.Reset')
+ '#Manager.Reset', wait, wait_timeout)
- def manage_power(self, command, resource_uri, action_name):
+ def manage_power(self, command, resource_uri, action_name, wait=False,
+ wait_timeout=120):
key = "Actions"
reset_type_values = ['On', 'ForceOff', 'GracefulShutdown',
'GracefulRestart', 'ForceRestart', 'Nmi',
@@ -1147,6 +1169,30 @@ class RedfishUtils(object):
response = self.post_request(self.root_uri + action_uri, payload)
if response['ret'] is False:
return response
+
+ # If requested to wait for the service to be available again, block
+ # until it's ready
+ if wait:
+ elapsed_time = 0
+ start_time = time.time()
+ # Start with a large enough sleep. Some services will process new
+ # requests while in the middle of shutting down, thus breaking out
+ # early.
+ time.sleep(30)
+
+ # Periodically check for the service's availability.
+ while elapsed_time <= wait_timeout:
+ status = self.check_service_availability()
+ if status['available']:
+ # It's available; we're done
+ break
+ time.sleep(5)
+ elapsed_time = time.time() - start_time
+
+ if elapsed_time > wait_timeout:
+ # Exhausted the wait timer; error
+ return {'ret': False, 'changed': True,
+ 'msg': 'The service did not become available after %d seconds' % wait_timeout}
return {'ret': True, 'changed': True}
def manager_reset_to_defaults(self, command):
diff --git a/ansible_collections/community/general/plugins/modules/ansible_galaxy_install.py b/ansible_collections/community/general/plugins/modules/ansible_galaxy_install.py
index d382ed93a..b0f3aeb5d 100644
--- a/ansible_collections/community/general/plugins/modules/ansible_galaxy_install.py
+++ b/ansible_collections/community/general/plugins/modules/ansible_galaxy_install.py
@@ -32,6 +32,19 @@ attributes:
diff_mode:
support: none
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).
+ type: str
+ choices: [ present, latest ]
+ default: present
+ version_added: 9.1.0
type:
description:
- The type of installation performed by C(ansible-galaxy).
@@ -69,7 +82,8 @@ options:
default: false
force:
description:
- - Force overwriting an existing role or collection.
+ - 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
@@ -188,6 +202,7 @@ class AnsibleGalaxyInstall(ModuleHelper):
output_params = ('type', 'name', 'dest', 'requirements_file', 'force', 'no_deps')
module = dict(
argument_spec=dict(
+ state=dict(type='str', choices=['present', 'latest'], default='present'),
type=dict(type='str', choices=('collection', 'role', 'both'), required=True),
name=dict(type='str'),
requirements_file=dict(type='path'),
@@ -206,6 +221,7 @@ class AnsibleGalaxyInstall(ModuleHelper):
command_args_formats = dict(
type=cmd_runner_fmt.as_func(lambda v: [] if v == 'both' else [v]),
galaxy_cmd=cmd_runner_fmt.as_list(),
+ upgrade=cmd_runner_fmt.as_bool("--upgrade"),
requirements_file=cmd_runner_fmt.as_opt_val('-r'),
dest=cmd_runner_fmt.as_opt_val('-p'),
force=cmd_runner_fmt.as_bool("--force"),
@@ -244,9 +260,7 @@ class AnsibleGalaxyInstall(ModuleHelper):
def __init_module__(self):
self.runner, self.ansible_version = self._get_ansible_galaxy_version()
if self.ansible_version < (2, 11):
- self.module.fail_json(
- msg="Support for Ansible 2.9 and ansible-base 2.10 has been removed."
- )
+ self.module.fail_json(msg="Support for Ansible 2.9 and ansible-base 2.10 has been removed.")
self.vars.set("new_collections", {}, change=True)
self.vars.set("new_roles", {}, change=True)
if self.vars.type != "collection":
@@ -299,8 +313,9 @@ class AnsibleGalaxyInstall(ModuleHelper):
elif match.group("role"):
self.vars.new_roles[match.group("role")] = match.group("rversion")
- with self.runner("type galaxy_cmd force no_deps dest requirements_file name", output_process=process) as ctx:
- ctx.run(galaxy_cmd="install")
+ upgrade = (self.vars.type == "collection" and self.vars.state == "latest")
+ with self.runner("type galaxy_cmd upgrade force no_deps dest requirements_file name", output_process=process) as ctx:
+ ctx.run(galaxy_cmd="install", upgrade=upgrade)
if self.verbosity > 2:
self.vars.set("run_info", ctx.run_info)
diff --git a/ansible_collections/community/general/plugins/modules/cargo.py b/ansible_collections/community/general/plugins/modules/cargo.py
index ba9c05ed7..2fc729da2 100644
--- a/ansible_collections/community/general/plugins/modules/cargo.py
+++ b/ansible_collections/community/general/plugins/modules/cargo.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 Radek Sprta <mail@radeksprta.eu>
+# Copyright (c) 2024 Colin Nolan <cn580@alumni.york.ac.uk>
# 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
@@ -65,6 +66,13 @@ options:
type: str
default: present
choices: [ "present", "absent", "latest" ]
+ directory:
+ description:
+ - Path to the source directory to install the Rust package from.
+ - This is only used when installing packages.
+ type: path
+ required: false
+ version_added: 9.1.0
requirements:
- cargo installed
"""
@@ -98,8 +106,14 @@ EXAMPLES = r"""
community.general.cargo:
name: ludusavi
state: latest
+
+- name: Install "ludusavi" Rust package from source directory
+ community.general.cargo:
+ name: ludusavi
+ directory: /path/to/ludusavi/source
"""
+import json
import os
import re
@@ -115,6 +129,7 @@ class Cargo(object):
self.state = kwargs["state"]
self.version = kwargs["version"]
self.locked = kwargs["locked"]
+ self.directory = kwargs["directory"]
@property
def path(self):
@@ -143,7 +158,7 @@ class Cargo(object):
data, dummy = self._exec(cmd, True, False, False)
- package_regex = re.compile(r"^([\w\-]+) v(.+):$")
+ package_regex = re.compile(r"^([\w\-]+) v(\S+).*:$")
installed = {}
for line in data.splitlines():
package_info = package_regex.match(line)
@@ -163,19 +178,53 @@ class Cargo(object):
if self.version:
cmd.append("--version")
cmd.append(self.version)
+ if self.directory:
+ cmd.append("--path")
+ cmd.append(self.directory)
return self._exec(cmd)
def is_outdated(self, name):
installed_version = self.get_installed().get(name)
+ latest_version = (
+ self.get_latest_published_version(name)
+ if not self.directory
+ else self.get_source_directory_version(name)
+ )
+ return installed_version != latest_version
+ def get_latest_published_version(self, name):
cmd = ["search", name, "--limit", "1"]
data, dummy = self._exec(cmd, True, False, False)
match = re.search(r'"(.+)"', data)
- if match:
- latest_version = match.group(1)
-
- return installed_version != latest_version
+ if not match:
+ self.module.fail_json(
+ msg="No published version for package %s found" % name
+ )
+ return match.group(1)
+
+ def get_source_directory_version(self, name):
+ cmd = [
+ "metadata",
+ "--format-version",
+ "1",
+ "--no-deps",
+ "--manifest-path",
+ os.path.join(self.directory, "Cargo.toml"),
+ ]
+ data, dummy = self._exec(cmd, True, False, False)
+ manifest = json.loads(data)
+
+ package = next(
+ (package for package in manifest["packages"] if package["name"] == name),
+ None,
+ )
+ if not package:
+ self.module.fail_json(
+ msg="Package %s not defined in source, found: %s"
+ % (name, [x["name"] for x in manifest["packages"]])
+ )
+ return package["version"]
def uninstall(self, packages=None):
cmd = ["uninstall"]
@@ -191,16 +240,21 @@ def main():
state=dict(default="present", choices=["present", "absent", "latest"]),
version=dict(default=None, type="str"),
locked=dict(default=False, type="bool"),
+ directory=dict(default=None, type="path"),
)
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
name = module.params["name"]
state = module.params["state"]
version = module.params["version"]
+ directory = module.params["directory"]
if not name:
module.fail_json(msg="Package name must be specified")
+ if directory is not None and not os.path.isdir(directory):
+ module.fail_json(msg="Source directory does not exist")
+
# Set LANG env since we parse stdout
module.run_command_environ_update = dict(
LANG="C", LC_ALL="C", LC_MESSAGES="C", LC_CTYPE="C"
diff --git a/ansible_collections/community/general/plugins/modules/consul_agent_check.py b/ansible_collections/community/general/plugins/modules/consul_agent_check.py
new file mode 100644
index 000000000..373926004
--- /dev/null
+++ b/ansible_collections/community/general/plugins/modules/consul_agent_check.py
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2024, Michael Ilg
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: consul_agent_check
+short_description: Add, modify, and delete checks within a consul cluster
+version_added: 9.1.0
+description:
+ - Allows the addition, modification and deletion of checks in a consul
+ cluster via the agent. For more details on using and configuring Checks,
+ see U(https://developer.hashicorp.com/consul/api-docs/agent/check).
+ - Currently, there is no complete way to retrieve the script, interval or TTL
+ metadata for a registered check. Without this metadata it is not possible to
+ tell if the data supplied with ansible represents a change to a check. As a
+ result this does not attempt to determine changes and will always report a
+ changed occurred. An API method is planned to supply this metadata so at that
+ stage change management will be added.
+author:
+ - Michael Ilg (@Ilgmi)
+extends_documentation_fragment:
+ - community.general.consul
+ - community.general.consul.actiongroup_consul
+ - community.general.consul.token
+ - community.general.attributes
+attributes:
+ check_mode:
+ support: full
+ details:
+ - The result is the object as it is defined in the module options and not the object structure of the consul API.
+ For a better overview of what the object structure looks like,
+ take a look at U(https://developer.hashicorp.com/consul/api-docs/agent/check#list-checks).
+ diff_mode:
+ support: partial
+ details:
+ - In check mode the diff will show the object as it is defined in the module options and not the object structure of the consul API.
+options:
+ state:
+ description:
+ - Whether the check should be present or absent.
+ choices: ['present', 'absent']
+ default: present
+ type: str
+ name:
+ description:
+ - Required name for the service check.
+ type: str
+ id:
+ description:
+ - Specifies a unique ID for this check on the node. This defaults to the O(name) parameter, but it may be necessary to provide
+ an ID for uniqueness. This value will return in the response as "CheckId".
+ type: str
+ interval:
+ description:
+ - The interval at which the service check will be run.
+ This is a number with a V(s) or V(m) suffix to signify the units of seconds or minutes, for example V(15s) or V(1m).
+ If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
+ - Required if one of the parameters O(args), O(http), or O(tcp) is specified.
+ type: str
+ notes:
+ description:
+ - Notes to attach to check when registering it.
+ type: str
+ args:
+ description:
+ - Specifies command arguments to run to update the status of the check.
+ - Requires O(interval) to be provided.
+ - Mutually exclusive with O(ttl), O(tcp) and O(http).
+ type: list
+ elements: str
+ ttl:
+ description:
+ - Checks can be registered with a TTL instead of a O(args) and O(interval)
+ this means that the service will check in with the agent before the
+ TTL expires. If it doesn't the check will be considered failed.
+ Required if registering a check and the script an interval are missing
+ Similar to the interval this is a number with a V(s) or V(m) suffix to
+ signify the units of seconds or minutes, for example V(15s) or V(1m).
+ If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
+ - Mutually exclusive with O(args), O(tcp) and O(http).
+ type: str
+ tcp:
+ description:
+ - Checks can be registered with a TCP port. This means that consul
+ will check if the connection attempt to that port is successful (that is, the port is currently accepting connections).
+ The format is V(host:port), for example V(localhost:80).
+ - Requires O(interval) to be provided.
+ - Mutually exclusive with O(args), O(ttl) and O(http).
+ type: str
+ version_added: '1.3.0'
+ http:
+ description:
+ - Checks can be registered with an HTTP endpoint. This means that consul
+ will check that the http endpoint returns a successful HTTP status.
+ - Requires O(interval) to be provided.
+ - Mutually exclusive with O(args), O(ttl) and O(tcp).
+ type: str
+ timeout:
+ description:
+ - A custom HTTP check timeout. The consul default is 10 seconds.
+ Similar to the interval this is a number with a V(s) or V(m) suffix to
+ signify the units of seconds or minutes, for example V(15s) or V(1m).
+ If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
+ type: str
+ service_id:
+ description:
+ - The ID for the service, must be unique per node. If O(state=absent),
+ defaults to the service name if supplied.
+ type: str
+'''
+
+EXAMPLES = '''
+- name: Register tcp check for service 'nginx'
+ community.general.consul_agent_check:
+ name: nginx_tcp_check
+ service_id: nginx
+ interval: 60s
+ tcp: localhost:80
+ notes: "Nginx Check"
+
+- name: Register http check for service 'nginx'
+ community.general.consul_agent_check:
+ name: nginx_http_check
+ service_id: nginx
+ interval: 60s
+ http: http://localhost:80/status
+ notes: "Nginx Check"
+
+- name: Remove check for service 'nginx'
+ community.general.consul_agent_check:
+ state: absent
+ id: nginx_http_check
+ service_id: "{{ nginx_service.ID }}"
+'''
+
+RETURN = """
+check:
+ description: The check as returned by the consul HTTP API.
+ returned: always
+ type: dict
+ sample:
+ CheckID: nginx_check
+ ServiceID: nginx
+ Interval: 30s
+ Type: http
+ Notes: Nginx Check
+operation:
+ description: The operation performed.
+ returned: changed
+ type: str
+ sample: update
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.general.plugins.module_utils.consul import (
+ AUTH_ARGUMENTS_SPEC,
+ OPERATION_CREATE,
+ OPERATION_UPDATE,
+ OPERATION_DELETE,
+ OPERATION_READ,
+ _ConsulModule,
+ validate_check,
+)
+
+_ARGUMENT_SPEC = {
+ "state": dict(default="present", choices=["present", "absent"]),
+ "name": dict(type='str'),
+ "id": dict(type='str'),
+ "interval": dict(type='str'),
+ "notes": dict(type='str'),
+ "args": dict(type='list', elements='str'),
+ "http": dict(type='str'),
+ "tcp": dict(type='str'),
+ "ttl": dict(type='str'),
+ "timeout": dict(type='str'),
+ "service_id": dict(type='str'),
+}
+
+_MUTUALLY_EXCLUSIVE = [
+ ('args', 'ttl', 'tcp', 'http'),
+]
+
+_REQUIRED_IF = [
+ ('state', 'present', ['name']),
+ ('state', 'absent', ('id', 'name'), True),
+]
+
+_REQUIRED_BY = {
+ 'args': 'interval',
+ 'http': 'interval',
+ 'tcp': 'interval',
+}
+
+_ARGUMENT_SPEC.update(AUTH_ARGUMENTS_SPEC)
+
+
+class ConsulAgentCheckModule(_ConsulModule):
+ api_endpoint = "agent/check"
+ result_key = "check"
+ unique_identifiers = ["id", "name"]
+ operational_attributes = {"Node", "CheckID", "Output", "ServiceName", "ServiceTags",
+ "Status", "Type", "ExposedPort", "Definition"}
+
+ def endpoint_url(self, operation, identifier=None):
+ if operation == OPERATION_READ:
+ return "agent/checks"
+ if operation in [OPERATION_CREATE, OPERATION_UPDATE]:
+ return "/".join([self.api_endpoint, "register"])
+ if operation == OPERATION_DELETE:
+ return "/".join([self.api_endpoint, "deregister", identifier])
+
+ return super(ConsulAgentCheckModule, self).endpoint_url(operation, identifier)
+
+ def read_object(self):
+ url = self.endpoint_url(OPERATION_READ)
+ checks = self.get(url)
+ identifier = self.id_from_obj(self.params)
+ if identifier in checks:
+ return checks[identifier]
+ return None
+
+ def prepare_object(self, existing, obj):
+ existing = super(ConsulAgentCheckModule, self).prepare_object(existing, obj)
+ validate_check(existing)
+ return existing
+
+ def delete_object(self, obj):
+ if not self._module.check_mode:
+ self.put(self.endpoint_url(OPERATION_DELETE, obj.get("CheckID")))
+ return {}
+
+
+def main():
+ module = AnsibleModule(
+ _ARGUMENT_SPEC,
+ mutually_exclusive=_MUTUALLY_EXCLUSIVE,
+ required_if=_REQUIRED_IF,
+ required_by=_REQUIRED_BY,
+ supports_check_mode=True,
+ )
+
+ consul_module = ConsulAgentCheckModule(module)
+ consul_module.execute()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/community/general/plugins/modules/consul_agent_service.py b/ansible_collections/community/general/plugins/modules/consul_agent_service.py
new file mode 100644
index 000000000..a8ef09897
--- /dev/null
+++ b/ansible_collections/community/general/plugins/modules/consul_agent_service.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2024, Michael Ilg
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: consul_agent_service
+short_description: Add, modify and delete services within a consul cluster
+version_added: 9.1.0
+description:
+ - Allows the addition, modification and deletion of services in a consul
+ cluster via the agent.
+ - There are currently no plans to create services and checks in one.
+ This is because the Consul API does not provide checks for a service and
+ the checks themselves do not match the module parameters.
+ Therefore, only a service without checks can be created in this module.
+author:
+ - Michael Ilg (@Ilgmi)
+extends_documentation_fragment:
+ - community.general.consul
+ - community.general.consul.actiongroup_consul
+ - community.general.consul.token
+ - community.general.attributes
+attributes:
+ check_mode:
+ support: full
+ diff_mode:
+ support: partial
+ details:
+ - In check mode the diff will miss operational attributes.
+options:
+ state:
+ description:
+ - Whether the service should be present or absent.
+ choices: ['present', 'absent']
+ default: present
+ type: str
+ name:
+ description:
+ - Unique name for the service on a node, must be unique per node,
+ required if registering a service.
+ type: str
+ id:
+ description:
+ - Specifies a unique ID for this service. This must be unique per agent. This defaults to the O(name) parameter if not provided.
+ If O(state=absent), defaults to the service name if supplied.
+ type: str
+ tags:
+ description:
+ - Tags that will be attached to the service registration.
+ type: list
+ elements: str
+ address:
+ description:
+ - The address to advertise that the service will be listening on.
+ This value will be passed as the C(address) parameter to Consul's
+ C(/v1/agent/service/register) API method, so refer to the Consul API
+ documentation for further details.
+ type: str
+ meta:
+ description:
+ - Optional meta data used for filtering.
+ For keys, the characters C(A-Z), C(a-z), C(0-9), C(_), C(-) are allowed.
+ Not allowed characters are replaced with underscores.
+ type: dict
+ service_port:
+ description:
+ - The port on which the service is listening. Can optionally be supplied for
+ registration of a service, that is if O(name) or O(id) is set.
+ type: int
+ enable_tag_override:
+ description:
+ - Specifies to disable the anti-entropy feature for this service's tags.
+ If EnableTagOverride is set to true then external agents can update this service in the catalog and modify the tags.
+ type: bool
+ default: False
+ weights:
+ description:
+ - Specifies weights for the service
+ type: dict
+ suboptions:
+ passing:
+ description:
+ - Weights for passing.
+ type: int
+ default: 1
+ warning:
+ description:
+ - Weights for warning.
+ type: int
+ default: 1
+ default: {"passing": 1, "warning": 1}
+'''
+
+EXAMPLES = '''
+- name: Register nginx service with the local consul agent
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+
+- name: Register nginx with a tcp check
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+
+- name: Register nginx with an http check
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+
+- name: Register external service nginx available at 10.1.5.23
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+ address: 10.1.5.23
+
+- name: Register nginx with some service tags
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+ tags:
+ - prod
+ - webservers
+
+- name: Register nginx with some service meta
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: nginx
+ service_port: 80
+ meta:
+ nginx_version: 1.25.3
+
+- name: Remove nginx service
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ service_id: nginx
+ state: absent
+
+- name: Register celery worker service
+ community.general.consul_agent_service:
+ host: consul1.example.com
+ token: some_management_acl
+ name: celery-worker
+ tags:
+ - prod
+ - worker
+'''
+
+RETURN = """
+service:
+ description: The service as returned by the consul HTTP API.
+ returned: always
+ type: dict
+ sample:
+ ID: nginx
+ Service: nginx
+ Address: localhost
+ Port: 80
+ Tags:
+ - http
+ Meta:
+ - nginx_version: 1.23.3
+ Datacenter: dc1
+ Weights:
+ Passing: 1
+ Warning: 1
+ ContentHash: 61a245cd985261ac
+ EnableTagOverride: false
+operation:
+ description: The operation performed.
+ returned: changed
+ type: str
+ sample: update
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.general.plugins.module_utils.consul import (
+ AUTH_ARGUMENTS_SPEC,
+ OPERATION_CREATE,
+ OPERATION_UPDATE,
+ OPERATION_DELETE,
+ _ConsulModule
+)
+
+_CHECK_MUTUALLY_EXCLUSIVE = [('args', 'ttl', 'tcp', 'http')]
+_CHECK_REQUIRED_BY = {
+ 'args': 'interval',
+ 'http': 'interval',
+ 'tcp': 'interval',
+}
+
+_ARGUMENT_SPEC = {
+ "state": dict(default="present", choices=["present", "absent"]),
+ "name": dict(type='str'),
+ "id": dict(type='str'),
+ "tags": dict(type='list', elements='str'),
+ "address": dict(type='str'),
+ "meta": dict(type='dict'),
+ "service_port": dict(type='int'),
+ "enable_tag_override": dict(type='bool', default=False),
+ "weights": dict(type='dict', options=dict(
+ passing=dict(type='int', default=1, no_log=False),
+ warning=dict(type='int', default=1)
+ ), default={"passing": 1, "warning": 1})
+}
+
+_REQUIRED_IF = [
+ ('state', 'present', ['name']),
+ ('state', 'absent', ('id', 'name'), True),
+]
+
+_ARGUMENT_SPEC.update(AUTH_ARGUMENTS_SPEC)
+
+
+class ConsulAgentServiceModule(_ConsulModule):
+ api_endpoint = "agent/service"
+ result_key = "service"
+ unique_identifiers = ["id", "name"]
+ operational_attributes = {"Service", "ContentHash", "Datacenter"}
+
+ def endpoint_url(self, operation, identifier=None):
+ if operation in [OPERATION_CREATE, OPERATION_UPDATE]:
+ return "/".join([self.api_endpoint, "register"])
+ if operation == OPERATION_DELETE:
+ return "/".join([self.api_endpoint, "deregister", identifier])
+
+ return super(ConsulAgentServiceModule, self).endpoint_url(operation, identifier)
+
+ def prepare_object(self, existing, obj):
+ existing = super(ConsulAgentServiceModule, self).prepare_object(existing, obj)
+ if "ServicePort" in existing:
+ existing["Port"] = existing.pop("ServicePort")
+
+ if "ID" not in existing:
+ existing["ID"] = existing["Name"]
+
+ return existing
+
+ def needs_update(self, api_obj, module_obj):
+ obj = {}
+ if "Service" in api_obj:
+ obj["Service"] = api_obj["Service"]
+ api_obj = self.prepare_object(api_obj, obj)
+
+ if "Name" in module_obj:
+ module_obj["Service"] = module_obj.pop("Name")
+ if "ServicePort" in module_obj:
+ module_obj["Port"] = module_obj.pop("ServicePort")
+
+ return super(ConsulAgentServiceModule, self).needs_update(api_obj, module_obj)
+
+ def delete_object(self, obj):
+ if not self._module.check_mode:
+ url = self.endpoint_url(OPERATION_DELETE, self.id_from_obj(obj, camel_case=True))
+ self.put(url)
+ return {}
+
+
+def main():
+ module = AnsibleModule(
+ _ARGUMENT_SPEC,
+ required_if=_REQUIRED_IF,
+ supports_check_mode=True,
+ )
+
+ consul_module = ConsulAgentServiceModule(module)
+ consul_module.execute()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/community/general/plugins/modules/consul_auth_method.py b/ansible_collections/community/general/plugins/modules/consul_auth_method.py
index afe549f6e..e28474c31 100644
--- a/ansible_collections/community/general/plugins/modules/consul_auth_method.py
+++ b/ansible_collections/community/general/plugins/modules/consul_auth_method.py
@@ -168,7 +168,7 @@ def normalize_ttl(ttl):
class ConsulAuthMethodModule(_ConsulModule):
api_endpoint = "acl/auth-method"
result_key = "auth_method"
- unique_identifier = "name"
+ unique_identifiers = ["name"]
def map_param(self, k, v, is_update):
if k == "config" and v:
diff --git a/ansible_collections/community/general/plugins/modules/consul_binding_rule.py b/ansible_collections/community/general/plugins/modules/consul_binding_rule.py
index 88496f867..6a2882cee 100644
--- a/ansible_collections/community/general/plugins/modules/consul_binding_rule.py
+++ b/ansible_collections/community/general/plugins/modules/consul_binding_rule.py
@@ -124,7 +124,7 @@ from ansible_collections.community.general.plugins.module_utils.consul import (
class ConsulBindingRuleModule(_ConsulModule):
api_endpoint = "acl/binding-rule"
result_key = "binding_rule"
- unique_identifier = "id"
+ unique_identifiers = ["id"]
def read_object(self):
url = "acl/binding-rules?authmethod={0}".format(self.params["auth_method"])
diff --git a/ansible_collections/community/general/plugins/modules/consul_policy.py b/ansible_collections/community/general/plugins/modules/consul_policy.py
index 2ed6021b0..36139ac09 100644
--- a/ansible_collections/community/general/plugins/modules/consul_policy.py
+++ b/ansible_collections/community/general/plugins/modules/consul_policy.py
@@ -145,7 +145,7 @@ _ARGUMENT_SPEC.update(AUTH_ARGUMENTS_SPEC)
class ConsulPolicyModule(_ConsulModule):
api_endpoint = "acl/policy"
result_key = "policy"
- unique_identifier = "id"
+ unique_identifiers = ["id"]
def endpoint_url(self, operation, identifier=None):
if operation == OPERATION_READ:
diff --git a/ansible_collections/community/general/plugins/modules/consul_role.py b/ansible_collections/community/general/plugins/modules/consul_role.py
index e07e2036f..d6c4e4dd9 100644
--- a/ansible_collections/community/general/plugins/modules/consul_role.py
+++ b/ansible_collections/community/general/plugins/modules/consul_role.py
@@ -212,7 +212,7 @@ from ansible_collections.community.general.plugins.module_utils.consul import (
class ConsulRoleModule(_ConsulModule):
api_endpoint = "acl/role"
result_key = "role"
- unique_identifier = "id"
+ unique_identifiers = ["id"]
def endpoint_url(self, operation, identifier=None):
if operation == OPERATION_READ:
diff --git a/ansible_collections/community/general/plugins/modules/consul_token.py b/ansible_collections/community/general/plugins/modules/consul_token.py
index 02bc544da..c8bc8bc27 100644
--- a/ansible_collections/community/general/plugins/modules/consul_token.py
+++ b/ansible_collections/community/general/plugins/modules/consul_token.py
@@ -235,13 +235,13 @@ def normalize_link_obj(api_obj, module_obj, key):
class ConsulTokenModule(_ConsulModule):
api_endpoint = "acl/token"
result_key = "token"
- unique_identifier = "accessor_id"
+ unique_identifiers = ["accessor_id"]
create_only_fields = {"expiration_ttl"}
def read_object(self):
# if `accessor_id` is not supplied we can only create objects and are not idempotent
- if not self.params.get(self.unique_identifier):
+ if not self.id_from_obj(self.params):
return None
return super(ConsulTokenModule, self).read_object()
diff --git a/ansible_collections/community/general/plugins/modules/django_check.py b/ansible_collections/community/general/plugins/modules/django_check.py
new file mode 100644
index 000000000..1553da7a3
--- /dev/null
+++ b/ansible_collections/community/general/plugins/modules/django_check.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+# -*- 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
+
+DOCUMENTATION = """
+module: django_check
+author:
+ - Alexei Znamensky (@russoz)
+short_description: Wrapper for C(django-admin check)
+version_added: 9.1.0
+description:
+ - This module is a wrapper for the execution of C(django-admin check).
+extends_documentation_fragment:
+ - community.general.attributes
+ - community.general.django
+options:
+ database:
+ description:
+ - Specify databases to run checks against.
+ - If not specified, Django will not run database tests.
+ type: list
+ elements: str
+ deploy:
+ description:
+ - Include additional checks relevant in a deployment setting.
+ type: bool
+ default: false
+ fail_level:
+ description:
+ - Message level that will trigger failure.
+ - Default is the Django default value. Check the documentation for the version being used.
+ type: str
+ choices: [CRITICAL, ERROR, WARNING, INFO, DEBUG]
+ tags:
+ description:
+ - Restrict checks to specific tags.
+ type: list
+ elements: str
+ apps:
+ description:
+ - Restrict checks to specific applications.
+ - Default is to check all applications.
+ type: list
+ elements: str
+notes:
+ - The outcome of the module is found in the common return values RV(ignore:stdout), RV(ignore:stderr), RV(ignore:rc).
+ - The module will fail if RV(ignore:rc) is not zero.
+attributes:
+ check_mode:
+ support: full
+ diff_mode:
+ support: none
+"""
+
+EXAMPLES = """
+- name: Check the entire project
+ community.general.django_check:
+ settings: myproject.settings
+
+- name: Create the project using specific databases
+ community.general.django_check:
+ database:
+ - somedb
+ - myotherdb
+ settings: fancysite.settings
+ pythonpath: /home/joedoe/project/fancysite
+ venv: /home/joedoe/project/fancysite/venv
+"""
+
+RETURN = """
+run_info:
+ description: Command-line execution information.
+ type: dict
+ returned: success and C(verbosity) >= 3
+"""
+
+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
+
+
+class DjangoCheck(DjangoModuleHelper):
+ module = dict(
+ argument_spec=dict(
+ database=dict(type="list", elements="str"),
+ deploy=dict(type="bool", default=False),
+ fail_level=dict(type="str", choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]),
+ tags=dict(type="list", elements="str"),
+ apps=dict(type="list", elements="str"),
+ ),
+ supports_check_mode=True,
+ )
+ arg_formats = dict(
+ database=cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_val)("--database"),
+ deploy=cmd_runner_fmt.as_bool("--deploy"),
+ fail_level=cmd_runner_fmt.as_opt_val("--fail-level"),
+ tags=cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_val)("--tag"),
+ apps=cmd_runner_fmt.as_list(),
+ )
+ django_admin_cmd = "check"
+ django_admin_arg_order = "database deploy fail_level tags apps"
+
+
+def main():
+ DjangoCheck.execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/general/plugins/modules/django_createcachetable.py b/ansible_collections/community/general/plugins/modules/django_createcachetable.py
new file mode 100644
index 000000000..b038e0358
--- /dev/null
+++ b/ansible_collections/community/general/plugins/modules/django_createcachetable.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# -*- 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
+
+DOCUMENTATION = """
+module: django_createcachetable
+author:
+ - Alexei Znamensky (@russoz)
+short_description: Wrapper for C(django-admin createcachetable)
+version_added: 9.1.0
+description:
+ - This module is a wrapper for the execution of C(django-admin createcachetable).
+extends_documentation_fragment:
+ - community.general.attributes
+ - community.general.django
+ - community.general.django.database
+attributes:
+ check_mode:
+ support: full
+ diff_mode:
+ support: none
+"""
+
+EXAMPLES = """
+- name: Create cache table in the default database
+ community.general.django_createcachetable:
+ settings: myproject.settings
+
+- name: Create cache table in the other database
+ community.general.django_createcachetable:
+ database: myotherdb
+ settings: fancysite.settings
+ pythonpath: /home/joedoe/project/fancysite
+ venv: /home/joedoe/project/fancysite/venv
+"""
+
+RETURN = """
+run_info:
+ description: Command-line execution information.
+ type: dict
+ returned: success and O(verbosity) >= 3
+"""
+
+from ansible_collections.community.general.plugins.module_utils.django import DjangoModuleHelper
+
+
+class DjangoCreateCacheTable(DjangoModuleHelper):
+ module = dict(
+ supports_check_mode=True,
+ )
+ django_admin_cmd = "createcachetable"
+ django_admin_arg_order = "noinput database dry_run"
+ _django_args = ["noinput", "database", "dry_run"]
+ _check_mode_arg = "dry_run"
+
+
+def main():
+ DjangoCreateCacheTable.execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/community/general/plugins/modules/git_config.py b/ansible_collections/community/general/plugins/modules/git_config.py
index a8d2ebe97..95969c1b3 100644
--- a/ansible_collections/community/general/plugins/modules/git_config.py
+++ b/ansible_collections/community/general/plugins/modules/git_config.py
@@ -18,7 +18,7 @@ author:
- Matthew Gamble (@djmattyg007)
- Marius Gedminas (@mgedmin)
requirements: ['git']
-short_description: Read and write git configuration
+short_description: Update git configuration
description:
- The M(community.general.git_config) module changes git configuration by invoking C(git config).
This is needed if you do not want to use M(ansible.builtin.template) for the entire git
@@ -36,6 +36,8 @@ options:
list_all:
description:
- List all settings (optionally limited to a given O(scope)).
+ - This option is B(deprecated) and will be removed from community.general 11.0.0.
+ Please use M(community.general.git_config_info) instead.
type: bool
default: false
name:
@@ -74,6 +76,8 @@ options:
description:
- When specifying the name of a single setting, supply a value to
set that setting to the given value.
+ - From community.general 11.0.0 on, O(value) will be required if O(state=present).
+ To read values, use the M(community.general.git_config_info) module instead.
type: str
add_mode:
description:
@@ -143,29 +147,6 @@ EXAMPLES = '''
repo: /etc
scope: local
value: 'root@{{ ansible_fqdn }}'
-
-- name: Read individual values from git config
- community.general.git_config:
- name: alias.ci
- scope: global
-
-- name: Scope system is also assumed when reading values, unless list_all=true
- community.general.git_config:
- name: alias.diffc
-
-- name: Read all values from git config
- community.general.git_config:
- list_all: true
- scope: global
-
-- name: When list_all is yes and no scope is specified, you get configuration from all scopes
- community.general.git_config:
- list_all: true
-
-- name: Specify a repository to include local settings
- community.general.git_config:
- list_all: true
- repo: /path/to/repo.git
'''
RETURN = '''
@@ -193,7 +174,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
- list_all=dict(required=False, type='bool', default=False),
+ list_all=dict(required=False, type='bool', default=False, removed_in_version='11.0.0', removed_from_collection='community.general'),
name=dict(type='str'),
repo=dict(type='path'),
file=dict(type='path'),
@@ -222,6 +203,14 @@ def main():
new_value = params['value'] or ''
add_mode = params['add_mode']
+ if not unset and not new_value and not params['list_all']:
+ module.deprecate(
+ 'If state=present, a value must be specified from community.general 11.0.0 on.'
+ ' To read a config value, use the community.general.git_config_info module instead.',
+ version='11.0.0',
+ collection_name='community.general',
+ )
+
scope = determine_scope(params)
cwd = determine_cwd(scope, params)
@@ -263,7 +252,7 @@ def main():
module.exit_json(changed=False, msg='', config_value=old_values[0] if old_values else '')
elif unset and not out:
module.exit_json(changed=False, msg='no setting to unset')
- elif new_value in old_values and (len(old_values) == 1 or add_mode == "add"):
+ elif new_value in old_values and (len(old_values) == 1 or add_mode == "add") and not unset:
module.exit_json(changed=False, msg="")
# Until this point, the git config was just read and in case no change is needed, the module has already exited.
diff --git a/ansible_collections/community/general/plugins/modules/homectl.py b/ansible_collections/community/general/plugins/modules/homectl.py
index ca4c19a87..7751651c8 100644
--- a/ansible_collections/community/general/plugins/modules/homectl.py
+++ b/ansible_collections/community/general/plugins/modules/homectl.py
@@ -17,6 +17,12 @@ short_description: Manage user accounts with systemd-homed
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.
+requirements:
+ - Python 3.12 or earlier
extends_documentation_fragment:
- community.general.attributes
attributes:
@@ -263,12 +269,21 @@ data:
}
'''
-import crypt
import json
-from ansible.module_utils.basic import AnsibleModule
+import traceback
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.basic import jsonify
from ansible.module_utils.common.text.formatters import human_to_bytes
+try:
+ import crypt
+except ImportError:
+ HAS_CRYPT = False
+ CRYPT_IMPORT_ERROR = traceback.format_exc()
+else:
+ HAS_CRYPT = True
+ CRYPT_IMPORT_ERROR = None
+
class Homectl(object):
'''#TODO DOC STRINGS'''
@@ -591,6 +606,12 @@ def main():
]
)
+ if not HAS_CRYPT:
+ module.fail_json(
+ msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
+ exception=CRYPT_IMPORT_ERROR,
+ )
+
homectl = Homectl(module)
homectl.result['state'] = homectl.state
diff --git a/ansible_collections/community/general/plugins/modules/ipa_dnsrecord.py b/ansible_collections/community/general/plugins/modules/ipa_dnsrecord.py
index cb4ce03dd..59475a55b 100644
--- a/ansible_collections/community/general/plugins/modules/ipa_dnsrecord.py
+++ b/ansible_collections/community/general/plugins/modules/ipa_dnsrecord.py
@@ -35,13 +35,14 @@ options:
record_type:
description:
- The type of DNS record name.
- - Currently, 'A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'NS', 'PTR', 'TXT', 'SRV' and 'MX' are supported.
+ - Currently, 'A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'NS', 'PTR', 'TXT', 'SRV', 'MX' and 'SSHFP' are supported.
- "'A6', 'CNAME', 'DNAME' and 'TXT' are added in version 2.5."
- "'SRV' and 'MX' are added in version 2.8."
- "'NS' are added in comunity.general 8.2.0."
+ - "'SSHFP' are added in community.general 9.1.0."
required: false
default: 'A'
- choices: ['A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'MX', 'NS', 'PTR', 'SRV', 'TXT']
+ choices: ['A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'MX', 'NS', 'PTR', 'SRV', 'TXT', 'SSHFP']
type: str
record_value:
description:
@@ -57,6 +58,7 @@ options:
- In the case of 'TXT' record type, this will be a text.
- In the case of 'SRV' record type, this will be a service record.
- In the case of 'MX' record type, this will be a mail exchanger record.
+ - In the case of 'SSHFP' record type, this will be an SSH fingerprint record.
type: str
record_values:
description:
@@ -71,6 +73,7 @@ options:
- In the case of 'TXT' record type, this will be a text.
- In the case of 'SRV' record type, this will be a service record.
- In the case of 'MX' record type, this will be a mail exchanger record.
+ - In the case of 'SSHFP' record type, this will be an SSH fingerprint record.
type: list
elements: str
record_ttl:
@@ -175,6 +178,20 @@ EXAMPLES = r'''
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: ChangeMe!
+
+- name: Retrieve the current sshfp fingerprints
+ ansible.builtin.command: ssh-keyscan -D localhost
+ register: ssh_hostkeys
+
+- name: Update the SSHFP records in DNS
+ community.general.ipa_dnsrecord:
+ name: "{{ inventory_hostname}}"
+ zone_name: example.com
+ record_type: 'SSHFP'
+ record_values: "{{ ssh_hostkeys.stdout.split('\n') | map('split', 'SSHFP ') | map('last') | list }}"
+ ipa_host: ipa.example.com
+ ipa_user: admin
+ ipa_pass: ChangeMe!
'''
RETURN = r'''
@@ -228,6 +245,8 @@ class DNSRecordIPAClient(IPAClient):
item.update(srvrecord=value)
elif details['record_type'] == 'MX':
item.update(mxrecord=value)
+ elif details['record_type'] == 'SSHFP':
+ item.update(sshfprecord=value)
self._post_json(method='dnsrecord_add', name=zone_name, item=item)
@@ -266,6 +285,8 @@ def get_dnsrecord_dict(details=None):
module_dnsrecord.update(srvrecord=details['record_values'])
elif details['record_type'] == 'MX' and details['record_values']:
module_dnsrecord.update(mxrecord=details['record_values'])
+ elif details['record_type'] == 'SSHFP' and details['record_values']:
+ module_dnsrecord.update(sshfprecord=details['record_values'])
if details.get('record_ttl'):
module_dnsrecord.update(dnsttl=details['record_ttl'])
@@ -328,7 +349,7 @@ def ensure(module, client):
def main():
- record_types = ['A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'NS', 'PTR', 'TXT', 'SRV', 'MX']
+ record_types = ['A', 'AAAA', 'A6', 'CNAME', 'DNAME', 'NS', 'PTR', 'TXT', 'SRV', 'MX', 'SSHFP']
argument_spec = ipa_argument_spec()
argument_spec.update(
zone_name=dict(type='str', required=True),
diff --git a/ansible_collections/community/general/plugins/modules/keycloak_client.py b/ansible_collections/community/general/plugins/modules/keycloak_client.py
index 3628e5a51..efaa66e26 100644
--- a/ansible_collections/community/general/plugins/modules/keycloak_client.py
+++ b/ansible_collections/community/general/plugins/modules/keycloak_client.py
@@ -340,6 +340,42 @@ options:
description:
- Override realm authentication flow bindings.
type: dict
+ suboptions:
+ browser:
+ description:
+ - Flow ID of the browser authentication flow.
+ - O(authentication_flow_binding_overrides.browser)
+ and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive.
+ type: str
+
+ browser_name:
+ description:
+ - Flow name of the browser authentication flow.
+ - O(authentication_flow_binding_overrides.browser)
+ and O(authentication_flow_binding_overrides.browser_name) are mutually exclusive.
+ aliases:
+ - browserName
+ type: str
+ version_added: 9.1.0
+
+ direct_grant:
+ description:
+ - Flow ID of the direct grant authentication flow.
+ - O(authentication_flow_binding_overrides.direct_grant)
+ and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive.
+ aliases:
+ - directGrant
+ type: str
+
+ direct_grant_name:
+ description:
+ - Flow name of the direct grant authentication flow.
+ - O(authentication_flow_binding_overrides.direct_grant)
+ and O(authentication_flow_binding_overrides.direct_grant_name) are mutually exclusive.
+ aliases:
+ - directGrantName
+ type: str
+ version_added: 9.1.0
aliases:
- authenticationFlowBindingOverrides
version_added: 3.4.0
@@ -781,6 +817,64 @@ def sanitize_cr(clientrep):
return normalise_cr(result)
+def get_authentication_flow_id(flow_name, realm, kc):
+ """ Get the authentication flow ID based on the flow name, realm, and Keycloak client.
+
+ Args:
+ flow_name (str): The name of the authentication flow.
+ realm (str): The name of the realm.
+ kc (KeycloakClient): The Keycloak client instance.
+
+ Returns:
+ str: The ID of the authentication flow.
+
+ Raises:
+ KeycloakAPIException: If the authentication flow with the given name is not found in the realm.
+ """
+ flow = kc.get_authentication_flow_by_alias(flow_name, realm)
+ if flow:
+ return flow["id"]
+ kc.module.fail_json(msg='Authentification flow %s not found in realm %s' % (flow_name, realm))
+
+
+def flow_binding_from_dict_to_model(newClientFlowBinding, realm, kc):
+ """ Convert a dictionary representing client flow bindings to a model representation.
+
+ Args:
+ newClientFlowBinding (dict): A dictionary containing client flow bindings.
+ realm (str): The name of the realm.
+ kc (KeycloakClient): An instance of the KeycloakClient class.
+
+ Returns:
+ dict: A dictionary representing the model flow bindings. The dictionary has two keys:
+ - "browser" (str or None): The ID of the browser authentication flow binding, or None if not provided.
+ - "direct_grant" (str or None): The ID of the direct grant authentication flow binding, or None if not provided.
+
+ Raises:
+ KeycloakAPIException: If the authentication flow with the given name is not found in the realm.
+
+ """
+
+ modelFlow = {
+ "browser": None,
+ "direct_grant": None
+ }
+
+ for k, v in newClientFlowBinding.items():
+ if not v:
+ continue
+ if k == "browser":
+ modelFlow["browser"] = v
+ elif k == "browser_name":
+ modelFlow["browser"] = get_authentication_flow_id(v, realm, kc)
+ elif k == "direct_grant":
+ modelFlow["direct_grant"] = v
+ elif k == "direct_grant_name":
+ modelFlow["direct_grant"] = get_authentication_flow_id(v, realm, kc)
+
+ return modelFlow
+
+
def main():
"""
Module execution
@@ -799,6 +893,13 @@ def main():
config=dict(type='dict'),
)
+ authentication_flow_spec = dict(
+ browser=dict(type='str'),
+ browser_name=dict(type='str', aliases=['browserName']),
+ direct_grant=dict(type='str', aliases=['directGrant']),
+ direct_grant_name=dict(type='str', aliases=['directGrantName']),
+ )
+
meta_args = dict(
state=dict(default='present', choices=['present', 'absent']),
realm=dict(type='str', default='master'),
@@ -838,7 +939,13 @@ def main():
use_template_scope=dict(type='bool', aliases=['useTemplateScope']),
use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']),
always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']),
- authentication_flow_binding_overrides=dict(type='dict', aliases=['authenticationFlowBindingOverrides']),
+ authentication_flow_binding_overrides=dict(
+ type='dict',
+ aliases=['authenticationFlowBindingOverrides'],
+ options=authentication_flow_spec,
+ required_one_of=[['browser', 'direct_grant', 'browser_name', 'direct_grant_name']],
+ mutually_exclusive=[['browser', 'browser_name'], ['direct_grant', 'direct_grant_name']],
+ ),
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']),
authorization_settings=dict(type='dict', aliases=['authorizationSettings']),
default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']),
@@ -900,6 +1007,8 @@ def main():
# they are not specified
if client_param == 'protocol_mappers':
new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value]
+ elif client_param == 'authentication_flow_binding_overrides':
+ new_param_value = flow_binding_from_dict_to_model(new_param_value, realm, kc)
changeset[camel(client_param)] = new_param_value
diff --git a/ansible_collections/community/general/plugins/modules/keycloak_clientscope.py b/ansible_collections/community/general/plugins/modules/keycloak_clientscope.py
index d24e0f1f2..b962b932c 100644
--- a/ansible_collections/community/general/plugins/modules/keycloak_clientscope.py
+++ b/ansible_collections/community/general/plugins/modules/keycloak_clientscope.py
@@ -301,10 +301,37 @@ end_state:
'''
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
- keycloak_argument_spec, get_token, KeycloakError
+ keycloak_argument_spec, get_token, KeycloakError, is_struct_included
from ansible.module_utils.basic import AnsibleModule
+def normalise_cr(clientscoperep, remove_ids=False):
+ """ Re-sorts any properties where the order so that diff's is minimised, and adds default values where appropriate so that the
+ the change detection is more effective.
+
+ :param clientscoperep: the clientscoperep dict to be sanitized
+ :param remove_ids: If set to true, then the unique ID's of objects is removed to make the diff and checks for changed
+ not alert when the ID's of objects are not usually known, (e.g. for protocol_mappers)
+ :return: normalised clientscoperep dict
+ """
+ # Avoid the dict passed in to be modified
+ clientscoperep = clientscoperep.copy()
+
+ if 'attributes' in clientscoperep:
+ clientscoperep['attributes'] = list(sorted(clientscoperep['attributes']))
+
+ if 'protocolMappers' in clientscoperep:
+ clientscoperep['protocolMappers'] = sorted(clientscoperep['protocolMappers'], key=lambda x: (x.get('name'), x.get('protocol'), x.get('protocolMapper')))
+ for mapper in clientscoperep['protocolMappers']:
+ if remove_ids:
+ mapper.pop('id', None)
+
+ # Set to a default value.
+ mapper['consentRequired'] = mapper.get('consentRequired', False)
+
+ return clientscoperep
+
+
def sanitize_cr(clientscoperep):
""" Removes probably sensitive details from a clientscoperep representation.
@@ -317,7 +344,7 @@ def sanitize_cr(clientscoperep):
if 'attributes' in result:
if 'saml.signing.private.key' in result['attributes']:
result['attributes']['saml.signing.private.key'] = 'no_log'
- return result
+ return normalise_cr(result)
def main():
@@ -458,6 +485,13 @@ def main():
result['diff'] = dict(before=sanitize_cr(before_clientscope), after=sanitize_cr(desired_clientscope))
if module.check_mode:
+ # We can only compare the current clientscope with the proposed updates we have
+ before_norm = normalise_cr(before_clientscope, remove_ids=True)
+ desired_norm = normalise_cr(desired_clientscope, remove_ids=True)
+ if module._diff:
+ result['diff'] = dict(before=sanitize_cr(before_norm),
+ after=sanitize_cr(desired_norm))
+ result['changed'] = not is_struct_included(desired_norm, before_norm)
module.exit_json(**result)
# do the update
diff --git a/ansible_collections/community/general/plugins/modules/launchd.py b/ansible_collections/community/general/plugins/modules/launchd.py
index e5942ea7c..a6427bdb2 100644
--- a/ansible_collections/community/general/plugins/modules/launchd.py
+++ b/ansible_collections/community/general/plugins/modules/launchd.py
@@ -514,7 +514,8 @@ def main():
result['status']['current_pid'] != result['status']['previous_pid']):
result['changed'] = True
if module.check_mode:
- result['changed'] = True
+ if result['status']['current_state'] != action:
+ result['changed'] = True
module.exit_json(**result)
diff --git a/ansible_collections/community/general/plugins/modules/openbsd_pkg.py b/ansible_collections/community/general/plugins/modules/openbsd_pkg.py
index c83113611..69ac7bff8 100644
--- a/ansible_collections/community/general/plugins/modules/openbsd_pkg.py
+++ b/ansible_collections/community/general/plugins/modules/openbsd_pkg.py
@@ -24,7 +24,10 @@ attributes:
check_mode:
support: full
diff_mode:
- support: none
+ support: partial
+ version_added: 9.1.0
+ details:
+ - Only works when check mode is not enabled.
options:
name:
description:
@@ -159,6 +162,20 @@ def execute_command(cmd, module):
return module.run_command(cmd_args, environ_update={'TERM': 'dumb'})
+def get_all_installed(module):
+ """
+ Get all installed packaged. Used to support diff mode
+ """
+ command = 'pkg_info -Iq'
+
+ rc, stdout, stderr = execute_command(command, module)
+
+ if stderr:
+ module.fail_json(msg="failed in get_all_installed(): %s" % stderr)
+
+ return stdout
+
+
# Function used to find out if a package is currently installed.
def get_package_state(names, pkg_spec, module):
info_cmd = 'pkg_info -Iq'
@@ -573,10 +590,13 @@ def main():
result['name'] = name
result['state'] = state
result['build'] = build
+ result['diff'] = {}
# The data structure used to keep track of package information.
pkg_spec = {}
+ new_package_list = original_package_list = get_all_installed(module)
+
if build is True:
if not os.path.isdir(ports_dir):
module.fail_json(msg="the ports source directory %s does not exist" % (ports_dir))
@@ -661,6 +681,10 @@ def main():
result['changed'] = combined_changed
+ if result['changed'] and not module.check_mode:
+ new_package_list = get_all_installed(module)
+ result['diff'] = dict(before=original_package_list, after=new_package_list)
+
module.exit_json(**result)
diff --git a/ansible_collections/community/general/plugins/modules/pacman.py b/ansible_collections/community/general/plugins/modules/pacman.py
index 7f67b9103..f13bde317 100644
--- a/ansible_collections/community/general/plugins/modules/pacman.py
+++ b/ansible_collections/community/general/plugins/modules/pacman.py
@@ -367,8 +367,9 @@ class Pacman(object):
self.install_packages(pkgs)
self.success()
- # This shouldn't happen...
- self.fail("This is a bug")
+ # This happens if an empty list has been provided for name
+ self.add_exit_infos(msg='Nothing to do')
+ self.success()
def install_packages(self, pkgs):
pkgs_to_install = []
diff --git a/ansible_collections/community/general/plugins/modules/proxmox_kvm.py b/ansible_collections/community/general/plugins/modules/proxmox_kvm.py
index 9fe805c7a..71cbb51fc 100644
--- a/ansible_collections/community/general/plugins/modules/proxmox_kvm.py
+++ b/ansible_collections/community/general/plugins/modules/proxmox_kvm.py
@@ -174,6 +174,7 @@ options:
- Allow to force stop VM.
- Can be used with states V(stopped), V(restarted), and V(absent).
- This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(false).
+ - Requires parameter O(archive).
type: bool
format:
description:
diff --git a/ansible_collections/community/general/plugins/modules/proxmox_vm_info.py b/ansible_collections/community/general/plugins/modules/proxmox_vm_info.py
index 39d8307a4..e10b9dff6 100644
--- a/ansible_collections/community/general/plugins/modules/proxmox_vm_info.py
+++ b/ansible_collections/community/general/plugins/modules/proxmox_vm_info.py
@@ -57,6 +57,13 @@ options:
- pending
default: none
version_added: 8.1.0
+ network:
+ description:
+ - Whether to retrieve the current network status.
+ - Requires enabled/running qemu-guest-agent on qemu VMs.
+ type: bool
+ default: false
+ version_added: 9.1.0
extends_documentation_fragment:
- community.general.proxmox.actiongroup_proxmox
- community.general.proxmox.documentation
@@ -172,7 +179,7 @@ class ProxmoxVmInfoAnsible(ProxmoxAnsible):
msg="Failed to retrieve VMs information from cluster resources: %s" % e
)
- def get_vms_from_nodes(self, cluster_machines, type, vmid=None, name=None, node=None, config=None):
+ def get_vms_from_nodes(self, cluster_machines, type, vmid=None, name=None, node=None, config=None, network=False):
# Leave in dict only machines that user wants to know about
filtered_vms = {
vm: info for vm, info in cluster_machines.items() if not (
@@ -201,17 +208,23 @@ class ProxmoxVmInfoAnsible(ProxmoxAnsible):
config_type = 0 if config == "pending" else 1
# GET /nodes/{node}/qemu/{vmid}/config current=[0/1]
desired_vm["config"] = call_vm_getter(this_vm_id).config().get(current=config_type)
+ if network:
+ if type == "qemu":
+ desired_vm["network"] = call_vm_getter(this_vm_id).agent("network-get-interfaces").get()['result']
+ elif type == "lxc":
+ desired_vm["network"] = call_vm_getter(this_vm_id).interfaces.get()
+
return filtered_vms
- def get_qemu_vms(self, cluster_machines, vmid=None, name=None, node=None, config=None):
+ def get_qemu_vms(self, cluster_machines, vmid=None, name=None, node=None, config=None, network=False):
try:
- return self.get_vms_from_nodes(cluster_machines, "qemu", vmid, name, node, config)
+ return self.get_vms_from_nodes(cluster_machines, "qemu", vmid, name, node, config, network)
except Exception as e:
self.module.fail_json(msg="Failed to retrieve QEMU VMs information: %s" % e)
- def get_lxc_vms(self, cluster_machines, vmid=None, name=None, node=None, config=None):
+ def get_lxc_vms(self, cluster_machines, vmid=None, name=None, node=None, config=None, network=False):
try:
- return self.get_vms_from_nodes(cluster_machines, "lxc", vmid, name, node, config)
+ return self.get_vms_from_nodes(cluster_machines, "lxc", vmid, name, node, config, network)
except Exception as e:
self.module.fail_json(msg="Failed to retrieve LXC VMs information: %s" % e)
@@ -229,6 +242,7 @@ def main():
type="str", choices=["none", "current", "pending"],
default="none", required=False
),
+ network=dict(type="bool", default=False, required=False),
)
module_args.update(vm_info_args)
@@ -245,6 +259,7 @@ def main():
vmid = module.params["vmid"]
name = module.params["name"]
config = module.params["config"]
+ network = module.params["network"]
result = dict(changed=False)
@@ -256,12 +271,12 @@ def main():
vms = {}
if type == "lxc":
- vms = proxmox.get_lxc_vms(cluster_machines, vmid, name, node, config)
+ vms = proxmox.get_lxc_vms(cluster_machines, vmid, name, node, config, network)
elif type == "qemu":
- vms = proxmox.get_qemu_vms(cluster_machines, vmid, name, node, config)
+ vms = proxmox.get_qemu_vms(cluster_machines, vmid, name, node, config, network)
else:
- vms = proxmox.get_qemu_vms(cluster_machines, vmid, name, node, config)
- vms.update(proxmox.get_lxc_vms(cluster_machines, vmid, name, node, config))
+ vms = proxmox.get_qemu_vms(cluster_machines, vmid, name, node, config, network)
+ vms.update(proxmox.get_lxc_vms(cluster_machines, vmid, name, node, config, network))
result["proxmox_vms"] = [info for vm, info in sorted(vms.items())]
module.exit_json(**result)
diff --git a/ansible_collections/community/general/plugins/modules/redfish_command.py b/ansible_collections/community/general/plugins/modules/redfish_command.py
index d351e7c1d..0f7a64b81 100644
--- a/ansible_collections/community/general/plugins/modules/redfish_command.py
+++ b/ansible_collections/community/general/plugins/modules/redfish_command.py
@@ -288,6 +288,20 @@ options:
type: str
choices: [ ResetAll, PreserveNetworkAndUsers, PreserveNetwork ]
version_added: 8.6.0
+ wait:
+ required: false
+ description:
+ - Block until the service is ready again.
+ type: bool
+ default: false
+ version_added: 9.1.0
+ wait_timeout:
+ required: false
+ description:
+ - How long to block until the service is ready again before giving up.
+ type: int
+ default: 120
+ version_added: 9.1.0
author:
- "Jose Delarosa (@jose-delarosa)"
@@ -685,6 +699,16 @@ EXAMPLES = '''
username: "{{ username }}"
password: "{{ password }}"
+ - name: Restart manager power gracefully and wait for it to be available
+ community.general.redfish_command:
+ category: Manager
+ command: GracefulRestart
+ resource_id: BMC
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ wait: True
+
- name: Restart manager power gracefully
community.general.redfish_command:
category: Manager
@@ -841,7 +865,9 @@ def main():
),
strip_etag_quotes=dict(type='bool', default=False),
reset_to_defaults_mode=dict(choices=['ResetAll', 'PreserveNetworkAndUsers', 'PreserveNetwork']),
- bios_attributes=dict(type="dict")
+ bios_attributes=dict(type="dict"),
+ wait=dict(type='bool', default=False),
+ wait_timeout=dict(type='int', default=120),
),
required_together=[
('username', 'password'),
@@ -1016,7 +1042,7 @@ def main():
command = 'PowerGracefulRestart'
if command.startswith('Power'):
- result = rf_utils.manage_manager_power(command)
+ result = rf_utils.manage_manager_power(command, module.params['wait'], module.params['wait_timeout'])
elif command == 'ClearLogs':
result = rf_utils.clear_logs()
elif command == 'VirtualMediaInsert':
diff --git a/ansible_collections/community/general/plugins/modules/redfish_info.py b/ansible_collections/community/general/plugins/modules/redfish_info.py
index 3b594b7a2..efcb34f01 100644
--- a/ansible_collections/community/general/plugins/modules/redfish_info.py
+++ b/ansible_collections/community/general/plugins/modules/redfish_info.py
@@ -359,6 +359,16 @@ EXAMPLES = '''
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
+
+ - name: Check the availability of the service with a timeout of 5 seconds
+ community.general.redfish_info:
+ category: Service
+ command: CheckAvailability
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ timeout: 5
+ register: result
'''
RETURN = '''
@@ -385,6 +395,7 @@ CATEGORY_COMMANDS_ALL = {
"GetUpdateStatus"],
"Manager": ["GetManagerNicInventory", "GetVirtualMedia", "GetLogs", "GetNetworkProtocols",
"GetHealthReport", "GetHostInterfaces", "GetManagerInventory", "GetServiceIdentification"],
+ "Service": ["CheckAvailability"],
}
CATEGORY_COMMANDS_DEFAULT = {
@@ -393,7 +404,8 @@ CATEGORY_COMMANDS_DEFAULT = {
"Accounts": "ListUsers",
"Update": "GetFirmwareInventory",
"Sessions": "GetSessions",
- "Manager": "GetManagerNicInventory"
+ "Manager": "GetManagerNicInventory",
+ "Service": "CheckAvailability",
}
@@ -473,7 +485,13 @@ def main():
module.fail_json(msg="Invalid Category: %s" % category)
# Organize by Categories / Commands
- if category == "Systems":
+ if category == "Service":
+ # service-level commands are always available
+ for command in command_list:
+ if command == "CheckAvailability":
+ result["service"] = rf_utils.check_service_availability()
+
+ elif category == "Systems":
# execute only if we find a Systems resource
resource = rf_utils._find_systems_resource()
if resource['ret'] is False:
diff --git a/ansible_collections/community/general/plugins/modules/redis_info.py b/ansible_collections/community/general/plugins/modules/redis_info.py
index f352d53d7..c75abcf21 100644
--- a/ansible_collections/community/general/plugins/modules/redis_info.py
+++ b/ansible_collections/community/general/plugins/modules/redis_info.py
@@ -30,6 +30,11 @@ options:
version_added: 7.5.0
ca_certs:
version_added: 7.5.0
+ cluster:
+ default: false
+ description: Get informations about cluster status as RV(cluster).
+ type: bool
+ version_added: 9.1.0
seealso:
- module: community.general.redis
author: "Pavlo Bashynskyi (@levonet)"
@@ -43,6 +48,15 @@ EXAMPLES = r'''
- name: Print server information
ansible.builtin.debug:
var: result.info
+
+- name: Get server cluster information
+ community.general.redis_info:
+ cluster: true
+ register: result
+
+- name: Print server cluster information
+ ansible.builtin.debug:
+ var: result.cluster_info
'''
RETURN = r'''
@@ -178,6 +192,25 @@ info:
"used_memory_scripts_human": "0B",
"used_memory_startup": 791264
}
+cluster:
+ description: The default set of cluster information sections U(https://redis.io/commands/cluster-info).
+ returned: success if O(cluster=true)
+ version_added: 9.1.0
+ type: dict
+ sample: {
+ "cluster_state": ok,
+ "cluster_slots_assigned": 16384,
+ "cluster_slots_ok": 16384,
+ "cluster_slots_pfail": 0,
+ "cluster_slots_fail": 0,
+ "cluster_known_nodes": 6,
+ "cluster_size": 3,
+ "cluster_current_epoch": 6,
+ "cluster_my_epoch": 2,
+ "cluster_stats_messages_sent": 1483972,
+ "cluster_stats_messages_received": 1483968,
+ "total_cluster_links_buffer_limit_exceeded": 0
+ }
'''
import traceback
@@ -202,14 +235,19 @@ def redis_client(**client_params):
# Module execution.
def main():
+ module_args = dict(
+ cluster=dict(type='bool', default=False),
+ )
+ module_args.update(redis_auth_argument_spec(tls_default=False))
module = AnsibleModule(
- argument_spec=redis_auth_argument_spec(tls_default=False),
+ argument_spec=module_args,
supports_check_mode=True,
)
fail_imports(module, module.params['tls'])
redis_params = redis_auth_params(module)
+ cluster = module.params['cluster']
# Connect and check
client = redis_client(**redis_params)
@@ -219,7 +257,13 @@ def main():
module.fail_json(msg="unable to connect to database: %s" % to_native(e), exception=traceback.format_exc())
info = client.info()
- module.exit_json(changed=False, info=info)
+
+ result = dict(changed=False, info=info)
+
+ if cluster:
+ result['cluster_info'] = client.execute_command('CLUSTER INFO')
+
+ module.exit_json(**result)
if __name__ == '__main__':
diff --git a/ansible_collections/community/general/plugins/modules/udm_user.py b/ansible_collections/community/general/plugins/modules/udm_user.py
index dcbf0ec85..5a2e09049 100644
--- a/ansible_collections/community/general/plugins/modules/udm_user.py
+++ b/ansible_collections/community/general/plugins/modules/udm_user.py
@@ -20,6 +20,12 @@ description:
- "This module allows to manage posix users on a univention corporate
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.
+requirements:
+ - Python 3.12 or earlier
extends_documentation_fragment:
- community.general.attributes
attributes:
@@ -324,10 +330,10 @@ EXAMPLES = '''
RETURN = '''# '''
-import crypt
from datetime import date, timedelta
+import traceback
-from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.univention_umc import (
umc_module_for_add,
umc_module_for_edit,
@@ -335,6 +341,15 @@ from ansible_collections.community.general.plugins.module_utils.univention_umc i
base_dn,
)
+try:
+ import crypt
+except ImportError:
+ HAS_CRYPT = False
+ CRYPT_IMPORT_ERROR = traceback.format_exc()
+else:
+ HAS_CRYPT = True
+ CRYPT_IMPORT_ERROR = None
+
def main():
expiry = date.strftime(date.today() + timedelta(days=365), "%Y-%m-%d")
@@ -451,6 +466,13 @@ def main():
('state', 'present', ['firstname', 'lastname', 'password'])
])
)
+
+ if not HAS_CRYPT:
+ module.fail_json(
+ msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
+ exception=CRYPT_IMPORT_ERROR,
+ )
+
username = module.params['username']
position = module.params['position']
ou = module.params['ou']
diff --git a/ansible_collections/community/general/plugins/plugin_utils/keys_filter.py b/ansible_collections/community/general/plugins/plugin_utils/keys_filter.py
new file mode 100644
index 000000000..94234a15d
--- /dev/null
+++ b/ansible_collections/community/general/plugins/plugin_utils/keys_filter.py
@@ -0,0 +1,141 @@
+# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com>
+# Copyright (c) 2024 Felix Fontein <felix@fontein.de>
+# 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
+
+import re
+
+from ansible.errors import AnsibleFilterError
+from ansible.module_utils.six import string_types
+from ansible.module_utils.common._collections_compat import Mapping, Sequence
+
+
+def _keys_filter_params(data, matching_parameter):
+ """test parameters:
+ * data must be a list of dictionaries. All keys must be strings.
+ * matching_parameter is member of a list.
+ """
+
+ mp = matching_parameter
+ ml = ['equal', 'starts_with', 'ends_with', 'regex']
+
+ if not isinstance(data, Sequence):
+ msg = "First argument must be a list. %s is %s"
+ raise AnsibleFilterError(msg % (data, type(data)))
+
+ for elem in data:
+ if not isinstance(elem, Mapping):
+ msg = "The data items must be dictionaries. %s is %s"
+ raise AnsibleFilterError(msg % (elem, type(elem)))
+
+ for elem in data:
+ if not all(isinstance(item, string_types) for item in elem.keys()):
+ msg = "Top level keys must be strings. keys: %s"
+ raise AnsibleFilterError(msg % elem.keys())
+
+ if mp not in ml:
+ msg = "The matching_parameter must be one of %s. matching_parameter=%s"
+ raise AnsibleFilterError(msg % (ml, mp))
+
+ return
+
+
+def _keys_filter_target_str(target, matching_parameter):
+ """
+ Test:
+ * target is a non-empty string or list.
+ * If target is list all items are strings.
+ * target is a string or list with single string if matching_parameter=regex.
+ Convert target and return:
+ * tuple of unique target items, or
+ * tuple with single item, or
+ * compiled regex if matching_parameter=regex.
+ """
+
+ if not isinstance(target, Sequence):
+ msg = "The target must be a string or a list. target is %s."
+ raise AnsibleFilterError(msg % type(target))
+
+ if len(target) == 0:
+ msg = "The target can't be empty."
+ raise AnsibleFilterError(msg)
+
+ if isinstance(target, list):
+ for elem in target:
+ if not isinstance(elem, string_types):
+ msg = "The target items must be strings. %s is %s"
+ raise AnsibleFilterError(msg % (elem, type(elem)))
+
+ if matching_parameter == 'regex':
+ if isinstance(target, string_types):
+ r = target
+ else:
+ if len(target) > 1:
+ msg = "Single item is required in the target list if matching_parameter=regex."
+ raise AnsibleFilterError(msg)
+ else:
+ r = target[0]
+ try:
+ tt = re.compile(r)
+ except re.error:
+ msg = "The target must be a valid regex if matching_parameter=regex. target is %s"
+ raise AnsibleFilterError(msg % r)
+ elif isinstance(target, string_types):
+ tt = (target, )
+ else:
+ tt = tuple(set(target))
+
+ return tt
+
+
+def _keys_filter_target_dict(target, matching_parameter):
+ """
+ Test:
+ * target is a list of dictionaries with attributes 'after' and 'before'.
+ * Attributes 'before' must be valid regex if matching_parameter=regex.
+ * Otherwise, the attributes 'before' must be strings.
+ Convert target and return:
+ * iterator that aggregates attributes 'before' and 'after', or
+ * iterator that aggregates compiled regex of attributes 'before' and 'after' if matching_parameter=regex.
+ """
+
+ if not isinstance(target, list):
+ msg = "The target must be a list. target is %s."
+ raise AnsibleFilterError(msg % (target, type(target)))
+
+ if len(target) == 0:
+ msg = "The target can't be empty."
+ raise AnsibleFilterError(msg)
+
+ for elem in target:
+ if not isinstance(elem, Mapping):
+ msg = "The target items must be dictionaries. %s is %s"
+ raise AnsibleFilterError(msg % (elem, type(elem)))
+ if not all(k in elem for k in ('before', 'after')):
+ msg = "All dictionaries in target must include attributes: after, before."
+ raise AnsibleFilterError(msg)
+ if not isinstance(elem['before'], string_types):
+ msg = "The attributes before must be strings. %s is %s"
+ raise AnsibleFilterError(msg % (elem['before'], type(elem['before'])))
+ if not isinstance(elem['after'], string_types):
+ msg = "The attributes after must be strings. %s is %s"
+ raise AnsibleFilterError(msg % (elem['after'], type(elem['after'])))
+
+ before = [d['before'] for d in target]
+ after = [d['after'] for d in target]
+
+ if matching_parameter == 'regex':
+ try:
+ tr = map(re.compile, before)
+ tz = list(zip(tr, after))
+ except re.error:
+ msg = ("The attributes before must be valid regex if matching_parameter=regex."
+ " Not all items are valid regex in: %s")
+ raise AnsibleFilterError(msg % before)
+ else:
+ tz = list(zip(before, after))
+
+ return tz
diff --git a/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml
index 1ecd9980d..5c4af6d16 100644
--- a/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml
@@ -4,10 +4,16 @@
# SPDX-License-Identifier: GPL-3.0-or-later
###################################################
+- name: Make directory install_c
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/install_c"
+ state: directory
+
- name: Install collection netbox.netbox
community.general.ansible_galaxy_install:
type: collection
name: netbox.netbox
+ dest: "{{ remote_tmp_dir }}/install_c"
register: install_c0
- name: Assert collection netbox.netbox was installed
@@ -20,6 +26,7 @@
community.general.ansible_galaxy_install:
type: collection
name: netbox.netbox
+ dest: "{{ remote_tmp_dir }}/install_c"
register: install_c1
- name: Assert collection was not installed
@@ -28,10 +35,16 @@
- install_c1 is not changed
###################################################
+- name: Make directory install_r
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/install_r"
+ state: directory
+
- name: Install role ansistrano.deploy
community.general.ansible_galaxy_install:
type: role
name: ansistrano.deploy
+ dest: "{{ remote_tmp_dir }}/install_r"
register: install_r0
- name: Assert collection ansistrano.deploy was installed
@@ -44,6 +57,7 @@
community.general.ansible_galaxy_install:
type: role
name: ansistrano.deploy
+ dest: "{{ remote_tmp_dir }}/install_r"
register: install_r1
- name: Assert role was not installed
@@ -86,3 +100,44 @@
assert:
that:
- install_rq1 is not changed
+
+###################################################
+- name: Make directory upgrade_c
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/upgrade_c"
+ state: directory
+
+- name: Install collection netbox.netbox 3.17.0
+ community.general.ansible_galaxy_install:
+ type: collection
+ name: netbox.netbox:3.17.0
+ dest: "{{ remote_tmp_dir }}/upgrade_c"
+ register: upgrade_c0
+
+- name: Assert collection netbox.netbox was installed
+ assert:
+ that:
+ - upgrade_c0 is changed
+ - '"netbox.netbox" in upgrade_c0.new_collections'
+
+- name: Upgrade collection netbox.netbox
+ community.general.ansible_galaxy_install:
+ state: latest
+ type: collection
+ name: netbox.netbox
+ dest: "{{ remote_tmp_dir }}/upgrade_c"
+ register: upgrade_c1
+
+- name: Upgrade collection netbox.netbox (again)
+ community.general.ansible_galaxy_install:
+ state: latest
+ type: collection
+ name: netbox.netbox
+ dest: "{{ remote_tmp_dir }}/upgrade_c"
+ register: upgrade_c2
+
+- name: Assert collection was not installed
+ assert:
+ that:
+ - upgrade_c1 is changed
+ - upgrade_c2 is not changed
diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
index 29f27c3fd..89f13960a 100644
--- a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
@@ -16,6 +16,7 @@
- block:
- import_tasks: test_general.yml
- import_tasks: test_version.yml
+ - import_tasks: test_directory.yml
environment: "{{ cargo_environment }}"
when: has_cargo | default(false)
- import_tasks: test_rustup_cargo.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_directory.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_directory.yml
new file mode 100644
index 000000000..f4275ede6
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_directory.yml
@@ -0,0 +1,122 @@
+---
+# Copyright (c) 2024 Colin Nolan <cn580@alumni.york.ac.uk>
+# 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 temp directory
+ tempfile:
+ state: directory
+ register: temp_directory
+
+- name: Test block
+ vars:
+ manifest_path: "{{ temp_directory.path }}/Cargo.toml"
+ package_name: hello-world-directory-test
+ block:
+ - name: Initialize package
+ ansible.builtin.command:
+ cmd: "cargo init --name {{ package_name }}"
+ args:
+ chdir: "{{ temp_directory.path }}"
+
+ - name: Set package version (1.0.0)
+ ansible.builtin.lineinfile:
+ path: "{{ manifest_path }}"
+ regexp: '^version = ".*"$'
+ line: 'version = "1.0.0"'
+
+ - name: Ensure package is uninstalled
+ community.general.cargo:
+ name: "{{ package_name }}"
+ state: absent
+ directory: "{{ temp_directory.path }}"
+ register: uninstall_absent
+
+ - name: Install package
+ community.general.cargo:
+ name: "{{ package_name }}"
+ directory: "{{ temp_directory.path }}"
+ register: install_absent
+
+ - name: Change package version (1.0.1)
+ ansible.builtin.lineinfile:
+ path: "{{ manifest_path }}"
+ regexp: '^version = ".*"$'
+ line: 'version = "1.0.1"'
+
+ - name: Install package again (present)
+ community.general.cargo:
+ name: "{{ package_name }}"
+ state: present
+ directory: "{{ temp_directory.path }}"
+ register: install_present_state
+
+ - name: Install package again (latest)
+ community.general.cargo:
+ name: "{{ package_name }}"
+ state: latest
+ directory: "{{ temp_directory.path }}"
+ register: install_latest_state
+
+ - name: Change package version (2.0.0)
+ ansible.builtin.lineinfile:
+ path: "{{ manifest_path }}"
+ regexp: '^version = ".*"$'
+ line: 'version = "2.0.0"'
+
+ - name: Install package with given version (matched)
+ community.general.cargo:
+ name: "{{ package_name }}"
+ version: "2.0.0"
+ directory: "{{ temp_directory.path }}"
+ register: install_given_version_matched
+
+ - name: Install package with given version (unmatched)
+ community.general.cargo:
+ name: "{{ package_name }}"
+ version: "2.0.1"
+ directory: "{{ temp_directory.path }}"
+ register: install_given_version_unmatched
+ ignore_errors: true
+
+ - name: Uninstall package
+ community.general.cargo:
+ name: "{{ package_name }}"
+ state: absent
+ directory: "{{ temp_directory.path }}"
+ register: uninstall_present
+
+ - name: Install non-existant package
+ community.general.cargo:
+ name: "{{ package_name }}-non-existant"
+ state: present
+ directory: "{{ temp_directory.path }}"
+ register: install_non_existant
+ ignore_errors: true
+
+ - name: Install non-existant source directory
+ community.general.cargo:
+ name: "{{ package_name }}"
+ state: present
+ directory: "{{ temp_directory.path }}/non-existant"
+ register: install_non_existant_source
+ ignore_errors: true
+
+ always:
+ - name: Remove temp directory
+ file:
+ path: "{{ temp_directory.path }}"
+ state: absent
+
+- name: Check assertions
+ assert:
+ that:
+ - uninstall_absent is not changed
+ - install_absent is changed
+ - install_present_state is not changed
+ - install_latest_state is changed
+ - install_given_version_matched is changed
+ - install_given_version_unmatched is failed
+ - uninstall_present is changed
+ - install_non_existant is failed
+ - install_non_existant_source is failed
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_check.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_check.yml
new file mode 100644
index 000000000..e1229c794
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_check.yml
@@ -0,0 +1,114 @@
+---
+# Copyright (c) 2024, Michael Ilg (@Ilgmi)
+# 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 a service
+ community.general.consul_agent_service:
+ name: nginx
+ service_port: 80
+ address: localhost
+ tags:
+ - http
+ meta:
+ nginx_version: 1.25.3
+ register: result
+
+- set_fact:
+ nginx_service: "{{result.service}}"
+
+- assert:
+ that:
+ - result is changed
+ - result.service.ID is defined
+
+- name: Add a check for service
+ community.general.consul_agent_check:
+ name: nginx_check
+ id: nginx_check
+ interval: 30s
+ http: http://localhost:80/morestatus
+ notes: "Nginx Check"
+ service_id: "{{ nginx_service.ID }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.check is defined
+ - result.check.CheckID == 'nginx_check'
+ - result.check.ServiceID == 'nginx'
+ - result.check.Interval == '30s'
+ - result.check.Type == 'http'
+ - result.check.Notes == 'Nginx Check'
+
+- set_fact:
+ nginx_service_check: "{{ result.check }}"
+
+- name: Update check for service
+ community.general.consul_agent_check:
+ name: "{{ nginx_service_check.Name }}"
+ id: "{{ nginx_service_check.CheckID }}"
+ interval: 60s
+ http: http://localhost:80/morestatus
+ notes: "New Nginx Check"
+ service_id: "{{ nginx_service.ID }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.check is defined
+ - result.check.CheckID == 'nginx_check'
+ - result.check.ServiceID == 'nginx'
+ - result.check.Interval == '1m0s'
+ - result.check.Type == 'http'
+ - result.check.Notes == 'New Nginx Check'
+
+- name: Remove check
+ community.general.consul_agent_check:
+ id: "{{ nginx_service_check.Name }}"
+ state: absent
+ service_id: "{{ nginx_service.ID }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result is not failed
+ - result.operation == 'remove'
+
+- name: Add a check
+ community.general.consul_agent_check:
+ name: check
+ id: check
+ interval: 30s
+ tcp: localhost:80
+ notes: "check"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.check is defined
+
+- name: Update a check
+ community.general.consul_agent_check:
+ name: check
+ id: check
+ interval: 60s
+ tcp: localhost:80
+ notes: "check"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.check is defined
+ - result.check.Interval == '1m0s'
+
+- name: Remove check
+ community.general.consul_agent_check:
+ id: check
+ state: absent
+ register: result \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_service.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_service.yml
new file mode 100644
index 000000000..95270f74b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_agent_service.yml
@@ -0,0 +1,89 @@
+---
+# Copyright (c) 2024, Michael Ilg (@Ilgmi)
+# 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 a service
+ community.general.consul_agent_service:
+ name: nginx
+ service_port: 80
+ address: localhost
+ tags:
+ - http
+ meta:
+ nginx_version: 1.25.3
+ register: result
+
+- set_fact:
+ nginx_service: "{{result.service}}"
+
+- assert:
+ that:
+ - result is changed
+ - result.service.ID is defined
+ - result.service.Service == 'nginx'
+ - result.service.Address == 'localhost'
+ - result.service.Port == 80
+ - result.service.Tags[0] == 'http'
+ - result.service.Meta.nginx_version is defined
+ - result.service.Meta.nginx_version == '1.25.3'
+ - result.service.ContentHash is defined
+
+- name: Update service
+ community.general.consul_agent_service:
+ id: "{{ nginx_service.ID }}"
+ name: "{{ nginx_service.Service }}"
+ service_port: 8080
+ address: 127.0.0.1
+ tags:
+ - http
+ - new_tag
+ meta:
+ nginx_version: 1.0.0
+ nginx: 1.25.3
+ register: result
+- assert:
+ that:
+ - result is changed
+ - result.service.ID is defined
+ - result.service.Service == 'nginx'
+ - result.service.Address == '127.0.0.1'
+ - result.service.Port == 8080
+ - result.service.Tags[0] == 'http'
+ - result.service.Tags[1] == 'new_tag'
+ - result.service.Meta.nginx_version is defined
+ - result.service.Meta.nginx_version == '1.0.0'
+ - result.service.Meta.nginx is defined
+ - result.service.Meta.nginx == '1.25.3'
+ - result.service.ContentHash is defined
+
+- name: Update service not changed when updating again without changes
+ community.general.consul_agent_service:
+ id: "{{ nginx_service.ID }}"
+ name: "{{ nginx_service.Service }}"
+ service_port: 8080
+ address: 127.0.0.1
+ tags:
+ - http
+ - new_tag
+ meta:
+ nginx_version: 1.0.0
+ nginx: 1.25.3
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.operation is not defined
+
+- name: Remove service
+ community.general.consul_agent_service:
+ id: "{{ nginx_service.ID }}"
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result is not failed
+ - result.operation == 'remove' \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
index 6fef2b998..0ac58fc40 100644
--- a/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
@@ -97,6 +97,8 @@
- import_tasks: consul_token.yml
- import_tasks: consul_auth_method.yml
- import_tasks: consul_binding_rule.yml
+ - import_tasks: consul_agent_service.yml
+ - import_tasks: consul_agent_check.yml
module_defaults:
group/community.general.consul:
token: "{{ consul_management_token }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/aliases b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/aliases
new file mode 100644
index 000000000..12d1d6617
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml
new file mode 100644
index 000000000..94825c9d6
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml
@@ -0,0 +1,79 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Debug ansible_version
+ ansible.builtin.debug:
+ var: ansible_version
+ when: not quite_test | d(true) | bool
+ tags: ansible_version
+
+- name: Test keep keys equal (default)
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ list1 | community.general.keep_keys(target=tt) }}"
+ tt: [k0_x0, k1_x1]
+ tags: equal_default
+
+- name: Test keep keys regex string
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ list1 | community.general.keep_keys(target=tt, matching_parameter=mp) }}"
+ mp: regex
+ tt: '^.*[01]_x.*$'
+ tags: regex_string
+
+- name: Test keep keys targets1
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ loop: "{{ targets1 }}"
+ loop_control:
+ label: "{{ item.mp }}: {{ item.tt }}"
+ vars:
+ rr: "{{ list1 | community.general.keep_keys(target=item.tt, matching_parameter=item.mp) }}"
+ tags: targets1
+
+- name: Test keep keys targets2
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result2) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ loop: "{{ targets2 }}"
+ loop_control:
+ label: "{{ item.mp }}: {{ item.tt }}"
+ vars:
+ rr: "{{ list2 | community.general.keep_keys(target=item.tt, matching_parameter=item.mp) }}"
+ tags: targets2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/main.yml
new file mode 100644
index 000000000..23457d1e1
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/tasks/main.yml
@@ -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
+
+- name: Test keep_keys
+ import_tasks: keep_keys.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/vars/main.yml
new file mode 100644
index 000000000..b25325253
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_keep_keys/vars/main.yml
@@ -0,0 +1,33 @@
+---
+# 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
+
+targets1:
+ - {mp: equal, tt: [k0_x0, k1_x1]}
+ - {mp: starts_with, tt: [k0, k1]}
+ - {mp: ends_with, tt: [x0, x1]}
+ - {mp: regex, tt: ['^.*[01]_x.*$']}
+ - {mp: regex, tt: '^.*[01]_x.*$'}
+
+list1:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+result1:
+ - {k0_x0: A0, k1_x1: B0}
+ - {k0_x0: A1, k1_x1: B1}
+
+targets2:
+ - {mp: equal, tt: k0_x0}
+ - {mp: starts_with, tt: k0}
+ - {mp: ends_with, tt: x0}
+ - {mp: regex, tt: '^.*0_x.*$'}
+
+list2:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+result2:
+ - {k0_x0: A0}
+ - {k0_x0: A1}
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/aliases b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/aliases
new file mode 100644
index 000000000..12d1d6617
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/main.yml
new file mode 100644
index 000000000..d4215d8c5
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/main.yml
@@ -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
+
+- name: Test remove_keys
+ import_tasks: remove_keys.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/remove_keys.yml b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/remove_keys.yml
new file mode 100644
index 000000000..121cd88cf
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/tasks/remove_keys.yml
@@ -0,0 +1,79 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Debug ansible_version
+ ansible.builtin.debug:
+ var: ansible_version
+ when: not quite_test | d(true) | bool
+ tags: ansible_version
+
+- name: Test remove keys equal (default)
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ list1 | community.general.remove_keys(target=tt) }}"
+ tt: [k0_x0, k1_x1]
+ tags: equal_default
+
+- name: Test remove keys regex string
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ list1 | community.general.remove_keys(target=tt, matching_parameter=mp) }}"
+ mp: regex
+ tt: '^.*[01]_x.*$'
+ tags: regex_string
+
+- name: Test remove keys targets1
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ loop: "{{ targets1 }}"
+ loop_control:
+ label: "{{ item.mp }}: {{ item.tt }}"
+ vars:
+ rr: "{{ list1 | community.general.remove_keys(target=item.tt, matching_parameter=item.mp) }}"
+ tags: targets1
+
+- name: Test remove keys targets2
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result2) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ loop: "{{ targets2 }}"
+ loop_control:
+ label: "{{ item.mp }}: {{ item.tt }}"
+ vars:
+ rr: "{{ list2 | community.general.remove_keys(target=item.tt, matching_parameter=item.mp) }}"
+ tags: targets1
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/vars/main.yml
new file mode 100644
index 000000000..a52d09a34
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_remove_keys/vars/main.yml
@@ -0,0 +1,33 @@
+---
+# 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
+
+targets1:
+ - {mp: equal, tt: [k0_x0, k1_x1]}
+ - {mp: starts_with, tt: [k0, k1]}
+ - {mp: ends_with, tt: [x0, x1]}
+ - {mp: regex, tt: ['^.*[01]_x.*$']}
+ - {mp: regex, tt: '^.*[01]_x.*$'}
+
+list1:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+result1:
+ - {k2_x2: [C0], k3_x3: foo}
+ - {k2_x2: [C1], k3_x3: bar}
+
+targets2:
+ - {mp: equal, tt: k0_x0}
+ - {mp: starts_with, tt: k0}
+ - {mp: ends_with, tt: x0}
+ - {mp: regex, tt: '^.*0_x.*$'}
+
+list2:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+result2:
+ - {k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k1_x1: B1, k2_x2: [C1], k3_x3: bar}
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/aliases b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/aliases
new file mode 100644
index 000000000..12d1d6617
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/fn-test-replace_keys.yml b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/fn-test-replace_keys.yml
new file mode 100644
index 000000000..e324376a5
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/fn-test-replace_keys.yml
@@ -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
+
+- name: Test replace keys
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(item.result) | length) == 0
+ success_msg: |
+ [OK] {{ item.label }}
+ result:
+ {{ rr | to_nice_yaml(indent=2) | indent(2) }}
+ fail_msg: |
+ [ERR] {{ item.label }}
+ result:
+ {{ rr | to_nice_yaml(indent=2) | indent(2) }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ item.data |
+ community.general.replace_keys(target=item.target, matching_parameter=item.match) }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/main.yml
new file mode 100644
index 000000000..35addaf94
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/main.yml
@@ -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
+
+- name: Test replace_keys
+ import_tasks: replace_keys.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/replace_keys.yml b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/replace_keys.yml
new file mode 100644
index 000000000..a57921b81
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/tasks/replace_keys.yml
@@ -0,0 +1,56 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Debug ansible_version
+ ansible.builtin.debug:
+ var: ansible_version
+ when: not quiet_test | d(true) | bool
+ tags: ansible_version
+
+- name: Test replace keys equal (default)
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ vars:
+ rr: "{{ list1 | community.general.replace_keys(target=tt) }}"
+ tt:
+ - {before: k0_x0, after: a0}
+ - {before: k1_x1, after: a1}
+ tags: equal_default
+
+- name: Test replace keys targets1
+ ansible.builtin.assert:
+ that:
+ - (rr | difference(result1) | length) == 0
+ success_msg: |
+ [OK] result:
+ {{ rr | to_yaml }}
+ fail_msg: |
+ [ERR] result:
+ {{ rr | to_yaml }}
+ quiet: "{{ quiet_test | d(true) | bool }}"
+ loop: "{{ targets1 | dict2items }}"
+ loop_control:
+ label: "{{ item.key }}"
+ vars:
+ rr: "{{ list1 | community.general.replace_keys(target=item.value, matching_parameter=item.key) }}"
+ tags: targets1
+
+- name: Test replace keys targets2
+ include_tasks:
+ file: fn-test-replace_keys.yml
+ apply:
+ tags: targets2
+ loop: "{{ targets2 }}"
+ loop_control:
+ label: "{{ item.label }}"
+ tags: targets2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/vars/main.yml
new file mode 100644
index 000000000..167e08396
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_replace_keys/vars/main.yml
@@ -0,0 +1,58 @@
+---
+# 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
+
+list1:
+ - {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo}
+ - {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar}
+
+result1:
+ - {a0: A0, a1: B0, k2_x2: [C0], k3_x3: foo}
+ - {a0: A1, a1: B1, k2_x2: [C1], k3_x3: bar}
+
+targets1:
+ equal:
+ - {before: k0_x0, after: a0}
+ - {before: k1_x1, after: a1}
+ starts_with:
+ - {before: k0, after: a0}
+ - {before: k1, after: a1}
+ ends_with:
+ - {before: x0, after: a0}
+ - {before: x1, after: a1}
+ regex:
+ - {before: "^.*0_x.*$", after: a0}
+ - {before: "^.*1_x.*$", after: a1}
+
+list2:
+ - {aaa1: A, bbb1: B, ccc1: C}
+ - {aaa2: D, bbb2: E, ccc2: F}
+
+targets2:
+ - label: If more keys match the same attribute before the last one will be used.
+ match: regex
+ target:
+ - {before: "^.*_x.*$", after: X}
+ data: "{{ list1 }}"
+ result:
+ - X: foo
+ - X: bar
+ - label: If there are items with equal attribute before the first one will be used.
+ match: regex
+ target:
+ - {before: "^.*_x.*$", after: X}
+ - {before: "^.*_x.*$", after: Y}
+ data: "{{ list1 }}"
+ result:
+ - X: foo
+ - X: bar
+ - label: If there are more matches for a key the first one will be used.
+ match: starts_with
+ target:
+ - {before: a, after: X}
+ - {before: aa, after: Y}
+ data: "{{ list2 }}"
+ result:
+ - {X: A, bbb1: B, ccc1: C}
+ - {X: D, bbb2: E, ccc2: F}
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml
index dfa535a2d..5f8c52c96 100644
--- a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml
@@ -25,4 +25,28 @@
- unset_result.diff.before == option_value + "\n"
- unset_result.diff.after == "\n"
- get_result.config_value == ''
+
+- import_tasks: setup_value.yml
+
+- name: unsetting value with value specified
+ git_config:
+ name: "{{ option_name }}"
+ scope: "{{ option_scope }}"
+ value: "{{ option_value }}"
+ state: absent
+ register: unset_result
+
+- name: getting value
+ git_config:
+ name: "{{ option_name }}"
+ scope: "{{ option_scope }}"
+ register: get_result
+
+- name: assert unset changed and deleted value
+ assert:
+ that:
+ - unset_result is changed
+ - unset_result.diff.before == option_value + "\n"
+ - unset_result.diff.after == "\n"
+ - get_result.config_value == ''
...
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
index 5e7c7fae3..e1a7d2ebf 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
@@ -103,3 +103,131 @@
assert:
that:
- check_client_when_present_and_changed is changed
+
+- name: Desire client with flow binding overrides
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authentication_flow_binding_overrides:
+ browser_name: browser
+ direct_grant_name: direct grant
+ register: desire_client_with_flow_binding_overrides
+
+- name: Assert flows are set
+ assert:
+ that:
+ - desire_client_with_flow_binding_overrides is changed
+ - "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser | length > 0
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant | length > 0
+
+- name: Backup flow UUIDs
+ set_fact:
+ flow_browser_uuid: "{{ desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser }}"
+ flow_direct_grant_uuid: "{{ desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant }}"
+
+- name: Desire client with flow binding overrides remove direct_grant_name
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authentication_flow_binding_overrides:
+ browser_name: browser
+ register: desire_client_with_flow_binding_overrides
+
+- name: Assert flows are updated
+ assert:
+ that:
+ - desire_client_with_flow_binding_overrides is changed
+ - "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser | length > 0
+ - "'direct_grant' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
+
+- name: Desire client with flow binding overrides remove browser add direct_grant
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authentication_flow_binding_overrides:
+ direct_grant_name: direct grant
+ register: desire_client_with_flow_binding_overrides
+
+- name: Assert flows are updated
+ assert:
+ that:
+ - desire_client_with_flow_binding_overrides is changed
+ - "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
+ - "'browser' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant | length > 0
+
+- name: Desire client with flow binding overrides with UUIDs
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authentication_flow_binding_overrides:
+ browser: "{{ flow_browser_uuid }}"
+ direct_grant: "{{ flow_direct_grant_uuid }}"
+ register: desire_client_with_flow_binding_overrides
+
+- name: Assert flows are updated
+ assert:
+ that:
+ - desire_client_with_flow_binding_overrides is changed
+ - "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.browser == flow_browser_uuid
+ - desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides.direct_grant == flow_direct_grant_uuid
+
+- name: Unset flow binding overrides
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authentication_flow_binding_overrides:
+ browser: "{{ None }}"
+ direct_grant: null
+ register: desire_client_with_flow_binding_overrides
+
+- name: Assert flows are removed
+ assert:
+ that:
+ - desire_client_with_flow_binding_overrides is changed
+ - "'authenticationFlowBindingOverrides' in desire_client_with_flow_binding_overrides.end_state"
+ - "'browser' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides"
+ - "'direct_grant' not in desire_client_with_flow_binding_overrides.end_state.authenticationFlowBindingOverrides" \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
index 6816afb34..fcdffe7d2 100644
--- a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
+++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
@@ -47,6 +47,9 @@ TC_FORMATS = dict(
simple_fixed_false=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), False, ["--always-here", "--forever"], None),
simple_fixed_none=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), None, ["--always-here", "--forever"], None),
simple_fixed_str=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), "something", ["--always-here", "--forever"], None),
+ stack_optval__str=(partial(cmd_runner_fmt.stack(cmd_runner_fmt.as_optval), "-t"), ["potatoes", "bananas"], ["-tpotatoes", "-tbananas"], None),
+ stack_opt_val__str=(partial(cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_val), "-t"), ["potatoes", "bananas"], ["-t", "potatoes", "-t", "bananas"], None),
+ stack_opt_eq_val__int=(partial(cmd_runner_fmt.stack(cmd_runner_fmt.as_opt_eq_val), "--answer"), [42, 17], ["--answer=42", "--answer=17"], None),
)
if tuple(version_info) >= (3, 1):
from collections import OrderedDict
@@ -67,7 +70,7 @@ TC_FORMATS_IDS = sorted(TC_FORMATS.keys())
def test_arg_format(func, value, expected, exception):
fmt_func = func()
try:
- actual = fmt_func(value, ctx_ignore_none=True)
+ actual = fmt_func(value)
print("formatted string = {0}".format(actual))
assert actual == expected, "actual = {0}".format(actual)
except Exception as e:
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.py
new file mode 100644
index 000000000..8aec71900
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.py
@@ -0,0 +1,13 @@
+# Copyright (c) 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
+
+
+from ansible_collections.community.general.plugins.modules import django_check
+from .helper import Helper
+
+
+Helper.from_module(django_check, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.yaml
new file mode 100644
index 000000000..6156aaa2c
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_check.yaml
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 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
+
+---
+- id: success
+ input:
+ settings: whatever.settings
+ run_command_calls:
+ - command: [/testbin/python, -m, django, check, --no-color, --settings=whatever.settings]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: "whatever\n"
+ err: ""
+- id: multiple_databases
+ input:
+ settings: whatever.settings
+ database:
+ - abc
+ - def
+ run_command_calls:
+ - command: [/testbin/python, -m, django, check, --no-color, --settings=whatever.settings, --database, abc, --database, def]
+ environ: *env-def
+ rc: 0
+ out: "whatever\n"
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.py
new file mode 100644
index 000000000..5a4b89c0c
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.py
@@ -0,0 +1,13 @@
+# Copyright (c) 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
+
+
+from ansible_collections.community.general.plugins.modules import django_createcachetable
+from .helper import Helper
+
+
+Helper.from_module(django_createcachetable, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.yaml
new file mode 100644
index 000000000..1808b163f
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_createcachetable.yaml
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 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
+
+---
+- id: command_success
+ input:
+ settings: whatever.settings
+ run_command_calls:
+ - command: [/testbin/python, -m, django, createcachetable, --no-color, --settings=whatever.settings, --noinput, --database=default]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: "whatever\n"
+ err: ""