mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-29 18:06:53 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c45cba53c | ||
|
|
9fe31235d8 | ||
|
|
fc7609628e | ||
|
|
a1d4051a12 | ||
|
|
acdf19c9e6 | ||
|
|
e2513b318e | ||
|
|
cc2794ad05 | ||
|
|
154b5f86fd | ||
|
|
e302058e2d | ||
|
|
1affd48260 | ||
|
|
dc5a89b040 | ||
|
|
71d8109275 | ||
|
|
73362a1e43 | ||
|
|
bc4fda8b14 | ||
|
|
2766898ea8 | ||
|
|
02f123877a | ||
|
|
d4c29e19c0 | ||
|
|
200ab045fa | ||
|
|
9b4decd831 | ||
|
|
566ec0a002 | ||
|
|
2e72051b6c | ||
|
|
89d33bbd7b | ||
|
|
76b6c8e184 | ||
|
|
fdc279def9 | ||
|
|
a471fa88b8 | ||
|
|
bf4e5dc3c0 | ||
|
|
185bdaaa39 | ||
|
|
55d44975dd | ||
|
|
43772cfbbb | ||
|
|
e93b6231ec | ||
|
|
605a557a8d | ||
|
|
d97f1a31ba | ||
|
|
5949b29a12 | ||
|
|
993d580adc | ||
|
|
1ac7783c5c | ||
|
|
f0724c0975 | ||
|
|
f3aca8a575 | ||
|
|
ad1cf82a34 | ||
|
|
42d0a55984 | ||
|
|
4f4d962f7c | ||
|
|
5343880fa5 | ||
|
|
5ea44edc64 | ||
|
|
8152cb3e1f | ||
|
|
eae0c4f92b | ||
|
|
57277e0661 | ||
|
|
53a941cee7 | ||
|
|
e6edf9cdea | ||
|
|
5550ba1946 | ||
|
|
374378beeb | ||
|
|
5222df306b | ||
|
|
788c722b3e | ||
|
|
2ddbda2aa7 | ||
|
|
cb939cbb75 | ||
|
|
7db1613730 | ||
|
|
38a16b421d | ||
|
|
bd8b7e3737 |
@@ -73,6 +73,19 @@ stages:
|
||||
- test: 3
|
||||
- test: 4
|
||||
- test: extra
|
||||
- stage: Sanity_2_18
|
||||
displayName: Sanity 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.18/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_17
|
||||
displayName: Sanity 2.17
|
||||
dependsOn: []
|
||||
@@ -99,19 +112,6 @@ stages:
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_15
|
||||
displayName: Sanity 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.15/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
### Units
|
||||
- stage: Units_devel
|
||||
displayName: Units devel
|
||||
@@ -128,6 +128,17 @@ stages:
|
||||
- test: '3.11'
|
||||
- test: '3.12'
|
||||
- test: '3.13'
|
||||
- stage: Units_2_18
|
||||
displayName: Units 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.18/units/{0}/1
|
||||
targets:
|
||||
- test: 3.8
|
||||
- test: "3.13"
|
||||
- stage: Units_2_17
|
||||
displayName: Units 2.17
|
||||
dependsOn: []
|
||||
@@ -151,17 +162,6 @@ stages:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- test: "3.11"
|
||||
- stage: Units_2_15
|
||||
displayName: Units 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.15/units/{0}/1
|
||||
targets:
|
||||
- test: 3.5
|
||||
- test: "3.10"
|
||||
|
||||
## Remote
|
||||
- stage: Remote_devel_extra_vms
|
||||
@@ -200,6 +200,20 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_18
|
||||
displayName: Remote 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/{0}
|
||||
targets:
|
||||
- name: RHEL 9.4
|
||||
test: rhel/9.4
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_17
|
||||
displayName: Remote 2.17
|
||||
dependsOn: []
|
||||
@@ -232,30 +246,10 @@ stages:
|
||||
test: rhel/9.2
|
||||
- name: RHEL 8.8
|
||||
test: rhel/8.8
|
||||
# - name: FreeBSD 13.2
|
||||
# test: freebsd/13.2
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_15
|
||||
displayName: Remote 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.15/{0}
|
||||
targets:
|
||||
- name: RHEL 9.1
|
||||
test: rhel/9.1
|
||||
- name: RHEL 8.7
|
||||
test: rhel/8.7
|
||||
- name: RHEL 7.9
|
||||
test: rhel/7.9
|
||||
# - name: FreeBSD 13.1
|
||||
# test: freebsd/13.1
|
||||
# - name: FreeBSD 12.4
|
||||
# test: freebsd/12.4
|
||||
# - name: FreeBSD 13.2
|
||||
# test: freebsd/13.2
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -282,6 +276,20 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_18
|
||||
displayName: Docker 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/linux/{0}
|
||||
targets:
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_17
|
||||
displayName: Docker 2.17
|
||||
dependsOn: []
|
||||
@@ -314,20 +322,6 @@ stages:
|
||||
test: opensuse15
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_15
|
||||
displayName: Docker 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.15/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 37
|
||||
test: fedora37
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
groups:
|
||||
@@ -356,77 +350,79 @@ stages:
|
||||
- 3
|
||||
|
||||
### Generic
|
||||
- stage: Generic_devel
|
||||
displayName: Generic devel
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: devel/generic/{0}/1
|
||||
targets:
|
||||
- test: '3.8'
|
||||
- test: '3.11'
|
||||
- test: '3.13'
|
||||
- stage: Generic_2_17
|
||||
displayName: Generic 2.17
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.17/generic/{0}/1
|
||||
targets:
|
||||
- test: '3.7'
|
||||
- test: '3.12'
|
||||
- stage: Generic_2_16
|
||||
displayName: Generic 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.16/generic/{0}/1
|
||||
targets:
|
||||
- test: '2.7'
|
||||
- test: '3.6'
|
||||
- test: '3.11'
|
||||
- stage: Generic_2_15
|
||||
displayName: Generic 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.15/generic/{0}/1
|
||||
targets:
|
||||
- test: '3.9'
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - stage: Generic_devel
|
||||
# displayName: Generic devel
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: devel/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '3.8'
|
||||
# - test: '3.11'
|
||||
# - test: '3.13'
|
||||
# - stage: Generic_2_18
|
||||
# displayName: Generic 2.18
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: 2.18/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '3.8'
|
||||
# - test: '3.13'
|
||||
# - stage: Generic_2_17
|
||||
# displayName: Generic 2.17
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: 2.17/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '3.7'
|
||||
# - test: '3.12'
|
||||
# - stage: Generic_2_16
|
||||
# displayName: Generic 2.16
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: 2.16/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '2.7'
|
||||
# - test: '3.6'
|
||||
# - test: '3.11'
|
||||
|
||||
- stage: Summary
|
||||
condition: succeededOrFailed()
|
||||
dependsOn:
|
||||
- Sanity_devel
|
||||
- Sanity_2_18
|
||||
- Sanity_2_17
|
||||
- Sanity_2_16
|
||||
- Sanity_2_15
|
||||
- Units_devel
|
||||
- Units_2_18
|
||||
- Units_2_17
|
||||
- Units_2_16
|
||||
- Units_2_15
|
||||
- Remote_devel_extra_vms
|
||||
- Remote_devel
|
||||
- Remote_2_18
|
||||
- Remote_2_17
|
||||
- Remote_2_16
|
||||
- Remote_2_15
|
||||
- Docker_devel
|
||||
- Docker_2_18
|
||||
- Docker_2_17
|
||||
- Docker_2_16
|
||||
- Docker_2_15
|
||||
- Docker_community_devel
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - Generic_devel
|
||||
# - Generic_2_18
|
||||
# - Generic_2_17
|
||||
# - Generic_2_16
|
||||
# - Generic_2_15
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
||||
4
.github/BOTMETA.yml
vendored
4
.github/BOTMETA.yml
vendored
@@ -131,6 +131,8 @@ files:
|
||||
maintainers: $team_huawei
|
||||
$doc_fragments/nomad.py:
|
||||
maintainers: chris93111 apecnascimento
|
||||
$doc_fragments/pipx.py:
|
||||
maintainers: russoz
|
||||
$doc_fragments/xenserver.py:
|
||||
labels: xenserver
|
||||
maintainers: bvitnik
|
||||
@@ -712,6 +714,8 @@ files:
|
||||
$modules/ipa_:
|
||||
maintainers: $team_ipa
|
||||
ignore: fxfitz
|
||||
$modules/ipa_getkeytab.py:
|
||||
maintainers: abakanovskii
|
||||
$modules/ipa_dnsrecord.py:
|
||||
maintainers: $team_ipa jwbernin
|
||||
$modules/ipbase_info.py:
|
||||
|
||||
23
.github/workflows/ansible-test.yml
vendored
23
.github/workflows/ansible-test.yml
vendored
@@ -31,6 +31,7 @@ jobs:
|
||||
ansible:
|
||||
- '2.13'
|
||||
- '2.14'
|
||||
- '2.15'
|
||||
# Ansible-test on various stable branches does not yet work well with cgroups v2.
|
||||
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
|
||||
# image for these stable branches. The list of branches where this is necessary will
|
||||
@@ -76,6 +77,10 @@ jobs:
|
||||
python: '3.8'
|
||||
- ansible: '2.14'
|
||||
python: '3.9'
|
||||
- ansible: '2.15'
|
||||
python: '3.5'
|
||||
- ansible: '2.15'
|
||||
python: '3.10'
|
||||
|
||||
steps:
|
||||
- name: >-
|
||||
@@ -166,16 +171,32 @@ jobs:
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# 2.15
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - ansible: '2.13'
|
||||
# docker: default
|
||||
# python: '3.9'
|
||||
# target: azp/generic/1/
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - ansible: '2.14'
|
||||
# docker: default
|
||||
# python: '3.10'
|
||||
# target: azp/generic/1/
|
||||
# - ansible: '2.15'
|
||||
# docker: default
|
||||
# python: '3.9'
|
||||
# target: azp/generic/1/
|
||||
|
||||
steps:
|
||||
- name: >-
|
||||
|
||||
187
CHANGELOG.md
187
CHANGELOG.md
@@ -2,45 +2,51 @@
|
||||
|
||||
**Topics**
|
||||
|
||||
- <a href="#v9-4-0">v9\.4\.0</a>
|
||||
- <a href="#v9-5-0">v9\.5\.0</a>
|
||||
- <a href="#release-summary">Release Summary</a>
|
||||
- <a href="#minor-changes">Minor Changes</a>
|
||||
- <a href="#deprecated-features">Deprecated Features</a>
|
||||
- <a href="#bugfixes">Bugfixes</a>
|
||||
- <a href="#new-modules">New Modules</a>
|
||||
- <a href="#v9-3-0">v9\.3\.0</a>
|
||||
- <a href="#v9-4-0">v9\.4\.0</a>
|
||||
- <a href="#release-summary-1">Release Summary</a>
|
||||
- <a href="#minor-changes-1">Minor Changes</a>
|
||||
- <a href="#deprecated-features-1">Deprecated Features</a>
|
||||
- <a href="#bugfixes-1">Bugfixes</a>
|
||||
- <a href="#new-modules-1">New Modules</a>
|
||||
- <a href="#v9-2-0">v9\.2\.0</a>
|
||||
- <a href="#v9-3-0">v9\.3\.0</a>
|
||||
- <a href="#release-summary-2">Release Summary</a>
|
||||
- <a href="#minor-changes-2">Minor Changes</a>
|
||||
- <a href="#bugfixes-2">Bugfixes</a>
|
||||
- <a href="#new-modules-2">New Modules</a>
|
||||
- <a href="#v9-2-0">v9\.2\.0</a>
|
||||
- <a href="#release-summary-3">Release Summary</a>
|
||||
- <a href="#minor-changes-3">Minor Changes</a>
|
||||
- <a href="#bugfixes-3">Bugfixes</a>
|
||||
- <a href="#new-plugins">New Plugins</a>
|
||||
- <a href="#filter">Filter</a>
|
||||
- <a href="#test">Test</a>
|
||||
- <a href="#v9-1-0">v9\.1\.0</a>
|
||||
- <a href="#release-summary-3">Release Summary</a>
|
||||
- <a href="#minor-changes-3">Minor Changes</a>
|
||||
- <a href="#deprecated-features-1">Deprecated Features</a>
|
||||
- <a href="#bugfixes-3">Bugfixes</a>
|
||||
- <a href="#release-summary-4">Release Summary</a>
|
||||
- <a href="#minor-changes-4">Minor Changes</a>
|
||||
- <a href="#deprecated-features-2">Deprecated Features</a>
|
||||
- <a href="#bugfixes-4">Bugfixes</a>
|
||||
- <a href="#known-issues">Known Issues</a>
|
||||
- <a href="#new-plugins-1">New Plugins</a>
|
||||
- <a href="#filter-1">Filter</a>
|
||||
- <a href="#new-modules-2">New Modules</a>
|
||||
- <a href="#new-modules-3">New Modules</a>
|
||||
- <a href="#v9-0-1">v9\.0\.1</a>
|
||||
- <a href="#release-summary-4">Release Summary</a>
|
||||
- <a href="#minor-changes-4">Minor Changes</a>
|
||||
- <a href="#bugfixes-4">Bugfixes</a>
|
||||
- <a href="#v9-0-0">v9\.0\.0</a>
|
||||
- <a href="#release-summary-5">Release Summary</a>
|
||||
- <a href="#minor-changes-5">Minor Changes</a>
|
||||
- <a href="#bugfixes-5">Bugfixes</a>
|
||||
- <a href="#v9-0-0">v9\.0\.0</a>
|
||||
- <a href="#release-summary-6">Release Summary</a>
|
||||
- <a href="#minor-changes-6">Minor Changes</a>
|
||||
- <a href="#breaking-changes--porting-guide">Breaking Changes / Porting Guide</a>
|
||||
- <a href="#deprecated-features-2">Deprecated Features</a>
|
||||
- <a href="#deprecated-features-3">Deprecated Features</a>
|
||||
- <a href="#removed-features-previously-deprecated">Removed Features \(previously deprecated\)</a>
|
||||
- <a href="#security-fixes">Security Fixes</a>
|
||||
- <a href="#bugfixes-5">Bugfixes</a>
|
||||
- <a href="#bugfixes-6">Bugfixes</a>
|
||||
- <a href="#new-plugins-2">New Plugins</a>
|
||||
- <a href="#become">Become</a>
|
||||
- <a href="#callback">Callback</a>
|
||||
@@ -48,20 +54,119 @@
|
||||
- <a href="#filter-2">Filter</a>
|
||||
- <a href="#lookup">Lookup</a>
|
||||
- <a href="#test-1">Test</a>
|
||||
- <a href="#new-modules-3">New Modules</a>
|
||||
- <a href="#new-modules-4">New Modules</a>
|
||||
This changelog describes changes after version 8\.0\.0\.
|
||||
|
||||
<a id="v9-4-0"></a>
|
||||
## v9\.4\.0
|
||||
<a id="v9-5-0"></a>
|
||||
## v9\.5\.0
|
||||
|
||||
<a id="release-summary"></a>
|
||||
### Release Summary
|
||||
|
||||
Bugfix and feature release\.
|
||||
Regular bugfix and feature release\.
|
||||
|
||||
Please note that this is the last feature release for community\.general 9\.x\.y\.
|
||||
From now on\, new features will only go into community\.general 10\.x\.y\.
|
||||
|
||||
<a id="minor-changes"></a>
|
||||
### Minor Changes
|
||||
|
||||
* dig lookup plugin \- add <code>port</code> option to specify DNS server port \([https\://github\.com/ansible\-collections/community\.general/pull/8966](https\://github\.com/ansible\-collections/community\.general/pull/8966)\)\.
|
||||
* flatpak \- improve the parsing of Flatpak application IDs based on official guidelines \([https\://github\.com/ansible\-collections/community\.general/pull/8909](https\://github\.com/ansible\-collections/community\.general/pull/8909)\)\.
|
||||
* gio\_mime \- adjust code ahead of the old <code>VardDict</code> deprecation \([https\://github\.com/ansible\-collections/community\.general/pull/8855](https\://github\.com/ansible\-collections/community\.general/pull/8855)\)\.
|
||||
* gitlab\_deploy\_key \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* gitlab\_group \- add many new parameters \([https\://github\.com/ansible\-collections/community\.general/pull/8908](https\://github\.com/ansible\-collections/community\.general/pull/8908)\)\.
|
||||
* gitlab\_group \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* gitlab\_issue \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* gitlab\_merge\_request \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* gitlab\_runner \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* icinga2\_host \- replace loop with dict comprehension \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* jira \- adjust code ahead of the old <code>VardDict</code> deprecation \([https\://github\.com/ansible\-collections/community\.general/pull/8856](https\://github\.com/ansible\-collections/community\.general/pull/8856)\)\.
|
||||
* keycloak\_client \- add <code>client\-x509</code> choice to <code>client\_authenticator\_type</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8973](https\://github\.com/ansible\-collections/community\.general/pull/8973)\)\.
|
||||
* keycloak\_user\_federation \- add the user federation config parameter <code>referral</code> to the module arguments \([https\://github\.com/ansible\-collections/community\.general/pull/8954](https\://github\.com/ansible\-collections/community\.general/pull/8954)\)\.
|
||||
* memset\_dns\_reload \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* memset\_memstore\_info \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* memset\_server\_info \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* memset\_zone \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* memset\_zone\_domain \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* memset\_zone\_record \- replace loop with <code>dict\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* nmcli \- add <code>conn\_enable</code> param to reload connection \([https\://github\.com/ansible\-collections/community\.general/issues/3752](https\://github\.com/ansible\-collections/community\.general/issues/3752)\, [https\://github\.com/ansible\-collections/community\.general/issues/8704](https\://github\.com/ansible\-collections/community\.general/issues/8704)\, [https\://github\.com/ansible\-collections/community\.general/pull/8897](https\://github\.com/ansible\-collections/community\.general/pull/8897)\)\.
|
||||
* nmcli \- add <code>state\=up</code> and <code>state\=down</code> to enable/disable connections \([https\://github\.com/ansible\-collections/community\.general/issues/3752](https\://github\.com/ansible\-collections/community\.general/issues/3752)\, [https\://github\.com/ansible\-collections/community\.general/issues/8704](https\://github\.com/ansible\-collections/community\.general/issues/8704)\, [https\://github\.com/ansible\-collections/community\.general/issues/7152](https\://github\.com/ansible\-collections/community\.general/issues/7152)\, [https\://github\.com/ansible\-collections/community\.general/pull/8897](https\://github\.com/ansible\-collections/community\.general/pull/8897)\)\.
|
||||
* nmcli \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* npm \- add <code>force</code> parameter to allow <code>\-\-force</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8885](https\://github\.com/ansible\-collections/community\.general/pull/8885)\)\.
|
||||
* one\_image \- add option <code>persistent</code> to manage image persistence \([https\://github\.com/ansible\-collections/community\.general/issues/3578](https\://github\.com/ansible\-collections/community\.general/issues/3578)\, [https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
|
||||
* one\_image \- extend xsd scheme to make it return a lot more info about image \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
|
||||
* one\_image \- refactor code to make it more similar to <code>one\_template</code> and <code>one\_vnet</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
|
||||
* one\_image\_info \- extend xsd scheme to make it return a lot more info about image \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
|
||||
* one\_image\_info \- refactor code to make it more similar to <code>one\_template</code> and <code>one\_vnet</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8889](https\://github\.com/ansible\-collections/community\.general/pull/8889)\)\.
|
||||
* open\_iscsi \- allow login to a portal with multiple targets without specifying any of them \([https\://github\.com/ansible\-collections/community\.general/pull/8719](https\://github\.com/ansible\-collections/community\.general/pull/8719)\)\.
|
||||
* opennebula\.py \- add VM <code>id</code> and VM <code>host</code> to inventory host data \([https\://github\.com/ansible\-collections/community\.general/pull/8532](https\://github\.com/ansible\-collections/community\.general/pull/8532)\)\.
|
||||
* passwordstore lookup plugin \- add subkey creation/update support \([https\://github\.com/ansible\-collections/community\.general/pull/8952](https\://github\.com/ansible\-collections/community\.general/pull/8952)\)\.
|
||||
* proxmox inventory plugin \- clean up authentication code \([https\://github\.com/ansible\-collections/community\.general/pull/8917](https\://github\.com/ansible\-collections/community\.general/pull/8917)\)\.
|
||||
* redfish\_command \- add handling of the <code>PasswordChangeRequired</code> message from services in the <code>UpdateUserPassword</code> command to directly modify the user\'s password if the requested user is the one invoking the operation \([https\://github\.com/ansible\-collections/community\.general/issues/8652](https\://github\.com/ansible\-collections/community\.general/issues/8652)\, [https\://github\.com/ansible\-collections/community\.general/pull/8653](https\://github\.com/ansible\-collections/community\.general/pull/8653)\)\.
|
||||
* redfish\_confg \- remove <code>CapacityBytes</code> from required paramaters of the <code>CreateVolume</code> command \([https\://github\.com/ansible\-collections/community\.general/pull/8956](https\://github\.com/ansible\-collections/community\.general/pull/8956)\)\.
|
||||
* redfish\_config \- add parameter <code>storage\_none\_volume\_deletion</code> to <code>CreateVolume</code> command in order to control the automatic deletion of non\-RAID volumes \([https\://github\.com/ansible\-collections/community\.general/pull/8990](https\://github\.com/ansible\-collections/community\.general/pull/8990)\)\.
|
||||
* redfish\_info \- adds <code>RedfishURI</code> and <code>StorageId</code> to Disk inventory \([https\://github\.com/ansible\-collections/community\.general/pull/8937](https\://github\.com/ansible\-collections/community\.general/pull/8937)\)\.
|
||||
* scaleway\_container \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_container\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_container\_namespace \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_container\_namespace\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_container\_registry \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_container\_registry\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_function \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_function\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_function\_namespace \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_function\_namespace\_info \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8858](https\://github\.com/ansible\-collections/community\.general/pull/8858)\)\.
|
||||
* scaleway\_user\_data \- better construct when using <code>dict\.items\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
* udm\_dns\_record \- replace loop with <code>dict\.update\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8876](https\://github\.com/ansible\-collections/community\.general/pull/8876)\)\.
|
||||
|
||||
<a id="deprecated-features"></a>
|
||||
### Deprecated Features
|
||||
|
||||
* hipchat \- the hipchat service has been discontinued and the self\-hosted variant has been End of Life since 2020\. The module is therefore deprecated and will be removed from community\.general 11\.0\.0 if nobody provides compelling reasons to still keep it \([https\://github\.com/ansible\-collections/community\.general/pull/8919](https\://github\.com/ansible\-collections/community\.general/pull/8919)\)\.
|
||||
|
||||
<a id="bugfixes"></a>
|
||||
### Bugfixes
|
||||
|
||||
* cloudflare\_dns \- fix changing Cloudflare SRV records \([https\://github\.com/ansible\-collections/community\.general/issues/8679](https\://github\.com/ansible\-collections/community\.general/issues/8679)\, [https\://github\.com/ansible\-collections/community\.general/pull/8948](https\://github\.com/ansible\-collections/community\.general/pull/8948)\)\.
|
||||
* cmd\_runner module utils \- call to <code>get\_best\_parsable\_locales\(\)</code> was missing parameter \([https\://github\.com/ansible\-collections/community\.general/pull/8929](https\://github\.com/ansible\-collections/community\.general/pull/8929)\)\.
|
||||
* dig lookup plugin \- fix using only the last nameserver specified \([https\://github\.com/ansible\-collections/community\.general/pull/8970](https\://github\.com/ansible\-collections/community\.general/pull/8970)\)\.
|
||||
* django\_command \- option <code>command</code> is now split lexically before passed to underlying PythonRunner \([https\://github\.com/ansible\-collections/community\.general/pull/8944](https\://github\.com/ansible\-collections/community\.general/pull/8944)\)\.
|
||||
* homectl \- the module now tries to use <code>legacycrypt</code> on Python 3\.13\+ \([https\://github\.com/ansible\-collections/community\.general/issues/4691](https\://github\.com/ansible\-collections/community\.general/issues/4691)\, [https\://github\.com/ansible\-collections/community\.general/pull/8987](https\://github\.com/ansible\-collections/community\.general/pull/8987)\)\.
|
||||
* ini\_file \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
|
||||
* ipa\_host \- add <code>force\_create</code>\, fix <code>enabled</code> and <code>disabled</code> states \([https\://github\.com/ansible\-collections/community\.general/issues/1094](https\://github\.com/ansible\-collections/community\.general/issues/1094)\, [https\://github\.com/ansible\-collections/community\.general/pull/8920](https\://github\.com/ansible\-collections/community\.general/pull/8920)\)\.
|
||||
* ipa\_hostgroup \- fix <code>enabled \`\` and \`\`disabled</code> states \([https\://github\.com/ansible\-collections/community\.general/issues/8408](https\://github\.com/ansible\-collections/community\.general/issues/8408)\, [https\://github\.com/ansible\-collections/community\.general/pull/8900](https\://github\.com/ansible\-collections/community\.general/pull/8900)\)\.
|
||||
* java\_keystore \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
|
||||
* jenkins\_plugin \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
|
||||
* kdeconfig \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
|
||||
* keycloak\_realm \- fix change detection in check mode by sorting the lists in the realms beforehand \([https\://github\.com/ansible\-collections/community\.general/pull/8877](https\://github\.com/ansible\-collections/community\.general/pull/8877)\)\.
|
||||
* keycloak\_user\_federation \- add module argument allowing users to configure the update mode for the parameter <code>bindCredential</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8898](https\://github\.com/ansible\-collections/community\.general/pull/8898)\)\.
|
||||
* keycloak\_user\_federation \- minimize change detection by setting <code>krbPrincipalAttribute</code> to <code>\'\'</code> in Keycloak responses if missing \([https\://github\.com/ansible\-collections/community\.general/pull/8785](https\://github\.com/ansible\-collections/community\.general/pull/8785)\)\.
|
||||
* keycloak\_user\_federation \- remove <code>lastSync</code> parameter from Keycloak responses to minimize diff/changes \([https\://github\.com/ansible\-collections/community\.general/pull/8812](https\://github\.com/ansible\-collections/community\.general/pull/8812)\)\.
|
||||
* keycloak\_userprofile \- fix empty response when fetching userprofile component by removing <code>parent\=parent\_id</code> filter \([https\://github\.com/ansible\-collections/community\.general/pull/8923](https\://github\.com/ansible\-collections/community\.general/pull/8923)\)\.
|
||||
* keycloak\_userprofile \- improve diff by deserializing the fetched <code>kc\.user\.profile\.config</code> and serialize it only when sending back \([https\://github\.com/ansible\-collections/community\.general/pull/8940](https\://github\.com/ansible\-collections/community\.general/pull/8940)\)\.
|
||||
* lxd\_container \- fix bug introduced in previous commit \([https\://github\.com/ansible\-collections/community\.general/pull/8895](https\://github\.com/ansible\-collections/community\.general/pull/8895)\, [https\://github\.com/ansible\-collections/community\.general/issues/8888](https\://github\.com/ansible\-collections/community\.general/issues/8888)\)\.
|
||||
* one\_service \- fix service creation after it was deleted with <code>unique</code> parameter \([https\://github\.com/ansible\-collections/community\.general/issues/3137](https\://github\.com/ansible\-collections/community\.general/issues/3137)\, [https\://github\.com/ansible\-collections/community\.general/pull/8887](https\://github\.com/ansible\-collections/community\.general/pull/8887)\)\.
|
||||
* pam\_limits \- pass absolute paths to <code>module\.atomic\_move\(\)</code> \([https\://github\.com/ansible/ansible/issues/83950](https\://github\.com/ansible/ansible/issues/83950)\, [https\://github\.com/ansible\-collections/community\.general/pull/8925](https\://github\.com/ansible\-collections/community\.general/pull/8925)\)\.
|
||||
* python\_runner module utils \- parameter <code>path\_prefix</code> was being handled as string when it should be a list \([https\://github\.com/ansible\-collections/community\.general/pull/8944](https\://github\.com/ansible\-collections/community\.general/pull/8944)\)\.
|
||||
* udm\_user \- the module now tries to use <code>legacycrypt</code> on Python 3\.13\+ \([https\://github\.com/ansible\-collections/community\.general/issues/4690](https\://github\.com/ansible\-collections/community\.general/issues/4690)\, [https\://github\.com/ansible\-collections/community\.general/pull/8987](https\://github\.com/ansible\-collections/community\.general/pull/8987)\)\.
|
||||
|
||||
<a id="new-modules"></a>
|
||||
### New Modules
|
||||
|
||||
* community\.general\.ipa\_getkeytab \- Manage keytab file in FreeIPA\.
|
||||
|
||||
<a id="v9-4-0"></a>
|
||||
## v9\.4\.0
|
||||
|
||||
<a id="release-summary-1"></a>
|
||||
### Release Summary
|
||||
|
||||
Bugfix and feature release\.
|
||||
|
||||
<a id="minor-changes-1"></a>
|
||||
### Minor Changes
|
||||
|
||||
* MH module utils \- add parameter <code>when</code> to <code>cause\_changes</code> decorator \([https\://github\.com/ansible\-collections/community\.general/pull/8766](https\://github\.com/ansible\-collections/community\.general/pull/8766)\)\.
|
||||
* MH module utils \- minor refactor in decorators \([https\://github\.com/ansible\-collections/community\.general/pull/8766](https\://github\.com/ansible\-collections/community\.general/pull/8766)\)\.
|
||||
* alternatives \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8833](https\://github\.com/ansible\-collections/community\.general/pull/8833)\)\.
|
||||
@@ -139,14 +244,14 @@ Bugfix and feature release\.
|
||||
* vars MH module utils \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8814](https\://github\.com/ansible\-collections/community\.general/pull/8814)\)\.
|
||||
* vmadm \- replace Python 2\.6 construct with dict comprehensions \([https\://github\.com/ansible\-collections/community\.general/pull/8822](https\://github\.com/ansible\-collections/community\.general/pull/8822)\)\.
|
||||
|
||||
<a id="deprecated-features"></a>
|
||||
<a id="deprecated-features-1"></a>
|
||||
### Deprecated Features
|
||||
|
||||
* MH decorator cause\_changes module utils \- deprecate parameters <code>on\_success</code> and <code>on\_failure</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8791](https\://github\.com/ansible\-collections/community\.general/pull/8791)\)\.
|
||||
* pipx \- support for versions of the command line tool <code>pipx</code> older than <code>1\.7\.0</code> is deprecated and will be removed in community\.general 11\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8793](https\://github\.com/ansible\-collections/community\.general/pull/8793)\)\.
|
||||
* pipx\_info \- support for versions of the command line tool <code>pipx</code> older than <code>1\.7\.0</code> is deprecated and will be removed in community\.general 11\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8793](https\://github\.com/ansible\-collections/community\.general/pull/8793)\)\.
|
||||
|
||||
<a id="bugfixes"></a>
|
||||
<a id="bugfixes-1"></a>
|
||||
### Bugfixes
|
||||
|
||||
* gitlab\_group\_access\_token \- fix crash in check mode caused by attempted access to a newly created access token \([https\://github\.com/ansible\-collections/community\.general/pull/8796](https\://github\.com/ansible\-collections/community\.general/pull/8796)\)\.
|
||||
@@ -159,7 +264,7 @@ Bugfix and feature release\.
|
||||
* keycloak\_user\_federation \- sort desired and after mapper list by name \(analog to before mapper list\) to minimize diff and make change detection more accurate \([https\://github\.com/ansible\-collections/community\.general/pull/8761](https\://github\.com/ansible\-collections/community\.general/pull/8761)\)\.
|
||||
* proxmox inventory plugin \- fixed a possible error on concatenating responses from proxmox\. In case an API call unexpectedly returned an empty result\, the inventory failed with a fatal error\. Added check for empty response \([https\://github\.com/ansible\-collections/community\.general/issues/8798](https\://github\.com/ansible\-collections/community\.general/issues/8798)\, [https\://github\.com/ansible\-collections/community\.general/pull/8794](https\://github\.com/ansible\-collections/community\.general/pull/8794)\)\.
|
||||
|
||||
<a id="new-modules"></a>
|
||||
<a id="new-modules-1"></a>
|
||||
### New Modules
|
||||
|
||||
* community\.general\.keycloak\_userprofile \- Allows managing Keycloak User Profiles\.
|
||||
@@ -168,12 +273,12 @@ Bugfix and feature release\.
|
||||
<a id="v9-3-0"></a>
|
||||
## v9\.3\.0
|
||||
|
||||
<a id="release-summary-1"></a>
|
||||
<a id="release-summary-2"></a>
|
||||
### Release Summary
|
||||
|
||||
Regular bugfix and feature release\.
|
||||
|
||||
<a id="minor-changes-1"></a>
|
||||
<a id="minor-changes-2"></a>
|
||||
### Minor Changes
|
||||
|
||||
* cgroup\_memory\_recap\, hipchat\, jabber\, log\_plays\, loganalytics\, logentries\, logstash\, slack\, splunk\, sumologic\, syslog\_json callback plugins \- make sure that all options are typed \([https\://github\.com/ansible\-collections/community\.general/pull/8628](https\://github\.com/ansible\-collections/community\.general/pull/8628)\)\.
|
||||
@@ -196,7 +301,7 @@ Regular bugfix and feature release\.
|
||||
* proxmox inventory plugin \- add new fact for LXC interface details \([https\://github\.com/ansible\-collections/community\.general/pull/8713](https\://github\.com/ansible\-collections/community\.general/pull/8713)\)\.
|
||||
* redis\, redis\_info \- add <code>client\_cert</code> and <code>client\_key</code> options to specify path to certificate for Redis authentication \([https\://github\.com/ansible\-collections/community\.general/pull/8654](https\://github\.com/ansible\-collections/community\.general/pull/8654)\)\.
|
||||
|
||||
<a id="bugfixes-1"></a>
|
||||
<a id="bugfixes-2"></a>
|
||||
### Bugfixes
|
||||
|
||||
* gitlab\_runner \- fix <code>paused</code> parameter being ignored \([https\://github\.com/ansible\-collections/community\.general/pull/8648](https\://github\.com/ansible\-collections/community\.general/pull/8648)\)\.
|
||||
@@ -207,7 +312,7 @@ Regular bugfix and feature release\.
|
||||
* proxmox \- fixed an issue where volume strings where overwritten instead of appended to in the new <code>build\_volume\(\)</code> method \([https\://github\.com/ansible\-collections/community\.general/pull/8646](https\://github\.com/ansible\-collections/community\.general/pull/8646)\)\.
|
||||
* proxmox \- removed the forced conversion of non\-string values to strings to be consistent with the module documentation \([https\://github\.com/ansible\-collections/community\.general/pull/8646](https\://github\.com/ansible\-collections/community\.general/pull/8646)\)\.
|
||||
|
||||
<a id="new-modules-1"></a>
|
||||
<a id="new-modules-2"></a>
|
||||
### New Modules
|
||||
|
||||
* community\.general\.bootc\_manage \- Bootc Switch and Upgrade\.
|
||||
@@ -217,12 +322,12 @@ Regular bugfix and feature release\.
|
||||
<a id="v9-2-0"></a>
|
||||
## v9\.2\.0
|
||||
|
||||
<a id="release-summary-2"></a>
|
||||
<a id="release-summary-3"></a>
|
||||
### Release Summary
|
||||
|
||||
Regular bugfix and feature release\.
|
||||
|
||||
<a id="minor-changes-2"></a>
|
||||
<a id="minor-changes-3"></a>
|
||||
### Minor Changes
|
||||
|
||||
* CmdRunner module utils \- the parameter <code>force\_lang</code> now supports the special value <code>auto</code> which will automatically try and determine the best parsable locale in the system \([https\://github\.com/ansible\-collections/community\.general/pull/8517](https\://github\.com/ansible\-collections/community\.general/pull/8517)\)\.
|
||||
@@ -234,7 +339,7 @@ Regular bugfix and feature release\.
|
||||
* virtualbox inventory plugin \- expose a new parameter <code>enable\_advanced\_group\_parsing</code> to change how the VirtualBox dynamic inventory parses VM groups \([https\://github\.com/ansible\-collections/community\.general/issues/8508](https\://github\.com/ansible\-collections/community\.general/issues/8508)\, [https\://github\.com/ansible\-collections/community\.general/pull/8510](https\://github\.com/ansible\-collections/community\.general/pull/8510)\)\.
|
||||
* wdc\_redfish\_command \- minor change to handle upgrade file for Redfish WD platforms \([https\://github\.com/ansible\-collections/community\.general/pull/8444](https\://github\.com/ansible\-collections/community\.general/pull/8444)\)\.
|
||||
|
||||
<a id="bugfixes-2"></a>
|
||||
<a id="bugfixes-3"></a>
|
||||
### Bugfixes
|
||||
|
||||
* bitwarden lookup plugin \- fix <code>KeyError</code> in <code>search\_field</code> \([https\://github\.com/ansible\-collections/community\.general/issues/8549](https\://github\.com/ansible\-collections/community\.general/issues/8549)\, [https\://github\.com/ansible\-collections/community\.general/pull/8557](https\://github\.com/ansible\-collections/community\.general/pull/8557)\)\.
|
||||
@@ -259,12 +364,12 @@ Regular bugfix and feature release\.
|
||||
<a id="v9-1-0"></a>
|
||||
## v9\.1\.0
|
||||
|
||||
<a id="release-summary-3"></a>
|
||||
<a id="release-summary-4"></a>
|
||||
### Release Summary
|
||||
|
||||
Regular feature and bugfix release\.
|
||||
|
||||
<a id="minor-changes-3"></a>
|
||||
<a id="minor-changes-4"></a>
|
||||
### Minor Changes
|
||||
|
||||
* CmdRunner module util \- argument formats can be specified as plain functions without calling <code>cmd\_runner\_fmt\.as\_func\(\)</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8479](https\://github\.com/ansible\-collections/community\.general/pull/8479)\)\.
|
||||
@@ -281,14 +386,14 @@ Regular feature and bugfix release\.
|
||||
* redfish\_info \- add command <code>CheckAvailability</code> to check if a service is accessible \([https\://github\.com/ansible\-collections/community\.general/issues/8051](https\://github\.com/ansible\-collections/community\.general/issues/8051)\, [https\://github\.com/ansible\-collections/community\.general/pull/8434](https\://github\.com/ansible\-collections/community\.general/pull/8434)\)\.
|
||||
* redis\_info \- adds support for getting cluster info \([https\://github\.com/ansible\-collections/community\.general/pull/8464](https\://github\.com/ansible\-collections/community\.general/pull/8464)\)\.
|
||||
|
||||
<a id="deprecated-features-1"></a>
|
||||
<a id="deprecated-features-2"></a>
|
||||
### Deprecated Features
|
||||
|
||||
* CmdRunner module util \- setting the value of the <code>ignore\_none</code> parameter within a <code>CmdRunner</code> context is deprecated and that feature should be removed in community\.general 12\.0\.0 \([https\://github\.com/ansible\-collections/community\.general/pull/8479](https\://github\.com/ansible\-collections/community\.general/pull/8479)\)\.
|
||||
* git\_config \- the <code>list\_all</code> option has been deprecated and will be removed in community\.general 11\.0\.0\. Use the <code>community\.general\.git\_config\_info</code> module instead \([https\://github\.com/ansible\-collections/community\.general/pull/8453](https\://github\.com/ansible\-collections/community\.general/pull/8453)\)\.
|
||||
* git\_config \- using <code>state\=present</code> without providing <code>value</code> is deprecated and will be disallowed in community\.general 11\.0\.0\. Use the <code>community\.general\.git\_config\_info</code> module instead to read a value \([https\://github\.com/ansible\-collections/community\.general/pull/8453](https\://github\.com/ansible\-collections/community\.general/pull/8453)\)\.
|
||||
|
||||
<a id="bugfixes-3"></a>
|
||||
<a id="bugfixes-4"></a>
|
||||
### Bugfixes
|
||||
|
||||
* git\_config \- fix behavior of <code>state\=absent</code> if <code>value</code> is present \([https\://github\.com/ansible\-collections/community\.general/issues/8436](https\://github\.com/ansible\-collections/community\.general/issues/8436)\, [https\://github\.com/ansible\-collections/community\.general/pull/8452](https\://github\.com/ansible\-collections/community\.general/pull/8452)\)\.
|
||||
@@ -315,7 +420,7 @@ Regular feature and bugfix release\.
|
||||
* community\.general\.remove\_keys \- Remove specific keys from dictionaries in a list\.
|
||||
* community\.general\.replace\_keys \- Replace specific keys in a list of dictionaries\.
|
||||
|
||||
<a id="new-modules-2"></a>
|
||||
<a id="new-modules-3"></a>
|
||||
### New Modules
|
||||
|
||||
* community\.general\.consul\_agent\_check \- Add\, modify\, and delete checks within a consul cluster\.
|
||||
@@ -326,17 +431,17 @@ Regular feature and bugfix release\.
|
||||
<a id="v9-0-1"></a>
|
||||
## v9\.0\.1
|
||||
|
||||
<a id="release-summary-4"></a>
|
||||
<a id="release-summary-5"></a>
|
||||
### Release Summary
|
||||
|
||||
Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
|
||||
|
||||
<a id="minor-changes-4"></a>
|
||||
<a id="minor-changes-5"></a>
|
||||
### Minor Changes
|
||||
|
||||
* ansible\_galaxy\_install \- minor refactor in the module \([https\://github\.com/ansible\-collections/community\.general/pull/8413](https\://github\.com/ansible\-collections/community\.general/pull/8413)\)\.
|
||||
|
||||
<a id="bugfixes-4"></a>
|
||||
<a id="bugfixes-5"></a>
|
||||
### Bugfixes
|
||||
|
||||
* cpanm \- use new <code>VarDict</code> to prevent deprecation warning \([https\://github\.com/ansible\-collections/community\.general/issues/8410](https\://github\.com/ansible\-collections/community\.general/issues/8410)\, [https\://github\.com/ansible\-collections/community\.general/pull/8411](https\://github\.com/ansible\-collections/community\.general/pull/8411)\)\.
|
||||
@@ -355,12 +460,12 @@ Bugfix release for inclusion in Ansible 10\.0\.0rc1\.
|
||||
<a id="v9-0-0"></a>
|
||||
## v9\.0\.0
|
||||
|
||||
<a id="release-summary-5"></a>
|
||||
<a id="release-summary-6"></a>
|
||||
### Release Summary
|
||||
|
||||
This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-05\-20\.
|
||||
|
||||
<a id="minor-changes-5"></a>
|
||||
<a id="minor-changes-6"></a>
|
||||
### Minor Changes
|
||||
|
||||
* PythonRunner module utils \- specialisation of <code>CmdRunner</code> to execute Python scripts \([https\://github\.com/ansible\-collections/community\.general/pull/8289](https\://github\.com/ansible\-collections/community\.general/pull/8289)\)\.
|
||||
@@ -489,7 +594,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
|
||||
* django\_manage \- the module will now fail if <code>virtualenv</code> is specified but no virtual environment exists at that location \([https\://github\.com/ansible\-collections/community\.general/pull/8198](https\://github\.com/ansible\-collections/community\.general/pull/8198)\)\.
|
||||
* redfish\_command\, redfish\_config\, redfish\_info \- change the default for <code>timeout</code> from 10 to 60 \([https\://github\.com/ansible\-collections/community\.general/pull/8198](https\://github\.com/ansible\-collections/community\.general/pull/8198)\)\.
|
||||
|
||||
<a id="deprecated-features-2"></a>
|
||||
<a id="deprecated-features-3"></a>
|
||||
### Deprecated Features
|
||||
|
||||
* MH DependencyCtxMgr module\_utils \- deprecate <code>module\_utils\.mh\.mixin\.deps\.DependencyCtxMgr</code> in favour of <code>module\_utils\.deps</code> \([https\://github\.com/ansible\-collections/community\.general/pull/8280](https\://github\.com/ansible\-collections/community\.general/pull/8280)\)\.
|
||||
@@ -530,7 +635,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
|
||||
* cobbler\, gitlab\_runners\, icinga2\, linode\, lxd\, nmap\, online\, opennebula\, proxmox\, scaleway\, stackpath\_compute\, virtualbox\, and xen\_orchestra inventory plugin \- make sure all data received from the remote servers is marked as unsafe\, so remote code execution by obtaining texts that can be evaluated as templates is not possible \([https\://www\.die\-welt\.net/2024/03/remote\-code\-execution\-in\-ansible\-dynamic\-inventory\-plugins/](https\://www\.die\-welt\.net/2024/03/remote\-code\-execution\-in\-ansible\-dynamic\-inventory\-plugins/)\, [https\://github\.com/ansible\-collections/community\.general/pull/8098](https\://github\.com/ansible\-collections/community\.general/pull/8098)\)\.
|
||||
* keycloak\_identity\_provider \- the client secret was not correctly sanitized by the module\. The return values <code>proposed</code>\, <code>existing</code>\, and <code>end\_state</code>\, as well as the diff\, did contain the client secret unmasked \([https\://github\.com/ansible\-collections/community\.general/pull/8355](https\://github\.com/ansible\-collections/community\.general/pull/8355)\)\.
|
||||
|
||||
<a id="bugfixes-5"></a>
|
||||
<a id="bugfixes-6"></a>
|
||||
### Bugfixes
|
||||
|
||||
* aix\_filesystem \- fix <code>\_validate\_vg</code> not passing VG name to <code>lsvg\_cmd</code> \([https\://github\.com/ansible\-collections/community\.general/issues/8151](https\://github\.com/ansible\-collections/community\.general/issues/8151)\)\.
|
||||
@@ -645,7 +750,7 @@ This is release 9\.0\.0 of <code>community\.general</code>\, released on 2024\-0
|
||||
|
||||
* community\.general\.fqdn\_valid \- Validates fully\-qualified domain names against RFC 1123\.
|
||||
|
||||
<a id="new-modules-3"></a>
|
||||
<a id="new-modules-4"></a>
|
||||
### New Modules
|
||||
|
||||
* community\.general\.consul\_acl\_bootstrap \- Bootstrap ACLs in Consul\.
|
||||
|
||||
@@ -6,6 +6,105 @@ Community General Release Notes
|
||||
|
||||
This changelog describes changes after version 8.0.0.
|
||||
|
||||
v9.5.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Please note that this is the last feature release for community.general 9.x.y.
|
||||
From now on, new features will only go into community.general 10.x.y.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- dig lookup plugin - add ``port`` option to specify DNS server port (https://github.com/ansible-collections/community.general/pull/8966).
|
||||
- flatpak - improve the parsing of Flatpak application IDs based on official guidelines (https://github.com/ansible-collections/community.general/pull/8909).
|
||||
- gio_mime - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8855).
|
||||
- gitlab_deploy_key - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_group - add many new parameters (https://github.com/ansible-collections/community.general/pull/8908).
|
||||
- gitlab_group - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_issue - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_merge_request - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_runner - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- icinga2_host - replace loop with dict comprehension (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- jira - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8856).
|
||||
- keycloak_client - add ``client-x509`` choice to ``client_authenticator_type`` (https://github.com/ansible-collections/community.general/pull/8973).
|
||||
- keycloak_user_federation - add the user federation config parameter ``referral`` to the module arguments (https://github.com/ansible-collections/community.general/pull/8954).
|
||||
- memset_dns_reload - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_memstore_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_server_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone_domain - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone_record - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- nmcli - add ``conn_enable`` param to reload connection (https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/pull/8897).
|
||||
- nmcli - add ``state=up`` and ``state=down`` to enable/disable connections (https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/issues/7152, https://github.com/ansible-collections/community.general/pull/8897).
|
||||
- nmcli - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- npm - add ``force`` parameter to allow ``--force`` (https://github.com/ansible-collections/community.general/pull/8885).
|
||||
- one_image - add option ``persistent`` to manage image persistence (https://github.com/ansible-collections/community.general/issues/3578, https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image_info - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image_info - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- open_iscsi - allow login to a portal with multiple targets without specifying any of them (https://github.com/ansible-collections/community.general/pull/8719).
|
||||
- opennebula.py - add VM ``id`` and VM ``host`` to inventory host data (https://github.com/ansible-collections/community.general/pull/8532).
|
||||
- passwordstore lookup plugin - add subkey creation/update support (https://github.com/ansible-collections/community.general/pull/8952).
|
||||
- proxmox inventory plugin - clean up authentication code (https://github.com/ansible-collections/community.general/pull/8917).
|
||||
- redfish_command - add handling of the ``PasswordChangeRequired`` message from services in the ``UpdateUserPassword`` command to directly modify the user's password if the requested user is the one invoking the operation (https://github.com/ansible-collections/community.general/issues/8652, https://github.com/ansible-collections/community.general/pull/8653).
|
||||
- redfish_confg - remove ``CapacityBytes`` from required paramaters of the ``CreateVolume`` command (https://github.com/ansible-collections/community.general/pull/8956).
|
||||
- redfish_config - add parameter ``storage_none_volume_deletion`` to ``CreateVolume`` command in order to control the automatic deletion of non-RAID volumes (https://github.com/ansible-collections/community.general/pull/8990).
|
||||
- redfish_info - adds ``RedfishURI`` and ``StorageId`` to Disk inventory (https://github.com/ansible-collections/community.general/pull/8937).
|
||||
- scaleway_container - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_namespace - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_namespace_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_registry - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_registry_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_namespace - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_namespace_info - replace Python 2.6 construct with dict comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_user_data - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- udm_dns_record - replace loop with ``dict.update()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- hipchat - the hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020. The module is therefore deprecated and will be removed from community.general 11.0.0 if nobody provides compelling reasons to still keep it (https://github.com/ansible-collections/community.general/pull/8919).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- cloudflare_dns - fix changing Cloudflare SRV records (https://github.com/ansible-collections/community.general/issues/8679, https://github.com/ansible-collections/community.general/pull/8948).
|
||||
- cmd_runner module utils - call to ``get_best_parsable_locales()`` was missing parameter (https://github.com/ansible-collections/community.general/pull/8929).
|
||||
- dig lookup plugin - fix using only the last nameserver specified (https://github.com/ansible-collections/community.general/pull/8970).
|
||||
- django_command - option ``command`` is now split lexically before passed to underlying PythonRunner (https://github.com/ansible-collections/community.general/pull/8944).
|
||||
- homectl - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4691, https://github.com/ansible-collections/community.general/pull/8987).
|
||||
- ini_file - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- ipa_host - add ``force_create``, fix ``enabled`` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/1094, https://github.com/ansible-collections/community.general/pull/8920).
|
||||
- ipa_hostgroup - fix ``enabled `` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/8408, https://github.com/ansible-collections/community.general/pull/8900).
|
||||
- java_keystore - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- jenkins_plugin - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- kdeconfig - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- keycloak_realm - fix change detection in check mode by sorting the lists in the realms beforehand (https://github.com/ansible-collections/community.general/pull/8877).
|
||||
- keycloak_user_federation - add module argument allowing users to configure the update mode for the parameter ``bindCredential`` (https://github.com/ansible-collections/community.general/pull/8898).
|
||||
- keycloak_user_federation - minimize change detection by setting ``krbPrincipalAttribute`` to ``''`` in Keycloak responses if missing (https://github.com/ansible-collections/community.general/pull/8785).
|
||||
- keycloak_user_federation - remove ``lastSync`` parameter from Keycloak responses to minimize diff/changes (https://github.com/ansible-collections/community.general/pull/8812).
|
||||
- keycloak_userprofile - fix empty response when fetching userprofile component by removing ``parent=parent_id`` filter (https://github.com/ansible-collections/community.general/pull/8923).
|
||||
- keycloak_userprofile - improve diff by deserializing the fetched ``kc.user.profile.config`` and serialize it only when sending back (https://github.com/ansible-collections/community.general/pull/8940).
|
||||
- lxd_container - fix bug introduced in previous commit (https://github.com/ansible-collections/community.general/pull/8895, https://github.com/ansible-collections/community.general/issues/8888).
|
||||
- one_service - fix service creation after it was deleted with ``unique`` parameter (https://github.com/ansible-collections/community.general/issues/3137, https://github.com/ansible-collections/community.general/pull/8887).
|
||||
- pam_limits - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950, https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- python_runner module utils - parameter ``path_prefix`` was being handled as string when it should be a list (https://github.com/ansible-collections/community.general/pull/8944).
|
||||
- udm_user - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4690, https://github.com/ansible-collections/community.general/pull/8987).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- community.general.ipa_getkeytab - Manage keytab file in FreeIPA.
|
||||
|
||||
v9.4.0
|
||||
======
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ For more information about communication, see the [Ansible communication guide](
|
||||
|
||||
## Tested with Ansible
|
||||
|
||||
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
|
||||
## External requirements
|
||||
|
||||
|
||||
@@ -1248,3 +1248,181 @@ releases:
|
||||
name: one_vnet
|
||||
namespace: ''
|
||||
release_date: '2024-09-09'
|
||||
9.5.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- cloudflare_dns - fix changing Cloudflare SRV records (https://github.com/ansible-collections/community.general/issues/8679,
|
||||
https://github.com/ansible-collections/community.general/pull/8948).
|
||||
- cmd_runner module utils - call to ``get_best_parsable_locales()`` was missing
|
||||
parameter (https://github.com/ansible-collections/community.general/pull/8929).
|
||||
- dig lookup plugin - fix using only the last nameserver specified (https://github.com/ansible-collections/community.general/pull/8970).
|
||||
- django_command - option ``command`` is now split lexically before passed
|
||||
to underlying PythonRunner (https://github.com/ansible-collections/community.general/pull/8944).
|
||||
- homectl - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4691,
|
||||
https://github.com/ansible-collections/community.general/pull/8987).
|
||||
- ini_file - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
|
||||
https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- ipa_host - add ``force_create``, fix ``enabled`` and ``disabled`` states
|
||||
(https://github.com/ansible-collections/community.general/issues/1094, https://github.com/ansible-collections/community.general/pull/8920).
|
||||
- ipa_hostgroup - fix ``enabled `` and ``disabled`` states (https://github.com/ansible-collections/community.general/issues/8408,
|
||||
https://github.com/ansible-collections/community.general/pull/8900).
|
||||
- java_keystore - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
|
||||
https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- jenkins_plugin - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
|
||||
https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- kdeconfig - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
|
||||
https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- keycloak_realm - fix change detection in check mode by sorting the lists
|
||||
in the realms beforehand (https://github.com/ansible-collections/community.general/pull/8877).
|
||||
- keycloak_user_federation - add module argument allowing users to configure
|
||||
the update mode for the parameter ``bindCredential`` (https://github.com/ansible-collections/community.general/pull/8898).
|
||||
- keycloak_user_federation - minimize change detection by setting ``krbPrincipalAttribute``
|
||||
to ``''`` in Keycloak responses if missing (https://github.com/ansible-collections/community.general/pull/8785).
|
||||
- keycloak_user_federation - remove ``lastSync`` parameter from Keycloak responses
|
||||
to minimize diff/changes (https://github.com/ansible-collections/community.general/pull/8812).
|
||||
- keycloak_userprofile - fix empty response when fetching userprofile component
|
||||
by removing ``parent=parent_id`` filter (https://github.com/ansible-collections/community.general/pull/8923).
|
||||
- keycloak_userprofile - improve diff by deserializing the fetched ``kc.user.profile.config``
|
||||
and serialize it only when sending back (https://github.com/ansible-collections/community.general/pull/8940).
|
||||
- lxd_container - fix bug introduced in previous commit (https://github.com/ansible-collections/community.general/pull/8895,
|
||||
https://github.com/ansible-collections/community.general/issues/8888).
|
||||
- one_service - fix service creation after it was deleted with ``unique``
|
||||
parameter (https://github.com/ansible-collections/community.general/issues/3137,
|
||||
https://github.com/ansible-collections/community.general/pull/8887).
|
||||
- pam_limits - pass absolute paths to ``module.atomic_move()`` (https://github.com/ansible/ansible/issues/83950,
|
||||
https://github.com/ansible-collections/community.general/pull/8925).
|
||||
- python_runner module utils - parameter ``path_prefix`` was being handled
|
||||
as string when it should be a list (https://github.com/ansible-collections/community.general/pull/8944).
|
||||
- udm_user - the module now tries to use ``legacycrypt`` on Python 3.13+ (https://github.com/ansible-collections/community.general/issues/4690,
|
||||
https://github.com/ansible-collections/community.general/pull/8987).
|
||||
deprecated_features:
|
||||
- hipchat - the hipchat service has been discontinued and the self-hosted
|
||||
variant has been End of Life since 2020. The module is therefore deprecated
|
||||
and will be removed from community.general 11.0.0 if nobody provides compelling
|
||||
reasons to still keep it (https://github.com/ansible-collections/community.general/pull/8919).
|
||||
minor_changes:
|
||||
- dig lookup plugin - add ``port`` option to specify DNS server port (https://github.com/ansible-collections/community.general/pull/8966).
|
||||
- flatpak - improve the parsing of Flatpak application IDs based on official
|
||||
guidelines (https://github.com/ansible-collections/community.general/pull/8909).
|
||||
- gio_mime - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8855).
|
||||
- gitlab_deploy_key - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_group - add many new parameters (https://github.com/ansible-collections/community.general/pull/8908).
|
||||
- gitlab_group - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_issue - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_merge_request - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- gitlab_runner - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- icinga2_host - replace loop with dict comprehension (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- jira - adjust code ahead of the old ``VardDict`` deprecation (https://github.com/ansible-collections/community.general/pull/8856).
|
||||
- keycloak_client - add ``client-x509`` choice to ``client_authenticator_type``
|
||||
(https://github.com/ansible-collections/community.general/pull/8973).
|
||||
- keycloak_user_federation - add the user federation config parameter ``referral``
|
||||
to the module arguments (https://github.com/ansible-collections/community.general/pull/8954).
|
||||
- memset_dns_reload - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_memstore_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_server_info - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone_domain - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- memset_zone_record - replace loop with ``dict()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- nmcli - add ``conn_enable`` param to reload connection (https://github.com/ansible-collections/community.general/issues/3752,
|
||||
https://github.com/ansible-collections/community.general/issues/8704, https://github.com/ansible-collections/community.general/pull/8897).
|
||||
- nmcli - add ``state=up`` and ``state=down`` to enable/disable connections
|
||||
(https://github.com/ansible-collections/community.general/issues/3752, https://github.com/ansible-collections/community.general/issues/8704,
|
||||
https://github.com/ansible-collections/community.general/issues/7152, https://github.com/ansible-collections/community.general/pull/8897).
|
||||
- nmcli - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- npm - add ``force`` parameter to allow ``--force`` (https://github.com/ansible-collections/community.general/pull/8885).
|
||||
- one_image - add option ``persistent`` to manage image persistence (https://github.com/ansible-collections/community.general/issues/3578,
|
||||
https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image - extend xsd scheme to make it return a lot more info about image
|
||||
(https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image - refactor code to make it more similar to ``one_template`` and
|
||||
``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image_info - extend xsd scheme to make it return a lot more info about
|
||||
image (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- one_image_info - refactor code to make it more similar to ``one_template``
|
||||
and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||
- open_iscsi - allow login to a portal with multiple targets without specifying
|
||||
any of them (https://github.com/ansible-collections/community.general/pull/8719).
|
||||
- opennebula.py - add VM ``id`` and VM ``host`` to inventory host data (https://github.com/ansible-collections/community.general/pull/8532).
|
||||
- passwordstore lookup plugin - add subkey creation/update support (https://github.com/ansible-collections/community.general/pull/8952).
|
||||
- proxmox inventory plugin - clean up authentication code (https://github.com/ansible-collections/community.general/pull/8917).
|
||||
- redfish_command - add handling of the ``PasswordChangeRequired`` message
|
||||
from services in the ``UpdateUserPassword`` command to directly modify the
|
||||
user's password if the requested user is the one invoking the operation
|
||||
(https://github.com/ansible-collections/community.general/issues/8652, https://github.com/ansible-collections/community.general/pull/8653).
|
||||
- redfish_confg - remove ``CapacityBytes`` from required paramaters of the
|
||||
``CreateVolume`` command (https://github.com/ansible-collections/community.general/pull/8956).
|
||||
- redfish_config - add parameter ``storage_none_volume_deletion`` to ``CreateVolume``
|
||||
command in order to control the automatic deletion of non-RAID volumes (https://github.com/ansible-collections/community.general/pull/8990).
|
||||
- redfish_info - adds ``RedfishURI`` and ``StorageId`` to Disk inventory (https://github.com/ansible-collections/community.general/pull/8937).
|
||||
- scaleway_container - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_info - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_namespace - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_namespace_info - replace Python 2.6 construct with dict
|
||||
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_registry - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_container_registry_info - replace Python 2.6 construct with dict
|
||||
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_info - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_namespace - replace Python 2.6 construct with dict comprehensions
|
||||
(https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_function_namespace_info - replace Python 2.6 construct with dict
|
||||
comprehensions (https://github.com/ansible-collections/community.general/pull/8858).
|
||||
- scaleway_user_data - better construct when using ``dict.items()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
- udm_dns_record - replace loop with ``dict.update()`` (https://github.com/ansible-collections/community.general/pull/8876).
|
||||
release_summary: 'Regular bugfix and feature release.
|
||||
|
||||
|
||||
Please note that this is the last feature release for community.general 9.x.y.
|
||||
|
||||
From now on, new features will only go into community.general 10.x.y.'
|
||||
fragments:
|
||||
- 8532-expand-opennuebula-inventory-data.yml
|
||||
- 8652-Redfish-Password-Change-Required.yml
|
||||
- 8679-fix-cloudflare-srv.yml
|
||||
- 8719-openiscsi-add-multiple-targets.yaml
|
||||
- 8785-keycloak_user_federation-set-krbPrincipalAttribute-to-empty-string-if-missing.yaml
|
||||
- 8812-keycloak-user-federation-remove-lastSync-param-from-kc-responses.yml
|
||||
- 8855-gio_mime_vardict.yml
|
||||
- 8856-jira_vardict.yml
|
||||
- 8858-dict-comprehension.yml
|
||||
- 8876-dict-items-loop.yml
|
||||
- 8877-keycloak_realm-sort-lists-before-change-detection.yaml
|
||||
- 8885-add-force-flag-for-nmp.yml
|
||||
- 8887-fix-one_service-unique.yml
|
||||
- 8889-refactor-one-image-modules.yml
|
||||
- 8895-fix-comprehension.yaml
|
||||
- 8897-nmcli-add-reload-and-up-down.yml
|
||||
- 8898-add-arg-to-exclude-bind-credential-from-change-check.yaml
|
||||
- 8900-ipa-hostgroup-fix-states.yml
|
||||
- 8908-add-gitlab-group-params.yml
|
||||
- 8909-flatpak-improve-name-parsing.yaml
|
||||
- 8917-proxmox-clean-auth.yml
|
||||
- 8920-ipa-host-fix-state.yml
|
||||
- 8923-keycloak_userprofile-fix-empty-response-when-fetching-userprofile.yml
|
||||
- 8925-atomic.yml
|
||||
- 8929-cmd_runner-bugfix.yml
|
||||
- 8937-add-StorageId-RedfishURI-to-disk-facts.yml
|
||||
- 8940-keycloak_userprofile-improve-diff.yml
|
||||
- 8944-django-command-fix.yml
|
||||
- 8952-password-store-lookup-create-subkey-support.yml
|
||||
- 8954-keycloak-user-federation-add-referral-parameter.yml
|
||||
- 8956-remove-capacitybytes-from-the-required-parameters_list.yml
|
||||
- 8966-dig-add-port-option.yml
|
||||
- 8970-fix-dig-multi-nameservers.yml
|
||||
- 8973-keycloak_client-add-x509-auth.yml
|
||||
- 8987-legacycrypt.yml
|
||||
- 8990.yml
|
||||
- 9.5.0.yml
|
||||
- deprecate-hipchat.yml
|
||||
modules:
|
||||
- description: Manage keytab file in FreeIPA.
|
||||
name: ipa_getkeytab
|
||||
namespace: ''
|
||||
release_date: '2024-10-07'
|
||||
|
||||
@@ -68,20 +68,27 @@ This is meant to be done once, then every time you need to execute the command y
|
||||
with runner("version") as ctx:
|
||||
dummy, stdout, dummy = ctx.run()
|
||||
|
||||
# passes arg 'data' to AnsibleModule.run_command()
|
||||
with runner("type name", data=stdin_data) as ctx:
|
||||
dummy, stdout, dummy = ctx.run()
|
||||
|
||||
# Another way of expressing it
|
||||
dummy, stdout, dummy = runner("version").run()
|
||||
|
||||
Note that you can pass values for the arguments when calling ``run()``,
|
||||
otherwise ``CmdRunner`` uses the module options with the exact same names to
|
||||
provide values for the runner arguments. If no value is passed and no module option
|
||||
is found for the name specified, then an exception is raised, unless the
|
||||
argument is using ``cmd_runner_fmt.as_fixed`` as format function like the
|
||||
``version`` in the example above. See more about it below.
|
||||
Note that you can pass values for the arguments when calling ``run()``, otherwise ``CmdRunner``
|
||||
uses the module options with the exact same names to provide values for the runner arguments.
|
||||
If no value is passed and no module option is found for the name specified, then an exception is raised, unless
|
||||
the argument is using ``cmd_runner_fmt.as_fixed`` as format function like the ``version`` in the example above.
|
||||
See more about it below.
|
||||
|
||||
In the first example, values of ``type``, ``force``, ``no_deps`` and others
|
||||
are taken straight from the module, whilst ``galaxy_cmd`` and ``upgrade`` are
|
||||
passed explicitly.
|
||||
|
||||
.. note::
|
||||
|
||||
It is not possible to automatically retrieve values of suboptions.
|
||||
|
||||
That generates a resulting command line similar to (example taken from the
|
||||
output of an integration test):
|
||||
|
||||
@@ -110,7 +117,7 @@ into something formatted for the command line.
|
||||
Argument format function
|
||||
""""""""""""""""""""""""
|
||||
|
||||
An ``arg_format`` function should be of the form:
|
||||
An ``arg_format`` function is defined in the form similar to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -155,7 +162,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_list()``
|
||||
- Example:
|
||||
- Examples:
|
||||
+----------------------+---------------------+
|
||||
| Value | Outcome |
|
||||
+======================+=====================+
|
||||
@@ -167,12 +174,11 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
- ``cmd_runner_fmt.as_bool()``
|
||||
This method receives two different parameters: ``args_true`` and ``args_false``, latter being optional.
|
||||
If the boolean evaluation of ``value`` is ``True``, the format function returns ``args_true``.
|
||||
If the boolean evaluation is ``False``, then the function returns ``args_false``
|
||||
if it was provided, or ``[]`` otherwise.
|
||||
If the boolean evaluation is ``False``, then the function returns ``args_false`` if it was provided, or ``[]`` otherwise.
|
||||
|
||||
- Creation:
|
||||
- Creation (one arg):
|
||||
``cmd_runner_fmt.as_bool("--force")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+------------+--------------------+
|
||||
| Value | Outcome |
|
||||
+============+====================+
|
||||
@@ -180,6 +186,30 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
+------------+--------------------+
|
||||
| ``False`` | ``[]`` |
|
||||
+------------+--------------------+
|
||||
- Creation (two args, ``None`` treated as ``False``):
|
||||
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it")``
|
||||
- Examples:
|
||||
+------------+----------------------+
|
||||
| Value | Outcome |
|
||||
+============+======================+
|
||||
| ``True`` | ``["--relax"]`` |
|
||||
+------------+----------------------+
|
||||
| ``False`` | ``["--dont-do-it"]`` |
|
||||
+------------+----------------------+
|
||||
| | ``["--dont-do-it"]`` |
|
||||
+------------+----------------------+
|
||||
- Creation (two args, ``None`` is ignored):
|
||||
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it", ignore_none=True)``
|
||||
- Examples:
|
||||
+------------+----------------------+
|
||||
| Value | Outcome |
|
||||
+============+======================+
|
||||
| ``True`` | ``["--relax"]`` |
|
||||
+------------+----------------------+
|
||||
| ``False`` | ``["--dont-do-it"]`` |
|
||||
+------------+----------------------+
|
||||
| | ``[]`` |
|
||||
+------------+----------------------+
|
||||
|
||||
- ``cmd_runner_fmt.as_bool_not()``
|
||||
This method receives one parameter, which is returned by the function when the boolean evaluation
|
||||
@@ -187,7 +217,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_bool_not("--no-deps")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+-------------+---------------------+
|
||||
| Value | Outcome |
|
||||
+=============+=====================+
|
||||
@@ -202,7 +232,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_optval("-i")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+---------------+---------------------+
|
||||
| Value | Outcome |
|
||||
+===============+=====================+
|
||||
@@ -216,7 +246,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_opt_val("--name")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+--------------+--------------------------+
|
||||
| Value | Outcome |
|
||||
+==============+==========================+
|
||||
@@ -229,7 +259,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_opt_eq_val("--num-cpus")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+------------+-------------------------+
|
||||
| Value | Outcome |
|
||||
+============+=========================+
|
||||
@@ -243,7 +273,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_fixed("--version")``
|
||||
- Example:
|
||||
- Examples:
|
||||
+---------+-----------------------+
|
||||
| Value | Outcome |
|
||||
+=========+=======================+
|
||||
@@ -265,7 +295,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_map(dict(a=1, b=2, c=3), default=42)``
|
||||
- Example:
|
||||
- Examples:
|
||||
+---------------------+---------------+
|
||||
| Value | Outcome |
|
||||
+=====================+===============+
|
||||
@@ -359,6 +389,8 @@ Settings that can be passed to the ``CmdRunner`` constructor are:
|
||||
Command to be executed. It can be a single string, the executable name, or a list
|
||||
of strings containing the executable name as the first element and, optionally, fixed parameters.
|
||||
Those parameters are used in all executions of the runner.
|
||||
The *executable* pointed by this parameter (whether itself when ``str`` or its first element when ``list``) is
|
||||
processed using ``AnsibleModule.get_bin_path()`` *unless* it is an absolute path or contains the character ``/``.
|
||||
- ``arg_formats: dict``
|
||||
Mapping of argument names to formatting functions.
|
||||
- ``default_args_order: str``
|
||||
@@ -394,6 +426,10 @@ When creating a context, the additional settings that can be passed to the call
|
||||
Defaults to ``False``.
|
||||
- ``check_mode_return: any``
|
||||
If ``check_mode_skip=True``, then return this value instead.
|
||||
- valid named arguments to ``AnsibleModule.run_command()``
|
||||
Other than ``args``, any valid argument to ``run_command()`` can be passed when setting up the run context.
|
||||
For example, ``data`` can be used to send information to the command's standard input.
|
||||
Or ``cwd`` can be used to run the command inside a specific working directory.
|
||||
|
||||
Additionally, any other valid parameters for ``AnsibleModule.run_command()`` may be passed, but unexpected behavior
|
||||
might occur if redefining options already present in the runner or its context creation. Use with caution.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace: community
|
||||
name: general
|
||||
version: 9.4.0
|
||||
version: 9.5.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
@@ -75,6 +75,10 @@ plugin_routing:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
|
||||
hipchat:
|
||||
deprecation:
|
||||
removal_version: 11.0.0
|
||||
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
rax_cbs_attachments:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
|
||||
42
plugins/doc_fragments/pipx.py
Normal file
42
plugins/doc_fragments/pipx.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024, Alexei Znamensky <russoz@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
global:
|
||||
description:
|
||||
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
|
||||
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
|
||||
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
|
||||
type: bool
|
||||
default: false
|
||||
executable:
|
||||
description:
|
||||
- Path to the C(pipx) installed in the system.
|
||||
- >
|
||||
If not specified, the module will use C(python -m pipx) to run the tool,
|
||||
using the same Python interpreter as ansible itself.
|
||||
type: path
|
||||
notes:
|
||||
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
|
||||
- Please note that C(pipx) requires Python 3.6 or above.
|
||||
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
|
||||
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
|
||||
- >
|
||||
This module will honor C(pipx) environment variables such as but not limited to E(PIPX_HOME) and E(PIPX_BIN_DIR)
|
||||
passed using the R(environment Ansible keyword, playbooks_environment).
|
||||
|
||||
seealso:
|
||||
- name: C(pipx) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://pipx.pypa.io/latest/docs/
|
||||
|
||||
'''
|
||||
@@ -199,6 +199,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
continue
|
||||
|
||||
server['name'] = vm.NAME
|
||||
server['id'] = vm.ID
|
||||
if hasattr(vm.HISTORY_RECORDS, 'HISTORY') and vm.HISTORY_RECORDS.HISTORY:
|
||||
server['host'] = vm.HISTORY_RECORDS.HISTORY[-1].HOSTNAME
|
||||
server['LABELS'] = labels
|
||||
server['v4_first_ip'] = self._get_vm_ipv4(vm)
|
||||
server['v6_first_ip'] = self._get_vm_ipv6(vm)
|
||||
|
||||
@@ -275,11 +275,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
return self.session
|
||||
|
||||
def _get_auth(self):
|
||||
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
|
||||
|
||||
if self.proxmox_password:
|
||||
|
||||
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
|
||||
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password})
|
||||
|
||||
a = self._get_session()
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ DOCUMENTATION = '''
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 7.5.0
|
||||
port:
|
||||
description: Use port as target port when looking up DNS records.
|
||||
default: 53
|
||||
type: int
|
||||
version_added: 9.5.0
|
||||
notes:
|
||||
- ALL is not a record per-se, merely the listed fields are available for any record results you retrieve in the form of a dictionary.
|
||||
- While the 'dig' lookup plugin supports anything which dnspython supports out of the box, only a subset can be converted into a dictionary.
|
||||
@@ -330,11 +335,13 @@ class LookupModule(LookupBase):
|
||||
myres.use_edns(0, ednsflags=dns.flags.DO, payload=edns_size)
|
||||
|
||||
domains = []
|
||||
nameservers = []
|
||||
qtype = self.get_option('qtype')
|
||||
flat = self.get_option('flat')
|
||||
fail_on_error = self.get_option('fail_on_error')
|
||||
real_empty = self.get_option('real_empty')
|
||||
tcp = self.get_option('tcp')
|
||||
port = self.get_option('port')
|
||||
try:
|
||||
rdclass = dns.rdataclass.from_text(self.get_option('class'))
|
||||
except Exception as e:
|
||||
@@ -345,7 +352,6 @@ class LookupModule(LookupBase):
|
||||
if t.startswith('@'): # e.g. "@10.0.1.2,192.0.2.1" is ok.
|
||||
nsset = t[1:].split(',')
|
||||
for ns in nsset:
|
||||
nameservers = []
|
||||
# Check if we have a valid IP address. If so, use that, otherwise
|
||||
# try to resolve name to address using system's resolver. If that
|
||||
# fails we bail out.
|
||||
@@ -358,7 +364,6 @@ class LookupModule(LookupBase):
|
||||
nameservers.append(nsaddr)
|
||||
except Exception as e:
|
||||
raise AnsibleError("dns lookup NS: %s" % to_native(e))
|
||||
myres.nameservers = nameservers
|
||||
continue
|
||||
if '=' in t:
|
||||
try:
|
||||
@@ -397,6 +402,11 @@ class LookupModule(LookupBase):
|
||||
|
||||
# print "--- domain = {0} qtype={1} rdclass={2}".format(domain, qtype, rdclass)
|
||||
|
||||
if port:
|
||||
myres.port = port
|
||||
if len(nameservers) > 0:
|
||||
myres.nameservers = nameservers
|
||||
|
||||
if qtype.upper() == 'PTR':
|
||||
reversed_domains = []
|
||||
for domain in domains:
|
||||
|
||||
@@ -14,7 +14,7 @@ DOCUMENTATION = '''
|
||||
short_description: manage passwords with passwordstore.org's pass utility
|
||||
description:
|
||||
- Enables Ansible to retrieve, create or update passwords from the passwordstore.org pass utility.
|
||||
It also retrieves YAML style keys stored as multilines in the passwordfile.
|
||||
It can also retrieve, create or update YAML style keys stored as multilines in the passwordfile.
|
||||
- To avoid problems when accessing multiple secrets at once, add C(auto-expand-secmem) to
|
||||
C(~/.gnupg/gpg-agent.conf). Where this is not possible, consider using O(lock=readwrite) instead.
|
||||
options:
|
||||
@@ -33,11 +33,11 @@ DOCUMENTATION = '''
|
||||
env:
|
||||
- name: PASSWORD_STORE_DIR
|
||||
create:
|
||||
description: Create the password if it does not already exist. Takes precedence over O(missing).
|
||||
description: Create the password or the subkey if it does not already exist. Takes precedence over O(missing).
|
||||
type: bool
|
||||
default: false
|
||||
overwrite:
|
||||
description: Overwrite the password if it does already exist.
|
||||
description: Overwrite the password or the subkey if it does already exist.
|
||||
type: bool
|
||||
default: false
|
||||
umask:
|
||||
@@ -53,7 +53,9 @@ DOCUMENTATION = '''
|
||||
type: bool
|
||||
default: false
|
||||
subkey:
|
||||
description: Return a specific subkey of the password. When set to V(password), always returns the first line.
|
||||
description:
|
||||
- By default return a specific subkey of the password. When set to V(password), always returns the first line.
|
||||
- With O(overwrite=true), it will create the subkey and return it.
|
||||
type: str
|
||||
default: password
|
||||
userpass:
|
||||
@@ -64,7 +66,7 @@ DOCUMENTATION = '''
|
||||
type: integer
|
||||
default: 16
|
||||
backup:
|
||||
description: Used with O(overwrite=true). Backup the previous password in a subkey.
|
||||
description: Used with O(overwrite=true). Backup the previous password or subkey in a subkey.
|
||||
type: bool
|
||||
default: false
|
||||
nosymbols:
|
||||
@@ -189,6 +191,17 @@ tasks.yml: |
|
||||
vars:
|
||||
mypassword: "{{ lookup('community.general.passwordstore', 'example/test', missing='create')}}"
|
||||
|
||||
- name: >-
|
||||
Create a random 16 character password in a subkey. If the password file already exists, just add the subkey in it.
|
||||
If the subkey exists, returns it
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, subkey='foo') }}"
|
||||
|
||||
- name: >-
|
||||
Create a random 16 character password in a subkey. Overwrite if it already exists and backup the old one.
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.passwordstore', 'example/test', create=true, subkey='user', overwrite=true, backup=true) }}"
|
||||
|
||||
- name: Prints 'abc' if example/test does not exist, just give the password otherwise
|
||||
ansible.builtin.debug:
|
||||
var: mypassword
|
||||
@@ -411,15 +424,48 @@ class LookupModule(LookupBase):
|
||||
|
||||
def update_password(self):
|
||||
# generate new password, insert old lines from current result and return new password
|
||||
# if the target is a subkey, only modify the subkey
|
||||
newpass = self.get_newpass()
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass
|
||||
if self.paramvals['preserve'] or self.paramvals['timestamp']:
|
||||
msg += '\n'
|
||||
if self.paramvals['preserve'] and self.passoutput[1:]:
|
||||
msg += '\n'.join(self.passoutput[1:]) + '\n'
|
||||
if self.paramvals['timestamp'] and self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
subkey = self.paramvals["subkey"]
|
||||
|
||||
if subkey != "password":
|
||||
|
||||
msg_lines = []
|
||||
subkey_exists = False
|
||||
subkey_line = "{0}: {1}".format(subkey, newpass)
|
||||
oldpass = None
|
||||
|
||||
for line in self.passoutput:
|
||||
if line.startswith("{0}: ".format(subkey)):
|
||||
oldpass = self.passdict[subkey]
|
||||
line = subkey_line
|
||||
subkey_exists = True
|
||||
|
||||
msg_lines.append(line)
|
||||
|
||||
if not subkey_exists:
|
||||
msg_lines.insert(2, subkey_line)
|
||||
|
||||
if self.paramvals["timestamp"] and self.paramvals["backup"] and oldpass and oldpass != newpass:
|
||||
msg_lines.append(
|
||||
"lookup_pass: old subkey '{0}' password was {1} (Updated on {2})\n".format(
|
||||
subkey, oldpass, datetime
|
||||
)
|
||||
)
|
||||
|
||||
msg = os.linesep.join(msg_lines)
|
||||
|
||||
else:
|
||||
msg = newpass
|
||||
|
||||
if self.paramvals['preserve'] or self.paramvals['timestamp']:
|
||||
msg += '\n'
|
||||
if self.paramvals['preserve'] and self.passoutput[1:]:
|
||||
msg += '\n'.join(self.passoutput[1:]) + '\n'
|
||||
if self.paramvals['timestamp'] and self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
@@ -431,13 +477,21 @@ class LookupModule(LookupBase):
|
||||
# use pwgen to generate the password and insert values with pass -m
|
||||
newpass = self.get_newpass()
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass
|
||||
subkey = self.paramvals["subkey"]
|
||||
|
||||
if subkey != "password":
|
||||
msg = "\n\n{0}: {1}".format(subkey, newpass)
|
||||
else:
|
||||
msg = newpass
|
||||
|
||||
if self.paramvals['timestamp']:
|
||||
msg += '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
|
||||
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError('exit code {0} while running {1}. Error output: {2}'.format(e.returncode, e.cmd, e.output))
|
||||
|
||||
return newpass
|
||||
|
||||
def get_passresult(self):
|
||||
@@ -525,7 +579,10 @@ class LookupModule(LookupBase):
|
||||
self.parse_params(term) # parse the input into paramvals
|
||||
with self.opt_lock('readwrite'):
|
||||
if self.check_pass(): # password exists
|
||||
if self.paramvals['overwrite'] and self.paramvals['subkey'] == 'password':
|
||||
if self.paramvals['overwrite']:
|
||||
with self.opt_lock('write'):
|
||||
result.append(self.update_password())
|
||||
elif self.paramvals["subkey"] != "password" and not self.passdict.get(self.paramvals['subkey']): # password exists but not the subkey
|
||||
with self.opt_lock('write'):
|
||||
result.append(self.update_password())
|
||||
else:
|
||||
|
||||
@@ -239,7 +239,7 @@ class CmdRunner(object):
|
||||
self.check_rc = check_rc
|
||||
if force_lang == "auto":
|
||||
try:
|
||||
self.force_lang = get_best_parsable_locale()
|
||||
self.force_lang = get_best_parsable_locale(module)
|
||||
except RuntimeWarning:
|
||||
self.force_lang = "C"
|
||||
else:
|
||||
|
||||
@@ -16,6 +16,7 @@ from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||
HAS_PYONE = True
|
||||
|
||||
try:
|
||||
@@ -347,3 +348,90 @@ class OpenNebulaModule:
|
||||
result: the Ansible result
|
||||
"""
|
||||
raise NotImplementedError("Method requires implementation")
|
||||
|
||||
def get_image_list_id(self, image, element):
|
||||
"""
|
||||
This is a helper function for get_image_info to iterate over a simple list of objects
|
||||
"""
|
||||
list_of_id = []
|
||||
|
||||
if element == 'VMS':
|
||||
image_list = image.VMS
|
||||
if element == 'CLONES':
|
||||
image_list = image.CLONES
|
||||
if element == 'APP_CLONES':
|
||||
image_list = image.APP_CLONES
|
||||
|
||||
for iter in image_list.ID:
|
||||
list_of_id.append(
|
||||
# These are optional so firstly check for presence
|
||||
getattr(iter, 'ID', 'Null'),
|
||||
)
|
||||
return list_of_id
|
||||
|
||||
def get_image_snapshots_list(self, image):
|
||||
"""
|
||||
This is a helper function for get_image_info to iterate over a dictionary
|
||||
"""
|
||||
list_of_snapshots = []
|
||||
|
||||
for iter in image.SNAPSHOTS.SNAPSHOT:
|
||||
list_of_snapshots.append({
|
||||
'date': iter['DATE'],
|
||||
'parent': iter['PARENT'],
|
||||
'size': iter['SIZE'],
|
||||
# These are optional so firstly check for presence
|
||||
'allow_orhans': getattr(image.SNAPSHOTS, 'ALLOW_ORPHANS', 'Null'),
|
||||
'children': getattr(iter, 'CHILDREN', 'Null'),
|
||||
'active': getattr(iter, 'ACTIVE', 'Null'),
|
||||
'name': getattr(iter, 'NAME', 'Null'),
|
||||
})
|
||||
return list_of_snapshots
|
||||
|
||||
def get_image_info(self, image):
|
||||
"""
|
||||
This method is used by one_image and one_image_info modules to retrieve
|
||||
information from XSD scheme of an image
|
||||
Returns: a copy of the parameters that includes the resolved parameters.
|
||||
"""
|
||||
info = {
|
||||
'id': image.ID,
|
||||
'name': image.NAME,
|
||||
'state': IMAGE_STATES[image.STATE],
|
||||
'running_vms': image.RUNNING_VMS,
|
||||
'used': bool(image.RUNNING_VMS),
|
||||
'user_name': image.UNAME,
|
||||
'user_id': image.UID,
|
||||
'group_name': image.GNAME,
|
||||
'group_id': image.GID,
|
||||
'permissions': {
|
||||
'owner_u': image.PERMISSIONS.OWNER_U,
|
||||
'owner_m': image.PERMISSIONS.OWNER_M,
|
||||
'owner_a': image.PERMISSIONS.OWNER_A,
|
||||
'group_u': image.PERMISSIONS.GROUP_U,
|
||||
'group_m': image.PERMISSIONS.GROUP_M,
|
||||
'group_a': image.PERMISSIONS.GROUP_A,
|
||||
'other_u': image.PERMISSIONS.OTHER_U,
|
||||
'other_m': image.PERMISSIONS.OTHER_M,
|
||||
'other_a': image.PERMISSIONS.OTHER_A
|
||||
},
|
||||
'type': image.TYPE,
|
||||
'disk_type': image.DISK_TYPE,
|
||||
'persistent': image.PERSISTENT,
|
||||
'regtime': image.REGTIME,
|
||||
'source': image.SOURCE,
|
||||
'path': image.PATH,
|
||||
'fstype': getattr(image, 'FSTYPE', 'Null'),
|
||||
'size': image.SIZE,
|
||||
'cloning_ops': image.CLONING_OPS,
|
||||
'cloning_id': image.CLONING_ID,
|
||||
'target_snapshot': image.TARGET_SNAPSHOT,
|
||||
'datastore_id': image.DATASTORE_ID,
|
||||
'datastore': image.DATASTORE,
|
||||
'vms': self.get_image_list_id(image, 'VMS'),
|
||||
'clones': self.get_image_list_id(image, 'CLONES'),
|
||||
'app_clones': self.get_image_list_id(image, 'APP_CLONES'),
|
||||
'snapshots': self.get_image_snapshots_list(image),
|
||||
'template': image.TEMPLATE,
|
||||
}
|
||||
return info
|
||||
|
||||
@@ -9,6 +9,12 @@ __metaclass__ = type
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
|
||||
|
||||
|
||||
pipx_common_argspec = {
|
||||
"global": dict(type='bool', default=False),
|
||||
"executable": dict(type='path'),
|
||||
}
|
||||
|
||||
|
||||
_state_map = dict(
|
||||
install='install',
|
||||
install_all='install-all',
|
||||
|
||||
@@ -22,10 +22,12 @@ class PythonRunner(CmdRunner):
|
||||
if (os.path.isabs(python) or '/' in python):
|
||||
self.python = python
|
||||
elif self.has_venv:
|
||||
path_prefix = os.path.join(venv, "bin")
|
||||
if path_prefix is None:
|
||||
path_prefix = []
|
||||
path_prefix.append(os.path.join(venv, "bin"))
|
||||
if environ_update is None:
|
||||
environ_update = {}
|
||||
environ_update["PATH"] = "%s:%s" % (path_prefix, os.environ["PATH"])
|
||||
environ_update["PATH"] = "%s:%s" % (":".join(path_prefix), os.environ["PATH"])
|
||||
environ_update["VIRTUAL_ENV"] = venv
|
||||
|
||||
python_cmd = [self.python] + _ensure_list(command)
|
||||
|
||||
@@ -165,11 +165,11 @@ class RedfishUtils(object):
|
||||
if not allow_no_resp:
|
||||
raise
|
||||
except HTTPError as e:
|
||||
msg = self._get_extended_message(e)
|
||||
msg, data = self._get_extended_message(e)
|
||||
return {'ret': False,
|
||||
'msg': "HTTP Error %s on GET request to '%s', extended message: '%s'"
|
||||
% (e.code, uri, msg),
|
||||
'status': e.code}
|
||||
'status': e.code, 'data': data}
|
||||
except URLError as e:
|
||||
return {'ret': False, 'msg': "URL Error on GET request to '%s': '%s'"
|
||||
% (uri, e.reason)}
|
||||
@@ -208,11 +208,11 @@ class RedfishUtils(object):
|
||||
data = None
|
||||
headers = {k.lower(): v for (k, v) in resp.info().items()}
|
||||
except HTTPError as e:
|
||||
msg = self._get_extended_message(e)
|
||||
msg, data = self._get_extended_message(e)
|
||||
return {'ret': False,
|
||||
'msg': "HTTP Error %s on POST request to '%s', extended message: '%s'"
|
||||
% (e.code, uri, msg),
|
||||
'status': e.code}
|
||||
'status': e.code, 'data': data}
|
||||
except URLError as e:
|
||||
return {'ret': False, 'msg': "URL Error on POST request to '%s': '%s'"
|
||||
% (uri, e.reason)}
|
||||
@@ -256,11 +256,11 @@ class RedfishUtils(object):
|
||||
follow_redirects='all',
|
||||
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
|
||||
except HTTPError as e:
|
||||
msg = self._get_extended_message(e)
|
||||
msg, data = self._get_extended_message(e)
|
||||
return {'ret': False, 'changed': False,
|
||||
'msg': "HTTP Error %s on PATCH request to '%s', extended message: '%s'"
|
||||
% (e.code, uri, msg),
|
||||
'status': e.code}
|
||||
'status': e.code, 'data': data}
|
||||
except URLError as e:
|
||||
return {'ret': False, 'changed': False,
|
||||
'msg': "URL Error on PATCH request to '%s': '%s'" % (uri, e.reason)}
|
||||
@@ -291,11 +291,11 @@ class RedfishUtils(object):
|
||||
follow_redirects='all',
|
||||
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
|
||||
except HTTPError as e:
|
||||
msg = self._get_extended_message(e)
|
||||
msg, data = self._get_extended_message(e)
|
||||
return {'ret': False,
|
||||
'msg': "HTTP Error %s on PUT request to '%s', extended message: '%s'"
|
||||
% (e.code, uri, msg),
|
||||
'status': e.code}
|
||||
'status': e.code, 'data': data}
|
||||
except URLError as e:
|
||||
return {'ret': False, 'msg': "URL Error on PUT request to '%s': '%s'"
|
||||
% (uri, e.reason)}
|
||||
@@ -317,11 +317,11 @@ class RedfishUtils(object):
|
||||
follow_redirects='all',
|
||||
use_proxy=True, timeout=self.timeout, ciphers=self.ciphers)
|
||||
except HTTPError as e:
|
||||
msg = self._get_extended_message(e)
|
||||
msg, data = self._get_extended_message(e)
|
||||
return {'ret': False,
|
||||
'msg': "HTTP Error %s on DELETE request to '%s', extended message: '%s'"
|
||||
% (e.code, uri, msg),
|
||||
'status': e.code}
|
||||
'status': e.code, 'data': data}
|
||||
except URLError as e:
|
||||
return {'ret': False, 'msg': "URL Error on DELETE request to '%s': '%s'"
|
||||
% (uri, e.reason)}
|
||||
@@ -391,8 +391,10 @@ class RedfishUtils(object):
|
||||
:param error: an HTTPError exception
|
||||
:type error: HTTPError
|
||||
:return: the ExtendedInfo message if present, else standard HTTP error
|
||||
:return: the JSON data of the response if present
|
||||
"""
|
||||
msg = http_client.responses.get(error.code, '')
|
||||
data = None
|
||||
if error.code >= 400:
|
||||
try:
|
||||
body = error.read().decode('utf-8')
|
||||
@@ -406,7 +408,7 @@ class RedfishUtils(object):
|
||||
msg = str(data['error']['@Message.ExtendedInfo'])
|
||||
except Exception:
|
||||
pass
|
||||
return msg
|
||||
return msg, data
|
||||
|
||||
def _init_session(self):
|
||||
pass
|
||||
@@ -864,6 +866,7 @@ class RedfishUtils(object):
|
||||
return response
|
||||
data = response['data']
|
||||
controller_name = 'Controller 1'
|
||||
storage_id = data['Id']
|
||||
if 'Controllers' in data:
|
||||
controllers_uri = data['Controllers'][u'@odata.id']
|
||||
|
||||
@@ -898,6 +901,7 @@ class RedfishUtils(object):
|
||||
data = response['data']
|
||||
|
||||
drive_result = {}
|
||||
drive_result['RedfishURI'] = data['@odata.id']
|
||||
for property in properties:
|
||||
if property in data:
|
||||
if data[property] is not None:
|
||||
@@ -909,6 +913,7 @@ class RedfishUtils(object):
|
||||
drive_result[property] = data[property]
|
||||
drive_results.append(drive_result)
|
||||
drives = {'Controller': controller_name,
|
||||
'StorageId': storage_id,
|
||||
'Drives': drive_results}
|
||||
result["entries"].append(drives)
|
||||
|
||||
@@ -1245,32 +1250,49 @@ class RedfishUtils(object):
|
||||
return response
|
||||
return {'ret': True, 'changed': True}
|
||||
|
||||
def _find_account_uri(self, username=None, acct_id=None):
|
||||
def _find_account_uri(self, username=None, acct_id=None, password_change_uri=None):
|
||||
if not any((username, acct_id)):
|
||||
return {'ret': False, 'msg':
|
||||
'Must provide either account_id or account_username'}
|
||||
|
||||
response = self.get_request(self.root_uri + self.accounts_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
|
||||
uris = [a.get('@odata.id') for a in data.get('Members', []) if
|
||||
a.get('@odata.id')]
|
||||
for uri in uris:
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if password_change_uri:
|
||||
# Password change required; go directly to the specified URI
|
||||
response = self.get_request(self.root_uri + password_change_uri)
|
||||
if response['ret'] is False:
|
||||
continue
|
||||
return response
|
||||
data = response['data']
|
||||
headers = response['headers']
|
||||
if username:
|
||||
if username == data.get('UserName'):
|
||||
return {'ret': True, 'data': data,
|
||||
'headers': headers, 'uri': uri}
|
||||
'headers': headers, 'uri': password_change_uri}
|
||||
if acct_id:
|
||||
if acct_id == data.get('Id'):
|
||||
return {'ret': True, 'data': data,
|
||||
'headers': headers, 'uri': uri}
|
||||
'headers': headers, 'uri': password_change_uri}
|
||||
else:
|
||||
# Walk the accounts collection to find the desired user
|
||||
response = self.get_request(self.root_uri + self.accounts_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
|
||||
uris = [a.get('@odata.id') for a in data.get('Members', []) if
|
||||
a.get('@odata.id')]
|
||||
for uri in uris:
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if response['ret'] is False:
|
||||
continue
|
||||
data = response['data']
|
||||
headers = response['headers']
|
||||
if username:
|
||||
if username == data.get('UserName'):
|
||||
return {'ret': True, 'data': data,
|
||||
'headers': headers, 'uri': uri}
|
||||
if acct_id:
|
||||
if acct_id == data.get('Id'):
|
||||
return {'ret': True, 'data': data,
|
||||
'headers': headers, 'uri': uri}
|
||||
|
||||
return {'ret': False, 'no_match': True, 'msg':
|
||||
'No account with the given account_id or account_username found'}
|
||||
@@ -1491,7 +1513,8 @@ class RedfishUtils(object):
|
||||
'Must provide account_password for UpdateUserPassword command'}
|
||||
|
||||
response = self._find_account_uri(username=user.get('account_username'),
|
||||
acct_id=user.get('account_id'))
|
||||
acct_id=user.get('account_id'),
|
||||
password_change_uri=user.get('account_passwordchangerequired'))
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
@@ -1534,6 +1557,31 @@ class RedfishUtils(object):
|
||||
resp['msg'] = 'Modified account service'
|
||||
return resp
|
||||
|
||||
def check_password_change_required(self, return_data):
|
||||
"""
|
||||
Checks a response if a user needs to change their password
|
||||
|
||||
:param return_data: The return data for a failed request
|
||||
:return: None or the URI of the account to update
|
||||
"""
|
||||
uri = None
|
||||
if 'data' in return_data:
|
||||
# Find the extended messages in the response payload
|
||||
extended_messages = return_data['data'].get('error', {}).get('@Message.ExtendedInfo', [])
|
||||
if len(extended_messages) == 0:
|
||||
extended_messages = return_data['data'].get('@Message.ExtendedInfo', [])
|
||||
# Go through each message and look for Base.1.X.PasswordChangeRequired
|
||||
for message in extended_messages:
|
||||
message_id = message.get('MessageId')
|
||||
if message_id is None:
|
||||
# While this is invalid, treat the lack of a MessageId as "no message"
|
||||
continue
|
||||
if message_id.startswith('Base.1.') and message_id.endswith('.PasswordChangeRequired'):
|
||||
# Password change required; get the URI of the user account
|
||||
uri = message['MessageArgs'][0]
|
||||
break
|
||||
return uri
|
||||
|
||||
def get_sessions(self):
|
||||
result = {}
|
||||
# listing all users has always been slower than other operations, why?
|
||||
@@ -3694,7 +3742,7 @@ class RedfishUtils(object):
|
||||
return {'ret': True, 'changed': True,
|
||||
'msg': "The following volumes were deleted: %s" % str(volume_ids)}
|
||||
|
||||
def create_volume(self, volume_details, storage_subsystem_id):
|
||||
def create_volume(self, volume_details, storage_subsystem_id, storage_none_volume_deletion=False):
|
||||
# Find the Storage resource from the requested ComputerSystem resource
|
||||
response = self.get_request(self.root_uri + self.systems_uri)
|
||||
if response['ret'] is False:
|
||||
@@ -3729,8 +3777,8 @@ class RedfishUtils(object):
|
||||
'msg': "Provided Storage Subsystem ID %s does not exist on the server" % storage_subsystem_id}
|
||||
|
||||
# Validate input parameters
|
||||
required_parameters = ['RAIDType', 'Drives', 'CapacityBytes']
|
||||
allowed_parameters = ['DisplayName', 'InitializeMethod', 'MediaSpanCount',
|
||||
required_parameters = ['RAIDType', 'Drives']
|
||||
allowed_parameters = ['CapacityBytes', 'DisplayName', 'InitializeMethod', 'MediaSpanCount',
|
||||
'Name', 'ReadCachePolicy', 'StripSizeBytes', 'VolumeUsage', 'WriteCachePolicy']
|
||||
|
||||
for parameter in required_parameters:
|
||||
@@ -3746,22 +3794,23 @@ class RedfishUtils(object):
|
||||
data = response['data']
|
||||
|
||||
# Deleting any volumes of RAIDType None present on the Storage Subsystem
|
||||
response = self.get_request(self.root_uri + data['Volumes']['@odata.id'])
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
volume_data = response['data']
|
||||
if storage_none_volume_deletion:
|
||||
response = self.get_request(self.root_uri + data['Volumes']['@odata.id'])
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
volume_data = response['data']
|
||||
|
||||
if "Members" in volume_data:
|
||||
for member in volume_data["Members"]:
|
||||
response = self.get_request(self.root_uri + member['@odata.id'])
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
member_data = response['data']
|
||||
|
||||
if member_data["RAIDType"] == "None":
|
||||
response = self.delete_request(self.root_uri + member['@odata.id'])
|
||||
if "Members" in volume_data:
|
||||
for member in volume_data["Members"]:
|
||||
response = self.get_request(self.root_uri + member['@odata.id'])
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
member_data = response['data']
|
||||
|
||||
if member_data["RAIDType"] == "None":
|
||||
response = self.delete_request(self.root_uri + member['@odata.id'])
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
|
||||
# Construct payload and issue POST command to create volume
|
||||
volume_details["Links"] = {}
|
||||
|
||||
@@ -9,23 +9,29 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ansible_galaxy_install
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
short_description: Install Ansible roles or collections using ansible-galaxy
|
||||
version_added: 3.5.0
|
||||
description:
|
||||
- This module allows the installation of Ansible collections or roles using C(ansible-galaxy).
|
||||
- This module allows the installation of Ansible collections or roles using C(ansible-galaxy).
|
||||
notes:
|
||||
- Support for B(Ansible 2.9/2.10) was removed in community.general 8.0.0.
|
||||
- >
|
||||
The module will try and run using the C(C.UTF-8) locale.
|
||||
If that fails, it will try C(en_US.UTF-8).
|
||||
If that one also fails, the module will fail.
|
||||
- Support for B(Ansible 2.9/2.10) was removed in community.general 8.0.0.
|
||||
- >
|
||||
The module will try and run using the C(C.UTF-8) locale.
|
||||
If that fails, it will try C(en_US.UTF-8).
|
||||
If that one also fails, the module will fail.
|
||||
seealso:
|
||||
- name: C(ansible-galaxy) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://docs.ansible.com/ansible/latest/cli/ansible-galaxy.html
|
||||
|
||||
requirements:
|
||||
- ansible-core 2.11 or newer
|
||||
- ansible-core 2.11 or newer
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
@@ -34,62 +40,63 @@ attributes:
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- >
|
||||
If O(state=present) then the collection or role will be installed.
|
||||
Note that the collections and roles are not updated with this option.
|
||||
- >
|
||||
Currently the O(state=latest) is ignored unless O(type=collection), and it will
|
||||
ensure the collection is installed and updated to the latest available version.
|
||||
- Please note that O(force=true) can be used to perform upgrade regardless of O(type).
|
||||
- >
|
||||
If O(state=present) then the collection or role will be installed.
|
||||
Note that the collections and roles are not updated with this option.
|
||||
- >
|
||||
Currently the O(state=latest) is ignored unless O(type=collection), and it will
|
||||
ensure the collection is installed and updated to the latest available version.
|
||||
- Please note that O(force=true) can be used to perform upgrade regardless of O(type).
|
||||
type: str
|
||||
choices: [ present, latest ]
|
||||
choices: [present, latest]
|
||||
default: present
|
||||
version_added: 9.1.0
|
||||
type:
|
||||
description:
|
||||
- The type of installation performed by C(ansible-galaxy).
|
||||
- If O(type=both), then O(requirements_file) must be passed and it may contain both roles and collections.
|
||||
- "Note however that the opposite is not true: if using a O(requirements_file), then O(type) can be any of the three choices."
|
||||
- The type of installation performed by C(ansible-galaxy).
|
||||
- If O(type=both), then O(requirements_file) must be passed and it may contain both roles and collections.
|
||||
- "Note however that the opposite is not true: if using a O(requirements_file), then O(type) can be any of the three choices."
|
||||
type: str
|
||||
choices: [collection, role, both]
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- Name of the collection or role being installed.
|
||||
- >
|
||||
Versions can be specified with C(ansible-galaxy) usual formats.
|
||||
For example, the collection V(community.docker:1.6.1) or the role V(ansistrano.deploy,3.8.0).
|
||||
- O(name) and O(requirements_file) are mutually exclusive.
|
||||
- Name of the collection or role being installed.
|
||||
- >
|
||||
Versions can be specified with C(ansible-galaxy) usual formats.
|
||||
For example, the collection V(community.docker:1.6.1) or the role V(ansistrano.deploy,3.8.0).
|
||||
- O(name) and O(requirements_file) are mutually exclusive.
|
||||
type: str
|
||||
requirements_file:
|
||||
description:
|
||||
- Path to a file containing a list of requirements to be installed.
|
||||
- It works for O(type) equals to V(collection) and V(role).
|
||||
- O(name) and O(requirements_file) are mutually exclusive.
|
||||
- Path to a file containing a list of requirements to be installed.
|
||||
- It works for O(type) equals to V(collection) and V(role).
|
||||
- O(name) and O(requirements_file) are mutually exclusive.
|
||||
type: path
|
||||
dest:
|
||||
description:
|
||||
- The path to the directory containing your collections or roles, according to the value of O(type).
|
||||
- >
|
||||
Please notice that C(ansible-galaxy) will not install collections with O(type=both), when O(requirements_file)
|
||||
contains both roles and collections and O(dest) is specified.
|
||||
- The path to the directory containing your collections or roles, according to the value of O(type).
|
||||
- >
|
||||
Please notice that C(ansible-galaxy) will not install collections with O(type=both), when O(requirements_file)
|
||||
contains both roles and collections and O(dest) is specified.
|
||||
type: path
|
||||
no_deps:
|
||||
description:
|
||||
- Refrain from installing dependencies.
|
||||
- Refrain from installing dependencies.
|
||||
version_added: 4.5.0
|
||||
type: bool
|
||||
default: false
|
||||
force:
|
||||
description:
|
||||
- Force overwriting existing roles and/or collections.
|
||||
- It can be used for upgrading, but the module output will always report C(changed=true).
|
||||
- Using O(force=true) is mandatory when downgrading.
|
||||
- Force overwriting existing roles and/or collections.
|
||||
- It can be used for upgrading, but the module output will always report C(changed=true).
|
||||
- Using O(force=true) is mandatory when downgrading.
|
||||
type: bool
|
||||
default: false
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Install collection community.network
|
||||
community.general.ansible_galaxy_install:
|
||||
type: collection
|
||||
@@ -111,76 +118,76 @@ EXAMPLES = """
|
||||
type: collection
|
||||
name: community.network:3.0.2
|
||||
force: true
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
type:
|
||||
description: The value of the O(type) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
name:
|
||||
description: The value of the O(name) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
dest:
|
||||
description: The value of the O(dest) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
requirements_file:
|
||||
description: The value of the O(requirements_file) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
force:
|
||||
description: The value of the O(force) parameter.
|
||||
type: bool
|
||||
returned: always
|
||||
installed_roles:
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the roles installed per path.
|
||||
- If O(name) is specified, returns that role name and the version installed per path.
|
||||
type: dict
|
||||
returned: always when installing roles
|
||||
contains:
|
||||
"<path>":
|
||||
description: Roles and versions for that path.
|
||||
type: dict
|
||||
sample:
|
||||
/home/user42/.ansible/roles:
|
||||
ansistrano.deploy: 3.9.0
|
||||
baztian.xfce: v0.0.3
|
||||
/custom/ansible/roles:
|
||||
ansistrano.deploy: 3.8.0
|
||||
installed_collections:
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the collections installed per path.
|
||||
- If O(name) is specified, returns that collection name and the version installed per path.
|
||||
type: dict
|
||||
returned: always when installing collections
|
||||
contains:
|
||||
"<path>":
|
||||
description: Collections and versions for that path
|
||||
type: dict
|
||||
sample:
|
||||
/home/az/.ansible/collections/ansible_collections:
|
||||
community.docker: 1.6.0
|
||||
community.general: 3.0.2
|
||||
/custom/ansible/ansible_collections:
|
||||
community.general: 3.1.0
|
||||
new_collections:
|
||||
description: New collections installed by this module.
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
community.general: 3.1.0
|
||||
community.docker: 1.6.1
|
||||
new_roles:
|
||||
description: New roles installed by this module.
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
ansistrano.deploy: 3.8.0
|
||||
---
|
||||
type:
|
||||
description: The value of the O(type) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
name:
|
||||
description: The value of the O(name) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
dest:
|
||||
description: The value of the O(dest) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
requirements_file:
|
||||
description: The value of the O(requirements_file) parameter.
|
||||
type: str
|
||||
returned: always
|
||||
force:
|
||||
description: The value of the O(force) parameter.
|
||||
type: bool
|
||||
returned: always
|
||||
installed_roles:
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the roles installed per path.
|
||||
- If O(name) is specified, returns that role name and the version installed per path.
|
||||
type: dict
|
||||
returned: always when installing roles
|
||||
contains:
|
||||
"<path>":
|
||||
description: Roles and versions for that path.
|
||||
type: dict
|
||||
sample:
|
||||
/home/user42/.ansible/roles:
|
||||
ansistrano.deploy: 3.9.0
|
||||
baztian.xfce: v0.0.3
|
||||
/custom/ansible/roles:
|
||||
ansistrano.deploy: 3.8.0
|
||||
installed_collections:
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the collections installed per path.
|
||||
- If O(name) is specified, returns that collection name and the version installed per path.
|
||||
type: dict
|
||||
returned: always when installing collections
|
||||
contains:
|
||||
"<path>":
|
||||
description: Collections and versions for that path
|
||||
type: dict
|
||||
sample:
|
||||
/home/az/.ansible/collections/ansible_collections:
|
||||
community.docker: 1.6.0
|
||||
community.general: 3.0.2
|
||||
/custom/ansible/ansible_collections:
|
||||
community.general: 3.1.0
|
||||
new_collections:
|
||||
description: New collections installed by this module.
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
community.general: 3.1.0
|
||||
community.docker: 1.6.1
|
||||
new_roles:
|
||||
description: New roles installed by this module.
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
ansistrano.deploy: 3.8.0
|
||||
baztian.xfce: v0.0.3
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
@@ -102,40 +102,40 @@ EXAMPLES = r'''
|
||||
- name: Create a @home subvolume under the root subvolume
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@home
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
- name: Remove the @home subvolume if it exists
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@home
|
||||
state: absent
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the root subvolume named @
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@
|
||||
snapshot_source: /
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the root subvolume and make it the new default subvolume
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@
|
||||
snapshot_source: /
|
||||
default: Yes
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the /@ subvolume and recursively creating intermediate subvolumes as required
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@snapshots/@2022_06_09
|
||||
snapshot_source: /@
|
||||
recursive: True
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
- name: Remove the /@ subvolume and recursively delete child subvolumes as required
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@snapshots/@2022_06_09
|
||||
snapshot_source: /@
|
||||
recursive: True
|
||||
device: /dev/vda2
|
||||
filesystem_device: /dev/vda2
|
||||
|
||||
'''
|
||||
|
||||
|
||||
@@ -716,12 +716,14 @@ class CloudflareAPI(object):
|
||||
"port": params['port'],
|
||||
"weight": params['weight'],
|
||||
"priority": params['priority'],
|
||||
"name": params['record'],
|
||||
"proto": params['proto'],
|
||||
"service": params['service']
|
||||
}
|
||||
|
||||
new_record = {"type": params['type'], "ttl": params['ttl'], 'data': srv_data}
|
||||
new_record = {
|
||||
"type": params['type'],
|
||||
"name": params['service'] + '.' + params['proto'] + '.' + params['record'],
|
||||
"ttl": params['ttl'],
|
||||
'data': srv_data,
|
||||
}
|
||||
search_value = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
|
||||
search_record = params['service'] + '.' + params['proto'] + '.' + params['record']
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: cpanm
|
||||
short_description: Manages Perl library dependencies
|
||||
description:
|
||||
- Manage Perl library dependencies using cpanminus.
|
||||
- Manage Perl library dependencies using cpanminus.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
@@ -27,76 +27,82 @@ options:
|
||||
name:
|
||||
type: str
|
||||
description:
|
||||
- The Perl library to install. Valid values change according to the O(mode), see notes for more details.
|
||||
- Note that for installing from a local path the parameter O(from_path) should be used.
|
||||
- The Perl library to install. Valid values change according to the O(mode), see notes for more details.
|
||||
- Note that for installing from a local path the parameter O(from_path) should be used.
|
||||
aliases: [pkg]
|
||||
from_path:
|
||||
type: path
|
||||
description:
|
||||
- The local directory or C(tar.gz) file to install from.
|
||||
- The local directory or C(tar.gz) file to install from.
|
||||
notest:
|
||||
description:
|
||||
- Do not run unit tests.
|
||||
- Do not run unit tests.
|
||||
type: bool
|
||||
default: false
|
||||
locallib:
|
||||
description:
|
||||
- Specify the install base to install modules.
|
||||
- Specify the install base to install modules.
|
||||
type: path
|
||||
mirror:
|
||||
description:
|
||||
- Specifies the base URL for the CPAN mirror to use.
|
||||
- Specifies the base URL for the CPAN mirror to use.
|
||||
type: str
|
||||
mirror_only:
|
||||
description:
|
||||
- Use the mirror's index file instead of the CPAN Meta DB.
|
||||
- Use the mirror's index file instead of the CPAN Meta DB.
|
||||
type: bool
|
||||
default: false
|
||||
installdeps:
|
||||
description:
|
||||
- Only install dependencies.
|
||||
- Only install dependencies.
|
||||
type: bool
|
||||
default: false
|
||||
version:
|
||||
description:
|
||||
- Version specification for the perl module. When O(mode) is V(new), C(cpanm) version operators are accepted.
|
||||
- Version specification for the perl module. When O(mode) is V(new), C(cpanm) version operators are accepted.
|
||||
type: str
|
||||
executable:
|
||||
description:
|
||||
- Override the path to the cpanm executable.
|
||||
- Override the path to the cpanm executable.
|
||||
type: path
|
||||
mode:
|
||||
description:
|
||||
- Controls the module behavior. See notes below for more details.
|
||||
- The default changed from V(compatibility) to V(new) in community.general 9.0.0.
|
||||
- Controls the module behavior. See notes below for more details.
|
||||
- The default changed from V(compatibility) to V(new) in community.general 9.0.0.
|
||||
type: str
|
||||
choices: [compatibility, new]
|
||||
default: new
|
||||
version_added: 3.0.0
|
||||
name_check:
|
||||
description:
|
||||
- When O(mode=new), this parameter can be used to check if there is a module O(name) installed (at O(version), when specified).
|
||||
- When O(mode=new), this parameter can be used to check if there is a module O(name) installed (at O(version), when specified).
|
||||
type: str
|
||||
version_added: 3.0.0
|
||||
notes:
|
||||
- Please note that U(http://search.cpan.org/dist/App-cpanminus/bin/cpanm, cpanm) must be installed on the remote host.
|
||||
- "This module now comes with a choice of execution O(mode): V(compatibility) or V(new)."
|
||||
- >
|
||||
O(mode=compatibility): When using V(compatibility) mode, the module will keep backward compatibility.
|
||||
This was the default mode before community.general 9.0.0.
|
||||
O(name) must be either a module name or a distribution file. If the perl module given by O(name) is installed (at the exact O(version)
|
||||
when specified), then nothing happens. Otherwise, it will be installed using the C(cpanm) executable. O(name) cannot be an URL, or a git URL.
|
||||
C(cpanm) version specifiers do not work in this mode.
|
||||
- >
|
||||
O(mode=new): When using V(new) mode, the module will behave differently. The O(name) parameter may refer to a module name, a distribution file,
|
||||
a HTTP URL or a git repository URL as described in C(cpanminus) documentation. C(cpanm) version specifiers are recognized.
|
||||
This is the default mode from community.general 9.0.0 onwards.
|
||||
author:
|
||||
- "Franck Cuny (@fcuny)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
'''
|
||||
- Please note that U(http://search.cpan.org/dist/App-cpanminus/bin/cpanm, cpanm) must be installed on the remote host.
|
||||
- "This module now comes with a choice of execution O(mode): V(compatibility) or V(new)."
|
||||
- >
|
||||
O(mode=compatibility): When using V(compatibility) mode, the module will keep backward compatibility.
|
||||
This was the default mode before community.general 9.0.0.
|
||||
O(name) must be either a module name or a distribution file. If the perl module given by O(name) is installed (at the exact O(version)
|
||||
when specified), then nothing happens. Otherwise, it will be installed using the C(cpanm) executable. O(name) cannot be an URL, or a git URL.
|
||||
C(cpanm) version specifiers do not work in this mode.
|
||||
- >
|
||||
O(mode=new): When using V(new) mode, the module will behave differently. The O(name) parameter may refer to a module name, a distribution file,
|
||||
a HTTP URL or a git repository URL as described in C(cpanminus) documentation. C(cpanm) version specifiers are recognized.
|
||||
This is the default mode from community.general 9.0.0 onwards.
|
||||
|
||||
EXAMPLES = '''
|
||||
seealso:
|
||||
- name: C(cpanm) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://metacpan.org/dist/App-cpanminus/view/bin/cpanm
|
||||
author:
|
||||
- "Franck Cuny (@fcuny)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Install Dancer perl package
|
||||
community.general.cpanm:
|
||||
name: Dancer
|
||||
@@ -134,7 +140,7 @@ EXAMPLES = '''
|
||||
community.general.cpanm:
|
||||
name: Dancer
|
||||
version: '1.0'
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ run_info:
|
||||
returned: success and O(verbosity) >= 3
|
||||
"""
|
||||
|
||||
import shlex
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.django import DjangoModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt
|
||||
|
||||
@@ -74,6 +76,9 @@ class DjangoCommand(DjangoModuleHelper):
|
||||
)
|
||||
django_admin_arg_order = "extra_args"
|
||||
|
||||
def __init_module__(self):
|
||||
self.vars.command = shlex.split(self.vars.command)
|
||||
|
||||
|
||||
def main():
|
||||
DjangoCommand.execute()
|
||||
|
||||
@@ -329,13 +329,39 @@ def _match_flat_using_flatpak_column_feature(module, binary, parsed_name, method
|
||||
return row.split()[0]
|
||||
|
||||
|
||||
def _is_flatpak_id(part):
|
||||
# For guidelines on application IDs, refer to the following resources:
|
||||
# Flatpak:
|
||||
# https://docs.flatpak.org/en/latest/conventions.html#application-ids
|
||||
# Flathub:
|
||||
# https://docs.flathub.org/docs/for-app-authors/requirements#application-id
|
||||
if '.' not in part:
|
||||
return False
|
||||
sections = part.split('.')
|
||||
if len(sections) < 2:
|
||||
return False
|
||||
domain = sections[0]
|
||||
if not domain.islower():
|
||||
return False
|
||||
for section in sections[1:]:
|
||||
if not section.isalnum():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _parse_flatpak_name(name):
|
||||
if name.startswith('http://') or name.startswith('https://'):
|
||||
file_name = urlparse(name).path.split('/')[-1]
|
||||
file_name_without_extension = file_name.split('.')[0:-1]
|
||||
common_name = ".".join(file_name_without_extension)
|
||||
else:
|
||||
common_name = name
|
||||
parts = name.split('/')
|
||||
for part in parts:
|
||||
if _is_flatpak_id(part):
|
||||
common_name = part
|
||||
break
|
||||
else:
|
||||
common_name = name
|
||||
return common_name
|
||||
|
||||
|
||||
|
||||
@@ -9,16 +9,21 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: gconftool2
|
||||
author:
|
||||
- Kenneth D. Evensen (@kevensen)
|
||||
- Kenneth D. Evensen (@kevensen)
|
||||
short_description: Edit GNOME Configurations
|
||||
description:
|
||||
- This module allows for the manipulation of GNOME 2 Configuration via
|
||||
gconftool-2. Please see the gconftool-2(1) man pages for more details.
|
||||
- This module allows for the manipulation of GNOME 2 Configuration via gconftool-2. Please see the gconftool-2(1) man pages for more details.
|
||||
seealso:
|
||||
- name: C(gconftool-2) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://help.gnome.org/admin//system-admin-guide/2.32/gconf-6.html.en
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
@@ -28,42 +33,36 @@ options:
|
||||
key:
|
||||
type: str
|
||||
description:
|
||||
- A GConf preference key is an element in the GConf repository
|
||||
that corresponds to an application preference. See man gconftool-2(1).
|
||||
- A GConf preference key is an element in the GConf repository that corresponds to an application preference.
|
||||
required: true
|
||||
value:
|
||||
type: str
|
||||
description:
|
||||
- Preference keys typically have simple values such as strings,
|
||||
integers, or lists of strings and integers.
|
||||
This is ignored unless O(state=present). See man gconftool-2(1).
|
||||
- Preference keys typically have simple values such as strings, integers, or lists of strings and integers. This is ignored unless O(state=present).
|
||||
value_type:
|
||||
type: str
|
||||
description:
|
||||
- The type of value being set.
|
||||
This is ignored unless O(state=present). See man gconftool-2(1).
|
||||
choices: [ bool, float, int, string ]
|
||||
- The type of value being set. This is ignored unless O(state=present).
|
||||
choices: [bool, float, int, string]
|
||||
state:
|
||||
type: str
|
||||
description:
|
||||
- The action to take upon the key/value.
|
||||
required: true
|
||||
choices: [ absent, present ]
|
||||
choices: [absent, present]
|
||||
config_source:
|
||||
type: str
|
||||
description:
|
||||
- Specify a configuration source to use rather than the default path.
|
||||
See man gconftool-2(1).
|
||||
direct:
|
||||
description:
|
||||
- Access the config database directly, bypassing server. If O(direct) is
|
||||
specified then the O(config_source) must be specified as well.
|
||||
See man gconftool-2(1).
|
||||
- Access the config database directly, bypassing server. If O(direct) is specified then the O(config_source) must be specified as well.
|
||||
type: bool
|
||||
default: false
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Change the widget font to "Serif 12"
|
||||
community.general.gconftool2:
|
||||
key: "/desktop/gnome/interface/font_name"
|
||||
@@ -71,33 +70,33 @@ EXAMPLES = """
|
||||
value: "Serif 12"
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
key:
|
||||
description: The key specified in the module parameters.
|
||||
returned: success
|
||||
type: str
|
||||
sample: /desktop/gnome/interface/font_name
|
||||
value_type:
|
||||
description: The type of the value that was changed.
|
||||
returned: success
|
||||
type: str
|
||||
sample: string
|
||||
value:
|
||||
description:
|
||||
- The value of the preference key after executing the module or V(null) if key is removed.
|
||||
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Serif 12"
|
||||
previous_value:
|
||||
description:
|
||||
- The value of the preference key before executing the module.
|
||||
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Serif 12"
|
||||
...
|
||||
'''
|
||||
RETURN = """
|
||||
---
|
||||
key:
|
||||
description: The key specified in the module parameters.
|
||||
returned: success
|
||||
type: str
|
||||
sample: /desktop/gnome/interface/font_name
|
||||
value_type:
|
||||
description: The type of the value that was changed.
|
||||
returned: success
|
||||
type: str
|
||||
sample: string
|
||||
value:
|
||||
description:
|
||||
- The value of the preference key after executing the module or V(null) if key is removed.
|
||||
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Serif 12"
|
||||
previous_value:
|
||||
description:
|
||||
- The value of the preference key before executing the module.
|
||||
- From community.general 7.0.0 onwards it returns V(null) for a non-existent O(key), and returned V("") before that.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Serif 12"
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.gconftool2 import gconftool2_runner
|
||||
|
||||
@@ -7,46 +7,50 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: gconftool2_info
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
short_description: Retrieve GConf configurations
|
||||
version_added: 5.1.0
|
||||
description:
|
||||
- This module allows retrieving application preferences from the GConf database, with the help of C(gconftool-2).
|
||||
- This module allows retrieving application preferences from the GConf database, with the help of C(gconftool-2).
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
options:
|
||||
key:
|
||||
description:
|
||||
- The key name for an element in the GConf database.
|
||||
type: str
|
||||
required: true
|
||||
notes:
|
||||
- See man gconftool-2(1) for more details.
|
||||
seealso:
|
||||
- name: gconf repository (archived)
|
||||
description: Git repository for the project. It is an archived project, so the repository is read-only.
|
||||
link: https://gitlab.gnome.org/Archive/gconf
|
||||
'''
|
||||
- name: C(gconftool-2) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://help.gnome.org/admin//system-admin-guide/2.32/gconf-6.html.en
|
||||
- name: gconf repository (archived)
|
||||
description: Git repository for the project. It is an archived project, so the repository is read-only.
|
||||
link: https://gitlab.gnome.org/Archive/gconf
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Get value for a certain key in the database.
|
||||
community.general.gconftool2_info:
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
register: result
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
value:
|
||||
description:
|
||||
- The value of the property.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Monospace 10
|
||||
'''
|
||||
RETURN = """
|
||||
---
|
||||
value:
|
||||
description:
|
||||
- The value of the property.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Monospace 10
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.gconftool2 import gconftool2_runner
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: gio_mime
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
short_description: Set default handler for MIME type, for applications using Gnome GIO
|
||||
version_added: 7.5.0
|
||||
description:
|
||||
- This module allows configuring the default handler for a specific MIME type, to be used by applications built with th Gnome GIO API.
|
||||
- This module allows configuring the default handler for a specific MIME type, to be used by applications built with th Gnome GIO API.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
@@ -25,24 +26,28 @@ attributes:
|
||||
options:
|
||||
mime_type:
|
||||
description:
|
||||
- MIME type for which a default handler will be set.
|
||||
- MIME type for which a default handler will be set.
|
||||
type: str
|
||||
required: true
|
||||
handler:
|
||||
description:
|
||||
- Default handler will be set for the MIME type.
|
||||
- Default handler will be set for the MIME type.
|
||||
type: str
|
||||
required: true
|
||||
notes:
|
||||
- This module is a thin wrapper around the C(gio mime) command (and subcommand).
|
||||
- See man gio(1) for more details.
|
||||
- This module is a thin wrapper around the C(gio mime) command (and subcommand).
|
||||
- See man gio(1) for more details.
|
||||
seealso:
|
||||
- name: GIO Documentation
|
||||
description: Reference documentation for the GIO API..
|
||||
link: https://docs.gtk.org/gio/
|
||||
'''
|
||||
- name: C(gio) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://man.archlinux.org/man/gio.1
|
||||
- name: GIO Documentation
|
||||
description: Reference documentation for the GIO API..
|
||||
link: https://docs.gtk.org/gio/
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Set chrome as the default handler for https
|
||||
community.general.gio_mime:
|
||||
mime_type: x-scheme-handler/https
|
||||
@@ -50,26 +55,27 @@ EXAMPLES = """
|
||||
register: result
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
handler:
|
||||
description:
|
||||
- The handler set as default.
|
||||
returned: success
|
||||
type: str
|
||||
sample: google-chrome.desktop
|
||||
stdout:
|
||||
description:
|
||||
- The output of the C(gio) command.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Set google-chrome.desktop as the default for x-scheme-handler/https
|
||||
stderr:
|
||||
description:
|
||||
- The error output of the C(gio) command.
|
||||
returned: failure
|
||||
type: str
|
||||
sample: 'gio: Failed to load info for handler "never-existed.desktop"'
|
||||
'''
|
||||
RETURN = """
|
||||
---
|
||||
handler:
|
||||
description:
|
||||
- The handler set as default.
|
||||
returned: success
|
||||
type: str
|
||||
sample: google-chrome.desktop
|
||||
stdout:
|
||||
description:
|
||||
- The output of the C(gio) command.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Set google-chrome.desktop as the default for x-scheme-handler/https
|
||||
stderr:
|
||||
description:
|
||||
- The error output of the C(gio) command.
|
||||
returned: failure
|
||||
type: str
|
||||
sample: 'gio: Failed to load info for handler "never-existed.desktop"'
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.gio_mime import gio_mime_runner, gio_mime_get
|
||||
@@ -84,7 +90,7 @@ class GioMime(ModuleHelper):
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
mute_vardict_deprecation = True
|
||||
use_old_vardict = False
|
||||
|
||||
def __init_module__(self):
|
||||
self.runner = gio_mime_runner(self.module, check_rc=True)
|
||||
@@ -92,7 +98,7 @@ class GioMime(ModuleHelper):
|
||||
|
||||
def __run__(self):
|
||||
check_mode_return = (0, 'Module executed in check mode', '')
|
||||
if self.vars.has_changed("handler"):
|
||||
if self.vars.has_changed:
|
||||
with self.runner.context(args_order=["mime_type", "handler"], check_mode_skip=True, check_mode_return=check_mode_return) as ctx:
|
||||
rc, out, err = ctx.run()
|
||||
self.vars.stdout = out
|
||||
|
||||
@@ -196,9 +196,9 @@ class GitLabDeployKey(object):
|
||||
changed = False
|
||||
|
||||
for arg_key, arg_value in arguments.items():
|
||||
if arguments[arg_key] is not None:
|
||||
if getattr(deploy_key, arg_key) != arguments[arg_key]:
|
||||
setattr(deploy_key, arg_key, arguments[arg_key])
|
||||
if arg_value is not None:
|
||||
if getattr(deploy_key, arg_key) != arg_value:
|
||||
setattr(deploy_key, arg_key, arg_value)
|
||||
changed = True
|
||||
|
||||
return (changed, deploy_key)
|
||||
|
||||
@@ -33,66 +33,35 @@ attributes:
|
||||
support: none
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the group you want to create.
|
||||
required: true
|
||||
type: str
|
||||
path:
|
||||
description:
|
||||
- The path of the group you want to create, this will be api_url/group_path
|
||||
- If not supplied, the group_name will be used.
|
||||
type: str
|
||||
description:
|
||||
description:
|
||||
- A description for the group.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- create or delete group.
|
||||
- Possible values are present and absent.
|
||||
default: present
|
||||
type: str
|
||||
choices: ["present", "absent"]
|
||||
parent:
|
||||
description:
|
||||
- Allow to create subgroups
|
||||
- Id or Full path of parent group in the form of group/name
|
||||
type: str
|
||||
visibility:
|
||||
description:
|
||||
- Default visibility of the group
|
||||
choices: ["private", "internal", "public"]
|
||||
default: private
|
||||
type: str
|
||||
project_creation_level:
|
||||
description:
|
||||
- Determine if developers can create projects in the group.
|
||||
choices: ["developer", "maintainer", "noone"]
|
||||
type: str
|
||||
version_added: 3.7.0
|
||||
auto_devops_enabled:
|
||||
description:
|
||||
- Default to Auto DevOps pipeline for all projects within this group.
|
||||
type: bool
|
||||
version_added: 3.7.0
|
||||
subgroup_creation_level:
|
||||
description:
|
||||
- Allowed to create subgroups.
|
||||
choices: ["maintainer", "owner"]
|
||||
type: str
|
||||
version_added: 3.7.0
|
||||
require_two_factor_authentication:
|
||||
description:
|
||||
- Require all users in this group to setup two-factor authentication.
|
||||
type: bool
|
||||
version_added: 3.7.0
|
||||
avatar_path:
|
||||
description:
|
||||
- Absolute path image to configure avatar. File size should not exceed 200 kb.
|
||||
- This option is only used on creation, not for updates.
|
||||
type: path
|
||||
version_added: 4.2.0
|
||||
default_branch:
|
||||
description:
|
||||
- All merge requests and commits are made against this branch unless you specify a different one.
|
||||
type: str
|
||||
version_added: 9.5.0
|
||||
description:
|
||||
description:
|
||||
- A description for the group.
|
||||
type: str
|
||||
enabled_git_access_protocol:
|
||||
description:
|
||||
- V(all) means SSH and HTTP(S) is enabled.
|
||||
- V(ssh) means only SSH is enabled.
|
||||
- V(http) means only HTTP(S) is enabled.
|
||||
- Only available for top level groups.
|
||||
choices: ["all", "ssh", "http"]
|
||||
type: str
|
||||
version_added: 9.5.0
|
||||
force_delete:
|
||||
description:
|
||||
- Force delete group even if projects in it.
|
||||
@@ -100,6 +69,113 @@ options:
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 7.5.0
|
||||
lfs_enabled:
|
||||
description:
|
||||
- Projects in this group can use Git LFS.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
lock_duo_features_enabled:
|
||||
description:
|
||||
- Enforce GitLab Duo features for all subgroups.
|
||||
- Only available for top level groups.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
membership_lock:
|
||||
description:
|
||||
- Users cannot be added to projects in this group.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
mentions_disabled:
|
||||
description:
|
||||
- Group mentions are disabled.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
name:
|
||||
description:
|
||||
- Name of the group you want to create.
|
||||
required: true
|
||||
type: str
|
||||
parent:
|
||||
description:
|
||||
- Allow to create subgroups
|
||||
- Id or Full path of parent group in the form of group/name
|
||||
type: str
|
||||
path:
|
||||
description:
|
||||
- The path of the group you want to create, this will be api_url/group_path
|
||||
- If not supplied, the group_name will be used.
|
||||
type: str
|
||||
prevent_forking_outside_group:
|
||||
description:
|
||||
- Prevent forking outside of the group.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
prevent_sharing_groups_outside_hierarchy:
|
||||
description:
|
||||
- Members cannot invite groups outside of this group and its subgroups.
|
||||
- Only available for top level groups.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
project_creation_level:
|
||||
description:
|
||||
- Determine if developers can create projects in the group.
|
||||
choices: ["developer", "maintainer", "noone"]
|
||||
type: str
|
||||
version_added: 3.7.0
|
||||
request_access_enabled:
|
||||
description:
|
||||
- Users can request access (if visibility is public or internal).
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
service_access_tokens_expiration_enforced:
|
||||
description:
|
||||
- Service account token expiration.
|
||||
- Changes will not affect existing token expiration dates.
|
||||
- Only available for top level groups.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
share_with_group_lock:
|
||||
description:
|
||||
- Projects cannot be shared with other groups.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
require_two_factor_authentication:
|
||||
description:
|
||||
- Require all users in this group to setup two-factor authentication.
|
||||
type: bool
|
||||
version_added: 3.7.0
|
||||
state:
|
||||
description:
|
||||
- create or delete group.
|
||||
- Possible values are present and absent.
|
||||
default: present
|
||||
type: str
|
||||
choices: ["present", "absent"]
|
||||
subgroup_creation_level:
|
||||
description:
|
||||
- Allowed to create subgroups.
|
||||
choices: ["maintainer", "owner"]
|
||||
type: str
|
||||
version_added: 3.7.0
|
||||
two_factor_grace_period:
|
||||
description:
|
||||
- Delay 2FA enforcement (hours).
|
||||
type: str
|
||||
version_added: 9.5.0
|
||||
visibility:
|
||||
description:
|
||||
- Default visibility of the group
|
||||
choices: ["private", "internal", "public"]
|
||||
default: private
|
||||
type: str
|
||||
wiki_access_level:
|
||||
description:
|
||||
- V(enabled) means everyone can access the wiki.
|
||||
- V(private) means only members of this group can access the wiki.
|
||||
- V(disabled) means group-level wiki is disabled.
|
||||
choices: ["enabled", "private", "disabled"]
|
||||
type: str
|
||||
version_added: 9.5.0
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -202,23 +278,38 @@ class GitLabGroup(object):
|
||||
def create_or_update_group(self, name, parent, options):
|
||||
changed = False
|
||||
|
||||
payload = {
|
||||
'auto_devops_enabled': options['auto_devops_enabled'],
|
||||
'default_branch': options['default_branch'],
|
||||
'description': options['description'],
|
||||
'lfs_enabled': options['lfs_enabled'],
|
||||
'membership_lock': options['membership_lock'],
|
||||
'mentions_disabled': options['mentions_disabled'],
|
||||
'name': name,
|
||||
'path': options['path'],
|
||||
'prevent_forking_outside_group': options['prevent_forking_outside_group'],
|
||||
'project_creation_level': options['project_creation_level'],
|
||||
'request_access_enabled': options['request_access_enabled'],
|
||||
'require_two_factor_authentication': options['require_two_factor_authentication'],
|
||||
'share_with_group_lock': options['share_with_group_lock'],
|
||||
'subgroup_creation_level': options['subgroup_creation_level'],
|
||||
'visibility': options['visibility'],
|
||||
'wiki_access_level': options['wiki_access_level'],
|
||||
}
|
||||
if options.get('enabled_git_access_protocol') and parent is None:
|
||||
payload['enabled_git_access_protocol'] = options['enabled_git_access_protocol']
|
||||
if options.get('lock_duo_features_enabled') and parent is None:
|
||||
payload['lock_duo_features_enabled'] = options['lock_duo_features_enabled']
|
||||
if options.get('prevent_sharing_groups_outside_hierarchy') and parent is None:
|
||||
payload['prevent_sharing_groups_outside_hierarchy'] = options['prevent_sharing_groups_outside_hierarchy']
|
||||
if options.get('service_access_tokens_expiration_enforced') and parent is None:
|
||||
payload['service_access_tokens_expiration_enforced'] = options['service_access_tokens_expiration_enforced']
|
||||
if options.get('two_factor_grace_period'):
|
||||
payload['two_factor_grace_period'] = int(options['two_factor_grace_period'])
|
||||
|
||||
# Because we have already call userExists in main()
|
||||
if self.group_object is None:
|
||||
parent_id = self.get_group_id(parent)
|
||||
|
||||
payload = {
|
||||
'name': name,
|
||||
'path': options['path'],
|
||||
'parent_id': parent_id,
|
||||
'visibility': options['visibility'],
|
||||
'project_creation_level': options['project_creation_level'],
|
||||
'auto_devops_enabled': options['auto_devops_enabled'],
|
||||
'subgroup_creation_level': options['subgroup_creation_level'],
|
||||
}
|
||||
if options.get('description'):
|
||||
payload['description'] = options['description']
|
||||
if options.get('require_two_factor_authentication'):
|
||||
payload['require_two_factor_authentication'] = options['require_two_factor_authentication']
|
||||
payload['parent_id'] = self.get_group_id(parent)
|
||||
group = self.create_group(payload)
|
||||
|
||||
# add avatar to group
|
||||
@@ -229,15 +320,7 @@ class GitLabGroup(object):
|
||||
self._module.fail_json(msg='Cannot open {0}: {1}'.format(options['avatar_path'], e))
|
||||
changed = True
|
||||
else:
|
||||
changed, group = self.update_group(self.group_object, {
|
||||
'name': name,
|
||||
'description': options['description'],
|
||||
'visibility': options['visibility'],
|
||||
'project_creation_level': options['project_creation_level'],
|
||||
'auto_devops_enabled': options['auto_devops_enabled'],
|
||||
'subgroup_creation_level': options['subgroup_creation_level'],
|
||||
'require_two_factor_authentication': options['require_two_factor_authentication'],
|
||||
})
|
||||
changed, group = self.update_group(self.group_object, payload)
|
||||
|
||||
self.group_object = group
|
||||
if changed:
|
||||
@@ -277,9 +360,9 @@ class GitLabGroup(object):
|
||||
changed = False
|
||||
|
||||
for arg_key, arg_value in arguments.items():
|
||||
if arguments[arg_key] is not None:
|
||||
if getattr(group, arg_key) != arguments[arg_key]:
|
||||
setattr(group, arg_key, arguments[arg_key])
|
||||
if arg_value is not None:
|
||||
if getattr(group, arg_key) != arg_value:
|
||||
setattr(group, arg_key, arg_value)
|
||||
changed = True
|
||||
|
||||
return (changed, group)
|
||||
@@ -322,28 +405,41 @@ def main():
|
||||
argument_spec = basic_auth_argument_spec()
|
||||
argument_spec.update(auth_argument_spec())
|
||||
argument_spec.update(dict(
|
||||
name=dict(type='str', required=True),
|
||||
path=dict(type='str'),
|
||||
description=dict(type='str'),
|
||||
state=dict(type='str', default="present", choices=["absent", "present"]),
|
||||
parent=dict(type='str'),
|
||||
visibility=dict(type='str', default="private", choices=["internal", "private", "public"]),
|
||||
project_creation_level=dict(type='str', choices=['developer', 'maintainer', 'noone']),
|
||||
auto_devops_enabled=dict(type='bool'),
|
||||
subgroup_creation_level=dict(type='str', choices=['maintainer', 'owner']),
|
||||
require_two_factor_authentication=dict(type='bool'),
|
||||
avatar_path=dict(type='path'),
|
||||
default_branch=dict(type='str'),
|
||||
description=dict(type='str'),
|
||||
enabled_git_access_protocol=dict(type='str', choices=['all', 'ssh', 'http']),
|
||||
force_delete=dict(type='bool', default=False),
|
||||
lfs_enabled=dict(type='bool'),
|
||||
lock_duo_features_enabled=dict(type='bool'),
|
||||
membership_lock=dict(type='bool'),
|
||||
mentions_disabled=dict(type='bool'),
|
||||
name=dict(type='str', required=True),
|
||||
parent=dict(type='str'),
|
||||
path=dict(type='str'),
|
||||
prevent_forking_outside_group=dict(type='bool'),
|
||||
prevent_sharing_groups_outside_hierarchy=dict(type='bool'),
|
||||
project_creation_level=dict(type='str', choices=['developer', 'maintainer', 'noone']),
|
||||
request_access_enabled=dict(type='bool'),
|
||||
require_two_factor_authentication=dict(type='bool'),
|
||||
service_access_tokens_expiration_enforced=dict(type='bool'),
|
||||
share_with_group_lock=dict(type='bool'),
|
||||
state=dict(type='str', default="present", choices=["absent", "present"]),
|
||||
subgroup_creation_level=dict(type='str', choices=['maintainer', 'owner']),
|
||||
two_factor_grace_period=dict(type='str'),
|
||||
visibility=dict(type='str', default="private", choices=["internal", "private", "public"]),
|
||||
wiki_access_level=dict(type='str', choices=['enabled', 'private', 'disabled']),
|
||||
))
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
['api_username', 'api_token'],
|
||||
['api_username', 'api_oauth_token'],
|
||||
['api_username', 'api_job_token'],
|
||||
['api_token', 'api_oauth_token'],
|
||||
['api_token', 'api_job_token'],
|
||||
['api_token', 'api_oauth_token'],
|
||||
['api_username', 'api_job_token'],
|
||||
['api_username', 'api_oauth_token'],
|
||||
['api_username', 'api_token'],
|
||||
],
|
||||
required_together=[
|
||||
['api_username', 'api_password'],
|
||||
@@ -357,18 +453,31 @@ def main():
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
auto_devops_enabled = module.params['auto_devops_enabled']
|
||||
avatar_path = module.params['avatar_path']
|
||||
default_branch = module.params['default_branch']
|
||||
description = module.params['description']
|
||||
enabled_git_access_protocol = module.params['enabled_git_access_protocol']
|
||||
force_delete = module.params['force_delete']
|
||||
group_name = module.params['name']
|
||||
group_path = module.params['path']
|
||||
description = module.params['description']
|
||||
state = module.params['state']
|
||||
parent_identifier = module.params['parent']
|
||||
group_visibility = module.params['visibility']
|
||||
lfs_enabled = module.params['lfs_enabled']
|
||||
lock_duo_features_enabled = module.params['lock_duo_features_enabled']
|
||||
membership_lock = module.params['membership_lock']
|
||||
mentions_disabled = module.params['mentions_disabled']
|
||||
parent_identifier = module.params['parent']
|
||||
prevent_forking_outside_group = module.params['prevent_forking_outside_group']
|
||||
prevent_sharing_groups_outside_hierarchy = module.params['prevent_sharing_groups_outside_hierarchy']
|
||||
project_creation_level = module.params['project_creation_level']
|
||||
auto_devops_enabled = module.params['auto_devops_enabled']
|
||||
subgroup_creation_level = module.params['subgroup_creation_level']
|
||||
request_access_enabled = module.params['request_access_enabled']
|
||||
require_two_factor_authentication = module.params['require_two_factor_authentication']
|
||||
avatar_path = module.params['avatar_path']
|
||||
force_delete = module.params['force_delete']
|
||||
service_access_tokens_expiration_enforced = module.params['service_access_tokens_expiration_enforced']
|
||||
share_with_group_lock = module.params['share_with_group_lock']
|
||||
state = module.params['state']
|
||||
subgroup_creation_level = module.params['subgroup_creation_level']
|
||||
two_factor_grace_period = module.params['two_factor_grace_period']
|
||||
wiki_access_level = module.params['wiki_access_level']
|
||||
|
||||
# Define default group_path based on group_name
|
||||
if group_path is None:
|
||||
@@ -380,7 +489,7 @@ def main():
|
||||
if parent_identifier:
|
||||
parent_group = find_group(gitlab_instance, parent_identifier)
|
||||
if not parent_group:
|
||||
module.fail_json(msg="Failed create GitLab group: Parent group doesn't exists")
|
||||
module.fail_json(msg="Failed to create GitLab group: Parent group doesn't exist")
|
||||
|
||||
group_exists = gitlab_group.exists_group(parent_group.full_path + '/' + group_path)
|
||||
else:
|
||||
@@ -391,18 +500,31 @@ def main():
|
||||
gitlab_group.delete_group(force=force_delete)
|
||||
module.exit_json(changed=True, msg="Successfully deleted group %s" % group_name)
|
||||
else:
|
||||
module.exit_json(changed=False, msg="Group deleted or does not exists")
|
||||
module.exit_json(changed=False, msg="Group deleted or does not exist")
|
||||
|
||||
if state == 'present':
|
||||
if gitlab_group.create_or_update_group(group_name, parent_group, {
|
||||
"path": group_path,
|
||||
"description": description,
|
||||
"visibility": group_visibility,
|
||||
"project_creation_level": project_creation_level,
|
||||
"auto_devops_enabled": auto_devops_enabled,
|
||||
"subgroup_creation_level": subgroup_creation_level,
|
||||
"require_two_factor_authentication": require_two_factor_authentication,
|
||||
"avatar_path": avatar_path,
|
||||
"default_branch": default_branch,
|
||||
"description": description,
|
||||
"enabled_git_access_protocol": enabled_git_access_protocol,
|
||||
"lfs_enabled": lfs_enabled,
|
||||
"lock_duo_features_enabled": lock_duo_features_enabled,
|
||||
"membership_lock": membership_lock,
|
||||
"mentions_disabled": mentions_disabled,
|
||||
"path": group_path,
|
||||
"prevent_forking_outside_group": prevent_forking_outside_group,
|
||||
"prevent_sharing_groups_outside_hierarchy": prevent_sharing_groups_outside_hierarchy,
|
||||
"project_creation_level": project_creation_level,
|
||||
"request_access_enabled": request_access_enabled,
|
||||
"require_two_factor_authentication": require_two_factor_authentication,
|
||||
"service_access_tokens_expiration_enforced": service_access_tokens_expiration_enforced,
|
||||
"share_with_group_lock": share_with_group_lock,
|
||||
"subgroup_creation_level": subgroup_creation_level,
|
||||
"two_factor_grace_period": two_factor_grace_period,
|
||||
"visibility": group_visibility,
|
||||
"wiki_access_level": wiki_access_level,
|
||||
}):
|
||||
module.exit_json(changed=True, msg="Successfully created or updated the group %s" % group_name, group=gitlab_group.group_object._attrs)
|
||||
else:
|
||||
|
||||
@@ -264,14 +264,14 @@ class GitlabIssue(object):
|
||||
|
||||
if key == 'milestone_id':
|
||||
old_milestone = getattr(issue, 'milestone')['id'] if getattr(issue, 'milestone') else ""
|
||||
if options[key] != old_milestone:
|
||||
if value != old_milestone:
|
||||
return True
|
||||
elif key == 'assignee_ids':
|
||||
if options[key] != sorted([user["id"] for user in getattr(issue, 'assignees')]):
|
||||
if value != sorted([user["id"] for user in getattr(issue, 'assignees')]):
|
||||
return True
|
||||
|
||||
elif key == 'labels':
|
||||
if options[key] != sorted(getattr(issue, key)):
|
||||
if value != sorted(getattr(issue, key)):
|
||||
return True
|
||||
|
||||
elif getattr(issue, key) != value:
|
||||
|
||||
@@ -263,15 +263,15 @@ class GitlabMergeRequest(object):
|
||||
key = 'force_remove_source_branch'
|
||||
|
||||
if key == 'assignee_ids':
|
||||
if options[key] != sorted([user["id"] for user in getattr(mr, 'assignees')]):
|
||||
if value != sorted([user["id"] for user in getattr(mr, 'assignees')]):
|
||||
return True
|
||||
|
||||
elif key == 'reviewer_ids':
|
||||
if options[key] != sorted([user["id"] for user in getattr(mr, 'reviewers')]):
|
||||
if value != sorted([user["id"] for user in getattr(mr, 'reviewers')]):
|
||||
return True
|
||||
|
||||
elif key == 'labels':
|
||||
if options[key] != sorted(getattr(mr, key)):
|
||||
if value != sorted(getattr(mr, key)):
|
||||
return True
|
||||
|
||||
elif getattr(mr, key) != value:
|
||||
|
||||
@@ -15,7 +15,7 @@ module: gitlab_project
|
||||
short_description: Creates/updates/deletes GitLab Projects
|
||||
description:
|
||||
- When the project does not exist in GitLab, it will be created.
|
||||
- When the project does exists and O(state=absent), the project will be deleted.
|
||||
- When the project does exist and O(state=absent), the project will be deleted.
|
||||
- When changes are made to the project, the project will be updated.
|
||||
author:
|
||||
- Werner Dijkerman (@dj-wasabi)
|
||||
@@ -716,7 +716,7 @@ def main():
|
||||
if group_identifier:
|
||||
group = find_group(gitlab_instance, group_identifier)
|
||||
if group is None:
|
||||
module.fail_json(msg="Failed to create project: group %s doesn't exists" % group_identifier)
|
||||
module.fail_json(msg="Failed to create project: group %s doesn't exist" % group_identifier)
|
||||
|
||||
namespace_id = group.id
|
||||
else:
|
||||
|
||||
@@ -33,7 +33,10 @@ author:
|
||||
- Samy Coenen (@SamyCoenen)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python-gitlab >= 1.5.0
|
||||
- python-gitlab >= 1.5.0 for legacy runner registration workflow
|
||||
(runner registration token - U(https://docs.gitlab.com/runner/register/#register-with-a-runner-registration-token-deprecated))
|
||||
- python-gitlab >= 4.0.0 for new runner registration workflow
|
||||
(runner authentication token - U(https://docs.gitlab.com/runner/register/#register-with-a-runner-authentication-token))
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
- community.general.gitlab
|
||||
@@ -365,18 +368,18 @@ class GitLabRunner(object):
|
||||
changed = False
|
||||
|
||||
for arg_key, arg_value in arguments.items():
|
||||
if arguments[arg_key] is not None:
|
||||
if isinstance(arguments[arg_key], list):
|
||||
if arg_value is not None:
|
||||
if isinstance(arg_value, list):
|
||||
list1 = getattr(runner, arg_key)
|
||||
list1.sort()
|
||||
list2 = arguments[arg_key]
|
||||
list2 = arg_value
|
||||
list2.sort()
|
||||
if list1 != list2:
|
||||
setattr(runner, arg_key, arguments[arg_key])
|
||||
setattr(runner, arg_key, arg_value)
|
||||
changed = True
|
||||
else:
|
||||
if getattr(runner, arg_key) != arguments[arg_key]:
|
||||
setattr(runner, arg_key, arguments[arg_key])
|
||||
if getattr(runner, arg_key) != arg_value:
|
||||
setattr(runner, arg_key, arg_value)
|
||||
changed = True
|
||||
|
||||
return (changed, runner)
|
||||
|
||||
@@ -17,6 +17,10 @@ description:
|
||||
- Send a message to a Hipchat room, with options to control the formatting.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
deprecated:
|
||||
removed_in: 11.0.0
|
||||
why: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
alternative: There is none.
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
|
||||
@@ -61,17 +61,17 @@ EXAMPLES = """
|
||||
state: present
|
||||
|
||||
- name: Start the foo service (equivalent to `brew services start foo`)
|
||||
community.general.homebrew_service:
|
||||
community.general.homebrew_services:
|
||||
name: foo
|
||||
state: present
|
||||
|
||||
- name: Restart the foo service (equivalent to `brew services restart foo`)
|
||||
community.general.homebrew_service:
|
||||
community.general.homebrew_services:
|
||||
name: foo
|
||||
state: restarted
|
||||
|
||||
- name: Remove the foo service (equivalent to `brew services stop foo`)
|
||||
community.general.homebrew_service:
|
||||
community.general.homebrew_services:
|
||||
name: foo
|
||||
service_state: absent
|
||||
"""
|
||||
|
||||
@@ -18,11 +18,11 @@ version_added: 4.4.0
|
||||
description:
|
||||
- Manages a user's home directory managed by systemd-homed.
|
||||
notes:
|
||||
- This module does B(not) work with Python 3.13 or newer. It uses the deprecated L(crypt Python module,
|
||||
https://docs.python.org/3.12/library/crypt.html) from the Python standard library, which was removed
|
||||
from Python 3.13.
|
||||
- This module requires the deprecated L(crypt Python module,
|
||||
https://docs.python.org/3.12/library/crypt.html) library which was removed from Python 3.13.
|
||||
For Python 3.13 or newer, you need to install L(legacycrypt, https://pypi.org/project/legacycrypt/).
|
||||
requirements:
|
||||
- Python 3.12 or earlier
|
||||
- legacycrypt (on Python 3.13 or newer)
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
@@ -284,6 +284,17 @@ else:
|
||||
HAS_CRYPT = True
|
||||
CRYPT_IMPORT_ERROR = None
|
||||
|
||||
try:
|
||||
import legacycrypt
|
||||
if not HAS_CRYPT:
|
||||
crypt = legacycrypt
|
||||
except ImportError:
|
||||
HAS_LEGACYCRYPT = False
|
||||
LEGACYCRYPT_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_LEGACYCRYPT = True
|
||||
LEGACYCRYPT_IMPORT_ERROR = None
|
||||
|
||||
|
||||
class Homectl(object):
|
||||
'''#TODO DOC STRINGS'''
|
||||
@@ -606,9 +617,9 @@ def main():
|
||||
]
|
||||
)
|
||||
|
||||
if not HAS_CRYPT:
|
||||
if not HAS_CRYPT and not HAS_LEGACYCRYPT:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
|
||||
msg=missing_required_lib('crypt (part of standard library up to Python 3.12) or legacycrypt (PyPI)'),
|
||||
exception=CRYPT_IMPORT_ERROR,
|
||||
)
|
||||
|
||||
|
||||
@@ -282,9 +282,7 @@ def main():
|
||||
'vars.made_by': "ansible"
|
||||
}
|
||||
}
|
||||
|
||||
for key, value in variables.items():
|
||||
data['attrs']['vars.' + key] = value
|
||||
data['attrs'].update({'vars.' + key: value for key, value in variables.items()})
|
||||
|
||||
changed = False
|
||||
if icinga.exists(name):
|
||||
|
||||
@@ -569,7 +569,7 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
|
||||
module.fail_json(msg="Unable to create temporary file %s", traceback=traceback.format_exc())
|
||||
|
||||
try:
|
||||
module.atomic_move(tmpfile, target_filename)
|
||||
module.atomic_move(tmpfile, os.path.abspath(target_filename))
|
||||
except IOError:
|
||||
module.ansible.fail_json(msg='Unable to move temporary \
|
||||
file %s to %s, IOError' % (tmpfile, target_filename), traceback=traceback.format_exc())
|
||||
|
||||
247
plugins/modules/ipa_getkeytab.py
Normal file
247
plugins/modules/ipa_getkeytab.py
Normal file
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2024 Alexander Bakanovskii <skottttt228@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: ipa_getkeytab
|
||||
short_description: Manage keytab file in FreeIPA
|
||||
version_added: 9.5.0
|
||||
description:
|
||||
- Manage keytab file with C(ipa-getkeytab) utility.
|
||||
- See U(https://manpages.ubuntu.com/manpages/jammy/man1/ipa-getkeytab.1.html) for reference.
|
||||
author: "Alexander Bakanovskii (@abakanovskii)"
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- The base path where to put generated keytab file.
|
||||
type: path
|
||||
aliases: ["keytab"]
|
||||
required: true
|
||||
principal:
|
||||
description:
|
||||
- The non-realm part of the full principal name.
|
||||
type: str
|
||||
required: true
|
||||
ipa_host:
|
||||
description:
|
||||
- The IPA server to retrieve the keytab from (FQDN).
|
||||
type: str
|
||||
ldap_uri:
|
||||
description:
|
||||
- LDAP URI. If V(ldap://) is specified, STARTTLS is initiated by default.
|
||||
- Can not be used with the O(ipa_host) option.
|
||||
type: str
|
||||
bind_dn:
|
||||
description:
|
||||
- The LDAP DN to bind as when retrieving a keytab without Kerberos credentials.
|
||||
- Generally used with the O(bind_pw) option.
|
||||
type: str
|
||||
bind_pw:
|
||||
description:
|
||||
- The LDAP password to use when not binding with Kerberos.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Use this password for the key instead of one randomly generated.
|
||||
type: str
|
||||
ca_cert:
|
||||
description:
|
||||
- The path to the IPA CA certificate used to validate LDAPS/STARTTLS connections.
|
||||
type: path
|
||||
sasl_mech:
|
||||
description:
|
||||
- SASL mechanism to use if O(bind_dn) and O(bind_pw) are not specified.
|
||||
choices: ["GSSAPI", "EXTERNAL"]
|
||||
type: str
|
||||
retrieve_mode:
|
||||
description:
|
||||
- Retrieve an existing key from the server instead of generating a new one.
|
||||
- This is incompatible with the O(password), and will work only against a IPA server more recent than version 3.3.
|
||||
- The user requesting the keytab must have access to the keys for this operation to succeed.
|
||||
- Be aware that if set V(true), a new keytab will be generated.
|
||||
- This invalidates all previously retrieved keytabs for this service principal.
|
||||
type: bool
|
||||
encryption_types:
|
||||
description:
|
||||
- The list of encryption types to use to generate keys.
|
||||
- It will use local client defaults if not provided.
|
||||
- Valid values depend on the Kerberos library version and configuration.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- The state of the keytab file.
|
||||
- V(present) only check for existence of a file, if you want to recreate keytab with other parameters you should set O(force=true).
|
||||
type: str
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
force:
|
||||
description:
|
||||
- Force recreation if exists already.
|
||||
type: bool
|
||||
requirements:
|
||||
- freeipa-client
|
||||
- Managed host is FreeIPA client
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Get kerberos ticket
|
||||
ansible.builtin.shell: kinit admin
|
||||
args:
|
||||
stdin: "{{ aldpro_admin_password }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Create keytab
|
||||
community.general.ipa_getkeytab:
|
||||
path: /etc/ipa/test.keytab
|
||||
principal: HTTP/freeipa-dc02.ipa.test
|
||||
ipa_host: freeipa-dc01.ipa.test
|
||||
|
||||
- name: Retrieve already existing keytab
|
||||
community.general.ipa_getkeytab:
|
||||
path: /etc/ipa/test.keytab
|
||||
principal: HTTP/freeipa-dc02.ipa.test
|
||||
ipa_host: freeipa-dc01.ipa.test
|
||||
retrieve_mode: true
|
||||
|
||||
- name: Force keytab recreation
|
||||
community.general.ipa_getkeytab:
|
||||
path: /etc/ipa/test.keytab
|
||||
principal: HTTP/freeipa-dc02.ipa.test
|
||||
ipa_host: freeipa-dc01.ipa.test
|
||||
force: true
|
||||
'''
|
||||
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
|
||||
|
||||
|
||||
class IPAKeytab(object):
|
||||
def __init__(self, module, **kwargs):
|
||||
self.module = module
|
||||
self.path = kwargs['path']
|
||||
self.state = kwargs['state']
|
||||
self.principal = kwargs['principal']
|
||||
self.ipa_host = kwargs['ipa_host']
|
||||
self.ldap_uri = kwargs['ldap_uri']
|
||||
self.bind_dn = kwargs['bind_dn']
|
||||
self.bind_pw = kwargs['bind_pw']
|
||||
self.password = kwargs['password']
|
||||
self.ca_cert = kwargs['ca_cert']
|
||||
self.sasl_mech = kwargs['sasl_mech']
|
||||
self.retrieve_mode = kwargs['retrieve_mode']
|
||||
self.encryption_types = kwargs['encryption_types']
|
||||
|
||||
self.runner = CmdRunner(
|
||||
module,
|
||||
command='ipa-getkeytab',
|
||||
arg_formats=dict(
|
||||
retrieve_mode=cmd_runner_fmt.as_bool('--retrieve'),
|
||||
path=cmd_runner_fmt.as_opt_val('--keytab'),
|
||||
ipa_host=cmd_runner_fmt.as_opt_val('--server'),
|
||||
principal=cmd_runner_fmt.as_opt_val('--principal'),
|
||||
ldap_uri=cmd_runner_fmt.as_opt_val('--ldapuri'),
|
||||
bind_dn=cmd_runner_fmt.as_opt_val('--binddn'),
|
||||
bind_pw=cmd_runner_fmt.as_opt_val('--bindpw'),
|
||||
password=cmd_runner_fmt.as_opt_val('--password'),
|
||||
ca_cert=cmd_runner_fmt.as_opt_val('--cacert'),
|
||||
sasl_mech=cmd_runner_fmt.as_opt_val('--mech'),
|
||||
encryption_types=cmd_runner_fmt.as_opt_val('--enctypes'),
|
||||
)
|
||||
)
|
||||
|
||||
def _exec(self, check_rc=True):
|
||||
with self.runner(
|
||||
"retrieve_mode path ipa_host principal ldap_uri bind_dn bind_pw password ca_cert sasl_mech encryption_types",
|
||||
check_rc=check_rc
|
||||
) as ctx:
|
||||
rc, out, err = ctx.run()
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
arg_spec = dict(
|
||||
path=dict(type='path', required=True, aliases=["keytab"]),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
principal=dict(type='str', required=True),
|
||||
ipa_host=dict(type='str'),
|
||||
ldap_uri=dict(type='str'),
|
||||
bind_dn=dict(type='str'),
|
||||
bind_pw=dict(type='str'),
|
||||
password=dict(type='str', no_log=True),
|
||||
ca_cert=dict(type='path'),
|
||||
sasl_mech=dict(type='str', choices=["GSSAPI", "EXTERNAL"]),
|
||||
retrieve_mode=dict(type='bool'),
|
||||
encryption_types=dict(type='str'),
|
||||
force=dict(type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=arg_spec,
|
||||
mutually_exclusive=[('ipa_host', 'ldap_uri'), ('retrieve_mode', 'password')],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
path = module.params['path']
|
||||
state = module.params['state']
|
||||
force = module.params['force']
|
||||
|
||||
keytab = IPAKeytab(module,
|
||||
path=path,
|
||||
state=state,
|
||||
principal=module.params['principal'],
|
||||
ipa_host=module.params['ipa_host'],
|
||||
ldap_uri=module.params['ldap_uri'],
|
||||
bind_dn=module.params['bind_dn'],
|
||||
bind_pw=module.params['bind_pw'],
|
||||
password=module.params['password'],
|
||||
ca_cert=module.params['ca_cert'],
|
||||
sasl_mech=module.params['sasl_mech'],
|
||||
retrieve_mode=module.params['retrieve_mode'],
|
||||
encryption_types=module.params['encryption_types'],
|
||||
)
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if os.path.exists(path):
|
||||
if force and not module.check_mode:
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError as e:
|
||||
module.fail_json(msg="Error deleting: %s - %s." % (e.filename, e.strerror))
|
||||
keytab._exec()
|
||||
changed = True
|
||||
if force and module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
keytab._exec()
|
||||
|
||||
if state == 'absent':
|
||||
if os.path.exists(path):
|
||||
changed = True
|
||||
if not module.check_mode:
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError as e:
|
||||
module.fail_json(msg="Error deleting: %s - %s." % (e.filename, e.strerror))
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -74,10 +74,17 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
state:
|
||||
description: State to ensure.
|
||||
description:
|
||||
- State to ensure.
|
||||
default: present
|
||||
choices: ["absent", "disabled", "enabled", "present"]
|
||||
type: str
|
||||
force_creation:
|
||||
description:
|
||||
- Create host if O(state=disabled) or O(state=enabled) but not present.
|
||||
default: true
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
update_dns:
|
||||
description:
|
||||
- If set V(true) with O(state=absent), then removes DNS records of the host managed by FreeIPA DNS.
|
||||
@@ -233,26 +240,31 @@ def get_host_diff(client, ipa_host, module_host):
|
||||
def ensure(module, client):
|
||||
name = module.params['fqdn']
|
||||
state = module.params['state']
|
||||
force_creation = module.params['force_creation']
|
||||
|
||||
ipa_host = client.host_find(name=name)
|
||||
module_host = get_host_dict(description=module.params['description'],
|
||||
force=module.params['force'], ip_address=module.params['ip_address'],
|
||||
force=module.params['force'],
|
||||
ip_address=module.params['ip_address'],
|
||||
ns_host_location=module.params['ns_host_location'],
|
||||
ns_hardware_platform=module.params['ns_hardware_platform'],
|
||||
ns_os_version=module.params['ns_os_version'],
|
||||
user_certificate=module.params['user_certificate'],
|
||||
mac_address=module.params['mac_address'],
|
||||
random_password=module.params.get('random_password'),
|
||||
random_password=module.params['random_password'],
|
||||
)
|
||||
changed = False
|
||||
if state in ['present', 'enabled', 'disabled']:
|
||||
if not ipa_host:
|
||||
if not ipa_host and (force_creation or state == 'present'):
|
||||
changed = True
|
||||
if not module.check_mode:
|
||||
# OTP password generated by FreeIPA is visible only for host_add command
|
||||
# so, return directly from here.
|
||||
return changed, client.host_add(name=name, host=module_host)
|
||||
else:
|
||||
if state in ['disabled', 'enabled']:
|
||||
module.fail_json(msg="No host with name " + ipa_host + " found")
|
||||
|
||||
diff = get_host_diff(client, ipa_host, module_host)
|
||||
if len(diff) > 0:
|
||||
changed = True
|
||||
@@ -261,11 +273,10 @@ def ensure(module, client):
|
||||
for key in diff:
|
||||
data[key] = module_host.get(key)
|
||||
ipa_host_show = client.host_show(name=name)
|
||||
if ipa_host_show.get('has_keytab', False) and module.params.get('random_password'):
|
||||
if ipa_host_show.get('has_keytab', True) and (state == 'disabled' or module.params.get('random_password')):
|
||||
client.host_disable(name=name)
|
||||
return changed, client.host_mod(name=name, host=data)
|
||||
|
||||
else:
|
||||
elif state == 'absent':
|
||||
if ipa_host:
|
||||
changed = True
|
||||
update_dns = module.params.get('update_dns', False)
|
||||
@@ -288,7 +299,8 @@ def main():
|
||||
mac_address=dict(type='list', aliases=['macaddress'], elements='str'),
|
||||
update_dns=dict(type='bool'),
|
||||
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
random_password=dict(type='bool', no_log=False),)
|
||||
random_password=dict(type='bool', no_log=False),
|
||||
force_creation=dict(type='bool', default=True),)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
@@ -57,13 +57,14 @@ options:
|
||||
state:
|
||||
description:
|
||||
- State to ensure.
|
||||
- V("absent") and V("disabled") give the same results.
|
||||
- V("present") and V("enabled") give the same results.
|
||||
default: "present"
|
||||
choices: ["absent", "disabled", "enabled", "present"]
|
||||
type: str
|
||||
extends_documentation_fragment:
|
||||
- community.general.ipa.documentation
|
||||
- community.general.attributes
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -160,7 +161,7 @@ def ensure(module, client):
|
||||
module_hostgroup = get_hostgroup_dict(description=module.params['description'])
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if state in ['present', 'enabled']:
|
||||
if not ipa_hostgroup:
|
||||
changed = True
|
||||
if not module.check_mode:
|
||||
|
||||
@@ -150,13 +150,11 @@ EXAMPLES = '''
|
||||
name: example
|
||||
certificate: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
h19dUZ2co2fI/ibYiwxWk4aeNE6KWvCaTQOMQ8t6Uo2XKhpL/xnjoAgh1uCQN/69
|
||||
MG+34+RhUWzCfdZH7T8/qDxJw2kEPKluaYh7KnMsba+5jHjmtzix5QIDAQABo4IB
|
||||
h19dUZ2co2f...
|
||||
-----END CERTIFICATE-----
|
||||
private_key: |
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
DBVFTEVDVFJJQ0lURSBERSBGUkFOQ0UxFzAVBgNVBAsMDjAwMDIgNTUyMDgxMzE3
|
||||
GLlDNMw/uHyME7gHFsqJA7O11VY6O5WQ4IDP3m/s5ZV6s+Nn6Lerz17VZ99
|
||||
DBVFTEVDVFJ...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
password: changeit
|
||||
dest: /etc/security/keystore.jks
|
||||
@@ -472,7 +470,7 @@ class JavaKeystore:
|
||||
|
||||
if self.keystore_type == 'pkcs12':
|
||||
# Preserve properties of the destination file, if any.
|
||||
self.module.atomic_move(keystore_p12_path, self.keystore_path)
|
||||
self.module.atomic_move(os.path.abspath(keystore_p12_path), os.path.abspath(self.keystore_path))
|
||||
self.update_permissions()
|
||||
self.result['changed'] = True
|
||||
return self.result
|
||||
|
||||
@@ -685,7 +685,7 @@ class JenkinsPlugin(object):
|
||||
|
||||
# Move the updates file to the right place if we could read it
|
||||
if tmp_updates_file != updates_file:
|
||||
self.module.atomic_move(tmp_updates_file, updates_file)
|
||||
self.module.atomic_move(os.path.abspath(tmp_updates_file), os.path.abspath(updates_file))
|
||||
|
||||
# Check if we have the plugin data available
|
||||
if not data.get('plugins', {}).get(self.params['name']):
|
||||
@@ -718,7 +718,7 @@ class JenkinsPlugin(object):
|
||||
details=to_native(e))
|
||||
|
||||
# Move the file onto the right place
|
||||
self.module.atomic_move(tmp_f, f)
|
||||
self.module.atomic_move(os.path.abspath(tmp_f), os.path.abspath(f))
|
||||
|
||||
def uninstall(self):
|
||||
changed = False
|
||||
|
||||
@@ -531,7 +531,7 @@ class JIRA(StateModuleHelper):
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
mute_vardict_deprecation = True
|
||||
use_old_vardict = False
|
||||
state_param = 'operation'
|
||||
|
||||
def __init_module__(self):
|
||||
|
||||
@@ -214,7 +214,7 @@ def run_module(module, tmpdir, kwriteconfig):
|
||||
if module.params['backup'] and os.path.exists(b_path):
|
||||
result['backup_file'] = module.backup_local(result['path'])
|
||||
try:
|
||||
module.atomic_move(b_tmpfile, b_path)
|
||||
module.atomic_move(b_tmpfile, os.path.abspath(b_path))
|
||||
except IOError:
|
||||
module.ansible.fail_json(msg='Unable to move temporary file %s to %s, IOError' % (tmpfile, result['path']), traceback=traceback.format_exc())
|
||||
|
||||
|
||||
@@ -108,13 +108,14 @@ options:
|
||||
|
||||
client_authenticator_type:
|
||||
description:
|
||||
- How do clients authenticate with the auth server? Either V(client-secret) or
|
||||
V(client-jwt) can be chosen. When using V(client-secret), the module parameter
|
||||
O(secret) can set it, while for V(client-jwt), you can use the keys C(use.jwks.url),
|
||||
- How do clients authenticate with the auth server? Either V(client-secret),
|
||||
V(client-jwt), or V(client-x509) can be chosen. When using V(client-secret), the module parameter
|
||||
O(secret) can set it, for V(client-jwt), you can use the keys C(use.jwks.url),
|
||||
C(jwks.url), and C(jwt.credential.certificate) in the O(attributes) module parameter
|
||||
to configure its behavior.
|
||||
to configure its behavior. For V(client-x509) you can use the keys C(x509.allow.regex.pattern.comparison)
|
||||
and C(x509.subjectdn) in the O(attributes) module parameter to configure which certificate(s) to accept.
|
||||
- This is 'clientAuthenticatorType' in the Keycloak REST API.
|
||||
choices: ['client-secret', 'client-jwt']
|
||||
choices: ['client-secret', 'client-jwt', 'client-x509']
|
||||
aliases:
|
||||
- clientAuthenticatorType
|
||||
type: str
|
||||
@@ -533,7 +534,6 @@ options:
|
||||
description:
|
||||
- SAML Redirect Binding URL for the client's assertion consumer service (login responses).
|
||||
|
||||
|
||||
saml_force_name_id_format:
|
||||
description:
|
||||
- For SAML clients, Boolean specifying whether to ignore requested NameID subject format and using the configured one instead.
|
||||
@@ -581,6 +581,18 @@ options:
|
||||
- For OpenID-Connect clients, client certificate for validating JWT issued by
|
||||
client and signed by its key, base64-encoded.
|
||||
|
||||
x509.subjectdn:
|
||||
description:
|
||||
- For OpenID-Connect clients, subject which will be used to authenticate the client.
|
||||
type: str
|
||||
version_added: 9.5.0
|
||||
|
||||
x509.allow.regex.pattern.comparison:
|
||||
description:
|
||||
- For OpenID-Connect clients, boolean specifying whether to allow C(x509.subjectdn) as regular expression.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
- community.general.attributes
|
||||
@@ -624,6 +636,22 @@ EXAMPLES = '''
|
||||
delegate_to: localhost
|
||||
|
||||
|
||||
- name: Create or update a Keycloak client (minimal example), with x509 authentication
|
||||
community.general.keycloak_client:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
auth_realm: master
|
||||
auth_username: USERNAME
|
||||
auth_password: PASSWORD
|
||||
realm: master
|
||||
state: present
|
||||
client_id: test
|
||||
client_authenticator_type: client-x509
|
||||
attributes:
|
||||
x509.subjectdn: "CN=client"
|
||||
x509.allow.regex.pattern.comparison: false
|
||||
|
||||
|
||||
- name: Create or update a Keycloak client (with all the bells and whistles)
|
||||
community.general.keycloak_client:
|
||||
auth_client_id: admin-cli
|
||||
@@ -913,7 +941,7 @@ def main():
|
||||
base_url=dict(type='str', aliases=['baseUrl']),
|
||||
surrogate_auth_required=dict(type='bool', aliases=['surrogateAuthRequired']),
|
||||
enabled=dict(type='bool'),
|
||||
client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt'], aliases=['clientAuthenticatorType']),
|
||||
client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt', 'client-x509'], aliases=['clientAuthenticatorType']),
|
||||
secret=dict(type='str', no_log=True),
|
||||
registration_access_token=dict(type='str', aliases=['registrationAccessToken'], no_log=True),
|
||||
default_roles=dict(type='list', elements='str', aliases=['defaultRoles']),
|
||||
|
||||
@@ -803,7 +803,7 @@ def main():
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_norm),
|
||||
after=sanitize_cr(desired_norm))
|
||||
result['changed'] = (before_realm != desired_realm)
|
||||
result['changed'] = (before_norm != desired_norm)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -93,6 +93,24 @@ options:
|
||||
default: true
|
||||
version_added: 9.4.0
|
||||
|
||||
bind_credential_update_mode:
|
||||
description:
|
||||
- The value of the config parameter O(config.bindCredential) is redacted in the Keycloak responses.
|
||||
Comparing the redacted value with the desired value always evaluates to not equal. This means
|
||||
the before and desired states are never equal if the parameter is set.
|
||||
- Set to V(always) to include O(config.bindCredential) in the comparison of before and desired state.
|
||||
Because of the redacted value returned by Keycloak the module will always detect a change
|
||||
and make an update if a O(config.bindCredential) value is set.
|
||||
- Set to V(only_indirect) to exclude O(config.bindCredential) when comparing the before state with the
|
||||
desired state. The value of O(config.bindCredential) will only be updated if there are other changes
|
||||
to the user federation that require an update.
|
||||
type: str
|
||||
default: always
|
||||
choices:
|
||||
- always
|
||||
- only_indirect
|
||||
version_added: 9.5.0
|
||||
|
||||
config:
|
||||
description:
|
||||
- Dict specifying the configuration options for the provider; the contents differ depending on
|
||||
@@ -442,6 +460,17 @@ options:
|
||||
- Max lifespan of cache entry in milliseconds.
|
||||
type: int
|
||||
|
||||
referral:
|
||||
description:
|
||||
- Specifies if LDAP referrals should be followed or ignored. Please note that enabling
|
||||
referrals can slow down authentication as it allows the LDAP server to decide which other
|
||||
LDAP servers to use. This could potentially include untrusted servers.
|
||||
type: str
|
||||
choices:
|
||||
- ignore
|
||||
- follow
|
||||
version_added: 9.5.0
|
||||
|
||||
mappers:
|
||||
description:
|
||||
- A list of dicts defining mappers associated with this Identity Provider.
|
||||
@@ -721,15 +750,23 @@ from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
def normalize_kc_comp(comp):
|
||||
if 'config' in comp:
|
||||
# kc completely removes the parameter `krbPrincipalAttribute` if it is set to `''`; the unset kc parameter is equivalent to `''`;
|
||||
# to make change detection and diff more accurate we set it again in the kc responses
|
||||
if 'krbPrincipalAttribute' not in comp['config']:
|
||||
comp['config']['krbPrincipalAttribute'] = ['']
|
||||
|
||||
# kc stores a timestamp of the last sync in `lastSync` to time the periodic sync, it is removed to minimize diff/changes
|
||||
comp['config'].pop('lastSync', None)
|
||||
|
||||
|
||||
def sanitize(comp):
|
||||
compcopy = deepcopy(comp)
|
||||
if 'config' in compcopy:
|
||||
compcopy['config'] = {k: v[0] for k, v in compcopy['config'].items()}
|
||||
if 'bindCredential' in compcopy['config']:
|
||||
compcopy['config']['bindCredential'] = '**********'
|
||||
# an empty string is valid for krbPrincipalAttribute but is filtered out in diff
|
||||
if 'krbPrincipalAttribute' not in compcopy['config']:
|
||||
compcopy['config']['krbPrincipalAttribute'] = ''
|
||||
if 'mappers' in compcopy:
|
||||
for mapper in compcopy['mappers']:
|
||||
if 'config' in mapper:
|
||||
@@ -780,6 +817,7 @@ def main():
|
||||
priority=dict(type='int', default=0),
|
||||
rdnLDAPAttribute=dict(type='str'),
|
||||
readTimeout=dict(type='int'),
|
||||
referral=dict(type='str', choices=['ignore', 'follow']),
|
||||
searchScope=dict(type='str', choices=['1', '2'], default='1'),
|
||||
serverPrincipal=dict(type='str'),
|
||||
krbPrincipalAttribute=dict(type='str'),
|
||||
@@ -817,6 +855,7 @@ def main():
|
||||
provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'),
|
||||
parent_id=dict(type='str', aliases=['parentId']),
|
||||
remove_unspecified_mappers=dict(type='bool', default=True),
|
||||
bind_credential_update_mode=dict(type='str', default='always', choices=['always', 'only_indirect']),
|
||||
mappers=dict(type='list', elements='dict', options=mapper_spec),
|
||||
)
|
||||
|
||||
@@ -864,8 +903,9 @@ def main():
|
||||
|
||||
# Filter and map the parameters names that apply
|
||||
comp_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers', 'remove_unspecified_mappers'] and
|
||||
module.params.get(x) is not None]
|
||||
if x not in list(keycloak_argument_spec().keys())
|
||||
+ ['state', 'realm', 'mappers', 'remove_unspecified_mappers', 'bind_credential_update_mode']
|
||||
and module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
if cid is None:
|
||||
@@ -885,6 +925,8 @@ def main():
|
||||
if cid is not None and before_comp:
|
||||
before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '')
|
||||
|
||||
normalize_kc_comp(before_comp)
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
|
||||
@@ -994,6 +1036,7 @@ def main():
|
||||
kc.delete_component(default_mapper['id'], realm)
|
||||
|
||||
after_comp['mappers'] = kc.get_components(urlencode(dict(parent=cid)), realm)
|
||||
normalize_kc_comp(after_comp)
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize(after_comp))
|
||||
result['end_state'] = sanitize(after_comp)
|
||||
@@ -1004,8 +1047,15 @@ def main():
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
desired_copy = deepcopy(desired_comp)
|
||||
before_copy = deepcopy(before_comp)
|
||||
# exclude bindCredential when checking wether an update is required, therefore
|
||||
# updating it only if there are other changes
|
||||
if module.params['bind_credential_update_mode'] == 'only_indirect':
|
||||
desired_copy.get('config', []).pop('bindCredential', None)
|
||||
before_copy.get('config', []).pop('bindCredential', None)
|
||||
# no changes
|
||||
if desired_comp == before_comp:
|
||||
if desired_copy == before_copy:
|
||||
result['changed'] = False
|
||||
result['end_state'] = sanitize(desired_comp)
|
||||
result['msg'] = "No changes required to user federation {id}.".format(id=cid)
|
||||
@@ -1041,6 +1091,7 @@ def main():
|
||||
|
||||
after_comp = kc.get_component(cid, realm)
|
||||
after_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name') or '')
|
||||
normalize_kc_comp(after_comp)
|
||||
after_comp_sanitized = sanitize(after_comp)
|
||||
before_comp_sanitized = sanitize(before_comp)
|
||||
result['end_state'] = after_comp_sanitized
|
||||
|
||||
@@ -612,9 +612,7 @@ def main():
|
||||
attribute['validations']['person-name-prohibited-characters'] = (
|
||||
attribute['validations'].pop('personNameProhibitedCharacters')
|
||||
)
|
||||
# special JSON parsing for kc_user_profile_config
|
||||
value = json.dumps(kc_user_profile_config[0])
|
||||
changeset[camel(component_param)][config_param].append(value)
|
||||
changeset[camel(component_param)][config_param].append(kc_user_profile_config[0])
|
||||
# usual camelCase parameters
|
||||
else:
|
||||
changeset[camel(component_param)][camel(config_param)] = []
|
||||
@@ -641,7 +639,7 @@ def main():
|
||||
changeset_copy = deepcopy(changeset)
|
||||
|
||||
# Get a list of all Keycloak components that are of userprofile provider type.
|
||||
realm_userprofiles = kc.get_components(urlencode(dict(type=provider_type, parent=parent_id)), parent_id)
|
||||
realm_userprofiles = kc.get_components(urlencode(dict(type=provider_type)), parent_id)
|
||||
|
||||
# If this component is present get its userprofile ID. Confusingly the userprofile ID is
|
||||
# also known as the Provider ID.
|
||||
@@ -662,6 +660,10 @@ def main():
|
||||
changeset['id'] = userprofile_id
|
||||
changeset_copy['id'] = userprofile_id
|
||||
|
||||
# keycloak returns kc.user.profile.config as a single JSON formatted string, so we have to deserialize it
|
||||
if 'config' in userprofile and 'kc.user.profile.config' in userprofile['config']:
|
||||
userprofile['config']['kc.user.profile.config'][0] = json.loads(userprofile['config']['kc.user.profile.config'][0])
|
||||
|
||||
# Compare top-level parameters
|
||||
for param, value in changeset.items():
|
||||
before_realm_userprofile[param] = userprofile[param]
|
||||
@@ -680,6 +682,10 @@ def main():
|
||||
# Check all the possible states of the resource and do what is needed to
|
||||
# converge current state with desired state (create, update or delete
|
||||
# the userprofile).
|
||||
|
||||
# keycloak expects kc.user.profile.config as a single JSON formatted string, so we have to serialize it
|
||||
if 'config' in changeset and 'kc.user.profile.config' in changeset['config']:
|
||||
changeset['config']['kc.user.profile.config'][0] = json.dumps(changeset['config']['kc.user.profile.config'][0])
|
||||
if userprofile_id and state == 'present':
|
||||
if result['changed']:
|
||||
if module._diff:
|
||||
|
||||
@@ -618,7 +618,7 @@ class LXDContainerManagement(object):
|
||||
data = (self._get_instance_state_json() or {}).get('metadata', None) or {}
|
||||
network = {
|
||||
k: v
|
||||
for k, v in data.get('network', {}).items()
|
||||
for k, v in (data.get('network') or {}).items()
|
||||
if k not in ignore_devices
|
||||
}
|
||||
addresses = {
|
||||
@@ -768,7 +768,7 @@ class LXDContainerManagement(object):
|
||||
self.old_instance_json = self._get_instance_json()
|
||||
self.old_sections = {
|
||||
section: adjust_content(content)
|
||||
for section, content in self.old_instance_json.get('metadata', {}).items()
|
||||
for section, content in (self.old_instance_json.get('metadata') or {}).items()
|
||||
if section in set(CONFIG_PARAMS) - set(CONFIG_CREATION_PARAMS)
|
||||
}
|
||||
|
||||
|
||||
@@ -304,22 +304,7 @@ EXAMPLES = '''
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
auth_key: 'topSecret'
|
||||
@@ -330,22 +315,7 @@ EXAMPLES = '''
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1:80'
|
||||
@@ -367,22 +337,7 @@ EXAMPLES = '''
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
auth_key: 'topSecret'
|
||||
@@ -392,22 +347,7 @@ EXAMPLES = '''
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
@@ -455,22 +395,7 @@ EXAMPLES = '''
|
||||
validate_certs: true
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
hostname: 'metrics.example.com'
|
||||
@@ -480,22 +405,7 @@ EXAMPLES = '''
|
||||
validate_certs: true
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
@@ -551,22 +461,7 @@ EXAMPLES = '''
|
||||
validate_certs: 'true'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
ssh_keypair:
|
||||
hostname: director.example.com
|
||||
@@ -590,22 +485,7 @@ EXAMPLES = '''
|
||||
validate_certs: 'true'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
FAKECERTsdKgAwI...
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
role: amqp
|
||||
|
||||
@@ -178,9 +178,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
|
||||
retvals = reload_dns(args)
|
||||
|
||||
|
||||
@@ -163,9 +163,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
|
||||
retvals = get_facts(args)
|
||||
|
||||
|
||||
@@ -288,9 +288,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
|
||||
retvals = get_facts(args)
|
||||
|
||||
|
||||
@@ -300,9 +300,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
args['check_mode'] = module.check_mode
|
||||
|
||||
# validate some API-specific limitations.
|
||||
|
||||
@@ -244,9 +244,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
args['check_mode'] = module.check_mode
|
||||
|
||||
# validate some API-specific limitations.
|
||||
|
||||
@@ -374,9 +374,7 @@ def main():
|
||||
)
|
||||
|
||||
# populate the dict with the user-provided vars.
|
||||
args = dict()
|
||||
for key, arg in module.params.items():
|
||||
args[key] = arg
|
||||
args = dict(module.params)
|
||||
args['check_mode'] = module.check_mode
|
||||
|
||||
# perform some Memset API-specific validation
|
||||
|
||||
@@ -10,15 +10,20 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
author: Kairo Araujo (@kairoaraujo)
|
||||
module: mksysb
|
||||
short_description: Generates AIX mksysb rootvg backups
|
||||
description:
|
||||
- This module manages a basic AIX mksysb (image) of rootvg.
|
||||
- This module manages a basic AIX mksysb (image) of rootvg.
|
||||
seealso:
|
||||
- name: C(mksysb) command manual page
|
||||
description: Manual page for the command.
|
||||
link: https://www.ibm.com/docs/en/aix/7.3?topic=m-mksysb-command
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
@@ -27,72 +32,73 @@ attributes:
|
||||
options:
|
||||
backup_crypt_files:
|
||||
description:
|
||||
- Backup encrypted files.
|
||||
- Backup encrypted files.
|
||||
type: bool
|
||||
default: true
|
||||
backup_dmapi_fs:
|
||||
description:
|
||||
- Back up DMAPI filesystem files.
|
||||
- Back up DMAPI filesystem files.
|
||||
type: bool
|
||||
default: true
|
||||
create_map_files:
|
||||
description:
|
||||
- Creates a new MAP files.
|
||||
- Creates a new MAP files.
|
||||
type: bool
|
||||
default: false
|
||||
exclude_files:
|
||||
description:
|
||||
- Excludes files using C(/etc/rootvg.exclude).
|
||||
- Excludes files using C(/etc/rootvg.exclude).
|
||||
type: bool
|
||||
default: false
|
||||
exclude_wpar_files:
|
||||
description:
|
||||
- Excludes WPAR files.
|
||||
- Excludes WPAR files.
|
||||
type: bool
|
||||
default: false
|
||||
extended_attrs:
|
||||
description:
|
||||
- Backup extended attributes.
|
||||
- Backup extended attributes.
|
||||
type: bool
|
||||
default: true
|
||||
name:
|
||||
type: str
|
||||
description:
|
||||
- Backup name
|
||||
- Backup name
|
||||
required: true
|
||||
new_image_data:
|
||||
description:
|
||||
- Creates a new file data.
|
||||
- Creates a new file data.
|
||||
type: bool
|
||||
default: true
|
||||
software_packing:
|
||||
description:
|
||||
- Exclude files from packing option listed in
|
||||
C(/etc/exclude_packing.rootvg).
|
||||
- Exclude files from packing option listed in C(/etc/exclude_packing.rootvg).
|
||||
type: bool
|
||||
default: false
|
||||
storage_path:
|
||||
type: str
|
||||
description:
|
||||
- Storage path where the mksysb will stored.
|
||||
- Storage path where the mksysb will stored.
|
||||
required: true
|
||||
use_snapshot:
|
||||
description:
|
||||
- Creates backup using snapshots.
|
||||
- Creates backup using snapshots.
|
||||
type: bool
|
||||
default: false
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Running a backup image mksysb
|
||||
community.general.mksysb:
|
||||
name: myserver
|
||||
storage_path: /repository/images
|
||||
exclude_files: true
|
||||
exclude_wpar_files: true
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
---
|
||||
changed:
|
||||
description: Return changed for mksysb actions as true or false.
|
||||
returned: always
|
||||
@@ -101,7 +107,7 @@ msg:
|
||||
description: Return message regarding the action.
|
||||
returned: always
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
@@ -34,9 +34,11 @@ options:
|
||||
state:
|
||||
description:
|
||||
- Whether the device should exist or not, taking action if the state is different from what is stated.
|
||||
- Using O(state=present) to create connection will automatically bring connection up.
|
||||
- Using O(state=up) and O(state=down) will not modify connection with other parameters. These states have been added in community.general 9.5.0.
|
||||
type: str
|
||||
required: true
|
||||
choices: [ absent, present ]
|
||||
choices: [ absent, present, up, down ]
|
||||
autoconnect:
|
||||
description:
|
||||
- Whether the connection should start on boot.
|
||||
@@ -48,6 +50,13 @@ options:
|
||||
- The name used to call the connection. Pattern is <type>[-<ifname>][-<num>].
|
||||
type: str
|
||||
required: true
|
||||
conn_reload:
|
||||
description:
|
||||
- Whether the connection should be reloaded if it was modified.
|
||||
type: bool
|
||||
required: false
|
||||
default: false
|
||||
version_added: 9.5.0
|
||||
ifname:
|
||||
description:
|
||||
- The interface to bind the connection to.
|
||||
@@ -1309,6 +1318,25 @@ EXAMPLES = r'''
|
||||
type: ethernet
|
||||
state: present
|
||||
|
||||
- name: Change the property of a setting e.g. MTU and reload connection
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
mtu: 1500
|
||||
type: ethernet
|
||||
state: present
|
||||
conn_reload: true
|
||||
|
||||
- name: Disable connection
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
state: down
|
||||
|
||||
- name: Reload and enable connection
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
state: up
|
||||
reload: true
|
||||
|
||||
- name: Add second ip4 address
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
@@ -1581,6 +1609,7 @@ class Nmcli(object):
|
||||
self.ignore_unsupported_suboptions = module.params['ignore_unsupported_suboptions']
|
||||
self.autoconnect = module.params['autoconnect']
|
||||
self.conn_name = module.params['conn_name']
|
||||
self.conn_reload = module.params['conn_reload']
|
||||
self.slave_type = module.params['slave_type']
|
||||
self.master = module.params['master']
|
||||
self.ifname = module.params['ifname']
|
||||
@@ -1944,7 +1973,7 @@ class Nmcli(object):
|
||||
convert_func = self.list_to_string
|
||||
|
||||
if callable(convert_func):
|
||||
options[setting] = convert_func(options[setting])
|
||||
options[setting] = convert_func(value)
|
||||
|
||||
return options
|
||||
|
||||
@@ -2165,6 +2194,10 @@ class Nmcli(object):
|
||||
cmd = [self.nmcli_bin, 'con', 'up', self.conn_name]
|
||||
return self.execute_command(cmd)
|
||||
|
||||
def reload_connection(self):
|
||||
cmd = [self.nmcli_bin, 'con', 'reload']
|
||||
return self.execute_command(cmd)
|
||||
|
||||
def connection_update(self, nmcli_command):
|
||||
if nmcli_command == 'create':
|
||||
cmd = [self.nmcli_bin, 'con', 'add', 'type']
|
||||
@@ -2431,8 +2464,9 @@ def main():
|
||||
argument_spec=dict(
|
||||
ignore_unsupported_suboptions=dict(type='bool', default=False),
|
||||
autoconnect=dict(type='bool', default=True),
|
||||
state=dict(type='str', required=True, choices=['absent', 'present']),
|
||||
state=dict(type='str', required=True, choices=['absent', 'present', 'up', 'down']),
|
||||
conn_name=dict(type='str', required=True),
|
||||
conn_reload=dict(type='bool', default=False),
|
||||
master=dict(type='str'),
|
||||
slave_type=dict(type='str', choices=['bond', 'bridge', 'team', 'ovs-port']),
|
||||
ifname=dict(type='str'),
|
||||
@@ -2639,6 +2673,8 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, **result)
|
||||
(rc, out, err) = nmcli.modify_connection()
|
||||
if nmcli.conn_reload:
|
||||
(rc, out, err) = nmcli.reload_connection()
|
||||
else:
|
||||
result['Exists'] = 'Connections already exist and no changes made'
|
||||
if module.check_mode:
|
||||
@@ -2650,6 +2686,27 @@ def main():
|
||||
(rc, out, err) = nmcli.create_connection()
|
||||
if rc is not None and rc != 0:
|
||||
module.fail_json(name=nmcli.conn_name, msg=err, rc=rc)
|
||||
|
||||
elif nmcli.state == 'up':
|
||||
if nmcli.connection_exists():
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
if nmcli.conn_reload:
|
||||
(rc, out, err) = nmcli.reload_connection()
|
||||
(rc, out, err) = nmcli.up_connection()
|
||||
if rc != 0:
|
||||
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
|
||||
|
||||
elif nmcli.state == 'down':
|
||||
if nmcli.connection_exists():
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
if nmcli.conn_reload:
|
||||
(rc, out, err) = nmcli.reload_connection()
|
||||
(rc, out, err) = nmcli.down_connection()
|
||||
if rc != 0:
|
||||
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
|
||||
|
||||
except NmcliModuleError as e:
|
||||
module.fail_json(name=nmcli.conn_name, msg=str(e))
|
||||
|
||||
|
||||
@@ -96,6 +96,12 @@ options:
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 2.5.0
|
||||
force:
|
||||
description:
|
||||
- Use the C(--force) flag when installing.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 9.5.0
|
||||
requirements:
|
||||
- npm installed in bin path (recommended /usr/local/bin)
|
||||
'''
|
||||
@@ -117,6 +123,11 @@ EXAMPLES = r'''
|
||||
name: coffee-script
|
||||
global: true
|
||||
|
||||
- name: Force Install "coffee-script" node.js package.
|
||||
community.general.npm:
|
||||
name: coffee-script
|
||||
force: true
|
||||
|
||||
- name: Remove the globally package "coffee-script".
|
||||
community.general.npm:
|
||||
name: coffee-script
|
||||
@@ -167,6 +178,7 @@ class Npm(object):
|
||||
self.state = kwargs['state']
|
||||
self.no_optional = kwargs['no_optional']
|
||||
self.no_bin_links = kwargs['no_bin_links']
|
||||
self.force = kwargs['force']
|
||||
|
||||
if kwargs['executable']:
|
||||
self.executable = kwargs['executable'].split(' ')
|
||||
@@ -191,6 +203,7 @@ class Npm(object):
|
||||
registry=cmd_runner_fmt.as_opt_val('--registry'),
|
||||
no_optional=cmd_runner_fmt.as_bool('--no-optional'),
|
||||
no_bin_links=cmd_runner_fmt.as_bool('--no-bin-links'),
|
||||
force=cmd_runner_fmt.as_bool('--force'),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -212,7 +225,7 @@ class Npm(object):
|
||||
params['name_version'] = self.name_version if add_package_name else None
|
||||
|
||||
with self.runner(
|
||||
"exec_args global_ production ignore_scripts unsafe_perm name_version registry no_optional no_bin_links",
|
||||
"exec_args global_ production ignore_scripts unsafe_perm name_version registry no_optional no_bin_links force",
|
||||
check_rc=check_rc, cwd=cwd
|
||||
) as ctx:
|
||||
rc, out, err = ctx.run(**params)
|
||||
@@ -289,6 +302,7 @@ def main():
|
||||
ci=dict(default=False, type='bool'),
|
||||
no_optional=dict(default=False, type='bool'),
|
||||
no_bin_links=dict(default=False, type='bool'),
|
||||
force=dict(default=False, type='bool'),
|
||||
)
|
||||
arg_spec['global'] = dict(default=False, type='bool')
|
||||
module = AnsibleModule(
|
||||
@@ -318,7 +332,8 @@ def main():
|
||||
unsafe_perm=module.params['unsafe_perm'],
|
||||
state=state,
|
||||
no_optional=module.params['no_optional'],
|
||||
no_bin_links=module.params['no_bin_links'])
|
||||
no_bin_links=module.params['no_bin_links'],
|
||||
force=module.params['force'])
|
||||
|
||||
changed = False
|
||||
if module.params['ci']:
|
||||
|
||||
@@ -17,6 +17,7 @@ description:
|
||||
requirements:
|
||||
- pyone
|
||||
extends_documentation_fragment:
|
||||
- community.general.opennebula
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
@@ -24,23 +25,6 @@ attributes:
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
api_url:
|
||||
description:
|
||||
- URL of the OpenNebula RPC server.
|
||||
- It is recommended to use HTTPS so that the username/password are not
|
||||
- transferred over the network unencrypted.
|
||||
- If not set then the value of the E(ONE_URL) environment variable is used.
|
||||
type: str
|
||||
api_username:
|
||||
description:
|
||||
- Name of the user to login into the OpenNebula RPC server. If not set
|
||||
- then the value of the E(ONE_USERNAME) environment variable is used.
|
||||
type: str
|
||||
api_password:
|
||||
description:
|
||||
- Password of the user to login into OpenNebula RPC server. If not set
|
||||
- then the value of the E(ONE_PASSWORD) environment variable is used.
|
||||
type: str
|
||||
id:
|
||||
description:
|
||||
- A O(id) of the image you would like to manage.
|
||||
@@ -67,6 +51,11 @@ options:
|
||||
- A name that will be assigned to the existing or new image.
|
||||
- In the case of cloning, by default O(new_name) will take the name of the origin image with the prefix 'Copy of'.
|
||||
type: str
|
||||
persistent:
|
||||
description:
|
||||
- Whether the image should be persistent or non-persistent.
|
||||
type: bool
|
||||
version_added: 9.5.0
|
||||
author:
|
||||
- "Milan Ilic (@ilicmilan)"
|
||||
'''
|
||||
@@ -92,6 +81,11 @@ EXAMPLES = '''
|
||||
id: 37
|
||||
enabled: false
|
||||
|
||||
- name: Make the IMAGE persistent
|
||||
community.general.one_image:
|
||||
id: 37
|
||||
persistent: true
|
||||
|
||||
- name: Enable the IMAGE by name
|
||||
community.general.one_image:
|
||||
name: bar-image
|
||||
@@ -114,300 +108,448 @@ RETURN = '''
|
||||
id:
|
||||
description: image id
|
||||
type: int
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: 153
|
||||
name:
|
||||
description: image name
|
||||
type: str
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: app1
|
||||
group_id:
|
||||
description: image's group id
|
||||
type: int
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: 1
|
||||
group_name:
|
||||
description: image's group name
|
||||
type: str
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: one-users
|
||||
owner_id:
|
||||
description: image's owner id
|
||||
type: int
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: 143
|
||||
owner_name:
|
||||
description: image's owner name
|
||||
type: str
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: ansible-test
|
||||
state:
|
||||
description: state of image instance
|
||||
type: str
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: READY
|
||||
used:
|
||||
description: is image in use
|
||||
type: bool
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: true
|
||||
running_vms:
|
||||
description: count of running vms that use this image
|
||||
type: int
|
||||
returned: success
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample: 7
|
||||
permissions:
|
||||
description: The image's permissions.
|
||||
type: dict
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
contains:
|
||||
owner_u:
|
||||
description: The image's owner USAGE permissions.
|
||||
type: str
|
||||
sample: 1
|
||||
owner_m:
|
||||
description: The image's owner MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
owner_a:
|
||||
description: The image's owner ADMIN permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_u:
|
||||
description: The image's group USAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_m:
|
||||
description: The image's group MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_a:
|
||||
description: The image's group ADMIN permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_u:
|
||||
description: The image's other users USAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_m:
|
||||
description: The image's other users MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_a:
|
||||
description: The image's other users ADMIN permissions
|
||||
type: str
|
||||
sample: 0
|
||||
sample:
|
||||
owner_u: 1
|
||||
owner_m: 0
|
||||
owner_a: 0
|
||||
group_u: 0
|
||||
group_m: 0
|
||||
group_a: 0
|
||||
other_u: 0
|
||||
other_m: 0
|
||||
other_a: 0
|
||||
type:
|
||||
description: The image's type.
|
||||
type: str
|
||||
sample: 0
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
disk_type:
|
||||
description: The image's format type.
|
||||
type: str
|
||||
sample: 0
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
persistent:
|
||||
description: The image's persistence status (1 means true, 0 means false).
|
||||
type: int
|
||||
sample: 1
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
source:
|
||||
description: The image's source.
|
||||
type: str
|
||||
sample: /var/lib/one//datastores/100/somerandomstringxd
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
path:
|
||||
description: The image's filesystem path.
|
||||
type: str
|
||||
sample: /var/tmp/hello.qcow2
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
fstype:
|
||||
description: The image's filesystem type.
|
||||
type: str
|
||||
sample: ext4
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
size:
|
||||
description: The image's size in MegaBytes.
|
||||
type: int
|
||||
sample: 10000
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
cloning_ops:
|
||||
description: The image's cloning operations per second.
|
||||
type: int
|
||||
sample: 0
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
cloning_id:
|
||||
description: The image's cloning ID.
|
||||
type: int
|
||||
sample: -1
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
target_snapshot:
|
||||
description: The image's target snapshot.
|
||||
type: int
|
||||
sample: 1
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
datastore_id:
|
||||
description: The image's datastore ID.
|
||||
type: int
|
||||
sample: 100
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
datastore:
|
||||
description: The image's datastore name.
|
||||
type: int
|
||||
sample: image_datastore
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
vms:
|
||||
description: The image's list of vm ID's.
|
||||
type: list
|
||||
elements: int
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
version_added: 9.5.0
|
||||
clones:
|
||||
description: The image's list of clones ID's.
|
||||
type: list
|
||||
elements: int
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
version_added: 9.5.0
|
||||
app_clones:
|
||||
description: The image's list of app_clones ID's.
|
||||
type: list
|
||||
elements: int
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
version_added: 9.5.0
|
||||
snapshots:
|
||||
description: The image's list of snapshots.
|
||||
type: list
|
||||
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||
version_added: 9.5.0
|
||||
sample:
|
||||
- date: 123123
|
||||
parent: 1
|
||||
size: 10228
|
||||
allow_orphans: 1
|
||||
children: 0
|
||||
active: 1
|
||||
name: SampleName
|
||||
'''
|
||||
|
||||
try:
|
||||
import pyone
|
||||
HAS_PYONE = True
|
||||
except ImportError:
|
||||
HAS_PYONE = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import os
|
||||
|
||||
|
||||
def get_image(module, client, predicate):
|
||||
# Filter -2 means fetch all images user can Use
|
||||
pool = client.imagepool.info(-2, -1, -1, -1)
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if predicate(image):
|
||||
return image
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_image_by_name(module, client, image_name):
|
||||
return get_image(module, client, lambda image: (image.NAME == image_name))
|
||||
|
||||
|
||||
def get_image_by_id(module, client, image_id):
|
||||
return get_image(module, client, lambda image: (image.ID == image_id))
|
||||
|
||||
|
||||
def get_image_instance(module, client, requested_id, requested_name):
|
||||
if requested_id:
|
||||
return get_image_by_id(module, client, requested_id)
|
||||
else:
|
||||
return get_image_by_name(module, client, requested_name)
|
||||
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
|
||||
|
||||
|
||||
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||
|
||||
|
||||
def get_image_info(image):
|
||||
info = {
|
||||
'id': image.ID,
|
||||
'name': image.NAME,
|
||||
'state': IMAGE_STATES[image.STATE],
|
||||
'running_vms': image.RUNNING_VMS,
|
||||
'used': bool(image.RUNNING_VMS),
|
||||
'user_name': image.UNAME,
|
||||
'user_id': image.UID,
|
||||
'group_name': image.GNAME,
|
||||
'group_id': image.GID,
|
||||
}
|
||||
class ImageModule(OpenNebulaModule):
|
||||
def __init__(self):
|
||||
argument_spec = dict(
|
||||
id=dict(type='int', required=False),
|
||||
name=dict(type='str', required=False),
|
||||
state=dict(type='str', choices=['present', 'absent', 'cloned', 'renamed'], default='present'),
|
||||
enabled=dict(type='bool', required=False),
|
||||
new_name=dict(type='str', required=False),
|
||||
persistent=dict(type='bool', required=False),
|
||||
)
|
||||
required_if = [
|
||||
['state', 'renamed', ['id']]
|
||||
]
|
||||
mutually_exclusive = [
|
||||
['id', 'name'],
|
||||
]
|
||||
|
||||
return info
|
||||
OpenNebulaModule.__init__(self,
|
||||
argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
required_if=required_if)
|
||||
|
||||
def run(self, one, module, result):
|
||||
params = module.params
|
||||
id = params.get('id')
|
||||
name = params.get('name')
|
||||
desired_state = params.get('state')
|
||||
enabled = params.get('enabled')
|
||||
new_name = params.get('new_name')
|
||||
persistent = params.get('persistent')
|
||||
|
||||
def wait_for_state(module, client, image_id, wait_timeout, state_predicate):
|
||||
import time
|
||||
start_time = time.time()
|
||||
self.result = {}
|
||||
|
||||
image = self.get_image_instance(id, name)
|
||||
if not image and desired_state != 'absent':
|
||||
# Using 'if id:' doesn't work properly when id=0
|
||||
if id is not None:
|
||||
module.fail_json(msg="There is no image with id=" + str(id))
|
||||
elif name is not None:
|
||||
module.fail_json(msg="There is no image with name=" + name)
|
||||
|
||||
if desired_state == 'absent':
|
||||
self.result = self.delete_image(image)
|
||||
else:
|
||||
if persistent is not None:
|
||||
self.result = self.change_persistence(image, persistent)
|
||||
if enabled is not None:
|
||||
self.result = self.enable_image(image, enabled)
|
||||
if desired_state == "cloned":
|
||||
self.result = self.clone_image(image, new_name)
|
||||
elif desired_state == "renamed":
|
||||
self.result = self.rename_image(image, new_name)
|
||||
|
||||
self.exit()
|
||||
|
||||
def get_image(self, predicate):
|
||||
# Filter -2 means fetch all images user can Use
|
||||
pool = self.one.imagepool.info(-2, -1, -1, -1)
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if predicate(image):
|
||||
return image
|
||||
|
||||
return None
|
||||
|
||||
def get_image_by_name(self, image_name):
|
||||
return self.get_image(lambda image: (image.NAME == image_name))
|
||||
|
||||
def get_image_by_id(self, image_id):
|
||||
return self.get_image(lambda image: (image.ID == image_id))
|
||||
|
||||
def get_image_instance(self, requested_id, requested_name):
|
||||
# Using 'if requested_id:' doesn't work properly when requested_id=0
|
||||
if requested_id is not None:
|
||||
return self.get_image_by_id(requested_id)
|
||||
else:
|
||||
return self.get_image_by_name(requested_name)
|
||||
|
||||
def wait_for_ready(self, image_id, wait_timeout=60):
|
||||
import time
|
||||
start_time = time.time()
|
||||
|
||||
while (time.time() - start_time) < wait_timeout:
|
||||
image = self.one.image.info(image_id)
|
||||
state = image.STATE
|
||||
|
||||
if state in [IMAGE_STATES.index('ERROR')]:
|
||||
self.module.fail_json(msg="Got an ERROR state: " + image.TEMPLATE['ERROR'])
|
||||
|
||||
if state in [IMAGE_STATES.index('READY')]:
|
||||
return True
|
||||
|
||||
time.sleep(1)
|
||||
self.module.fail_json(msg="Wait timeout has expired!")
|
||||
|
||||
def wait_for_delete(self, image_id, wait_timeout=60):
|
||||
import time
|
||||
start_time = time.time()
|
||||
|
||||
while (time.time() - start_time) < wait_timeout:
|
||||
# It might be already deleted by the time this function is called
|
||||
try:
|
||||
image = self.one.image.info(image_id)
|
||||
except Exception:
|
||||
check_image = self.get_image_instance(image_id)
|
||||
if not check_image:
|
||||
return True
|
||||
|
||||
state = image.STATE
|
||||
|
||||
if state in [IMAGE_STATES.index('DELETE')]:
|
||||
return True
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
self.module.fail_json(msg="Wait timeout has expired!")
|
||||
|
||||
def enable_image(self, image, enable):
|
||||
image = self.one.image.info(image.ID)
|
||||
changed = False
|
||||
|
||||
while (time.time() - start_time) < wait_timeout:
|
||||
image = client.image.info(image_id)
|
||||
state = image.STATE
|
||||
|
||||
if state_predicate(state):
|
||||
return image
|
||||
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
||||
if enable:
|
||||
self.module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
|
||||
else:
|
||||
self.module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
|
||||
|
||||
time.sleep(1)
|
||||
if ((enable and state != IMAGE_STATES.index('READY')) or
|
||||
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
||||
changed = True
|
||||
|
||||
module.fail_json(msg="Wait timeout has expired!")
|
||||
if changed and not self.module.check_mode:
|
||||
self.one.image.enable(image.ID, enable)
|
||||
|
||||
result = OpenNebulaModule.get_image_info(image)
|
||||
result['changed'] = changed
|
||||
|
||||
def wait_for_ready(module, client, image_id, wait_timeout=60):
|
||||
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('READY')]))
|
||||
|
||||
|
||||
def wait_for_delete(module, client, image_id, wait_timeout=60):
|
||||
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('DELETE')]))
|
||||
|
||||
|
||||
def enable_image(module, client, image, enable):
|
||||
image = client.image.info(image.ID)
|
||||
changed = False
|
||||
|
||||
state = image.STATE
|
||||
|
||||
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
||||
if enable:
|
||||
module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
|
||||
else:
|
||||
module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
|
||||
|
||||
if ((enable and state != IMAGE_STATES.index('READY')) or
|
||||
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
||||
changed = True
|
||||
|
||||
if changed and not module.check_mode:
|
||||
client.image.enable(image.ID, enable)
|
||||
|
||||
result = get_image_info(image)
|
||||
result['changed'] = changed
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def clone_image(module, client, image, new_name):
|
||||
if new_name is None:
|
||||
new_name = "Copy of " + image.NAME
|
||||
|
||||
tmp_image = get_image_by_name(module, client, new_name)
|
||||
if tmp_image:
|
||||
result = get_image_info(tmp_image)
|
||||
result['changed'] = False
|
||||
return result
|
||||
|
||||
if image.STATE == IMAGE_STATES.index('DISABLED'):
|
||||
module.fail_json(msg="Cannot clone DISABLED image")
|
||||
def change_persistence(self, image, enable):
|
||||
image = self.one.image.info(image.ID)
|
||||
changed = False
|
||||
|
||||
if not module.check_mode:
|
||||
new_id = client.image.clone(image.ID, new_name)
|
||||
wait_for_ready(module, client, new_id)
|
||||
image = client.image.info(new_id)
|
||||
state = image.STATE
|
||||
|
||||
result = get_image_info(image)
|
||||
result['changed'] = True
|
||||
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
||||
if enable:
|
||||
self.module.fail_json(msg="Cannot enable persistence for " + IMAGE_STATES[state] + " image!")
|
||||
else:
|
||||
self.module.fail_json(msg="Cannot disable persistence for " + IMAGE_STATES[state] + " image!")
|
||||
|
||||
return result
|
||||
if ((enable and state != IMAGE_STATES.index('READY')) or
|
||||
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
||||
changed = True
|
||||
|
||||
if changed and not self.module.check_mode:
|
||||
self.one.image.persistent(image.ID, enable)
|
||||
|
||||
def rename_image(module, client, image, new_name):
|
||||
if new_name is None:
|
||||
module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
|
||||
result = OpenNebulaModule.get_image_info(image)
|
||||
result['changed'] = changed
|
||||
|
||||
if new_name == image.NAME:
|
||||
result = get_image_info(image)
|
||||
result['changed'] = False
|
||||
return result
|
||||
|
||||
tmp_image = get_image_by_name(module, client, new_name)
|
||||
if tmp_image:
|
||||
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
|
||||
def clone_image(self, image, new_name):
|
||||
if new_name is None:
|
||||
new_name = "Copy of " + image.NAME
|
||||
|
||||
if not module.check_mode:
|
||||
client.image.rename(image.ID, new_name)
|
||||
tmp_image = self.get_image_by_name(new_name)
|
||||
if tmp_image:
|
||||
result = OpenNebulaModule.get_image_info(tmp_image)
|
||||
result['changed'] = False
|
||||
return result
|
||||
|
||||
result = get_image_info(image)
|
||||
result['changed'] = True
|
||||
return result
|
||||
if image.STATE == IMAGE_STATES.index('DISABLED'):
|
||||
self.module.fail_json(msg="Cannot clone DISABLED image")
|
||||
|
||||
if not self.module.check_mode:
|
||||
new_id = self.one.image.clone(image.ID, new_name)
|
||||
self.wait_for_ready(new_id)
|
||||
image = self.one.image.info(new_id)
|
||||
|
||||
def delete_image(module, client, image):
|
||||
result = OpenNebulaModule.get_image_info(image)
|
||||
result['changed'] = True
|
||||
|
||||
if not image:
|
||||
return {'changed': False}
|
||||
return result
|
||||
|
||||
if image.RUNNING_VMS > 0:
|
||||
module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
|
||||
def rename_image(self, image, new_name):
|
||||
if new_name is None:
|
||||
self.module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
|
||||
|
||||
if not module.check_mode:
|
||||
client.image.delete(image.ID)
|
||||
wait_for_delete(module, client, image.ID)
|
||||
if new_name == image.NAME:
|
||||
result = OpenNebulaModule.get_image_info(image)
|
||||
result['changed'] = False
|
||||
return result
|
||||
|
||||
return {'changed': True}
|
||||
tmp_image = self.get_image_by_name(new_name)
|
||||
if tmp_image:
|
||||
self.module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
|
||||
|
||||
if not self.module.check_mode:
|
||||
self.one.image.rename(image.ID, new_name)
|
||||
|
||||
def get_connection_info(module):
|
||||
result = OpenNebulaModule.get_image_info(image)
|
||||
result['changed'] = True
|
||||
return result
|
||||
|
||||
url = module.params.get('api_url')
|
||||
username = module.params.get('api_username')
|
||||
password = module.params.get('api_password')
|
||||
def delete_image(self, image):
|
||||
if not image:
|
||||
return {'changed': False}
|
||||
|
||||
if not url:
|
||||
url = os.environ.get('ONE_URL')
|
||||
if image.RUNNING_VMS > 0:
|
||||
self.module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
|
||||
|
||||
if not username:
|
||||
username = os.environ.get('ONE_USERNAME')
|
||||
if not self.module.check_mode:
|
||||
self.one.image.delete(image.ID)
|
||||
self.wait_for_delete(image.ID)
|
||||
|
||||
if not password:
|
||||
password = os.environ.get('ONE_PASSWORD')
|
||||
|
||||
if not (url and username and password):
|
||||
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
|
||||
from collections import namedtuple
|
||||
|
||||
auth_params = namedtuple('auth', ('url', 'username', 'password'))
|
||||
|
||||
return auth_params(url=url, username=username, password=password)
|
||||
return {'changed': True}
|
||||
|
||||
|
||||
def main():
|
||||
fields = {
|
||||
"api_url": {"required": False, "type": "str"},
|
||||
"api_username": {"required": False, "type": "str"},
|
||||
"api_password": {"required": False, "type": "str", "no_log": True},
|
||||
"id": {"required": False, "type": "int"},
|
||||
"name": {"required": False, "type": "str"},
|
||||
"state": {
|
||||
"default": "present",
|
||||
"choices": ['present', 'absent', 'cloned', 'renamed'],
|
||||
"type": "str"
|
||||
},
|
||||
"enabled": {"required": False, "type": "bool"},
|
||||
"new_name": {"required": False, "type": "str"},
|
||||
}
|
||||
|
||||
module = AnsibleModule(argument_spec=fields,
|
||||
mutually_exclusive=[['id', 'name']],
|
||||
supports_check_mode=True)
|
||||
|
||||
if not HAS_PYONE:
|
||||
module.fail_json(msg='This module requires pyone to work!')
|
||||
|
||||
auth = get_connection_info(module)
|
||||
params = module.params
|
||||
id = params.get('id')
|
||||
name = params.get('name')
|
||||
state = params.get('state')
|
||||
enabled = params.get('enabled')
|
||||
new_name = params.get('new_name')
|
||||
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
|
||||
|
||||
result = {}
|
||||
|
||||
if not id and state == 'renamed':
|
||||
module.fail_json(msg="Option 'id' is required when the state is 'renamed'")
|
||||
|
||||
image = get_image_instance(module, client, id, name)
|
||||
if not image and state != 'absent':
|
||||
if id:
|
||||
module.fail_json(msg="There is no image with id=" + str(id))
|
||||
else:
|
||||
module.fail_json(msg="There is no image with name=" + name)
|
||||
|
||||
if state == 'absent':
|
||||
result = delete_image(module, client, image)
|
||||
else:
|
||||
result = get_image_info(image)
|
||||
changed = False
|
||||
result['changed'] = False
|
||||
|
||||
if enabled is not None:
|
||||
result = enable_image(module, client, image, enabled)
|
||||
if state == "cloned":
|
||||
result = clone_image(module, client, image, new_name)
|
||||
elif state == "renamed":
|
||||
result = rename_image(module, client, image, new_name)
|
||||
|
||||
changed = changed or result['changed']
|
||||
result['changed'] = changed
|
||||
|
||||
module.exit_json(**result)
|
||||
ImageModule().run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -17,29 +17,14 @@ description:
|
||||
requirements:
|
||||
- pyone
|
||||
extends_documentation_fragment:
|
||||
- community.general.opennebula
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
options:
|
||||
api_url:
|
||||
description:
|
||||
- URL of the OpenNebula RPC server.
|
||||
- It is recommended to use HTTPS so that the username/password are not
|
||||
- transferred over the network unencrypted.
|
||||
- If not set then the value of the E(ONE_URL) environment variable is used.
|
||||
type: str
|
||||
api_username:
|
||||
description:
|
||||
- Name of the user to login into the OpenNebula RPC server. If not set
|
||||
- then the value of the E(ONE_USERNAME) environment variable is used.
|
||||
type: str
|
||||
api_password:
|
||||
description:
|
||||
- Password of the user to login into OpenNebula RPC server. If not set
|
||||
- then the value of the E(ONE_PASSWORD) environment variable is used.
|
||||
type: str
|
||||
ids:
|
||||
description:
|
||||
- A list of images ids whose facts you want to gather.
|
||||
- Module can use integers too.
|
||||
aliases: ['id']
|
||||
type: list
|
||||
elements: str
|
||||
@@ -66,9 +51,16 @@ EXAMPLES = '''
|
||||
msg: result
|
||||
|
||||
- name: Gather facts about an image using ID
|
||||
community.general.one_image_info:
|
||||
ids: 123
|
||||
|
||||
- name: Gather facts about an image using list of ID
|
||||
community.general.one_image_info:
|
||||
ids:
|
||||
- 123
|
||||
- 456
|
||||
- 789
|
||||
- 0
|
||||
|
||||
- name: Gather facts about an image using the name
|
||||
community.general.one_image_info:
|
||||
@@ -93,182 +85,285 @@ images:
|
||||
returned: success
|
||||
contains:
|
||||
id:
|
||||
description: image id
|
||||
description: The image's id.
|
||||
type: int
|
||||
sample: 153
|
||||
name:
|
||||
description: image name
|
||||
description: The image's name.
|
||||
type: str
|
||||
sample: app1
|
||||
group_id:
|
||||
description: image's group id
|
||||
description: The image's group id
|
||||
type: int
|
||||
sample: 1
|
||||
group_name:
|
||||
description: image's group name
|
||||
description: The image's group name.
|
||||
type: str
|
||||
sample: one-users
|
||||
owner_id:
|
||||
description: image's owner id
|
||||
description: The image's owner id.
|
||||
type: int
|
||||
sample: 143
|
||||
owner_name:
|
||||
description: image's owner name
|
||||
description: The image's owner name.
|
||||
type: str
|
||||
sample: ansible-test
|
||||
state:
|
||||
description: state of image instance
|
||||
description: The image's state.
|
||||
type: str
|
||||
sample: READY
|
||||
used:
|
||||
description: is image in use
|
||||
description: The image's usage status.
|
||||
type: bool
|
||||
sample: true
|
||||
running_vms:
|
||||
description: count of running vms that use this image
|
||||
description: The image's count of running vms that use this image.
|
||||
type: int
|
||||
sample: 7
|
||||
permissions:
|
||||
description: The image's permissions.
|
||||
type: dict
|
||||
version_added: 9.5.0
|
||||
contains:
|
||||
owner_u:
|
||||
description: The image's owner USAGE permissions.
|
||||
type: str
|
||||
sample: 1
|
||||
owner_m:
|
||||
description: The image's owner MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
owner_a:
|
||||
description: The image's owner ADMIN permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_u:
|
||||
description: The image's group USAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_m:
|
||||
description: The image's group MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
group_a:
|
||||
description: The image's group ADMIN permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_u:
|
||||
description: The image's other users USAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_m:
|
||||
description: The image's other users MANAGE permissions.
|
||||
type: str
|
||||
sample: 0
|
||||
other_a:
|
||||
description: The image's other users ADMIN permissions
|
||||
type: str
|
||||
sample: 0
|
||||
sample:
|
||||
owner_u: 1
|
||||
owner_m: 0
|
||||
owner_a: 0
|
||||
group_u: 0
|
||||
group_m: 0
|
||||
group_a: 0
|
||||
other_u: 0
|
||||
other_m: 0
|
||||
other_a: 0
|
||||
type:
|
||||
description: The image's type.
|
||||
type: int
|
||||
sample: 0
|
||||
version_added: 9.5.0
|
||||
disk_type:
|
||||
description: The image's format type.
|
||||
type: int
|
||||
sample: 0
|
||||
version_added: 9.5.0
|
||||
persistent:
|
||||
description: The image's persistence status (1 means true, 0 means false).
|
||||
type: int
|
||||
sample: 1
|
||||
version_added: 9.5.0
|
||||
source:
|
||||
description: The image's source.
|
||||
type: str
|
||||
sample: /var/lib/one//datastores/100/somerandomstringxd
|
||||
version_added: 9.5.0
|
||||
path:
|
||||
description: The image's filesystem path.
|
||||
type: str
|
||||
sample: /var/tmp/hello.qcow2
|
||||
version_added: 9.5.0
|
||||
fstype:
|
||||
description: The image's filesystem type.
|
||||
type: str
|
||||
sample: ext4
|
||||
version_added: 9.5.0
|
||||
size:
|
||||
description: The image's size in MegaBytes.
|
||||
type: int
|
||||
sample: 10000
|
||||
version_added: 9.5.0
|
||||
cloning_ops:
|
||||
description: The image's cloning operations per second.
|
||||
type: int
|
||||
sample: 0
|
||||
version_added: 9.5.0
|
||||
cloning_id:
|
||||
description: The image's cloning ID.
|
||||
type: int
|
||||
sample: -1
|
||||
version_added: 9.5.0
|
||||
target_snapshot:
|
||||
description: The image's target snapshot.
|
||||
type: int
|
||||
sample: 1
|
||||
version_added: 9.5.0
|
||||
datastore_id:
|
||||
description: The image's datastore ID.
|
||||
type: int
|
||||
sample: 100
|
||||
version_added: 9.5.0
|
||||
datastore:
|
||||
description: The image's datastore name.
|
||||
type: int
|
||||
sample: image_datastore
|
||||
version_added: 9.5.0
|
||||
vms:
|
||||
description: The image's list of vm ID's.
|
||||
type: list
|
||||
elements: int
|
||||
version_added: 9.5.0
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
clones:
|
||||
description: The image's list of clones ID's.
|
||||
type: list
|
||||
elements: int
|
||||
version_added: 9.5.0
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
app_clones:
|
||||
description: The image's list of app_clones ID's.
|
||||
type: list
|
||||
elements: int
|
||||
version_added: 9.5.0
|
||||
sample:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
snapshots:
|
||||
description: The image's list of snapshots.
|
||||
type: list
|
||||
version_added: 9.5.0
|
||||
sample:
|
||||
- date: 123123
|
||||
parent: 1
|
||||
size: 10228
|
||||
allow_orphans: 1
|
||||
children: 0
|
||||
active: 1
|
||||
name: SampleName
|
||||
'''
|
||||
|
||||
try:
|
||||
import pyone
|
||||
HAS_PYONE = True
|
||||
except ImportError:
|
||||
HAS_PYONE = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import os
|
||||
|
||||
|
||||
def get_all_images(client):
|
||||
pool = client.imagepool.info(-2, -1, -1, -1)
|
||||
# Filter -2 means fetch all images user can Use
|
||||
|
||||
return pool
|
||||
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
|
||||
|
||||
|
||||
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||
|
||||
|
||||
def get_image_info(image):
|
||||
info = {
|
||||
'id': image.ID,
|
||||
'name': image.NAME,
|
||||
'state': IMAGE_STATES[image.STATE],
|
||||
'running_vms': image.RUNNING_VMS,
|
||||
'used': bool(image.RUNNING_VMS),
|
||||
'user_name': image.UNAME,
|
||||
'user_id': image.UID,
|
||||
'group_name': image.GNAME,
|
||||
'group_id': image.GID,
|
||||
}
|
||||
return info
|
||||
class ImageInfoModule(OpenNebulaModule):
|
||||
def __init__(self):
|
||||
argument_spec = dict(
|
||||
ids=dict(type='list', aliases=['id'], elements='str', required=False),
|
||||
name=dict(type='str', required=False),
|
||||
)
|
||||
mutually_exclusive = [
|
||||
['ids', 'name'],
|
||||
]
|
||||
|
||||
OpenNebulaModule.__init__(self,
|
||||
argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=mutually_exclusive)
|
||||
|
||||
def get_images_by_ids(module, client, ids):
|
||||
images = []
|
||||
pool = get_all_images(client)
|
||||
def run(self, one, module, result):
|
||||
params = module.params
|
||||
ids = params.get('ids')
|
||||
name = params.get('name')
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if str(image.ID) in ids:
|
||||
images.append(image)
|
||||
ids.remove(str(image.ID))
|
||||
if len(ids) == 0:
|
||||
if ids:
|
||||
images = self.get_images_by_ids(ids)
|
||||
elif name:
|
||||
images = self.get_images_by_name(name)
|
||||
else:
|
||||
images = self.get_all_images().IMAGE
|
||||
|
||||
self.result = {
|
||||
'images': [OpenNebulaModule.get_image_info(image) for image in images]
|
||||
}
|
||||
|
||||
self.exit()
|
||||
|
||||
def get_all_images(self):
|
||||
pool = self.one.imagepool.info(-2, -1, -1, -1)
|
||||
# Filter -2 means fetch all images user can Use
|
||||
|
||||
return pool
|
||||
|
||||
def get_images_by_ids(self, ids):
|
||||
images = []
|
||||
pool = self.get_all_images()
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if str(image.ID) in ids:
|
||||
images.append(image)
|
||||
ids.remove(str(image.ID))
|
||||
if len(ids) == 0:
|
||||
break
|
||||
|
||||
if len(ids) > 0:
|
||||
self.module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
|
||||
|
||||
return images
|
||||
|
||||
def get_images_by_name(self, name_pattern):
|
||||
images = []
|
||||
pattern = None
|
||||
|
||||
pool = self.get_all_images()
|
||||
|
||||
if name_pattern.startswith('~'):
|
||||
import re
|
||||
if name_pattern[1] == '*':
|
||||
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
|
||||
else:
|
||||
pattern = re.compile(name_pattern[1:])
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if pattern is not None:
|
||||
if pattern.match(image.NAME):
|
||||
images.append(image)
|
||||
elif name_pattern == image.NAME:
|
||||
images.append(image)
|
||||
break
|
||||
|
||||
if len(ids) > 0:
|
||||
module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
|
||||
# if the specific name is indicated
|
||||
if pattern is None and len(images) == 0:
|
||||
self.module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
def get_images_by_name(module, client, name_pattern):
|
||||
|
||||
images = []
|
||||
pattern = None
|
||||
|
||||
pool = get_all_images(client)
|
||||
|
||||
if name_pattern.startswith('~'):
|
||||
import re
|
||||
if name_pattern[1] == '*':
|
||||
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
|
||||
else:
|
||||
pattern = re.compile(name_pattern[1:])
|
||||
|
||||
for image in pool.IMAGE:
|
||||
if pattern is not None:
|
||||
if pattern.match(image.NAME):
|
||||
images.append(image)
|
||||
elif name_pattern == image.NAME:
|
||||
images.append(image)
|
||||
break
|
||||
|
||||
# if the specific name is indicated
|
||||
if pattern is None and len(images) == 0:
|
||||
module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
def get_connection_info(module):
|
||||
|
||||
url = module.params.get('api_url')
|
||||
username = module.params.get('api_username')
|
||||
password = module.params.get('api_password')
|
||||
|
||||
if not url:
|
||||
url = os.environ.get('ONE_URL')
|
||||
|
||||
if not username:
|
||||
username = os.environ.get('ONE_USERNAME')
|
||||
|
||||
if not password:
|
||||
password = os.environ.get('ONE_PASSWORD')
|
||||
|
||||
if not (url and username and password):
|
||||
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
|
||||
from collections import namedtuple
|
||||
|
||||
auth_params = namedtuple('auth', ('url', 'username', 'password'))
|
||||
|
||||
return auth_params(url=url, username=username, password=password)
|
||||
return images
|
||||
|
||||
|
||||
def main():
|
||||
fields = {
|
||||
"api_url": {"required": False, "type": "str"},
|
||||
"api_username": {"required": False, "type": "str"},
|
||||
"api_password": {"required": False, "type": "str", "no_log": True},
|
||||
"ids": {"required": False, "aliases": ['id'], "type": "list", "elements": "str"},
|
||||
"name": {"required": False, "type": "str"},
|
||||
}
|
||||
|
||||
module = AnsibleModule(argument_spec=fields,
|
||||
mutually_exclusive=[['ids', 'name']],
|
||||
supports_check_mode=True)
|
||||
|
||||
if not HAS_PYONE:
|
||||
module.fail_json(msg='This module requires pyone to work!')
|
||||
|
||||
auth = get_connection_info(module)
|
||||
params = module.params
|
||||
ids = params.get('ids')
|
||||
name = params.get('name')
|
||||
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
|
||||
|
||||
if ids:
|
||||
images = get_images_by_ids(module, client, ids)
|
||||
elif name:
|
||||
images = get_images_by_name(module, client, name)
|
||||
else:
|
||||
images = get_all_images(client).IMAGE
|
||||
|
||||
result = {
|
||||
'images': [get_image_info(image) for image in images],
|
||||
}
|
||||
|
||||
module.exit_json(**result)
|
||||
ImageInfoModule().run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -522,7 +522,7 @@ def create_service_and_operation(module, auth, template_id, service_name, owner_
|
||||
if unique:
|
||||
service = get_service_by_name(module, auth, service_name)
|
||||
|
||||
if not service:
|
||||
if not service or service["TEMPLATE"]["BODY"]["state"] == "DONE":
|
||||
if not module.check_mode:
|
||||
service = create_service(module, auth, template_id, service_name, custom_attrs, unique, wait, wait_timeout)
|
||||
changed = True
|
||||
@@ -637,7 +637,6 @@ def get_service_id_by_name(module, auth, service_name):
|
||||
|
||||
|
||||
def get_connection_info(module):
|
||||
|
||||
url = module.params.get('api_url')
|
||||
username = module.params.get('api_username')
|
||||
password = module.params.get('api_password')
|
||||
|
||||
@@ -45,6 +45,7 @@ options:
|
||||
login:
|
||||
description:
|
||||
- Whether the target node should be connected.
|
||||
- When O(target) is omitted, will login to all available.
|
||||
type: bool
|
||||
aliases: [ state ]
|
||||
node_auth:
|
||||
@@ -101,7 +102,6 @@ options:
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 4.1.0
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -117,8 +117,7 @@ EXAMPLES = r'''
|
||||
discover: true
|
||||
ip: 10.1.2.3
|
||||
|
||||
# NOTE: Only works if exactly one target is exported to the initiator
|
||||
- name: Discover targets on portal and login to the one available
|
||||
- name: Discover targets on portal and login to the ones available
|
||||
community.general.open_iscsi:
|
||||
portal: '{{ iscsi_target }}'
|
||||
login: true
|
||||
@@ -227,7 +226,7 @@ def target_loggedon(module, target, portal=None, port=None):
|
||||
module.fail_json(cmd=cmd, rc=rc, msg=err)
|
||||
|
||||
|
||||
def target_login(module, target, portal=None, port=None):
|
||||
def target_login(module, target, check_rc, portal=None, port=None):
|
||||
node_auth = module.params['node_auth']
|
||||
node_user = module.params['node_user']
|
||||
node_pass = module.params['node_pass']
|
||||
@@ -240,21 +239,22 @@ def target_login(module, target, portal=None, port=None):
|
||||
('node.session.auth.password', node_pass)]
|
||||
for (name, value) in params:
|
||||
cmd = [iscsiadm_cmd, '--mode', 'node', '--targetname', target, '--op=update', '--name', name, '--value', value]
|
||||
module.run_command(cmd, check_rc=True)
|
||||
module.run_command(cmd, check_rc=check_rc)
|
||||
|
||||
if node_user_in:
|
||||
params = [('node.session.auth.username_in', node_user_in),
|
||||
('node.session.auth.password_in', node_pass_in)]
|
||||
for (name, value) in params:
|
||||
cmd = '%s --mode node --targetname %s --op=update --name %s --value %s' % (iscsiadm_cmd, target, name, value)
|
||||
module.run_command(cmd, check_rc=True)
|
||||
module.run_command(cmd, check_rc=check_rc)
|
||||
|
||||
cmd = [iscsiadm_cmd, '--mode', 'node', '--targetname', target, '--login']
|
||||
if portal is not None and port is not None:
|
||||
cmd.append('--portal')
|
||||
cmd.append('%s:%s' % (portal, port))
|
||||
|
||||
module.run_command(cmd, check_rc=True)
|
||||
rc, out, err = module.run_command(cmd, check_rc=check_rc)
|
||||
return rc
|
||||
|
||||
|
||||
def target_logout(module, target):
|
||||
@@ -339,7 +339,10 @@ def main():
|
||||
),
|
||||
|
||||
required_together=[['node_user', 'node_pass'], ['node_user_in', 'node_pass_in']],
|
||||
required_if=[('discover', True, ['portal'])],
|
||||
required_if=[
|
||||
('discover', True, ['portal']),
|
||||
('auto_node_startup', True, ['target']),
|
||||
('auto_portal_startup', True, ['target'])],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@@ -369,6 +372,8 @@ def main():
|
||||
|
||||
# return json dict
|
||||
result = {'changed': False}
|
||||
login_to_all_nodes = False
|
||||
check_rc = True
|
||||
|
||||
if discover:
|
||||
if check:
|
||||
@@ -385,9 +390,10 @@ def main():
|
||||
if login is not None or automatic is not None:
|
||||
if target is None:
|
||||
if len(nodes) > 1:
|
||||
module.fail_json(msg="Need to specify a target")
|
||||
else:
|
||||
target = nodes[0]
|
||||
# Disable strict return code checking if there are multiple targets
|
||||
# That will allow to skip target where we have no rights to login
|
||||
login_to_all_nodes = True
|
||||
check_rc = False
|
||||
else:
|
||||
# check given target is in cache
|
||||
check_target = False
|
||||
@@ -402,26 +408,54 @@ def main():
|
||||
result['nodes'] = nodes
|
||||
|
||||
if login is not None:
|
||||
loggedon = target_loggedon(module, target, portal, port)
|
||||
if (login and loggedon) or (not login and not loggedon):
|
||||
result['changed'] |= False
|
||||
if login:
|
||||
result['devicenodes'] = target_device_node(target)
|
||||
elif not check:
|
||||
if login:
|
||||
target_login(module, target, portal, port)
|
||||
# give udev some time
|
||||
time.sleep(1)
|
||||
result['devicenodes'] = target_device_node(target)
|
||||
else:
|
||||
target_logout(module, target)
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
if login_to_all_nodes:
|
||||
result['devicenodes'] = []
|
||||
for index_target in nodes:
|
||||
loggedon = target_loggedon(module, index_target, portal, port)
|
||||
if (login and loggedon) or (not login and not loggedon):
|
||||
result['changed'] |= False
|
||||
if login:
|
||||
result['devicenodes'] += target_device_node(index_target)
|
||||
elif not check:
|
||||
if login:
|
||||
login_result = target_login(module, index_target, check_rc, portal, port)
|
||||
# give udev some time
|
||||
time.sleep(1)
|
||||
result['devicenodes'] += target_device_node(index_target)
|
||||
else:
|
||||
target_logout(module, index_target)
|
||||
# Check if there are multiple targets on a single portal and
|
||||
# do not mark the task changed if host could not login to one of them
|
||||
if len(nodes) > 1 and login_result == 24:
|
||||
result['changed'] |= False
|
||||
result['connection_changed'] = False
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
loggedon = target_loggedon(module, target, portal, port)
|
||||
if (login and loggedon) or (not login and not loggedon):
|
||||
result['changed'] |= False
|
||||
if login:
|
||||
result['devicenodes'] = target_device_node(target)
|
||||
elif not check:
|
||||
if login:
|
||||
target_login(module, target, portal, port)
|
||||
# give udev some time
|
||||
time.sleep(1)
|
||||
result['devicenodes'] = target_device_node(target)
|
||||
else:
|
||||
target_logout(module, target)
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
else:
|
||||
result['changed'] |= True
|
||||
result['connection_changed'] = True
|
||||
|
||||
if automatic is not None:
|
||||
if automatic is not None and not login_to_all_nodes:
|
||||
isauto = target_isauto(module, target)
|
||||
if (automatic and isauto) or (not automatic and not isauto):
|
||||
result['changed'] |= False
|
||||
@@ -437,7 +471,7 @@ def main():
|
||||
result['changed'] |= True
|
||||
result['automatic_changed'] = True
|
||||
|
||||
if automatic_portal is not None:
|
||||
if automatic_portal is not None and not login_to_all_nodes:
|
||||
isauto = target_isauto(module, target, portal, port)
|
||||
if (automatic_portal and isauto) or (not automatic_portal and not isauto):
|
||||
result['changed'] |= False
|
||||
|
||||
@@ -339,7 +339,7 @@ def main():
|
||||
pass
|
||||
|
||||
# Move tempfile to newfile
|
||||
module.atomic_move(nf.name, limits_conf)
|
||||
module.atomic_move(os.path.abspath(nf.name), os.path.abspath(limits_conf))
|
||||
|
||||
try:
|
||||
nf.close()
|
||||
|
||||
@@ -9,169 +9,150 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: pipx
|
||||
short_description: Manages applications installed with pipx
|
||||
version_added: 3.8.0
|
||||
description:
|
||||
- Manage Python applications installed in isolated virtualenvs using pipx.
|
||||
- Manage Python applications installed in isolated virtualenvs using pipx.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
- community.general.pipx
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
options:
|
||||
state:
|
||||
type: str
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
- install
|
||||
- install_all
|
||||
- uninstall
|
||||
- uninstall_all
|
||||
- inject
|
||||
- uninject
|
||||
- upgrade
|
||||
- upgrade_shared
|
||||
- upgrade_all
|
||||
- reinstall
|
||||
- reinstall_all
|
||||
- latest
|
||||
- pin
|
||||
- unpin
|
||||
default: install
|
||||
description:
|
||||
- Desired state for the application.
|
||||
- The states V(present) and V(absent) are aliases to V(install) and V(uninstall), respectively.
|
||||
- The state V(latest) is equivalent to executing the task twice, with state V(install) and then V(upgrade).
|
||||
It was added in community.general 5.5.0.
|
||||
- The states V(install_all), V(uninject), V(upgrade_shared), V(pin) and V(unpin) are only available in C(pipx>=1.6.0),
|
||||
make sure to have a compatible version when using this option. These states have been added in community.general 9.4.0.
|
||||
name:
|
||||
type: str
|
||||
description:
|
||||
- >
|
||||
The name of the application to be installed. It must to be a simple package name.
|
||||
For passing package specifications or installing from URLs or directories,
|
||||
please use the O(source) option.
|
||||
source:
|
||||
type: str
|
||||
description:
|
||||
- >
|
||||
If the application source, such as a package with version specifier, or an URL,
|
||||
directory or any other accepted specification. See C(pipx) documentation for more details.
|
||||
- When specified, the C(pipx) command will use O(source) instead of O(name).
|
||||
install_apps:
|
||||
description:
|
||||
- Add apps from the injected packages.
|
||||
- Only used when O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
install_deps:
|
||||
description:
|
||||
- Include applications of dependent packages.
|
||||
- Only used when O(state=install), O(state=latest), or O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
inject_packages:
|
||||
description:
|
||||
- Packages to be injected into an existing virtual environment.
|
||||
- Only used when O(state=inject).
|
||||
type: list
|
||||
elements: str
|
||||
force:
|
||||
description:
|
||||
- Force modification of the application's virtual environment. See C(pipx) for details.
|
||||
- Only used when O(state=install), O(state=upgrade), O(state=upgrade_all), O(state=latest), or O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
include_injected:
|
||||
description:
|
||||
- Upgrade the injected packages along with the application.
|
||||
- Only used when O(state=upgrade), O(state=upgrade_all), or O(state=latest).
|
||||
- This is used with O(state=upgrade) and O(state=latest) since community.general 6.6.0.
|
||||
type: bool
|
||||
default: false
|
||||
index_url:
|
||||
description:
|
||||
- Base URL of Python Package Index.
|
||||
- Only used when O(state=install), O(state=upgrade), O(state=latest), or O(state=inject).
|
||||
type: str
|
||||
python:
|
||||
description:
|
||||
- Python version to be used when creating the application virtual environment. Must be 3.6+.
|
||||
- Only used when O(state=install), O(state=latest), O(state=reinstall), or O(state=reinstall_all).
|
||||
type: str
|
||||
system_site_packages:
|
||||
description:
|
||||
- Give application virtual environment access to the system site-packages directory.
|
||||
- Only used when O(state=install) or O(state=latest).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.6.0
|
||||
executable:
|
||||
description:
|
||||
- Path to the C(pipx) installed in the system.
|
||||
- >
|
||||
If not specified, the module will use C(python -m pipx) to run the tool,
|
||||
using the same Python interpreter as ansible itself.
|
||||
type: path
|
||||
editable:
|
||||
description:
|
||||
- Install the project in editable mode.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 4.6.0
|
||||
pip_args:
|
||||
description:
|
||||
- Arbitrary arguments to pass directly to C(pip).
|
||||
type: str
|
||||
version_added: 4.6.0
|
||||
suffix:
|
||||
description:
|
||||
- Optional suffix for virtual environment and executable names.
|
||||
- "B(Warning:) C(pipx) documentation states this is an B(experimental) feature subject to change."
|
||||
type: str
|
||||
version_added: 9.3.0
|
||||
global:
|
||||
description:
|
||||
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
|
||||
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
|
||||
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 9.4.0
|
||||
spec_metadata:
|
||||
description:
|
||||
- Spec metadata file for O(state=install_all).
|
||||
- This content of the file is usually generated with C(pipx list --json), and it can be obtained with M(community.general.pipx_info)
|
||||
with O(community.general.pipx_info#module:include_raw=true) and obtaining the content from the RV(community.general.pipx_info#module:raw_output).
|
||||
type: path
|
||||
version_added: 9.4.0
|
||||
state:
|
||||
type: str
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
- install
|
||||
- install_all
|
||||
- uninstall
|
||||
- uninstall_all
|
||||
- inject
|
||||
- uninject
|
||||
- upgrade
|
||||
- upgrade_shared
|
||||
- upgrade_all
|
||||
- reinstall
|
||||
- reinstall_all
|
||||
- latest
|
||||
- pin
|
||||
- unpin
|
||||
default: install
|
||||
description:
|
||||
- Desired state for the application.
|
||||
- The states V(present) and V(absent) are aliases to V(install) and V(uninstall), respectively.
|
||||
- The state V(latest) is equivalent to executing the task twice, with state V(install) and then V(upgrade). It was added in community.general
|
||||
5.5.0.
|
||||
- The states V(install_all), V(uninject), V(upgrade_shared), V(pin) and V(unpin) are only available in C(pipx>=1.6.0), make sure to have a
|
||||
compatible version when using this option. These states have been added in community.general 9.4.0.
|
||||
name:
|
||||
type: str
|
||||
description:
|
||||
- The name of the application. In C(pipx) documentation it is also referred to as the name of the virtual environment where the application
|
||||
will be installed.
|
||||
- If O(name) is a simple package name without version specifiers, then that name is used as the Python package name to be installed.
|
||||
- Use O(source) for passing package specifications or installing from URLs or directories.
|
||||
source:
|
||||
type: str
|
||||
description:
|
||||
- Source for the package. This option is used when O(state=install) or O(state=latest), and it is ignored with other states.
|
||||
- Use O(source) when installing a Python package with version specifier, or from a local path, from a VCS URL or compressed file.
|
||||
- The value of this option is passed as-is to C(pipx).
|
||||
- O(name) is still required when using O(source) to establish the application name without fetching the package from a remote source.
|
||||
install_apps:
|
||||
description:
|
||||
- Add apps from the injected packages.
|
||||
- Only used when O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
install_deps:
|
||||
description:
|
||||
- Include applications of dependent packages.
|
||||
- Only used when O(state=install), O(state=latest), or O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
inject_packages:
|
||||
description:
|
||||
- Packages to be injected into an existing virtual environment.
|
||||
- Only used when O(state=inject).
|
||||
type: list
|
||||
elements: str
|
||||
force:
|
||||
description:
|
||||
- Force modification of the application's virtual environment. See C(pipx) for details.
|
||||
- Only used when O(state=install), O(state=upgrade), O(state=upgrade_all), O(state=latest), or O(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
include_injected:
|
||||
description:
|
||||
- Upgrade the injected packages along with the application.
|
||||
- Only used when O(state=upgrade), O(state=upgrade_all), or O(state=latest).
|
||||
- This is used with O(state=upgrade) and O(state=latest) since community.general 6.6.0.
|
||||
type: bool
|
||||
default: false
|
||||
index_url:
|
||||
description:
|
||||
- Base URL of Python Package Index.
|
||||
- Only used when O(state=install), O(state=upgrade), O(state=latest), or O(state=inject).
|
||||
type: str
|
||||
python:
|
||||
description:
|
||||
- Python version to be used when creating the application virtual environment. Must be 3.6+.
|
||||
- Only used when O(state=install), O(state=latest), O(state=reinstall), or O(state=reinstall_all).
|
||||
type: str
|
||||
system_site_packages:
|
||||
description:
|
||||
- Give application virtual environment access to the system site-packages directory.
|
||||
- Only used when O(state=install) or O(state=latest).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.6.0
|
||||
editable:
|
||||
description:
|
||||
- Install the project in editable mode.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 4.6.0
|
||||
pip_args:
|
||||
description:
|
||||
- Arbitrary arguments to pass directly to C(pip).
|
||||
type: str
|
||||
version_added: 4.6.0
|
||||
suffix:
|
||||
description:
|
||||
- Optional suffix for virtual environment and executable names.
|
||||
- "B(Warning:) C(pipx) documentation states this is an B(experimental) feature subject to change."
|
||||
type: str
|
||||
version_added: 9.3.0
|
||||
global:
|
||||
version_added: 9.4.0
|
||||
spec_metadata:
|
||||
description:
|
||||
- Spec metadata file for O(state=install_all).
|
||||
- This content of the file is usually generated with C(pipx list --json), and it can be obtained with M(community.general.pipx_info) with
|
||||
O(community.general.pipx_info#module:include_raw=true) and obtaining the content from the RV(community.general.pipx_info#module:raw_output).
|
||||
type: path
|
||||
version_added: 9.4.0
|
||||
notes:
|
||||
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
|
||||
- Please note that C(pipx) requires Python 3.6 or above.
|
||||
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
|
||||
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
|
||||
- >
|
||||
This module will honor C(pipx) environment variables such as but not limited to C(PIPX_HOME) and C(PIPX_BIN_DIR)
|
||||
passed using the R(environment Ansible keyword, playbooks_environment).
|
||||
- >
|
||||
This first implementation does not verify whether a specified version constraint has been installed or not.
|
||||
Hence, when using version operators, C(pipx) module will always try to execute the operation,
|
||||
even when the application was previously installed.
|
||||
This feature will be added in the future.
|
||||
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
|
||||
- >
|
||||
This first implementation does not verify whether a specified version constraint has been installed or not.
|
||||
Hence, when using version operators, C(pipx) module will always try to execute the operation,
|
||||
even when the application was previously installed.
|
||||
This feature will be added in the future.
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
'''
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Install tox
|
||||
community.general.pipx:
|
||||
name: tox
|
||||
@@ -200,20 +181,20 @@ EXAMPLES = '''
|
||||
- name: Install multiple packages from list
|
||||
vars:
|
||||
pipx_packages:
|
||||
- pycowsay
|
||||
- black
|
||||
- tox
|
||||
- pycowsay
|
||||
- black
|
||||
- tox
|
||||
community.general.pipx:
|
||||
name: "{{ item }}"
|
||||
state: latest
|
||||
with_items: "{{ pipx_packages }}"
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner
|
||||
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner, pipx_common_argspec
|
||||
|
||||
from ansible.module_utils.facts.compat import ansible_facts
|
||||
|
||||
@@ -240,13 +221,12 @@ class PipX(StateModuleHelper):
|
||||
index_url=dict(type='str'),
|
||||
python=dict(type='str'),
|
||||
system_site_packages=dict(type='bool', default=False),
|
||||
executable=dict(type='path'),
|
||||
editable=dict(type='bool', default=False),
|
||||
pip_args=dict(type='str'),
|
||||
suffix=dict(type='str'),
|
||||
spec_metadata=dict(type='path'),
|
||||
)
|
||||
argument_spec["global"] = dict(type='bool', default=False)
|
||||
argument_spec.update(pipx_common_argspec)
|
||||
|
||||
module = dict(
|
||||
argument_spec=argument_spec,
|
||||
@@ -328,7 +308,7 @@ class PipX(StateModuleHelper):
|
||||
def state_install_all(self):
|
||||
self.changed = True
|
||||
with self.runner('state global index_url force python system_site_packages editable pip_args spec_metadata', check_mode_skip=True) as ctx:
|
||||
ctx.run(name_source=[self.vars.name, self.vars.source])
|
||||
ctx.run()
|
||||
self._capture_results(ctx)
|
||||
|
||||
def state_upgrade(self):
|
||||
|
||||
@@ -9,66 +9,46 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: pipx_info
|
||||
short_description: Rretrieves information about applications installed with pipx
|
||||
version_added: 5.6.0
|
||||
description:
|
||||
- Retrieve details about Python applications installed in isolated virtualenvs using pipx.
|
||||
- Retrieve details about Python applications installed in isolated virtualenvs using pipx.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
- community.general.pipx
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of an application installed with C(pipx).
|
||||
type: str
|
||||
include_deps:
|
||||
description:
|
||||
- Include dependent packages in the output.
|
||||
type: bool
|
||||
default: false
|
||||
include_injected:
|
||||
description:
|
||||
- Include injected packages in the output.
|
||||
type: bool
|
||||
default: false
|
||||
include_raw:
|
||||
description:
|
||||
- Returns the raw output of C(pipx list --json).
|
||||
- The raw output is not affected by O(include_deps) or O(include_injected).
|
||||
type: bool
|
||||
default: false
|
||||
executable:
|
||||
description:
|
||||
- Path to the C(pipx) installed in the system.
|
||||
- >
|
||||
If not specified, the module will use C(python -m pipx) to run the tool,
|
||||
using the same Python interpreter as ansible itself.
|
||||
type: path
|
||||
global:
|
||||
description:
|
||||
- The module will pass the C(--global) argument to C(pipx), to execute actions in global scope.
|
||||
- The C(--global) is only available in C(pipx>=1.6.0), so make sure to have a compatible version when using this option.
|
||||
Moreover, a nasty bug with C(--global) was fixed in C(pipx==1.7.0), so it is strongly recommended you used that version or newer.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 9.3.0
|
||||
notes:
|
||||
- This module requires C(pipx) version 0.16.2.1 or above. From community.general 11.0.0 onwards, the module will require C(pipx>=1.7.0).
|
||||
- Please note that C(pipx) requires Python 3.6 or above.
|
||||
- This module does not install the C(pipx) python package, however that can be easily done with the module M(ansible.builtin.pip).
|
||||
- This module does not require C(pipx) to be in the shell C(PATH), but it must be loadable by Python as a module.
|
||||
- >
|
||||
This module will honor C(pipx) environment variables such as but not limited to E(PIPX_HOME) and E(PIPX_BIN_DIR)
|
||||
passed using the R(environment Ansible keyword, playbooks_environment).
|
||||
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
|
||||
name:
|
||||
description:
|
||||
- Name of an application installed with C(pipx).
|
||||
type: str
|
||||
include_deps:
|
||||
description:
|
||||
- Include dependent packages in the output.
|
||||
type: bool
|
||||
default: false
|
||||
include_injected:
|
||||
description:
|
||||
- Include injected packages in the output.
|
||||
type: bool
|
||||
default: false
|
||||
include_raw:
|
||||
description:
|
||||
- Returns the raw output of C(pipx list --json).
|
||||
- The raw output is not affected by O(include_deps) or O(include_injected).
|
||||
type: bool
|
||||
default: false
|
||||
global:
|
||||
version_added: 9.3.0
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
'''
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: retrieve all installed applications
|
||||
community.general.pipx_info: {}
|
||||
|
||||
@@ -86,9 +66,10 @@ EXAMPLES = '''
|
||||
community.general.pipx_info:
|
||||
name: ansible-lint
|
||||
include_deps: true
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
---
|
||||
application:
|
||||
description: The list of installed applications
|
||||
returned: success
|
||||
@@ -128,20 +109,13 @@ cmd:
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample: [
|
||||
"/usr/bin/python3.10",
|
||||
"-m",
|
||||
"pipx",
|
||||
"list",
|
||||
"--include-injected",
|
||||
"--json"
|
||||
]
|
||||
'''
|
||||
sample: ["/usr/bin/python3.10", "-m", "pipx", "list", "--include-injected", "--json"]
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner
|
||||
from ansible_collections.community.general.plugins.module_utils.pipx import pipx_runner, pipx_common_argspec
|
||||
|
||||
from ansible.module_utils.facts.compat import ansible_facts
|
||||
|
||||
@@ -153,9 +127,8 @@ class PipXInfo(ModuleHelper):
|
||||
include_deps=dict(type='bool', default=False),
|
||||
include_injected=dict(type='bool', default=False),
|
||||
include_raw=dict(type='bool', default=False),
|
||||
executable=dict(type='path'),
|
||||
)
|
||||
argument_spec["global"] = dict(type='bool', default=False)
|
||||
argument_spec.update(pipx_common_argspec)
|
||||
module = dict(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
|
||||
@@ -911,6 +911,7 @@ def main():
|
||||
'account_oemaccounttypes': module.params['oem_account_types'],
|
||||
'account_updatename': module.params['update_username'],
|
||||
'account_properties': module.params['account_properties'],
|
||||
'account_passwordchangerequired': None,
|
||||
}
|
||||
|
||||
# timeout
|
||||
@@ -983,10 +984,16 @@ def main():
|
||||
# execute only if we find an Account service resource
|
||||
result = rf_utils._find_accountservice_resource()
|
||||
if result['ret'] is False:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
|
||||
for command in command_list:
|
||||
result = ACCOUNTS_COMMANDS[command](user)
|
||||
# If a password change is required and the user is attempting to
|
||||
# modify their password, try to proceed.
|
||||
user['account_passwordchangerequired'] = rf_utils.check_password_change_required(result)
|
||||
if len(command_list) == 1 and command_list[0] == "UpdateUserPassword" and user['account_passwordchangerequired']:
|
||||
result = rf_utils.update_user_password(user)
|
||||
else:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
else:
|
||||
for command in command_list:
|
||||
result = ACCOUNTS_COMMANDS[command](user)
|
||||
|
||||
elif category == "Systems":
|
||||
# execute only if we find a System resource
|
||||
|
||||
@@ -145,6 +145,13 @@ options:
|
||||
type: str
|
||||
default: ''
|
||||
version_added: '7.3.0'
|
||||
storage_none_volume_deletion:
|
||||
required: false
|
||||
description:
|
||||
- Indicates if all non-RAID volumes are automatically deleted prior to creating the new volume.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: '9.5.0'
|
||||
volume_ids:
|
||||
required: false
|
||||
description:
|
||||
@@ -164,6 +171,9 @@ options:
|
||||
required: false
|
||||
description:
|
||||
- Setting dict of volume to be created.
|
||||
- If C(CapacityBytes) key is not specified in this dictionary, the size of
|
||||
the volume will be determined by the Redfish service. It is possible the
|
||||
size will not be the maximum available size.
|
||||
type: dict
|
||||
default: {}
|
||||
version_added: '7.5.0'
|
||||
@@ -415,6 +425,7 @@ def main():
|
||||
hostinterface_id=dict(),
|
||||
sessions_config=dict(type='dict', default={}),
|
||||
storage_subsystem_id=dict(type='str', default=''),
|
||||
storage_none_volume_deletion=dict(type='bool', default=False),
|
||||
volume_ids=dict(type='list', default=[], elements='str'),
|
||||
secure_boot_enable=dict(type='bool', default=True),
|
||||
volume_details=dict(type='dict', default={}),
|
||||
@@ -481,6 +492,7 @@ def main():
|
||||
# Volume creation options
|
||||
volume_details = module.params['volume_details']
|
||||
storage_subsystem_id = module.params['storage_subsystem_id']
|
||||
storage_none_volume_deletion = module.params['storage_none_volume_deletion']
|
||||
|
||||
# ciphers
|
||||
ciphers = module.params['ciphers']
|
||||
@@ -524,7 +536,7 @@ def main():
|
||||
elif command == "DeleteVolumes":
|
||||
result = rf_utils.delete_volumes(storage_subsystem_id, volume_ids)
|
||||
elif command == "CreateVolume":
|
||||
result = rf_utils.create_volume(volume_details, storage_subsystem_id)
|
||||
result = rf_utils.create_volume(volume_details, storage_subsystem_id, storage_none_volume_deletion)
|
||||
|
||||
elif category == "Manager":
|
||||
# execute only if we find a Manager service resource
|
||||
|
||||
@@ -260,8 +260,7 @@ def absent_strategy(api, wished_cn):
|
||||
changed = False
|
||||
|
||||
cn_list = api.fetch_all_resources("containers")
|
||||
cn_lookup = dict((cn["name"], cn)
|
||||
for cn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
if wished_cn["name"] not in cn_lookup:
|
||||
return changed, {}
|
||||
@@ -285,8 +284,7 @@ def present_strategy(api, wished_cn):
|
||||
changed = False
|
||||
|
||||
cn_list = api.fetch_all_resources("containers")
|
||||
cn_lookup = dict((cn["name"], cn)
|
||||
for cn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
payload_cn = payload_from_wished_cn(wished_cn)
|
||||
|
||||
|
||||
@@ -97,8 +97,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
def info_strategy(api, wished_cn):
|
||||
cn_list = api.fetch_all_resources("containers")
|
||||
cn_lookup = dict((fn["name"], fn)
|
||||
for fn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
if wished_cn["name"] not in cn_lookup:
|
||||
msg = "Error during container lookup: Unable to find container named '%s' in namespace '%s'" % (wished_cn["name"],
|
||||
|
||||
@@ -167,8 +167,7 @@ def absent_strategy(api, wished_cn):
|
||||
changed = False
|
||||
|
||||
cn_list = api.fetch_all_resources("namespaces")
|
||||
cn_lookup = dict((cn["name"], cn)
|
||||
for cn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
if wished_cn["name"] not in cn_lookup:
|
||||
return changed, {}
|
||||
@@ -192,8 +191,7 @@ def present_strategy(api, wished_cn):
|
||||
changed = False
|
||||
|
||||
cn_list = api.fetch_all_resources("namespaces")
|
||||
cn_lookup = dict((cn["name"], cn)
|
||||
for cn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
payload_cn = payload_from_wished_cn(wished_cn)
|
||||
|
||||
|
||||
@@ -88,8 +88,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
def info_strategy(api, wished_cn):
|
||||
cn_list = api.fetch_all_resources("namespaces")
|
||||
cn_lookup = dict((fn["name"], fn)
|
||||
for fn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
if wished_cn["name"] not in cn_lookup:
|
||||
msg = "Error during container namespace lookup: Unable to find container namespace named '%s' in project '%s'" % (wished_cn["name"],
|
||||
|
||||
@@ -150,8 +150,7 @@ def absent_strategy(api, wished_cr):
|
||||
changed = False
|
||||
|
||||
cr_list = api.fetch_all_resources("namespaces")
|
||||
cr_lookup = dict((cr["name"], cr)
|
||||
for cr in cr_list)
|
||||
cr_lookup = {cr["name"]: cr for cr in cr_list}
|
||||
|
||||
if wished_cr["name"] not in cr_lookup:
|
||||
return changed, {}
|
||||
@@ -175,8 +174,7 @@ def present_strategy(api, wished_cr):
|
||||
changed = False
|
||||
|
||||
cr_list = api.fetch_all_resources("namespaces")
|
||||
cr_lookup = dict((cr["name"], cr)
|
||||
for cr in cr_list)
|
||||
cr_lookup = {cr["name"]: cr for cr in cr_list}
|
||||
|
||||
payload_cr = payload_from_wished_cr(wished_cr)
|
||||
|
||||
|
||||
@@ -87,8 +87,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
def info_strategy(api, wished_cn):
|
||||
cn_list = api.fetch_all_resources("namespaces")
|
||||
cn_lookup = dict((fn["name"], fn)
|
||||
for fn in cn_list)
|
||||
cn_lookup = {cn["name"]: cn for cn in cn_list}
|
||||
|
||||
if wished_cn["name"] not in cn_lookup:
|
||||
msg = "Error during container registries lookup: Unable to find container registry named '%s' in project '%s'" % (wished_cn["name"],
|
||||
|
||||
@@ -245,8 +245,7 @@ def absent_strategy(api, wished_fn):
|
||||
changed = False
|
||||
|
||||
fn_list = api.fetch_all_resources("functions")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
if wished_fn["name"] not in fn_lookup:
|
||||
return changed, {}
|
||||
@@ -270,8 +269,7 @@ def present_strategy(api, wished_fn):
|
||||
changed = False
|
||||
|
||||
fn_list = api.fetch_all_resources("functions")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
payload_fn = payload_from_wished_fn(wished_fn)
|
||||
|
||||
|
||||
@@ -96,8 +96,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
def info_strategy(api, wished_fn):
|
||||
fn_list = api.fetch_all_resources("functions")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
if wished_fn["name"] not in fn_lookup:
|
||||
msg = "Error during function lookup: Unable to find function named '%s' in namespace '%s'" % (wished_fn["name"],
|
||||
|
||||
@@ -168,8 +168,7 @@ def absent_strategy(api, wished_fn):
|
||||
changed = False
|
||||
|
||||
fn_list = api.fetch_all_resources("namespaces")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
if wished_fn["name"] not in fn_lookup:
|
||||
return changed, {}
|
||||
@@ -193,8 +192,7 @@ def present_strategy(api, wished_fn):
|
||||
changed = False
|
||||
|
||||
fn_list = api.fetch_all_resources("namespaces")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
payload_fn = payload_from_wished_fn(wished_fn)
|
||||
|
||||
|
||||
@@ -88,8 +88,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
def info_strategy(api, wished_fn):
|
||||
fn_list = api.fetch_all_resources("namespaces")
|
||||
fn_lookup = dict((fn["name"], fn)
|
||||
for fn in fn_list)
|
||||
fn_lookup = {fn["name"]: fn for fn in fn_list}
|
||||
|
||||
if wished_fn["name"] not in fn_lookup:
|
||||
msg = "Error during function namespace lookup: Unable to find function namespace named '%s' in project '%s'" % (wished_fn["name"],
|
||||
|
||||
@@ -149,7 +149,7 @@ def core(module):
|
||||
|
||||
# Then we patch keys that are different
|
||||
for key, value in user_data.items():
|
||||
if key not in present_user_data or user_data[key] != present_user_data[key]:
|
||||
if key not in present_user_data or value != present_user_data[key]:
|
||||
|
||||
changed = True
|
||||
if compute_api.module.check_mode:
|
||||
|
||||
@@ -196,8 +196,7 @@ def main():
|
||||
else:
|
||||
obj['name'] = name
|
||||
|
||||
for k, v in data.items():
|
||||
obj[k] = v
|
||||
obj.update(data)
|
||||
diff = obj.diff()
|
||||
changed = obj.diff() != []
|
||||
if not module.check_mode:
|
||||
|
||||
@@ -21,11 +21,11 @@ description:
|
||||
server (UCS).
|
||||
It uses the python API of the UCS to create a new object or edit it."
|
||||
notes:
|
||||
- This module does B(not) work with Python 3.13 or newer. It uses the deprecated L(crypt Python module,
|
||||
https://docs.python.org/3.12/library/crypt.html) from the Python standard library, which was removed
|
||||
from Python 3.13.
|
||||
- This module requires the deprecated L(crypt Python module,
|
||||
https://docs.python.org/3.12/library/crypt.html) library which was removed from Python 3.13.
|
||||
For Python 3.13 or newer, you need to install L(legacycrypt, https://pypi.org/project/legacycrypt/).
|
||||
requirements:
|
||||
- Python 3.12 or earlier
|
||||
- legacycrypt (on Python 3.13 or newer)
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
@@ -350,6 +350,17 @@ else:
|
||||
HAS_CRYPT = True
|
||||
CRYPT_IMPORT_ERROR = None
|
||||
|
||||
try:
|
||||
import legacycrypt
|
||||
if not HAS_CRYPT:
|
||||
crypt = legacycrypt
|
||||
except ImportError:
|
||||
HAS_LEGACYCRYPT = False
|
||||
LEGACYCRYPT_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_LEGACYCRYPT = True
|
||||
LEGACYCRYPT_IMPORT_ERROR = None
|
||||
|
||||
|
||||
def main():
|
||||
expiry = date.strftime(date.today() + timedelta(days=365), "%Y-%m-%d")
|
||||
@@ -467,10 +478,10 @@ def main():
|
||||
])
|
||||
)
|
||||
|
||||
if not HAS_CRYPT:
|
||||
if not HAS_CRYPT and not HAS_LEGACYCRYPT:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib('crypt (part of Python 3.13 standard library)'),
|
||||
exception=CRYPT_IMPORT_ERROR,
|
||||
msg=missing_required_lib('crypt (part of standard library up to Python 3.12) or legacycrypt (PyPI)'),
|
||||
exception=LEGACYCRYPT_IMPORT_ERROR,
|
||||
)
|
||||
|
||||
username = module.params['username']
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: xfconf
|
||||
author:
|
||||
- "Joseph Benden (@jbenden)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
- "Joseph Benden (@jbenden)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
short_description: Edit XFCE4 Configurations
|
||||
description:
|
||||
- This module allows for the manipulation of Xfce 4 Configuration with the help of
|
||||
xfconf-query. Please see the xfconf-query(1) man page for more details.
|
||||
- This module allows for the manipulation of Xfce 4 Configuration with the help of C(xfconf-query).
|
||||
seealso:
|
||||
- name: xfconf-query(1) man page
|
||||
description: Manual page of the C(xfconf-query) tool at the XFCE documentation site.
|
||||
link: 'https://docs.xfce.org/xfce/xfconf/xfconf-query'
|
||||
- name: xfconf-query(1) man page
|
||||
description: Manual page of the C(xfconf-query) tool at the XFCE documentation site.
|
||||
link: 'https://docs.xfce.org/xfce/xfconf/xfconf-query'
|
||||
|
||||
- name: xfconf - Configuration Storage System
|
||||
description: XFCE documentation for the Xfconf configuration system.
|
||||
link: 'https://docs.xfce.org/xfce/xfconf/start'
|
||||
- name: xfconf - Configuration Storage System
|
||||
description: XFCE documentation for the Xfconf configuration system.
|
||||
link: 'https://docs.xfce.org/xfce/xfconf/start'
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
@@ -38,55 +38,50 @@ attributes:
|
||||
options:
|
||||
channel:
|
||||
description:
|
||||
- A Xfconf preference channel is a top-level tree key, inside of the
|
||||
Xfconf repository that corresponds to the location for which all
|
||||
application properties/keys are stored. See man xfconf-query(1).
|
||||
- A Xfconf preference channel is a top-level tree key, inside of the Xfconf repository that corresponds to the location for which all application
|
||||
properties/keys are stored. See man xfconf-query(1).
|
||||
required: true
|
||||
type: str
|
||||
property:
|
||||
description:
|
||||
- A Xfce preference key is an element in the Xfconf repository
|
||||
that corresponds to an application preference. See man xfconf-query(1).
|
||||
- A Xfce preference key is an element in the Xfconf repository that corresponds to an application preference. See man xfconf-query(1).
|
||||
required: true
|
||||
type: str
|
||||
value:
|
||||
description:
|
||||
- Preference properties typically have simple values such as strings,
|
||||
integers, or lists of strings and integers. See man xfconf-query(1).
|
||||
- Preference properties typically have simple values such as strings, integers, or lists of strings and integers. See man xfconf-query(1).
|
||||
type: list
|
||||
elements: raw
|
||||
value_type:
|
||||
description:
|
||||
- The type of value being set.
|
||||
- When providing more than one O(value_type), the length of the list must
|
||||
be equal to the length of O(value).
|
||||
- If only one O(value_type) is provided, but O(value) contains more than
|
||||
on element, that O(value_type) will be applied to all elements of O(value).
|
||||
- If the O(property) being set is an array and it can possibly have only one
|
||||
element in the array, then O(force_array=true) must be used to ensure
|
||||
that C(xfconf-query) will interpret the value as an array rather than a
|
||||
scalar.
|
||||
- Support for V(uchar), V(char), V(uint64), and V(int64) has been added in community.general 4.8.0.
|
||||
- The type of value being set.
|
||||
- When providing more than one O(value_type), the length of the list must be equal to the length of O(value).
|
||||
- If only one O(value_type) is provided, but O(value) contains more than on element, that O(value_type) will be applied to all elements of
|
||||
O(value).
|
||||
- If the O(property) being set is an array and it can possibly have only one element in the array, then O(force_array=true) must be used to
|
||||
ensure that C(xfconf-query) will interpret the value as an array rather than a scalar.
|
||||
- Support for V(uchar), V(char), V(uint64), and V(int64) has been added in community.general 4.8.0.
|
||||
type: list
|
||||
elements: str
|
||||
choices: [ string, int, double, bool, uint, uchar, char, uint64, int64, float ]
|
||||
choices: [string, int, double, bool, uint, uchar, char, uint64, int64, float]
|
||||
state:
|
||||
type: str
|
||||
description:
|
||||
- The action to take upon the property/value.
|
||||
- The state V(get) has been removed in community.general 5.0.0. Please use the module M(community.general.xfconf_info) instead.
|
||||
choices: [ present, absent ]
|
||||
- The action to take upon the property/value.
|
||||
- The state V(get) has been removed in community.general 5.0.0. Please use the module M(community.general.xfconf_info) instead.
|
||||
choices: [present, absent]
|
||||
default: "present"
|
||||
force_array:
|
||||
description:
|
||||
- Force array even if only one element.
|
||||
- Force array even if only one element.
|
||||
type: bool
|
||||
default: false
|
||||
aliases: ['array']
|
||||
version_added: 1.0.0
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Change the DPI to "192"
|
||||
xfconf:
|
||||
channel: "xsettings"
|
||||
@@ -110,60 +105,58 @@ EXAMPLES = """
|
||||
force_array: true
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
channel:
|
||||
description: The channel specified in the module parameters
|
||||
returned: success
|
||||
type: str
|
||||
sample: "xsettings"
|
||||
property:
|
||||
description: The property specified in the module parameters
|
||||
returned: success
|
||||
type: str
|
||||
sample: "/Xft/DPI"
|
||||
value_type:
|
||||
description:
|
||||
- The type of the value that was changed (V(none) for O(state=reset)).
|
||||
Either a single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"int" or ["str", "str", "str"]'
|
||||
value:
|
||||
description:
|
||||
- The value of the preference key after executing the module. Either a
|
||||
single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"192" or ["orange", "yellow", "violet"]'
|
||||
previous_value:
|
||||
description:
|
||||
- The value of the preference key before executing the module.
|
||||
Either a single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"96" or ["red", "blue", "green"]'
|
||||
cmd:
|
||||
description:
|
||||
- A list with the resulting C(xfconf-query) command executed by the module.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 5.4.0
|
||||
sample:
|
||||
- /usr/bin/xfconf-query
|
||||
- --channel
|
||||
- xfce4-panel
|
||||
- --property
|
||||
- /plugins/plugin-19/timezone
|
||||
- --create
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- Pacific/Auckland
|
||||
'''
|
||||
RETURN = """
|
||||
---
|
||||
channel:
|
||||
description: The channel specified in the module parameters
|
||||
returned: success
|
||||
type: str
|
||||
sample: "xsettings"
|
||||
property:
|
||||
description: The property specified in the module parameters
|
||||
returned: success
|
||||
type: str
|
||||
sample: "/Xft/DPI"
|
||||
value_type:
|
||||
description:
|
||||
- The type of the value that was changed (V(none) for O(state=reset)). Either a single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"int" or ["str", "str", "str"]'
|
||||
value:
|
||||
description:
|
||||
- The value of the preference key after executing the module. Either a single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"192" or ["orange", "yellow", "violet"]'
|
||||
previous_value:
|
||||
description:
|
||||
- The value of the preference key before executing the module. Either a single string value or a list of strings for array types.
|
||||
- This is a string or a list of strings.
|
||||
returned: success
|
||||
type: any
|
||||
sample: '"96" or ["red", "blue", "green"]'
|
||||
cmd:
|
||||
description:
|
||||
- A list with the resulting C(xfconf-query) command executed by the module.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 5.4.0
|
||||
sample:
|
||||
- /usr/bin/xfconf-query
|
||||
- --channel
|
||||
- xfce4-panel
|
||||
- --property
|
||||
- /plugins/plugin-19/timezone
|
||||
- --create
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- Pacific/Auckland
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.xfconf import xfconf_runner
|
||||
|
||||
@@ -7,17 +7,18 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: xfconf_info
|
||||
author:
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
- "Alexei Znamensky (@russoz)"
|
||||
short_description: Retrieve XFCE4 configurations
|
||||
version_added: 3.5.0
|
||||
description:
|
||||
- This module allows retrieving Xfce 4 configurations with the help of C(xfconf-query).
|
||||
- This module allows retrieving Xfce 4 configurations with the help of C(xfconf-query).
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
attributes:
|
||||
check_mode:
|
||||
version_added: 3.3.0
|
||||
@@ -40,10 +41,11 @@ options:
|
||||
- If not provided and a O(channel) is provided, then the module will list all available properties in that O(channel).
|
||||
type: str
|
||||
notes:
|
||||
- See man xfconf-query(1) for more details.
|
||||
'''
|
||||
- See man xfconf-query(1) for more details.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Get list of all available channels
|
||||
community.general.xfconf_info: {}
|
||||
register: result
|
||||
@@ -66,63 +68,64 @@ EXAMPLES = """
|
||||
register: result
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
channels:
|
||||
description:
|
||||
- List of available channels.
|
||||
- Returned when the module receives no parameter at all.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- xfce4-desktop
|
||||
- displays
|
||||
- xsettings
|
||||
- xfwm4
|
||||
properties:
|
||||
description:
|
||||
- List of available properties for a specific channel.
|
||||
- Returned by passing only the O(channel) parameter to the module.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- /Gdk/WindowScalingFactor
|
||||
- /Gtk/ButtonImages
|
||||
- /Gtk/CursorThemeSize
|
||||
- /Gtk/DecorationLayout
|
||||
- /Gtk/FontName
|
||||
- /Gtk/MenuImages
|
||||
- /Gtk/MonospaceFontName
|
||||
- /Net/DoubleClickTime
|
||||
- /Net/IconThemeName
|
||||
- /Net/ThemeName
|
||||
- /Xft/Antialias
|
||||
- /Xft/Hinting
|
||||
- /Xft/HintStyle
|
||||
- /Xft/RGBA
|
||||
is_array:
|
||||
description:
|
||||
- Flag indicating whether the property is an array or not.
|
||||
returned: success
|
||||
type: bool
|
||||
value:
|
||||
description:
|
||||
- The value of the property. Empty if the property is of array type.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Monospace 10
|
||||
value_array:
|
||||
description:
|
||||
- The array value of the property. Empty if the property is not of array type.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- Main
|
||||
- Work
|
||||
- Tmp
|
||||
'''
|
||||
RETURN = """
|
||||
---
|
||||
channels:
|
||||
description:
|
||||
- List of available channels.
|
||||
- Returned when the module receives no parameter at all.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- xfce4-desktop
|
||||
- displays
|
||||
- xsettings
|
||||
- xfwm4
|
||||
properties:
|
||||
description:
|
||||
- List of available properties for a specific channel.
|
||||
- Returned by passing only the O(channel) parameter to the module.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- /Gdk/WindowScalingFactor
|
||||
- /Gtk/ButtonImages
|
||||
- /Gtk/CursorThemeSize
|
||||
- /Gtk/DecorationLayout
|
||||
- /Gtk/FontName
|
||||
- /Gtk/MenuImages
|
||||
- /Gtk/MonospaceFontName
|
||||
- /Net/DoubleClickTime
|
||||
- /Net/IconThemeName
|
||||
- /Net/ThemeName
|
||||
- /Xft/Antialias
|
||||
- /Xft/Hinting
|
||||
- /Xft/HintStyle
|
||||
- /Xft/RGBA
|
||||
is_array:
|
||||
description:
|
||||
- Flag indicating whether the property is an array or not.
|
||||
returned: success
|
||||
type: bool
|
||||
value:
|
||||
description:
|
||||
- The value of the property. Empty if the property is of array type.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Monospace 10
|
||||
value_array:
|
||||
description:
|
||||
- The array value of the property. Empty if the property is not of array type.
|
||||
returned: success
|
||||
type: list
|
||||
elements: str
|
||||
sample:
|
||||
- Main
|
||||
- Work
|
||||
- Tmp
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
|
||||
from ansible_collections.community.general.plugins.module_utils.xfconf import xfconf_runner
|
||||
|
||||
21
tests/integration/targets/django_command/aliases
Normal file
21
tests/integration/targets/django_command/aliases
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
azp/posix/2
|
||||
skip/python2
|
||||
skip/freebsd
|
||||
skip/macos
|
||||
skip/osx
|
||||
skip/rhel8.2
|
||||
skip/rhel8.3
|
||||
skip/rhel8.4
|
||||
skip/rhel8.5
|
||||
skip/rhel8.6
|
||||
skip/rhel8.7
|
||||
skip/rhel8.8
|
||||
skip/rhel9.0
|
||||
skip/rhel9.1
|
||||
skip/rhel9.2
|
||||
skip/rhel9.3
|
||||
skip/rhel9.4
|
||||
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# single_app_project/core/settings.py
|
||||
SECRET_KEY = 'testtesttesttesttest'
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
# single_app_project/manage.py
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'single_app_project.core.settings')
|
||||
from django.core.management import execute_from_command_line
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'p1.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,133 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
"""
|
||||
Django settings for p1 project.
|
||||
|
||||
Generated by 'django-admin startproj' using Django 3.1.5.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '%g@gyhl*q@@g(_ab@t^76dao^#b9-v8mw^50)x_bv6wpl+mukj'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'p1.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'p1.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = '/tmp/django-static'
|
||||
|
||||
if "DJANGO_ANSIBLE_RAISE" in os.environ:
|
||||
raise ValueError("DJANGO_ANSIBLE_RAISE={0}".format(os.environ["DJANGO_ANSIBLE_RAISE"]))
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
"""p1 URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
8
tests/integration/targets/django_command/meta/main.yml
Normal file
8
tests/integration/targets/django_command/meta/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
dependencies:
|
||||
- setup_pkg_mgr
|
||||
- setup_os_pkg_name
|
||||
91
tests/integration/targets/django_command/tasks/main.yaml
Normal file
91
tests/integration/targets/django_command/tasks/main.yaml
Normal file
@@ -0,0 +1,91 @@
|
||||
# Test code for django_command module
|
||||
#
|
||||
# Copyright (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
- name: Create temporary test directory
|
||||
tempfile:
|
||||
state: directory
|
||||
suffix: .django_command
|
||||
register: tmp_django_root
|
||||
|
||||
- name: Install OS package virtualenv
|
||||
package:
|
||||
name: "{{ os_package_name.virtualenv }}"
|
||||
state: present
|
||||
|
||||
- name: Ensure virtualenv is created
|
||||
command: >-
|
||||
virtualenv {{ tmp_django_root.path }}/venv
|
||||
|
||||
- name: Update python package pip
|
||||
pip:
|
||||
name: pip
|
||||
state: latest
|
||||
virtualenv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Install python package django
|
||||
pip:
|
||||
name: django
|
||||
state: present
|
||||
virtualenv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Copy files
|
||||
copy:
|
||||
src: base_test/
|
||||
dest: "{{ tmp_django_root.path }}"
|
||||
mode: preserve
|
||||
|
||||
- name: Create project
|
||||
command:
|
||||
chdir: "{{ tmp_django_root.path }}/startproj"
|
||||
cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startproject test_django_command_1"
|
||||
|
||||
- name: Create app
|
||||
command:
|
||||
chdir: "{{ tmp_django_root.path }}/startproj"
|
||||
cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startapp app1"
|
||||
|
||||
- name: Check
|
||||
community.general.django_command:
|
||||
pythonpath: "{{ tmp_django_root.path }}/startproj/test_django_command_1"
|
||||
settings: test_django_command_1.settings
|
||||
command: check
|
||||
venv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Check simple_project
|
||||
community.general.django_command:
|
||||
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
|
||||
settings: p1.settings
|
||||
command: check
|
||||
venv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Check custom project
|
||||
community.general.django_command:
|
||||
pythonpath: "{{ tmp_django_root.path }}/1045-single-app-project/single_app_project"
|
||||
settings: core.settings
|
||||
command: check
|
||||
venv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Run collectstatic --noinput on simple project
|
||||
community.general.django_command:
|
||||
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
|
||||
settings: p1.settings
|
||||
command: collectstatic --noinput
|
||||
venv: "{{ tmp_django_root.path }}/venv"
|
||||
|
||||
- name: Trigger exception with environment variable
|
||||
community.general.django_command:
|
||||
pythonpath: "{{ tmp_django_root.path }}/simple_project/p1"
|
||||
settings: p1.settings
|
||||
command: collectstatic --noinput
|
||||
venv: "{{ tmp_django_root.path }}/venv"
|
||||
environment:
|
||||
DJANGO_ANSIBLE_RAISE: blah
|
||||
ignore_errors: true
|
||||
register: env_raise
|
||||
|
||||
- name: Check env variable reached manage.py
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'ValueError: DJANGO_ANSIBLE_RAISE=blah' in env_raise.msg"
|
||||
@@ -15,6 +15,11 @@
|
||||
ignore_errors: true
|
||||
|
||||
- block:
|
||||
- name: Install legacycrypt on Python 3.13+
|
||||
pip:
|
||||
name: legacycrypt
|
||||
when: ansible_python_version is version("3.13", ">=")
|
||||
|
||||
- name: Check and start systemd-homed service
|
||||
service:
|
||||
name: systemd-homed.service
|
||||
|
||||
7
tests/integration/targets/one_image/aliases
Normal file
7
tests/integration/targets/one_image/aliases
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
azp/generic/1
|
||||
cloud/opennebula
|
||||
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!
|
||||
210
tests/integration/targets/one_image/tasks/main.yml
Normal file
210
tests/integration/targets/one_image/tasks/main.yml
Normal file
@@ -0,0 +1,210 @@
|
||||
---
|
||||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Checks for existence
|
||||
- name: Make sure image is present by ID
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Assert that image is present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Make sure image is present by ID
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
name: my_image
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Assert that image is present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
# Updating an image
|
||||
- name: Clone image without name
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: cloned
|
||||
register: result
|
||||
|
||||
- name: Assert that image is cloned
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Clone image with name
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: renamed
|
||||
new_name: new_image
|
||||
register: result
|
||||
|
||||
- name: Assert that image is cloned
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Disable image
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
enabled: false
|
||||
register: result
|
||||
|
||||
- name: Assert that network is disabled
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Enable image
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
enabled: true
|
||||
register: result
|
||||
|
||||
- name: Assert that network is enabled
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Make image persistent
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
persistent: true
|
||||
register: result
|
||||
|
||||
- name: Assert that network is persistent
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Make image non-persistent
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
persistent: false
|
||||
register: result
|
||||
|
||||
- name: Assert that network is non-persistent
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Testing idempotence using the same tasks
|
||||
- name: Make image non-persistent
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
persistent: false
|
||||
enabled: true
|
||||
register: result
|
||||
|
||||
- name: Assert that network not changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
# Delete images
|
||||
- name: Deleting non-existing image
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 228
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- name: Assert that network not changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Delete an existing image
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- name: Assert that image was deleted
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Trying to run with wrong arguments
|
||||
- name: Try to use name and ID at the same time
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
name: name
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Assert that task failed
|
||||
assert:
|
||||
that:
|
||||
- result is failed
|
||||
|
||||
- name: Try to rename image without specifying new name
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: rename
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Assert that task failed
|
||||
assert:
|
||||
that:
|
||||
- result is failed
|
||||
|
||||
- name: Try to rename image without specifying new name
|
||||
one_image:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: rename
|
||||
register: result
|
||||
ignore_errors: true
|
||||
7
tests/integration/targets/one_image_info/aliases
Normal file
7
tests/integration/targets/one_image_info/aliases
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
azp/generic/1
|
||||
cloud/opennebula
|
||||
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!
|
||||
192
tests/integration/targets/one_image_info/tasks/main.yml
Normal file
192
tests/integration/targets/one_image_info/tasks/main.yml
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Checks for existence
|
||||
- name: Get info by ID
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
register: result
|
||||
|
||||
- name: Assert that image is present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Get info by list of ID
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
ids:
|
||||
- 2
|
||||
- 2
|
||||
- 8
|
||||
register: result
|
||||
|
||||
- name: Assert that image is present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Get info by list of ID
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
name: somename
|
||||
register: result
|
||||
|
||||
- name: Assert that image is present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Gather all info
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
register: result
|
||||
|
||||
- name: Assert that images are present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Gather info by regex
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
name: '~my_image-[0-9].*'
|
||||
register: result
|
||||
|
||||
- name: Assert that images are present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Gather info by regex and ignore upper/lower cases
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
name: '~*my_image-[0-9].*'
|
||||
register: result
|
||||
|
||||
- name: Assert that images are present
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
# Updating an image
|
||||
- name: Clone image without name
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: cloned
|
||||
register: result
|
||||
|
||||
- name: Assert that image is cloned
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Clone image with name
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
state: renamed
|
||||
new_name: new_image
|
||||
register: result
|
||||
|
||||
- name: Assert that image is cloned
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Disable image
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
enabled: false
|
||||
register: result
|
||||
|
||||
- name: Assert that network is disabled
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Enable image
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
enabled: true
|
||||
register: result
|
||||
|
||||
- name: Assert that network is enabled
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Make image persistent
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
persistent: true
|
||||
register: result
|
||||
|
||||
- name: Assert that network is persistent
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Make image non-persistent
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
persistent: false
|
||||
register: result
|
||||
|
||||
- name: Assert that network is non-persistent
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Testing errors
|
||||
- name: Try to use name and ID a the same time
|
||||
one_image_info:
|
||||
api_url: "{{ opennebula_url }}"
|
||||
api_username: "{{ opennebula_username }}"
|
||||
api_password: "{{ opennebula_password }}"
|
||||
id: 0
|
||||
name: somename
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Assert that network not changed
|
||||
assert:
|
||||
that:
|
||||
- result is failed
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user