mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-01 02:43:16 +00:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a81760d47 | ||
|
|
c3db6343e5 | ||
|
|
e87c2c9eb4 | ||
|
|
90a1743acf | ||
|
|
eb6ef5ae2e | ||
|
|
89dd500159 | ||
|
|
9bab144d06 | ||
|
|
31eddc0ffe | ||
|
|
bc64c4035e | ||
|
|
3ed65a0a37 | ||
|
|
93008fd41c | ||
|
|
1462ed0b4a | ||
|
|
139fcdba88 | ||
|
|
e9b8692025 | ||
|
|
3d8049190c | ||
|
|
54025a2efc | ||
|
|
94015c2096 | ||
|
|
b1a711633b | ||
|
|
79d15d526a | ||
|
|
3aeaab2708 | ||
|
|
470f4e8c02 | ||
|
|
f7f79defab | ||
|
|
d4d1c847cf | ||
|
|
044c706d64 | ||
|
|
e22667b72f | ||
|
|
4fc0c9a6d8 | ||
|
|
93650233e4 | ||
|
|
a41d1851a5 | ||
|
|
bd4da7f2c0 | ||
|
|
bf853f6f35 | ||
|
|
5dc34829c4 | ||
|
|
061b861211 | ||
|
|
e4ce977079 | ||
|
|
4776ee20e3 | ||
|
|
fdfcd15960 | ||
|
|
b934e06569 | ||
|
|
5589bcb659 | ||
|
|
41af1c3693 | ||
|
|
3802d54922 | ||
|
|
ff4aff0bef | ||
|
|
86b19a2bf4 | ||
|
|
1fc53eea22 | ||
|
|
3ecbadf694 | ||
|
|
2181c2b090 | ||
|
|
0c295d4f61 | ||
|
|
db451bf68a | ||
|
|
91095240f4 | ||
|
|
e92908b66e | ||
|
|
ef09ea519c | ||
|
|
611e024550 | ||
|
|
94b4034fd2 | ||
|
|
1bfdee0830 | ||
|
|
9350954aa3 | ||
|
|
2f05cd3330 | ||
|
|
27906ca76b | ||
|
|
278b0607f5 | ||
|
|
00d1160b56 | ||
|
|
4266163c13 | ||
|
|
ec7f885e2f | ||
|
|
4f71c9384e | ||
|
|
53e5f51e57 | ||
|
|
83ff925417 | ||
|
|
8ff611089b | ||
|
|
4def87bc53 | ||
|
|
3d70bfa1e4 | ||
|
|
554ec94110 | ||
|
|
ab4f96105c | ||
|
|
d8cf32e6c4 | ||
|
|
ead9524dc3 | ||
|
|
5d5d403415 | ||
|
|
d483fd9482 | ||
|
|
8da9cf3276 | ||
|
|
3c5c3a0113 | ||
|
|
7def57a71f | ||
|
|
e5930aabcb | ||
|
|
48bfba435f | ||
|
|
9740b76f3c | ||
|
|
24cf561135 | ||
|
|
61324ed9eb | ||
|
|
99336ba5fe | ||
|
|
9d99ccef2d | ||
|
|
a146eb3118 | ||
|
|
c7f7bd6050 | ||
|
|
54099d77ff | ||
|
|
ee07d8320a | ||
|
|
0729f0c262 | ||
|
|
57cd48f3cf | ||
|
|
afd2151672 | ||
|
|
ea9b272043 | ||
|
|
60addb332d | ||
|
|
1ade62c5bc | ||
|
|
7c8cc96d8b | ||
|
|
ca177a0ceb | ||
|
|
c0e769e5f5 | ||
|
|
585dbc3171 | ||
|
|
b400491ef3 | ||
|
|
490baed566 | ||
|
|
811c4a304a | ||
|
|
c0fde76b79 | ||
|
|
16c7615b82 | ||
|
|
474364c862 | ||
|
|
1da5f7dc54 | ||
|
|
559c914e36 | ||
|
|
91cca4ae49 | ||
|
|
82a9db9738 | ||
|
|
3fd84d71b8 | ||
|
|
a17124f3c4 | ||
|
|
efc2cbf840 | ||
|
|
aa136aca4c | ||
|
|
a1ca89b058 | ||
|
|
dd70419d18 | ||
|
|
ef5ac023cf | ||
|
|
8bc5494ad5 | ||
|
|
d95a821d5b | ||
|
|
b7697fe3de | ||
|
|
16e05ab5f3 | ||
|
|
5cf7ce705a | ||
|
|
c8b8668212 | ||
|
|
2d450a5a36 | ||
|
|
e08412c345 | ||
|
|
c355f93d62 | ||
|
|
80206b5a53 | ||
|
|
e978fd4d61 | ||
|
|
6fc8492ecf | ||
|
|
95beb452a8 | ||
|
|
c10e9e2650 | ||
|
|
ac35bf4acb | ||
|
|
50b9855ace | ||
|
|
2ab26db197 | ||
|
|
5fcf5d0c8b | ||
|
|
0f0ad6b6d1 | ||
|
|
95f3109ddc | ||
|
|
6037c5d1e6 | ||
|
|
a70d9773dd | ||
|
|
bc50b48205 | ||
|
|
02e6a8608f | ||
|
|
82f4b51873 | ||
|
|
589e8fd5e1 | ||
|
|
58f74b96ef | ||
|
|
1489c080a7 | ||
|
|
6f845f61f0 | ||
|
|
c17f5ff3e8 | ||
|
|
ff21afb227 | ||
|
|
c1d6e5c3c2 | ||
|
|
377b5d4ccd | ||
|
|
f3f7b2776f | ||
|
|
df8bfad9b9 | ||
|
|
8a231e4b36 | ||
|
|
671f850069 | ||
|
|
2fa36592e4 | ||
|
|
51d704bfe3 | ||
|
|
2b0e335752 | ||
|
|
cc28cde3a2 | ||
|
|
2d616bf4d1 | ||
|
|
25d9ab8dcd | ||
|
|
9abda18071 | ||
|
|
406fa12142 | ||
|
|
caaebb38e7 | ||
|
|
2bc74f4f04 | ||
|
|
e1e89f7735 | ||
|
|
efedd0d6e2 | ||
|
|
8079aea1ee | ||
|
|
ee7fdf5f8c | ||
|
|
ced1baad63 | ||
|
|
a0d4ee4fc1 | ||
|
|
d930c8d877 | ||
|
|
352e91a389 | ||
|
|
4b7554445b | ||
|
|
3a456a645d | ||
|
|
6f4580ebd9 | ||
|
|
8d83557e52 | ||
|
|
5ebd980e26 | ||
|
|
17447d2a84 | ||
|
|
ffee01cd9c | ||
|
|
38b4e316ae | ||
|
|
b52a6f3611 | ||
|
|
2435fb3f30 | ||
|
|
d6d9f84b0a | ||
|
|
4b04e3cc32 | ||
|
|
c681249364 | ||
|
|
57a4195b0d | ||
|
|
41a23f093d | ||
|
|
0bd085714f | ||
|
|
a4be229f67 | ||
|
|
9c4487ebc5 | ||
|
|
09ea441316 | ||
|
|
fef6abc8c8 | ||
|
|
618e567377 | ||
|
|
246abffce5 | ||
|
|
076ebb4b2d | ||
|
|
4948b521a3 | ||
|
|
e9ec26ff1b | ||
|
|
72d4476813 | ||
|
|
e96bfd07b4 | ||
|
|
c6d0419460 | ||
|
|
081b4068a0 | ||
|
|
8fba9ca751 | ||
|
|
fad4c2d956 | ||
|
|
6065dd0f18 | ||
|
|
a411ff5ea8 | ||
|
|
42b245eabf | ||
|
|
9a676bb88f | ||
|
|
cd26aec2f3 | ||
|
|
e9327a0464 |
@@ -29,14 +29,14 @@ schedules:
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-7
|
||||
- stable-6
|
||||
- stable-5
|
||||
- cron: 0 11 * * 0
|
||||
displayName: Weekly (old stable branches)
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-5
|
||||
- stable-4
|
||||
|
||||
variables:
|
||||
- name: checkoutPath
|
||||
@@ -73,19 +73,6 @@ stages:
|
||||
- test: 3
|
||||
- test: 4
|
||||
- test: extra
|
||||
- stage: Sanity_2_15
|
||||
displayName: Sanity 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.15/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_14
|
||||
displayName: Sanity 2.14
|
||||
dependsOn: []
|
||||
@@ -143,16 +130,6 @@ stages:
|
||||
- test: 3.9
|
||||
- test: '3.10'
|
||||
- test: '3.11'
|
||||
- stage: Units_2_15
|
||||
displayName: Units 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.15/units/{0}/1
|
||||
targets:
|
||||
- test: "3.10"
|
||||
- stage: Units_2_14
|
||||
displayName: Units 2.14
|
||||
dependsOn: []
|
||||
@@ -162,6 +139,7 @@ stages:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.14/units/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.9
|
||||
- stage: Units_2_13
|
||||
displayName: Units 2.13
|
||||
@@ -213,30 +191,16 @@ stages:
|
||||
parameters:
|
||||
testFormat: devel/{0}
|
||||
targets:
|
||||
- name: macOS 13.2
|
||||
test: macos/13.2
|
||||
- name: RHEL 9.1
|
||||
test: rhel/9.1
|
||||
- name: FreeBSD 13.2
|
||||
test: freebsd/13.2
|
||||
- name: FreeBSD 12.4
|
||||
test: freebsd/12.4
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_15
|
||||
displayName: Remote 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.15/{0}
|
||||
targets:
|
||||
- name: macOS 12.0
|
||||
test: macos/12.0
|
||||
- name: RHEL 7.9
|
||||
test: rhel/7.9
|
||||
- name: RHEL 9.1
|
||||
test: rhel/9.1
|
||||
- name: FreeBSD 13.1
|
||||
test: freebsd/13.1
|
||||
- name: FreeBSD 12.4
|
||||
test: freebsd/12.4
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -301,6 +265,8 @@ stages:
|
||||
parameters:
|
||||
testFormat: devel/linux/{0}
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 37
|
||||
test: fedora37
|
||||
- name: openSUSE 15
|
||||
@@ -315,20 +281,6 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_15
|
||||
displayName: Docker 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.15/linux/{0}
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_14
|
||||
displayName: Docker 2.14
|
||||
dependsOn: []
|
||||
@@ -392,7 +344,7 @@ stages:
|
||||
- name: Debian Bullseye
|
||||
test: debian-bullseye/3.9
|
||||
- name: ArchLinux
|
||||
test: archlinux/3.11
|
||||
test: archlinux/3.10
|
||||
- name: CentOS Stream 8
|
||||
test: centos-stream8/3.9
|
||||
groups:
|
||||
@@ -412,16 +364,6 @@ stages:
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: '3.11'
|
||||
- stage: Generic_2_15
|
||||
displayName: Generic 2.15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.15/generic/{0}/1
|
||||
targets:
|
||||
- test: 3.9
|
||||
- stage: Generic_2_14
|
||||
displayName: Generic 2.14
|
||||
dependsOn: []
|
||||
@@ -460,29 +402,24 @@ stages:
|
||||
- Sanity_2_12
|
||||
- Sanity_2_13
|
||||
- Sanity_2_14
|
||||
- Sanity_2_15
|
||||
- Units_devel
|
||||
- Units_2_12
|
||||
- Units_2_13
|
||||
- Units_2_14
|
||||
- Units_2_15
|
||||
- Remote_devel_extra_vms
|
||||
- Remote_devel
|
||||
- Remote_2_12
|
||||
- Remote_2_13
|
||||
- Remote_2_14
|
||||
- Remote_2_15
|
||||
- Docker_devel
|
||||
- Docker_2_12
|
||||
- Docker_2_13
|
||||
- Docker_2_14
|
||||
- Docker_2_15
|
||||
- Docker_community_devel
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - Generic_devel
|
||||
# - Generic_2_12
|
||||
# - Generic_2_13
|
||||
# - Generic_2_14
|
||||
# - Generic_2_15
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
||||
39
.github/BOTMETA.yml
vendored
39
.github/BOTMETA.yml
vendored
@@ -241,8 +241,6 @@ files:
|
||||
$lookups/manifold.py:
|
||||
labels: manifold
|
||||
maintainers: galanoff
|
||||
$lookups/merge_variables.py:
|
||||
maintainers: rlenferink m-a-r-k-e
|
||||
$lookups/onepass:
|
||||
labels: onepassword
|
||||
maintainers: samdoran
|
||||
@@ -267,8 +265,6 @@ files:
|
||||
maintainers: delineaKrehl tylerezimmerman
|
||||
$module_utils/:
|
||||
labels: module_utils
|
||||
$module_utils/btrfs.py:
|
||||
maintainers: gnfzdz
|
||||
$module_utils/deps.py:
|
||||
maintainers: russoz
|
||||
$module_utils/gconftool2.py:
|
||||
@@ -298,6 +294,7 @@ files:
|
||||
maintainers: $team_manageiq
|
||||
$module_utils/memset.py:
|
||||
labels: cloud memset
|
||||
maintainers: glitchcrab
|
||||
$module_utils/mh/:
|
||||
labels: module_helper
|
||||
maintainers: russoz
|
||||
@@ -397,8 +394,6 @@ files:
|
||||
maintainers: catcombo
|
||||
$modules/bower.py:
|
||||
maintainers: mwarkentin
|
||||
$modules/btrfs_:
|
||||
maintainers: gnfzdz
|
||||
$modules/bundler.py:
|
||||
maintainers: thoiberg
|
||||
$modules/bzr.py:
|
||||
@@ -539,6 +534,8 @@ files:
|
||||
maintainers: zimbatm
|
||||
$modules/gunicorn.py:
|
||||
maintainers: agmezr
|
||||
$modules/hana_query.py:
|
||||
maintainers: rainerleber
|
||||
$modules/haproxy.py:
|
||||
maintainers: ravibhure Normo
|
||||
$modules/heroku_collaborator.py:
|
||||
@@ -591,7 +588,7 @@ files:
|
||||
ignore: jose-delarosa
|
||||
maintainers: $team_redfish
|
||||
$modules/ilo_:
|
||||
ignore: jose-delarosa varini-hp
|
||||
ignore: jose-delarosa
|
||||
maintainers: $team_redfish
|
||||
$modules/imc_rest.py:
|
||||
labels: cisco
|
||||
@@ -623,8 +620,6 @@ files:
|
||||
maintainers: bregman-arie
|
||||
$modules/ipa_:
|
||||
maintainers: $team_ipa
|
||||
$modules/ipbase_info.py:
|
||||
maintainers: dominikkukacka
|
||||
$modules/ipa_pwpolicy.py:
|
||||
maintainers: adralioh
|
||||
$modules/ipa_service.py:
|
||||
@@ -672,22 +667,16 @@ files:
|
||||
ignore: DWSR
|
||||
labels: jira
|
||||
maintainers: Slezhuk tarka pertoft
|
||||
$modules/kdeconfig.py:
|
||||
maintainers: smeso
|
||||
$modules/kernel_blacklist.py:
|
||||
maintainers: matze
|
||||
$modules/keycloak_:
|
||||
maintainers: $team_keycloak
|
||||
$modules/keycloak_authentication.py:
|
||||
maintainers: elfelip Gaetan2907
|
||||
$modules/keycloak_authz_authorization_scope.py:
|
||||
maintainers: mattock
|
||||
$modules/keycloak_client_rolemapping.py:
|
||||
maintainers: Gaetan2907
|
||||
$modules/keycloak_clientscope.py:
|
||||
maintainers: Gaetan2907
|
||||
$modules/keycloak_clientscope_type.py:
|
||||
maintainers: simonpahl
|
||||
$modules/keycloak_clientsecret_info.py:
|
||||
maintainers: fynncfchen johncant
|
||||
$modules/keycloak_clientsecret_regenerate.py:
|
||||
@@ -796,7 +785,7 @@ files:
|
||||
labels: maven_artifact
|
||||
maintainers: tumbl3w33d turb
|
||||
$modules/memset_:
|
||||
ignore: glitchcrab
|
||||
maintainers: glitchcrab
|
||||
$modules/mksysb.py:
|
||||
labels: aix mksysb
|
||||
maintainers: $team_aix
|
||||
@@ -998,7 +987,7 @@ files:
|
||||
maintainers: sysadmind
|
||||
$modules/puppet.py:
|
||||
labels: puppet
|
||||
maintainers: emonty
|
||||
maintainers: nibalizer emonty
|
||||
$modules/pushbullet.py:
|
||||
maintainers: willybarro
|
||||
$modules/pushover.py:
|
||||
@@ -1053,8 +1042,7 @@ files:
|
||||
maintainers: $team_redfish TSKushal
|
||||
$modules/redhat_subscription.py:
|
||||
labels: redhat_subscription
|
||||
maintainers: $team_rhsm
|
||||
ignore: barnabycourt alikins kahowell
|
||||
maintainers: barnabycourt alikins kahowell
|
||||
$modules/redis.py:
|
||||
maintainers: slok
|
||||
$modules/redis_data.py:
|
||||
@@ -1077,9 +1065,9 @@ files:
|
||||
labels: rhn_register
|
||||
maintainers: jlaska $team_rhn
|
||||
$modules/rhsm_release.py:
|
||||
maintainers: seandst $team_rhsm
|
||||
maintainers: seandst
|
||||
$modules/rhsm_repository.py:
|
||||
maintainers: giovannisciortino $team_rhsm
|
||||
maintainers: giovannisciortino
|
||||
$modules/riak.py:
|
||||
maintainers: drewkerrigan jsmartin
|
||||
$modules/rocketchat.py:
|
||||
@@ -1100,6 +1088,10 @@ files:
|
||||
maintainers: nerzhul
|
||||
$modules/runit.py:
|
||||
maintainers: jsumners
|
||||
$modules/sap_task_list_execute:
|
||||
maintainers: rainerleber
|
||||
$modules/sapcar_extract.py:
|
||||
maintainers: RainerLeber
|
||||
$modules/say.py:
|
||||
maintainers: $team_ansible_core
|
||||
ignore: mpdehaan
|
||||
@@ -1328,7 +1320,7 @@ files:
|
||||
labels: m:xml xml
|
||||
maintainers: dagwieers magnus919 tbielawa cmprescott sm4rk0
|
||||
$modules/yarn.py:
|
||||
ignore: chrishoffman verkaufer
|
||||
maintainers: chrishoffman verkaufer
|
||||
$modules/yum_versionlock.py:
|
||||
maintainers: gyptazy aminvakil
|
||||
$modules/zfs:
|
||||
@@ -1394,7 +1386,7 @@ macros:
|
||||
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
|
||||
team_ipa: Akasurde Nosmoht fxfitz justchris1
|
||||
team_jboss: Wolfant jairojunior wbrefvem
|
||||
team_keycloak: eikef ndclt mattock
|
||||
team_keycloak: eikef ndclt
|
||||
team_linode: InTheCloudDan decentral1se displague rmcintosh Charliekenney23 LBGarber
|
||||
team_macos: Akasurde kyleabenson martinm82 danieljaouen indrajitr
|
||||
team_manageiq: abellotti cben gtanzillo yaacov zgalor dkorn evertmulder
|
||||
@@ -1404,7 +1396,6 @@ macros:
|
||||
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
|
||||
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt
|
||||
team_rhn: FlossWare alikins barnabycourt vritant
|
||||
team_rhsm: cnsnyder ptoscano
|
||||
team_scaleway: remyleone abarbare
|
||||
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
|
||||
team_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -47,7 +47,7 @@ body:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
*use your best guess if unsure*.
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -46,8 +46,8 @@ body:
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the file, module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
Write the short name of the rst file, module, plugin, task or
|
||||
feature below, *use your best guess if unsure*.
|
||||
placeholder: mysql_user
|
||||
validations:
|
||||
required: true
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -42,8 +42,8 @@ body:
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module or plugin, or which other part(s) of the collection this feature affects.
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
Write the short name of the module, plugin, task or feature below,
|
||||
*use your best guess if unsure*.
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
32
.github/pull_request_template.md
vendored
32
.github/pull_request_template.md
vendored
@@ -1,32 +0,0 @@
|
||||
##### SUMMARY
|
||||
<!--- Describe the change below, including rationale and design decisions -->
|
||||
|
||||
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
|
||||
|
||||
<!--- Please do not forget to include a changelog fragment:
|
||||
https://docs.ansible.com/ansible/devel/community/collection_development_process.html#creating-changelog-fragments
|
||||
No need to include one for docs-only or test-only PR, and for new plugin/module PRs.
|
||||
Read about more details in CONTRIBUTING.md.
|
||||
-->
|
||||
|
||||
##### ISSUE TYPE
|
||||
<!--- Pick one or more below and delete the rest.
|
||||
'Test Pull Request' is for PRs that add/extend tests without code changes. -->
|
||||
- Bugfix Pull Request
|
||||
- Docs Pull Request
|
||||
- Feature Pull Request
|
||||
- New Module/Plugin Pull Request
|
||||
- Refactoring Pull Request
|
||||
- Test Pull Request
|
||||
|
||||
##### COMPONENT NAME
|
||||
<!--- Write the SHORT NAME of the module, plugin, task or feature below. -->
|
||||
|
||||
##### ADDITIONAL INFORMATION
|
||||
<!--- Include additional information to help people understand the change here -->
|
||||
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
|
||||
|
||||
<!--- Paste verbatim command output below, e.g. before and after your change -->
|
||||
```paste below
|
||||
|
||||
```
|
||||
3
.github/pull_request_template.md.license
vendored
3
.github/pull_request_template.md.license
vendored
@@ -1,3 +0,0 @@
|
||||
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
|
||||
4
.github/workflows/ansible-test.yml
vendored
4
.github/workflows/ansible-test.yml
vendored
@@ -14,9 +14,9 @@ on:
|
||||
- main
|
||||
- stable-*
|
||||
pull_request:
|
||||
# Run EOL CI once per day (at 08:00 UTC)
|
||||
# Run EOL CI once per day (at 10:00 UTC)
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
- cron: '0 10 * * *'
|
||||
|
||||
concurrency:
|
||||
# Make sure there is at most one active run per PR, but do not cancel any non-PR runs
|
||||
|
||||
31
.github/workflows/codeql-analysis.yml
vendored
31
.github/workflows/codeql-analysis.yml
vendored
@@ -8,7 +8,6 @@ name: "Code scanning - action"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '26 19 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -25,12 +24,38 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: python
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
721
CHANGELOG.rst
721
CHANGELOG.rst
@@ -4,326 +4,537 @@ Community General Release Notes
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
This changelog describes changes after version 6.0.0.
|
||||
This changelog describes changes after version 5.0.0.
|
||||
|
||||
v7.0.0
|
||||
v6.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is release 7.0.0 of ``community.general``, released on 2023-05-09.
|
||||
Regular feature and bugfix release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- dnsimple - set custom User-Agent for API requests to DNSimple (https://github.com/ansible-collections/community.general/pull/5927).
|
||||
- flatpak_remote - add new boolean option ``enabled``. It controls, whether the remote is enabled or not (https://github.com/ansible-collections/community.general/pull/5926).
|
||||
- gitlab_project - add ``releases_access_level``, ``environments_access_level``, ``feature_flags_access_level``, ``infrastructure_access_level``, ``monitor_access_level``, and ``security_and_compliance_access_level`` options (https://github.com/ansible-collections/community.general/pull/5986).
|
||||
- jc filter plugin - added the ability to use parser plugins (https://github.com/ansible-collections/community.general/pull/6043).
|
||||
- keycloak_group - add new optional module parameter ``parents`` to properly handle keycloak subgroups (https://github.com/ansible-collections/community.general/pull/5814).
|
||||
- keycloak_user_federation - make ``org.keycloak.storage.ldap.mappers.LDAPStorageMapper`` the default value for mappers ``providerType`` (https://github.com/ansible-collections/community.general/pull/5863).
|
||||
- ldap modules - add ``xorder_discovery`` option (https://github.com/ansible-collections/community.general/issues/6045, https://github.com/ansible-collections/community.general/pull/6109).
|
||||
- lxd_container - add diff and check mode (https://github.com/ansible-collections/community.general/pull/5866).
|
||||
- mattermost, rocketchat, slack - replace missing default favicon with docs.ansible.com favicon (https://github.com/ansible-collections/community.general/pull/5928).
|
||||
- modprobe - add ``persistent`` option (https://github.com/ansible-collections/community.general/issues/4028, https://github.com/ansible-collections/community.general/pull/542).
|
||||
- osx_defaults - include stderr in error messages (https://github.com/ansible-collections/community.general/pull/6011).
|
||||
- proxmox - suppress urllib3 ``InsecureRequestWarnings`` when ``validate_certs`` option is ``false`` (https://github.com/ansible-collections/community.general/pull/5931).
|
||||
- redfish_command - adding ``EnableSecureBoot`` functionality (https://github.com/ansible-collections/community.general/pull/5899).
|
||||
- redfish_command - adding ``VerifyBiosAttributes`` functionality (https://github.com/ansible-collections/community.general/pull/5900).
|
||||
- sefcontext - add support for path substitutions (https://github.com/ansible-collections/community.general/issues/1193).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- gitlab_runner - the option ``access_level`` will lose its default value in community.general 8.0.0. From that version on, you have set this option to ``ref_protected`` explicitly, if you want to have a protected runner (https://github.com/ansible-collections/community.general/issues/5925).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- cartesian and flattened lookup plugins - adjust to parameter deprecation in ansible-core 2.14's ``listify_lookup_plugin_terms`` helper function (https://github.com/ansible-collections/community.general/pull/6074).
|
||||
- cloudflare_dns - fixed the idempotency for SRV DNS records (https://github.com/ansible-collections/community.general/pull/5972).
|
||||
- cloudflare_dns - fixed the possiblity of setting a root-level SRV DNS record (https://github.com/ansible-collections/community.general/pull/5972).
|
||||
- github_webhook - fix always changed state when no secret is provided (https://github.com/ansible-collections/community.general/pull/5994).
|
||||
- jenkins_plugin - fix error due to undefined variable when updates file is not downloaded (https://github.com/ansible-collections/community.general/pull/6100).
|
||||
- keycloak_client - fix accidental replacement of value for attribute ``saml.signing.private.key`` with ``no_log`` in wrong contexts (https://github.com/ansible-collections/community.general/pull/5934).
|
||||
- lxd_* modules, lxd inventory plugin - fix TLS/SSL certificate validation problems by using the correct purpose when creating the TLS context (https://github.com/ansible-collections/community.general/issues/5616, https://github.com/ansible-collections/community.general/pull/6034).
|
||||
- nmcli - fix change handling of values specified as an integer 0 (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - fix failure to handle WIFI settings when connection type not specified (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - fix improper detection of changes to ``wifi.wake-on-wlan`` (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - order is significant for lists of addresses (https://github.com/ansible-collections/community.general/pull/6048).
|
||||
- onepassword lookup plugin - Changed to ignore errors from "op account get" calls. Previously, errors would prevent auto-signin code from executing (https://github.com/ansible-collections/community.general/pull/5942).
|
||||
- terraform and timezone - slight refactoring to avoid linter reporting potentially undefined variables (https://github.com/ansible-collections/community.general/pull/5933).
|
||||
- various plugins and modules - remove unnecessary imports (https://github.com/ansible-collections/community.general/pull/5940).
|
||||
- yarn - fix ``global=true`` to check for the configured global folder instead of assuming the default (https://github.com/ansible-collections/community.general/pull/5829)
|
||||
- yarn - fix ``state=absent`` not working with ``global=true`` when the package does not include a binary (https://github.com/ansible-collections/community.general/pull/5829)
|
||||
- yarn - fix ``state=latest`` not working with ``global=true`` (https://github.com/ansible-collections/community.general/issues/5712).
|
||||
- zfs_delegate_admin - zfs allow output can now be parsed when uids/gids are not known to the host system (https://github.com/ansible-collections/community.general/pull/5943).
|
||||
- zypper - make package managing work on readonly filesystem of openSUSE MicroOS (https://github.com/ansible-collections/community.general/pull/5615).
|
||||
|
||||
v6.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- apache2_module - add module argument ``warn_mpm_absent`` to control whether warning are raised in some edge cases (https://github.com/ansible-collections/community.general/pull/5793).
|
||||
- apt_rpm - adds ``clean``, ``dist_upgrade`` and ``update_kernel`` parameters for clear caches, complete upgrade system, and upgrade kernel packages (https://github.com/ansible-collections/community.general/pull/5867).
|
||||
- bitwarden lookup plugin - can now retrieve secrets from custom fields (https://github.com/ansible-collections/community.general/pull/5694).
|
||||
- bitwarden lookup plugin - implement filtering results by ``collection_id`` parameter (https://github.com/ansible-collections/community.general/issues/5849).
|
||||
- cmd_runner module utils - ``cmd_runner_fmt.as_bool()`` can now take an extra parameter to format when value is false (https://github.com/ansible-collections/community.general/pull/5647).
|
||||
- cpanm - minor change, use feature from ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/6385).
|
||||
- dconf - be forgiving about boolean values: convert them to GVariant booleans automatically (https://github.com/ansible-collections/community.general/pull/6206).
|
||||
- dconf - if ``gi.repository.GLib`` is missing, try to respawn in a Python interpreter that has it (https://github.com/ansible-collections/community.general/pull/6491).
|
||||
- dconf - minor refactoring improving parameters and dependencies validation (https://github.com/ansible-collections/community.general/pull/6336).
|
||||
- dconf - parse GVariants for equality comparison when the Python module ``gi.repository`` is available (https://github.com/ansible-collections/community.general/pull/6049).
|
||||
- deps module utils - add function ``failed()`` providing the ability to check the dependency check result without triggering an exception (https://github.com/ansible-collections/community.general/pull/6383).
|
||||
- dig lookup plugin - Support multiple domains to be queried as indicated in docs (https://github.com/ansible-collections/community.general/pull/6334).
|
||||
- dig lookup plugin - support CAA record type (https://github.com/ansible-collections/community.general/pull/5913).
|
||||
- dnsimple - set custom User-Agent for API requests to DNSimple (https://github.com/ansible-collections/community.general/pull/5927).
|
||||
- dnsimple_info - minor refactor in the code (https://github.com/ansible-collections/community.general/pull/6440).
|
||||
- flatpak_remote - add new boolean option ``enabled``. It controls, whether the remote is enabled or not (https://github.com/ansible-collections/community.general/pull/5926).
|
||||
- gconftool2 - refactor using ``ModuleHelper`` and ``CmdRunner`` (https://github.com/ansible-collections/community.general/pull/5545).
|
||||
- gitlab_group_variable, gitlab_project_variable - refactor function out to module utils (https://github.com/ansible-collections/community.general/pull/6384).
|
||||
- gitlab_project - add ``builds_access_level``, ``container_registry_access_level`` and ``forking_access_level`` options (https://github.com/ansible-collections/community.general/pull/5706).
|
||||
- gitlab_project - add ``releases_access_level``, ``environments_access_level``, ``feature_flags_access_level``, ``infrastructure_access_level``, ``monitor_access_level``, and ``security_and_compliance_access_level`` options (https://github.com/ansible-collections/community.general/pull/5986).
|
||||
- gitlab_project - add new option ``topics`` for adding topics to GitLab projects (https://github.com/ansible-collections/community.general/pull/6278).
|
||||
- gitlab_runner - add new boolean option ``access_level_on_creation``. It controls, whether the value of ``access_level`` is used for runner registration or not. The option ``access_level`` has been ignored on registration so far and was only used on updates (https://github.com/ansible-collections/community.general/issues/5907, https://github.com/ansible-collections/community.general/pull/5908).
|
||||
- gitlab_runner - allow to register group runner (https://github.com/ansible-collections/community.general/pull/3935).
|
||||
- homebrew_cask - allows passing ``--greedy`` option to ``upgrade_all`` (https://github.com/ansible-collections/community.general/pull/6267).
|
||||
- idrac_redfish_command - add ``job_id`` to ``CreateBiosConfigJob`` response (https://github.com/ansible-collections/community.general/issues/5603).
|
||||
- ilo_redfish_utils module utils - change implementation of DNS Server IP and NTP Server IP update (https://github.com/ansible-collections/community.general/pull/5804).
|
||||
- ipa_group - allow to add and remove external users with the ``external_user`` option (https://github.com/ansible-collections/community.general/pull/5897).
|
||||
- ipa_hostgroup - add ``append`` parameter for adding a new hosts to existing hostgroups without changing existing hostgroup members (https://github.com/ansible-collections/community.general/pull/6203).
|
||||
- iptables_state - minor refactoring within the module (https://github.com/ansible-collections/community.general/pull/5844).
|
||||
- java_certs - add more detailed error output when extracting certificate from PKCS12 fails (https://github.com/ansible-collections/community.general/pull/5550).
|
||||
- jc filter plugin - added the ability to use parser plugins (https://github.com/ansible-collections/community.general/pull/6043).
|
||||
- jenkins_plugin - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5565).
|
||||
- jira - add worklog functionality (https://github.com/ansible-collections/community.general/issues/6209, https://github.com/ansible-collections/community.general/pull/6210).
|
||||
- keycloak_authentication - add flow type option to sub flows to allow the creation of 'form-flow' sub flows like in Keycloak's built-in registration flow (https://github.com/ansible-collections/community.general/pull/6318).
|
||||
- keycloak_group - add new optional module parameter ``parents`` to properly handle keycloak subgroups (https://github.com/ansible-collections/community.general/pull/5814).
|
||||
- keycloak_user_federation - make ``org.keycloak.storage.ldap.mappers.LDAPStorageMapper`` the default value for mappers ``providerType`` (https://github.com/ansible-collections/community.general/pull/5863).
|
||||
- ldap modules - add ``ca_path`` option (https://github.com/ansible-collections/community.general/pull/6185).
|
||||
- ldap modules - add ``xorder_discovery`` option (https://github.com/ansible-collections/community.general/issues/6045, https://github.com/ansible-collections/community.general/pull/6109).
|
||||
- ldap_search - the new ``base64_attributes`` allows to specify which attribute values should be Base64 encoded (https://github.com/ansible-collections/community.general/pull/6473).
|
||||
- lxd_container - add diff and check mode (https://github.com/ansible-collections/community.general/pull/5866).
|
||||
- lxd_project - refactored code out to module utils to clear sanity check (https://github.com/ansible-collections/community.general/pull/5549).
|
||||
- make - add ``command`` return value to the module output (https://github.com/ansible-collections/community.general/pull/6160).
|
||||
- mattermost, rocketchat, slack - replace missing default favicon with docs.ansible.com favicon (https://github.com/ansible-collections/community.general/pull/5928).
|
||||
- mksysb - improved the output of the module in case of errors (https://github.com/ansible-collections/community.general/issues/6263).
|
||||
- modprobe - add ``persistent`` option (https://github.com/ansible-collections/community.general/issues/4028, https://github.com/ansible-collections/community.general/pull/542).
|
||||
- module_helper module utils - updated the imports to make more MH features available at ``plugins/module_utils/module_helper.py`` (https://github.com/ansible-collections/community.general/pull/6464).
|
||||
- mssql_script - allow for ``GO`` statement to be mixed-case for scripts not using strict syntax (https://github.com/ansible-collections/community.general/pull/6457).
|
||||
- mssql_script - handle error condition for empty resultsets to allow for non-returning SQL statements (for example ``UPDATE`` and ``INSERT``) (https://github.com/ansible-collections/community.general/pull/6457).
|
||||
- mssql_script - improve batching logic to allow a wider variety of input scripts. For example, SQL scripts slurped from Windows machines which may contain carriage return (''\r'') characters (https://github.com/ansible-collections/community.general/pull/6457).
|
||||
- nmap inventory plugin - add new option ``open`` for only returning open ports (https://github.com/ansible-collections/community.general/pull/6200).
|
||||
- nmap inventory plugin - add new option ``port`` for port specific scan (https://github.com/ansible-collections/community.general/pull/6165).
|
||||
- nmap inventory plugin - add new options ``udp_scan``, ``icmp_timestamp``, and ``dns_resolve`` for different types of scans (https://github.com/ansible-collections/community.general/pull/5566).
|
||||
- nmap inventory plugin - added environment variables for configure ``address`` and ``exclude`` (https://github.com/ansible-collections/community.general/issues/6351).
|
||||
- nmcli - add ``default`` and ``default-or-eui64`` to the list of valid choices for ``addr_gen_mode6`` parameter (https://github.com/ansible-collections/community.general/pull/5974).
|
||||
- nmcli - add ``macvlan`` connection type (https://github.com/ansible-collections/community.general/pull/6312).
|
||||
- nmcli - add support for ``team.runner-fast-rate`` parameter for ``team`` connections (https://github.com/ansible-collections/community.general/issues/6065).
|
||||
- nmcli - new module option ``slave_type`` added to allow creation of various types of slave devices (https://github.com/ansible-collections/community.general/issues/473, https://github.com/ansible-collections/community.general/pull/6108).
|
||||
- one_vm - add a new ``updateconf`` option which implements the ``one.vm.updateconf`` API call (https://github.com/ansible-collections/community.general/pull/5812).
|
||||
- openbsd_pkg - set ``TERM`` to ``'dumb'`` in ``execute_command()`` to make module less dependant on the ``TERM`` environment variable set on the Ansible controller (https://github.com/ansible-collections/community.general/pull/6149).
|
||||
- opkg - allow installing a package in a certain version (https://github.com/ansible-collections/community.general/pull/5688).
|
||||
- opkg - refactored module to use ``CmdRunner`` for executing ``opkg`` (https://github.com/ansible-collections/community.general/pull/5718).
|
||||
- osx_defaults - include stderr in error messages (https://github.com/ansible-collections/community.general/pull/6011).
|
||||
- pipx - add ``system_site_packages`` parameter to give application access to system-wide packages (https://github.com/ansible-collections/community.general/pull/6308).
|
||||
- pipx - ensure ``include_injected`` parameter works with ``state=upgrade`` and ``state=latest`` (https://github.com/ansible-collections/community.general/pull/6212).
|
||||
- pipx - optional ``install_apps`` parameter added to install applications from injected packages (https://github.com/ansible-collections/community.general/pull/6198).
|
||||
- proxmox - added new module parameter ``tags`` for use with PVE 7+ (https://github.com/ansible-collections/community.general/pull/5714).
|
||||
- proxmox - suppress urllib3 ``InsecureRequestWarnings`` when ``validate_certs`` option is ``false`` (https://github.com/ansible-collections/community.general/pull/5931).
|
||||
- proxmox_kvm - add new ``archive`` parameter. This is needed to create a VM from an archive (backup) (https://github.com/ansible-collections/community.general/pull/6159).
|
||||
- proxmox_kvm - adds ``migrate`` parameter to manage online migrations between hosts (https://github.com/ansible-collections/community.general/pull/6448)
|
||||
- puppet - add new options ``skip_tags`` to exclude certain tagged resources during a puppet agent or apply (https://github.com/ansible-collections/community.general/pull/6293).
|
||||
- puppet - refactored module to use ``CmdRunner`` for executing ``puppet`` (https://github.com/ansible-collections/community.general/pull/5612).
|
||||
- rax_scaling_group - refactored out code to the ``rax`` module utils to clear the sanity check (https://github.com/ansible-collections/community.general/pull/5563).
|
||||
- redfish_command - add ``PerformRequestedOperations`` command to perform any operations necessary to continue the update flow (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_command - add ``update_apply_time`` to ``SimpleUpdate`` command (https://github.com/ansible-collections/community.general/issues/3910).
|
||||
- redfish_command - add ``update_status`` to output of ``SimpleUpdate`` command to allow a user monitor the update in progress (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_command - adding ``EnableSecureBoot`` functionality (https://github.com/ansible-collections/community.general/pull/5899).
|
||||
- redfish_command - adding ``VerifyBiosAttributes`` functionality (https://github.com/ansible-collections/community.general/pull/5900).
|
||||
- redfish_info - add ``GetUpdateStatus`` command to check the progress of a previous update request (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_info - adds commands to retrieve the HPE ThermalConfiguration and FanPercentMinimum settings from iLO (https://github.com/ansible-collections/community.general/pull/6208).
|
||||
- redfish_utils module utils - added PUT (``put_request()``) functionality (https://github.com/ansible-collections/community.general/pull/5490).
|
||||
- redhat_subscription - add a ``server_proxy_scheme`` parameter to configure the scheme for the proxy server (https://github.com/ansible-collections/community.general/pull/5662).
|
||||
- redhat_subscription - adds ``token`` parameter for subscription-manager authentication using Red Hat API token (https://github.com/ansible-collections/community.general/pull/5725).
|
||||
- redhat_subscription - credentials (``username``, ``activationkey``, and so on) are required now only if a system needs to be registered, or ``force_register`` is specified (https://github.com/ansible-collections/community.general/pull/5664).
|
||||
- redhat_subscription - the registration is done using the D-Bus ``rhsm`` service instead of spawning a ``subscription-manager register`` command, if possible; this avoids passing plain-text credentials as arguments to ``subscription-manager register``, which can be seen while that command runs (https://github.com/ansible-collections/community.general/pull/6122).
|
||||
- sefcontext - add support for path substitutions (https://github.com/ansible-collections/community.general/issues/1193).
|
||||
- shutdown - if no shutdown commands are found in the ``search_paths`` then the module will attempt to shutdown the system using ``systemctl shutdown`` (https://github.com/ansible-collections/community.general/issues/4269, https://github.com/ansible-collections/community.general/pull/6171).
|
||||
- slack - add option ``prepend_hash`` which allows to control whether a ``#`` is prepended to ``channel_id``. The current behavior (value ``auto``) is to prepend ``#`` unless some specific prefixes are found. That list of prefixes is incomplete, and there does not seem to exist a documented condition on when exactly ``#`` must not be prepended. We recommend to explicitly set ``prepend_hash=always`` or ``prepend_hash=never`` to avoid any ambiguity (https://github.com/ansible-collections/community.general/pull/5629).
|
||||
- snap - minor refactor when executing module (https://github.com/ansible-collections/community.general/pull/5773).
|
||||
- snap - refactor module to use ``CmdRunner`` to execute external commands (https://github.com/ansible-collections/community.general/pull/6468).
|
||||
- snap_alias - refactor code to module utils (https://github.com/ansible-collections/community.general/pull/6441).
|
||||
- snap_alias - refactored module to use ``CmdRunner`` to execute ``snap`` (https://github.com/ansible-collections/community.general/pull/5486).
|
||||
- spotinst_aws_elastigroup - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5553).
|
||||
- ssh_config - add ``host_key_algorithms`` option (https://github.com/ansible-collections/community.general/pull/5605).
|
||||
- ssh_config - add ``proxyjump`` option (https://github.com/ansible-collections/community.general/pull/5970).
|
||||
- ssh_config - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5720).
|
||||
- ssh_config - vendored StormSSH's config parser to avoid having to install StormSSH to use the module (https://github.com/ansible-collections/community.general/pull/6117).
|
||||
- sudoers - add ``setenv`` parameters to support passing environment variables via sudo. (https://github.com/ansible-collections/community.general/pull/5883)
|
||||
- sudoers - adds ``host`` parameter for setting hostname restrictions in sudoers rules (https://github.com/ansible-collections/community.general/issues/5702).
|
||||
- terraform - remove state file check condition and error block, because in the native implementation of terraform will not cause errors due to the non-existent file (https://github.com/ansible-collections/community.general/pull/6296).
|
||||
- udm_dns_record - minor refactor to the code (https://github.com/ansible-collections/community.general/pull/6382).
|
||||
- udm_share - added ``elements`` attribute to ``list`` type parameters (https://github.com/ansible-collections/community.general/pull/5557).
|
||||
- udm_user - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5559).
|
||||
- znode module - optional ``use_tls`` parameter added for encrypted communication (https://github.com/ansible-collections/community.general/issues/6154).
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- If you are not using this collection as part of Ansible, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.sap_libs`` if you are using any of the ``sapcar_extract``, ``sap_task_list_execute``, and ``hana_query`` modules.
|
||||
Without that collection installed, the redirects for these modules do not work.
|
||||
- ModuleHelper module utils - when the module sets output variables named ``msg``, ``exception``, ``output``, ``vars``, or ``changed``, the actual output will prefix those names with ``_`` (underscore symbol) only when they clash with output variables generated by ModuleHelper itself, which only occurs when handling exceptions. Please note that this breaking change does not require a new major release since before this release, it was not possible to add such variables to the output `due to a bug <https://github.com/ansible-collections/community.general/pull/5755>`__ (https://github.com/ansible-collections/community.general/pull/5765).
|
||||
- gconftool2 - fix processing of ``gconftool-2`` when ``key`` does not exist, returning ``null`` instead of empty string for both ``value`` and ``previous_value`` return values (https://github.com/ansible-collections/community.general/issues/6028).
|
||||
- gitlab_runner - the default of ``access_level_on_creation`` changed from ``false`` to ``true`` (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
- ldap_search - convert all string-like values to UTF-8 (https://github.com/ansible-collections/community.general/issues/5704, https://github.com/ansible-collections/community.general/pull/6473).
|
||||
- nmcli - the default of the ``hairpin`` option changed from ``true`` to ``false`` (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
- proxmox - the default of the ``unprivileged`` option changed from ``false`` to ``true`` (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- ModuleHelper module_utils - ``deps`` mixin for MH classes deprecated in favour of using the ``deps`` module_utils (https://github.com/ansible-collections/community.general/pull/6465).
|
||||
- consul - deprecate using parameters unused for ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5772).
|
||||
- gitlab_runner - the default of the new option ``access_level_on_creation`` will change from ``false`` to ``true`` in community.general 7.0.0. This will cause ``access_level`` to be used during runner registration as well, and not only during updates (https://github.com/ansible-collections/community.general/pull/5908).
|
||||
- gitlab_runner - the option ``access_level`` will lose its default value in community.general 8.0.0. From that version on, you have set this option to ``ref_protected`` explicitly, if you want to have a protected runner (https://github.com/ansible-collections/community.general/issues/5925).
|
||||
- manageiq_policies - deprecate ``state=list`` in favour of using ``community.general.manageiq_policies_info`` (https://github.com/ansible-collections/community.general/pull/5721).
|
||||
- manageiq_tags - deprecate ``state=list`` in favour of using ``community.general.manageiq_tags_info`` (https://github.com/ansible-collections/community.general/pull/5727).
|
||||
- rax - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax module utils - module utils code relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_cbs - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_cbs_attachments - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_cdb - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_cdb_database - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_cdb_user - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_clb - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_clb_nodes - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_clb_ssl - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_dns - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_dns_record - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_facts - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_files - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_files_objects - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_identity - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_keypair - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_meta - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_mon_alarm - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_mon_check - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_mon_entity - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_mon_notification - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_mon_notification_plan - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_network - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_queue - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_scaling_group - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rax_scaling_policy - module relies on deprecated library ``pyrax`` and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5752).
|
||||
- rhn_channel, rhn_register - RHN hosted at redhat.com was discontinued years
|
||||
ago, and Spacewalk 5 (which uses RHN) is EOL since 2020, May 31st;
|
||||
while these modules could work on Uyuni / SUSE Manager (fork of Spacewalk 5),
|
||||
we have not heard about anyone using them in those setups. Hence, these
|
||||
modules are deprecated, and will be removed in community.general 10.0.0
|
||||
in case there are no reports about being still useful, and potentially
|
||||
noone that steps up to maintain them
|
||||
(https://github.com/ansible-collections/community.general/pull/6493).
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- All ``sap`` modules have been removed from this collection.
|
||||
They have been migrated to the `community.sap_libs <https://galaxy.ansible.com/community/sap_libs>`_ collection.
|
||||
Redirections have been provided.
|
||||
Following modules are affected:
|
||||
- sapcar_extract
|
||||
- sap_task_list_execute
|
||||
- hana_query
|
||||
- cmd_runner module utils - the ``fmt`` alias of ``cmd_runner_fmt`` has been removed. Use ``cmd_runner_fmt`` instead (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
- newrelic_deployment - the ``appname`` and ``environment`` options have been removed. They did not do anything (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
- puppet - the alias ``show-diff`` of the ``show_diff`` option has been removed. Use ``show_diff`` instead (https://github.com/ansible-collections/community.general/pull/6428).
|
||||
- xfconf - generating facts was deprecated in community.general 3.0.0, however three factoids, ``property``, ``channel`` and ``value`` continued to be generated by mistake. This behaviour has been removed and ``xfconf`` generate no facts whatsoever (https://github.com/ansible-collections/community.general/pull/5502).
|
||||
- xfconf - generating facts was deprecated in community.general 3.0.0, however two factoids, ``previous_value`` and ``type`` continued to be generated by mistake. This behaviour has been removed and ``xfconf`` generate no facts whatsoever (https://github.com/ansible-collections/community.general/pull/5502).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- ModuleHelper - fix bug when adjusting the name of reserved output variables (https://github.com/ansible-collections/community.general/pull/5755).
|
||||
- alternatives - support subcommands on Fedora 37, which uses ``follower`` instead of ``slave`` (https://github.com/ansible-collections/community.general/pull/5794).
|
||||
- ansible_galaxy_install - set default to raise exception if command's return code is different from zero (https://github.com/ansible-collections/community.general/pull/5680).
|
||||
- ansible_galaxy_install - try ``C.UTF-8`` and then fall back to ``en_US.UTF-8`` before failing (https://github.com/ansible-collections/community.general/pull/5680).
|
||||
- archive - avoid deprecated exception class on Python 3 (https://github.com/ansible-collections/community.general/pull/6180).
|
||||
- archive - reduce RAM usage by generating CRC32 checksum over chunks (https://github.com/ansible-collections/community.general/pull/6274).
|
||||
- bitwarden lookup plugin - clarify what to do, if the bitwarden vault is not unlocked (https://github.com/ansible-collections/community.general/pull/5811).
|
||||
- cartesian and flattened lookup plugins - adjust to parameter deprecation in ansible-core 2.14's ``listify_lookup_plugin_terms`` helper function (https://github.com/ansible-collections/community.general/pull/6074).
|
||||
- chroot connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/5570).
|
||||
- cloudflare_dns - fixed the idempotency for SRV DNS records (https://github.com/ansible-collections/community.general/pull/5972).
|
||||
- cloudflare_dns - fixed the possiblity of setting a root-level SRV DNS record (https://github.com/ansible-collections/community.general/pull/5972).
|
||||
- cmd_runner module utils - fixed bug when handling default cases in ``cmd_runner_fmt.as_map()`` (https://github.com/ansible-collections/community.general/pull/5538).
|
||||
- cmd_runner module utils - formatting arguments ``cmd_runner_fmt.as_fixed()`` was expecting an non-existing argument (https://github.com/ansible-collections/community.general/pull/5538).
|
||||
- dependent lookup plugin - avoid warning on deprecated parameter for ``Templar.template()`` (https://github.com/ansible-collections/community.general/pull/5543).
|
||||
- deps module utils - do not fail when dependency cannot be found (https://github.com/ansible-collections/community.general/pull/6479).
|
||||
- dig lookup plugin - correctly handle DNSKEY record type's ``algorithm`` field (https://github.com/ansible-collections/community.general/pull/5914).
|
||||
- flatpak - fixes idempotency detection issues. In some cases the module could fail to properly detect already existing Flatpaks because of a parameter witch only checks the installed apps (https://github.com/ansible-collections/community.general/pull/6289).
|
||||
- gconftool2 - fix ``changed`` result always being ``true`` (https://github.com/ansible-collections/community.general/issues/6028).
|
||||
- gconftool2 - remove requirement of parameter ``value`` when ``state=absent`` (https://github.com/ansible-collections/community.general/issues/6028).
|
||||
- gem - fix force parameter not being passed to gem command when uninstalling (https://github.com/ansible-collections/community.general/pull/5822).
|
||||
- gem - fix hang due to interactive prompt for confirmation on specific version uninstall (https://github.com/ansible-collections/community.general/pull/5751).
|
||||
- github_webhook - fix always changed state when no secret is provided (https://github.com/ansible-collections/community.general/pull/5994).
|
||||
- gitlab_deploy_key - also update ``title`` and not just ``can_push`` (https://github.com/ansible-collections/community.general/pull/5888).
|
||||
- gitlab_group_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667).
|
||||
- gitlab_project_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667).
|
||||
- gitlab_runner - fix ``KeyError`` on runner creation and update (https://github.com/ansible-collections/community.general/issues/6112).
|
||||
- icinga2_host - fix the data structure sent to Icinga to make use of host templates and template vars (https://github.com/ansible-collections/community.general/pull/6286).
|
||||
- idrac_redfish_command - allow user to specify ``resource_id`` for ``CreateBiosConfigJob`` to specify an exact manager (https://github.com/ansible-collections/community.general/issues/2090).
|
||||
- influxdb_user - fix running in check mode when the user does not exist yet (https://github.com/ansible-collections/community.general/pull/6111).
|
||||
- ini_file - make ``section`` parameter not required so it is possible to pass ``null`` as a value. This only was possible in the past due to a bug in ansible-core that now has been fixed (https://github.com/ansible-collections/community.general/pull/6404).
|
||||
- interfaces_file - fix reading options in lines not starting with a space (https://github.com/ansible-collections/community.general/issues/6120).
|
||||
- jail connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/6118).
|
||||
- jenkins_build - fix the logical flaw when deleting a Jenkins build (https://github.com/ansible-collections/community.general/pull/5514).
|
||||
- jenkins_plugin - fix error due to undefined variable when updates file is not downloaded (https://github.com/ansible-collections/community.general/pull/6100).
|
||||
- keycloak - improve error messages (https://github.com/ansible-collections/community.general/pull/6318).
|
||||
- keycloak_client - fix accidental replacement of value for attribute ``saml.signing.private.key`` with ``no_log`` in wrong contexts (https://github.com/ansible-collections/community.general/pull/5934).
|
||||
- keycloak_client_rolemapping - calculate ``proposed`` and ``after`` return values properly (https://github.com/ansible-collections/community.general/pull/5619).
|
||||
- keycloak_client_rolemapping - remove only listed mappings with ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5619).
|
||||
- keycloak_user_federation - fixes federation creation issue. When a new federation was created and at the same time a default / standard mapper was also changed / updated the creation process failed as a bad None set variable led to a bad malformed url request (https://github.com/ansible-collections/community.general/pull/5750).
|
||||
- keycloak_user_federation - fixes idempotency detection issues. In some cases the module could fail to properly detect already existing user federations because of a buggy seemingly superflous extra query parameter (https://github.com/ansible-collections/community.general/pull/5732).
|
||||
- loganalytics callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- logdna callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- logstash callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- lxc_container - fix the arguments of the lxc command which broke the creation and cloning of containers (https://github.com/ansible-collections/community.general/issues/5578).
|
||||
- lxd_* modules, lxd inventory plugin - fix TLS/SSL certificate validation problems by using the correct purpose when creating the TLS context (https://github.com/ansible-collections/community.general/issues/5616, https://github.com/ansible-collections/community.general/pull/6034).
|
||||
- memset - fix memset urlerror handling (https://github.com/ansible-collections/community.general/pull/6114).
|
||||
- nmcli - fix change handling of values specified as an integer 0 (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - fix failure to handle WIFI settings when connection type not specified (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - fix improper detection of changes to ``wifi.wake-on-wlan`` (https://github.com/ansible-collections/community.general/pull/5431).
|
||||
- nmcli - fixed idempotency issue for bridge connections. Module forced default value of ``bridge.priority`` to nmcli if not set; if ``bridge.stp`` is disabled nmcli ignores it and keep default (https://github.com/ansible-collections/community.general/issues/3216, https://github.com/ansible-collections/community.general/issues/4683).
|
||||
- nmcli - fixed idempotency issue when module params is set to ``may_fail4=false`` and ``method4=disabled``; in this case nmcli ignores change and keeps their own default value ``yes`` (https://github.com/ansible-collections/community.general/pull/6106).
|
||||
- nmcli - implemented changing mtu value on vlan interfaces (https://github.com/ansible-collections/community.general/issues/4387).
|
||||
- nmcli - order is significant for lists of addresses (https://github.com/ansible-collections/community.general/pull/6048).
|
||||
- nsupdate - fix zone lookup. The SOA record for an existing zone is returned as an answer RR and not as an authority RR (https://github.com/ansible-collections/community.general/issues/5817, https://github.com/ansible-collections/community.general/pull/5818).
|
||||
- one_vm - avoid splitting labels that are ``None`` (https://github.com/ansible-collections/community.general/pull/5489).
|
||||
- one_vm - fix syntax error when creating VMs with a more complex template (https://github.com/ansible-collections/community.general/issues/6225).
|
||||
- onepassword lookup plugin - Changed to ignore errors from "op account get" calls. Previously, errors would prevent auto-signin code from executing (https://github.com/ansible-collections/community.general/pull/5942).
|
||||
- onepassword_raw - add missing parameter to plugin documentation (https://github.com/ansible-collections/community.general/issues/5506).
|
||||
- opkg - fix issue that ``force=reinstall`` would not reinstall an existing package (https://github.com/ansible-collections/community.general/pull/5705).
|
||||
- opkg - fixes bug when using ``update_cache=true`` (https://github.com/ansible-collections/community.general/issues/6004).
|
||||
- passwordstore lookup plugin - make compatible with ansible-core 2.16 (https://github.com/ansible-collections/community.general/pull/6447).
|
||||
- pipx - fixed handling of ``install_deps=true`` with ``state=latest`` and ``state=upgrade`` (https://github.com/ansible-collections/community.general/pull/6303).
|
||||
- portage - update the logic for generating the emerge command arguments to ensure that ``withbdeps: false`` results in a passing an ``n`` argument with the ``--with-bdeps`` emerge flag (https://github.com/ansible-collections/community.general/issues/6451, https://github.com/ansible-collections/community.general/pull/6456).
|
||||
- proxmox inventory plugin - fix bug while templating when using templates for the ``url``, ``user``, ``password``, ``token_id``, or ``token_secret`` options (https://github.com/ansible-collections/community.general/pull/5640).
|
||||
- proxmox inventory plugin - handle tags delimited by semicolon instead of comma, which happens from Proxmox 7.3 on (https://github.com/ansible-collections/community.general/pull/5602).
|
||||
- proxmox_disk - avoid duplicate ``vmid`` reference (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5493).
|
||||
- proxmox_disk - fixed issue with read timeout on import action (https://github.com/ansible-collections/community.general/pull/5803).
|
||||
- proxmox_disk - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672).
|
||||
- proxmox_nic - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672).
|
||||
- puppet - handling ``noop`` parameter was not working at all, now it is has been fixed (https://github.com/ansible-collections/community.general/issues/6452, https://github.com/ansible-collections/community.general/issues/6458).
|
||||
- redfish_utils - removed basic auth HTTP header when performing a GET on the service root resource and when performing a POST to the session collection (https://github.com/ansible-collections/community.general/issues/5886).
|
||||
- redhat_subscription - do not ignore ``consumer_name`` and other variables if ``activationkey`` is specified (https://github.com/ansible-collections/community.general/issues/3486, https://github.com/ansible-collections/community.general/pull/5627).
|
||||
- redhat_subscription - do not pass arguments to ``subscription-manager register`` for things already configured; now a specified ``rhsm_baseurl`` is properly set for subscription-manager (https://github.com/ansible-collections/community.general/pull/5583).
|
||||
- redhat_subscription - do not use D-Bus for registering when ``environment`` is specified, so it possible to specify again the environment names for registering, as the D-Bus APIs work only with IDs (https://github.com/ansible-collections/community.general/pull/6319).
|
||||
- redhat_subscription - try to unregister only when already registered when ``force_register`` is specified (https://github.com/ansible-collections/community.general/issues/6258, https://github.com/ansible-collections/community.general/pull/6259).
|
||||
- redhat_subscription - use the right D-Bus options for environments when registering a CentOS Stream 8 system and using ``environment`` (https://github.com/ansible-collections/community.general/pull/6275).
|
||||
- redhat_subscription, rhsm_release, rhsm_repository - cleanly fail when not running as root, rather than hanging on an interactive ``console-helper`` prompt; they all interact with ``subscription-manager``, which already requires to be run as root (https://github.com/ansible-collections/community.general/issues/734, https://github.com/ansible-collections/community.general/pull/6211).
|
||||
- rhsm_release - make ``release`` parameter not required so it is possible to pass ``null`` as a value. This only was possible in the past due to a bug in ansible-core that now has been fixed (https://github.com/ansible-collections/community.general/pull/6401).
|
||||
- rundeck module utils - fix errors caused by the API empty responses (https://github.com/ansible-collections/community.general/pull/6300)
|
||||
- rundeck_acl_policy - fix ``TypeError - byte indices must be integers or slices, not str`` error caused by empty API response. Update the module to use ``module_utils.rundeck`` functions (https://github.com/ansible-collections/community.general/pull/5887, https://github.com/ansible-collections/community.general/pull/6300).
|
||||
- rundeck_project - update the module to use ``module_utils.rundeck`` functions (https://github.com/ansible-collections/community.general/issues/5742) (https://github.com/ansible-collections/community.general/pull/6300)
|
||||
- snap_alias - module would only recognize snap names containing letter, numbers or the underscore character, failing to identify valid snap names such as ``lxd.lxc`` (https://github.com/ansible-collections/community.general/pull/6361).
|
||||
- splunk callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- sumologic callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- syslog_json callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761).
|
||||
- terraform - fix ``current`` workspace never getting appended to the ``all`` key in the ``workspace_ctf`` object (https://github.com/ansible-collections/community.general/pull/5735).
|
||||
- terraform - fix ``terraform init`` failure when there are multiple workspaces on the remote backend and when ``default`` workspace is missing by setting ``TF_WORKSPACE`` environmental variable to the value of ``workspace`` when used (https://github.com/ansible-collections/community.general/pull/5735).
|
||||
- terraform - fix broken ``warn()`` call (https://github.com/ansible-collections/community.general/pull/6497).
|
||||
- terraform and timezone - slight refactoring to avoid linter reporting potentially undefined variables (https://github.com/ansible-collections/community.general/pull/5933).
|
||||
- terraform module - disable ANSI escape sequences during validation phase (https://github.com/ansible-collections/community.general/pull/5843).
|
||||
- tss lookup plugin - allow to download secret attachments. Previously, we could not download secret attachments but now use ``fetch_attachments`` and ``file_download_path`` variables to download attachments (https://github.com/ansible-collections/community.general/issues/6224).
|
||||
- unixy callback plugin - fix plugin to work with ansible-core 2.14 by using Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600).
|
||||
- unixy callback plugin - fix typo introduced when updating to use Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600).
|
||||
- various plugins and modules - remove unnecessary imports (https://github.com/ansible-collections/community.general/pull/5940).
|
||||
- vdo - now uses ``yaml.safe_load()`` to parse command output instead of the deprecated ``yaml.load()`` which is potentially unsafe. Using ``yaml.load()`` without explicitely setting a ``Loader=`` is also an error in pyYAML 6.0 (https://github.com/ansible-collections/community.general/pull/5632).
|
||||
- vmadm - fix for index out of range error in ``get_vm_uuid`` (https://github.com/ansible-collections/community.general/pull/5628).
|
||||
- xenorchestra inventory plugin - fix failure to receive objects from server due to not checking the id of the response (https://github.com/ansible-collections/community.general/pull/6227).
|
||||
- xfs_quota - in case of a project quota, the call to ``xfs_quota`` did not initialize/reset the project (https://github.com/ansible-collections/community.general/issues/5143).
|
||||
- xml - fixed a bug where empty ``children`` list would not be set (https://github.com/ansible-collections/community.general/pull/5808).
|
||||
- yarn - fix ``global=true`` to check for the configured global folder instead of assuming the default (https://github.com/ansible-collections/community.general/pull/5829)
|
||||
- yarn - fix ``global=true`` to not fail when `executable` wasn't specified (https://github.com/ansible-collections/community.general/pull/6132)
|
||||
- yarn - fix ``state=absent`` not working with ``global=true`` when the package does not include a binary (https://github.com/ansible-collections/community.general/pull/5829)
|
||||
- yarn - fix ``state=latest`` not working with ``global=true`` (https://github.com/ansible-collections/community.general/issues/5712).
|
||||
- yarn - fixes bug where yarn module tasks would fail when warnings were emitted from Yarn. The ``yarn.list`` method was not filtering out warnings (https://github.com/ansible-collections/community.general/issues/6127).
|
||||
- zfs_delegate_admin - zfs allow output can now be parsed when uids/gids are not known to the host system (https://github.com/ansible-collections/community.general/pull/5943).
|
||||
- zypper - make package managing work on readonly filesystem of openSUSE MicroOS (https://github.com/ansible-collections/community.general/pull/5615).
|
||||
|
||||
New Plugins
|
||||
-----------
|
||||
|
||||
Lookup
|
||||
~~~~~~
|
||||
|
||||
- merge_variables - merge variables with a certain suffix
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- btrfs_info - Query btrfs filesystem info
|
||||
- btrfs_subvolume - Manage btrfs subvolumes
|
||||
- gitlab_project_badge - Manage project badges on GitLab Server
|
||||
- ilo_redfish_command - Manages Out-Of-Band controllers using Redfish APIs
|
||||
- ipbase_info - Retrieve IP geolocation and other facts of a host's IP address using the ipbase.com API
|
||||
- kdeconfig - Manage KDE configuration files
|
||||
- keycloak_authz_authorization_scope - Allows administration of Keycloak client authorization scopes via Keycloak API
|
||||
- keycloak_clientscope_type - Set the type of aclientscope in realm or client via Keycloak API
|
||||
- keycloak_clientsecret_info - Retrieve client secret via Keycloak API
|
||||
- keycloak_clientsecret_regenerate - Regenerate Keycloak client secret via Keycloak API
|
||||
- ocapi_command - Manages Out-Of-Band controllers using Open Composable API (OCAPI)
|
||||
- ocapi_info - Manages Out-Of-Band controllers using Open Composable API (OCAPI)
|
||||
|
||||
v6.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- opkg - allow installing a package in a certain version (https://github.com/ansible-collections/community.general/pull/5688).
|
||||
- proxmox - added new module parameter ``tags`` for use with PVE 7+ (https://github.com/ansible-collections/community.general/pull/5714).
|
||||
- puppet - refactored module to use ``CmdRunner`` for executing ``puppet`` (https://github.com/ansible-collections/community.general/pull/5612).
|
||||
- redhat_subscription - add a ``server_proxy_scheme`` parameter to configure the scheme for the proxy server (https://github.com/ansible-collections/community.general/pull/5662).
|
||||
- ssh_config - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5720).
|
||||
- sudoers - adds ``host`` parameter for setting hostname restrictions in sudoers rules (https://github.com/ansible-collections/community.general/issues/5702).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- manageiq_policies - deprecate ``state=list`` in favour of using ``community.general.manageiq_policies_info`` (https://github.com/ansible-collections/community.general/pull/5721).
|
||||
- rax - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_cbs - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_cbs_attachments - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_cdb - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_cdb_database - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_cdb_user - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_clb - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_clb_nodes - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_clb_ssl - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_dns - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_dns_record - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_facts - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_files - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_files_objects - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_identity - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_keypair - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_meta - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_mon_alarm - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_mon_check - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_mon_entity - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_mon_notification - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_mon_notification_plan - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_network - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_queue - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_scaling_group - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
- rax_scaling_policy - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- ansible_galaxy_install - set default to raise exception if command's return code is different from zero (https://github.com/ansible-collections/community.general/pull/5680).
|
||||
- ansible_galaxy_install - try ``C.UTF-8`` and then fall back to ``en_US.UTF-8`` before failing (https://github.com/ansible-collections/community.general/pull/5680).
|
||||
- gitlab_group_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667).
|
||||
- gitlab_project_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667).
|
||||
- lxc_container - fix the arguments of the lxc command which broke the creation and cloning of containers (https://github.com/ansible-collections/community.general/issues/5578).
|
||||
- opkg - fix issue that ``force=reinstall`` would not reinstall an existing package (https://github.com/ansible-collections/community.general/pull/5705).
|
||||
- proxmox_disk - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672).
|
||||
- proxmox_nic - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672).
|
||||
- unixy callback plugin - fix typo introduced when updating to use Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600).
|
||||
|
||||
v6.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- cmd_runner module utils - ``cmd_runner_fmt.as_bool()`` can now take an extra parameter to format when value is false (https://github.com/ansible-collections/community.general/pull/5647).
|
||||
- gconftool2 - refactor using ``ModuleHelper`` and ``CmdRunner`` (https://github.com/ansible-collections/community.general/pull/5545).
|
||||
- java_certs - add more detailed error output when extracting certificate from PKCS12 fails (https://github.com/ansible-collections/community.general/pull/5550).
|
||||
- jenkins_plugin - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5565).
|
||||
- lxd_project - refactored code out to module utils to clear sanity check (https://github.com/ansible-collections/community.general/pull/5549).
|
||||
- nmap inventory plugin - add new options ``udp_scan``, ``icmp_timestamp``, and ``dns_resolve`` for different types of scans (https://github.com/ansible-collections/community.general/pull/5566).
|
||||
- rax_scaling_group - refactored out code to the ``rax`` module utils to clear the sanity check (https://github.com/ansible-collections/community.general/pull/5563).
|
||||
- redfish_command - add ``PerformRequestedOperations`` command to perform any operations necessary to continue the update flow (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_command - add ``update_apply_time`` to ``SimpleUpdate`` command (https://github.com/ansible-collections/community.general/issues/3910).
|
||||
- redfish_command - add ``update_status`` to output of ``SimpleUpdate`` command to allow a user monitor the update in progress (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_info - add ``GetUpdateStatus`` command to check the progress of a previous update request (https://github.com/ansible-collections/community.general/issues/4276).
|
||||
- redfish_utils module utils - added PUT (``put_request()``) functionality (https://github.com/ansible-collections/community.general/pull/5490).
|
||||
- slack - add option ``prepend_hash`` which allows to control whether a ``#`` is prepended to ``channel_id``. The current behavior (value ``auto``) is to prepend ``#`` unless some specific prefixes are found. That list of prefixes is incomplete, and there does not seem to exist a documented condition on when exactly ``#`` must not be prepended. We recommend to explicitly set ``prepend_hash=always`` or ``prepend_hash=never`` to avoid any ambiguity (https://github.com/ansible-collections/community.general/pull/5629).
|
||||
- spotinst_aws_elastigroup - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5553).
|
||||
- ssh_config - add ``host_key_algorithms`` option (https://github.com/ansible-collections/community.general/pull/5605).
|
||||
- udm_share - added ``elements`` attribute to ``list`` type parameters (https://github.com/ansible-collections/community.general/pull/5557).
|
||||
- udm_user - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5559).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- The ``sap`` modules ``sapcar_extract``, ``sap_task_list_execute``, and ``hana_query``, will be removed from this collection in community.general 7.0.0 and replaced with redirects to ``community.sap_libs``. If you want to continue using these modules, make sure to also install ``community.sap_libs`` (it is part of the Ansible package) (https://github.com/ansible-collections/community.general/pull/5614).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- chroot connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/5570).
|
||||
- cmd_runner module utils - fixed bug when handling default cases in ``cmd_runner_fmt.as_map()`` (https://github.com/ansible-collections/community.general/pull/5538).
|
||||
- cmd_runner module utils - formatting arguments ``cmd_runner_fmt.as_fixed()`` was expecting an non-existing argument (https://github.com/ansible-collections/community.general/pull/5538).
|
||||
- keycloak_client_rolemapping - calculate ``proposed`` and ``after`` return values properly (https://github.com/ansible-collections/community.general/pull/5619).
|
||||
- keycloak_client_rolemapping - remove only listed mappings with ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5619).
|
||||
- proxmox inventory plugin - fix bug while templating when using templates for the ``url``, ``user``, ``password``, ``token_id``, or ``token_secret`` options (https://github.com/ansible-collections/community.general/pull/5640).
|
||||
- proxmox inventory plugin - handle tags delimited by semicolon instead of comma, which happens from Proxmox 7.3 on (https://github.com/ansible-collections/community.general/pull/5602).
|
||||
- redhat_subscription - do not ignore ``consumer_name`` and other variables if ``activationkey`` is specified (https://github.com/ansible-collections/community.general/issues/3486, https://github.com/ansible-collections/community.general/pull/5627).
|
||||
- redhat_subscription - do not pass arguments to ``subscription-manager register`` for things already configured; now a specified ``rhsm_baseurl`` is properly set for subscription-manager (https://github.com/ansible-collections/community.general/pull/5583).
|
||||
- unixy callback plugin - fix plugin to work with ansible-core 2.14 by using Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600).
|
||||
- vdo - now uses ``yaml.safe_load()`` to parse command output instead of the deprecated ``yaml.load()`` which is potentially unsafe. Using ``yaml.load()`` without explicitely setting a ``Loader=`` is also an error in pyYAML 6.0 (https://github.com/ansible-collections/community.general/pull/5632).
|
||||
- vmadm - fix for index out of range error in ``get_vm_uuid`` (https://github.com/ansible-collections/community.general/pull/5628).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- gitlab_project_badge - Manage project badges on GitLab Server
|
||||
- keycloak_clientsecret_info - Retrieve client secret via Keycloak API
|
||||
- keycloak_clientsecret_regenerate - Regenerate Keycloak client secret via Keycloak API
|
||||
|
||||
v6.0.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfix release for Ansible 7.0.0.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- dependent lookup plugin - avoid warning on deprecated parameter for ``Templar.template()`` (https://github.com/ansible-collections/community.general/pull/5543).
|
||||
- jenkins_build - fix the logical flaw when deleting a Jenkins build (https://github.com/ansible-collections/community.general/pull/5514).
|
||||
- one_vm - avoid splitting labels that are ``None`` (https://github.com/ansible-collections/community.general/pull/5489).
|
||||
- onepassword_raw - add missing parameter to plugin documentation (https://github.com/ansible-collections/community.general/issues/5506).
|
||||
- proxmox_disk - avoid duplicate ``vmid`` reference (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5493).
|
||||
|
||||
v6.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
New major release of community.general with lots of bugfixes, new features, some removed deprecated features, and some other breaking changes. Please check the coresponding sections of the changelog for more details.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- The internal structure of the collection was changed for modules and action plugins. These no longer live in a directory hierarchy ordered by topic, but instead are now all in a single (flat) directory. This has no impact on users *assuming they did not use internal FQCNs*. These will still work, but result in deprecation warnings. They were never officially supported and thus the redirects are kept as a courtsey, and this is not labelled as a breaking change. Note that for example the Ansible VScode plugin started recommending these internal names. If you followed its recommendation, you will now have to change back to the short names to avoid deprecation warnings, and potential errors in the future as these redirects will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5461).
|
||||
- newrelic_deployment - removed New Relic v1 API, added support for v2 API (https://github.com/ansible-collections/community.general/pull/5341).
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Added MIT license as ``LICENSES/MIT.txt`` for tests/unit/plugins/modules/packaging/language/test_gem.py (https://github.com/ansible-collections/community.general/pull/5065).
|
||||
- All software licenses are now in the ``LICENSES/`` directory of the collection root (https://github.com/ansible-collections/community.general/pull/5065, https://github.com/ansible-collections/community.general/pull/5079, https://github.com/ansible-collections/community.general/pull/5080, https://github.com/ansible-collections/community.general/pull/5083, https://github.com/ansible-collections/community.general/pull/5087, https://github.com/ansible-collections/community.general/pull/5095, https://github.com/ansible-collections/community.general/pull/5098, https://github.com/ansible-collections/community.general/pull/5106).
|
||||
- ModuleHelper module utils - added property ``verbosity`` to base class (https://github.com/ansible-collections/community.general/pull/5035).
|
||||
- ModuleHelper module utils - improved ``ModuleHelperException``, using ``to_native()`` for the exception message (https://github.com/ansible-collections/community.general/pull/4755).
|
||||
- The collection repository conforms to the `REUSE specification <https://reuse.software/spec/>`__ except for the changelog fragments (https://github.com/ansible-collections/community.general/pull/5138).
|
||||
- ali_instance - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5240).
|
||||
- ali_instance_info - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5240).
|
||||
- alternatives - add ``state=absent`` to be able to remove an alternative (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- alternatives - add ``subcommands`` parameter (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- ansible_galaxy_install - minor refactoring using latest ``ModuleHelper`` updates (https://github.com/ansible-collections/community.general/pull/4752).
|
||||
- ansible_galaxy_install - refactored module to use ``CmdRunner`` to execute ``ansible-galaxy`` (https://github.com/ansible-collections/community.general/pull/5477).
|
||||
- apk - add ``world`` parameter for supporting a custom world file (https://github.com/ansible-collections/community.general/pull/4976).
|
||||
- bitwarden lookup plugin - add option ``search`` to search for other attributes than name (https://github.com/ansible-collections/community.general/pull/5297).
|
||||
- cartesian lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- cmd_runner module util - added parameters ``check_mode_skip`` and ``check_mode_return`` to ``CmdRunner.context()``, so that the command is not executed when ``check_mode=True`` (https://github.com/ansible-collections/community.general/pull/4736).
|
||||
- cmd_runner module utils - add ``__call__`` method to invoke context (https://github.com/ansible-collections/community.general/pull/4791).
|
||||
- consul - adds ``ttl`` parameter for session (https://github.com/ansible-collections/community.general/pull/4996).
|
||||
- consul - minor refactoring (https://github.com/ansible-collections/community.general/pull/5367).
|
||||
- consul_session - adds ``token`` parameter for session (https://github.com/ansible-collections/community.general/pull/5193).
|
||||
- cpanm - refactored module to use ``CmdRunner`` to execute ``cpanm`` (https://github.com/ansible-collections/community.general/pull/5485).
|
||||
- cpanm - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- credstash lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- dependent lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- dig lookup plugin - add option ``fail_on_error`` to allow stopping execution on lookup failures (https://github.com/ansible-collections/community.general/pull/4973).
|
||||
- dig lookup plugin - start using Ansible's configuration manager to parse options. All documented options can now also be passed as lookup parameters (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- dnstxt lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- filetree lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- flattened lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- gitlab module util - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_branch - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_deploy_key - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_group - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_group_members - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_group_variable - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_hook - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_hook - minor refactoring (https://github.com/ansible-collections/community.general/pull/5271).
|
||||
- gitlab_project - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_project_members - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_project_variable - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_protected_branch - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_runner - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- gitlab_user - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259).
|
||||
- hiera lookup plugin - start using Ansible's configuration manager to parse options. The Hiera executable and config file can now also be passed as lookup parameters (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- homebrew, homebrew_tap - added Homebrew on Linux path to defaults (https://github.com/ansible-collections/community.general/pull/5241).
|
||||
- hponcfg - refactored module to use ``CmdRunner`` to execute ``hponcfg`` (https://github.com/ansible-collections/community.general/pull/5483).
|
||||
- keycloak_* modules - add ``http_agent`` parameter with default value ``Ansible`` (https://github.com/ansible-collections/community.general/issues/5023).
|
||||
- keyring lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- lastpass - use config manager for handling plugin options (https://github.com/ansible-collections/community.general/pull/5022).
|
||||
- ldap_attrs - allow for DNs to have ``{x}`` prefix on first RDN (https://github.com/ansible-collections/community.general/issues/977, https://github.com/ansible-collections/community.general/pull/5450).
|
||||
- linode inventory plugin - simplify option handling (https://github.com/ansible-collections/community.general/pull/5438).
|
||||
- listen_ports_facts - add new ``include_non_listening`` option which adds ``-a`` option to ``netstat`` and ``ss``. This shows both listening and non-listening (for TCP this means established connections) sockets, and returns ``state`` and ``foreign_address`` (https://github.com/ansible-collections/community.general/issues/4762, https://github.com/ansible-collections/community.general/pull/4953).
|
||||
- lmdb_kv lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- lxc_container - minor refactoring (https://github.com/ansible-collections/community.general/pull/5358).
|
||||
- machinectl become plugin - can now be used with a password from another user than root, if a polkit rule is present (https://github.com/ansible-collections/community.general/pull/4849).
|
||||
- machinectl become plugin - combine the success command when building the become command to be consistent with other become plugins (https://github.com/ansible-collections/community.general/pull/5287).
|
||||
- manifold lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- maven_artifact - add a new ``unredirected_headers`` option that can be used with ansible-core 2.12 and above. The default value is to not use ``Authorization`` and ``Cookie`` headers on redirects for security reasons. With ansible-core 2.11, all headers are still passed on for redirects (https://github.com/ansible-collections/community.general/pull/4812).
|
||||
- mksysb - refactored module to use ``CmdRunner`` to execute ``mksysb`` (https://github.com/ansible-collections/community.general/pull/5484).
|
||||
- mksysb - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- nagios - minor refactoring on parameter validation for different actions (https://github.com/ansible-collections/community.general/pull/5239).
|
||||
- netcup_dnsapi - add ``timeout`` parameter (https://github.com/ansible-collections/community.general/pull/5301).
|
||||
- nmcli - add ``transport_mode`` configuration for Infiniband devices (https://github.com/ansible-collections/community.general/pull/5361).
|
||||
- nmcli - add bond option ``xmit_hash_policy`` to bond options (https://github.com/ansible-collections/community.general/issues/5148).
|
||||
- nmcli - adds ``vpn`` type and parameter for supporting VPN with service type L2TP and PPTP (https://github.com/ansible-collections/community.general/pull/4746).
|
||||
- nmcli - honor IP options for VPNs (https://github.com/ansible-collections/community.general/pull/5228).
|
||||
- onepassword - support version 2 of the OnePassword CLI (https://github.com/ansible-collections/community.general/pull/4728)
|
||||
- opentelemetry callback plugin - allow configuring opentelementry callback via config file (https://github.com/ansible-collections/community.general/pull/4916).
|
||||
- opentelemetry callback plugin - send logs. This can be disabled by setting ``disable_logs=false`` (https://github.com/ansible-collections/community.general/pull/4175).
|
||||
- pacman - added parameters ``reason`` and ``reason_for`` to set/change the install reason of packages (https://github.com/ansible-collections/community.general/pull/4956).
|
||||
- passwordstore lookup plugin - allow options to be passed lookup options instead of being part of the term strings (https://github.com/ansible-collections/community.general/pull/5444).
|
||||
- passwordstore lookup plugin - allow using alternative password managers by detecting wrapper scripts, allow explicit configuration of pass and gopass backends (https://github.com/ansible-collections/community.general/issues/4766).
|
||||
- passwordstore lookup plugin - improve error messages to include stderr (https://github.com/ansible-collections/community.general/pull/5436)
|
||||
- pipx - added state ``latest`` to the module (https://github.com/ansible-collections/community.general/pull/5105).
|
||||
- pipx - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/5085).
|
||||
- pipx - module fails faster when ``name`` is missing for states ``upgrade`` and ``reinstall`` (https://github.com/ansible-collections/community.general/pull/5100).
|
||||
- pipx - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- pipx module utils - created new module util ``pipx`` providing a ``cmd_runner`` specific for the ``pipx`` module (https://github.com/ansible-collections/community.general/pull/5085).
|
||||
- portage - add knobs for Portage's ``--backtrack`` and ``--with-bdeps`` options (https://github.com/ansible-collections/community.general/pull/5349).
|
||||
- portage - use Portage's python module instead of calling gentoolkit-provided program in shell (https://github.com/ansible-collections/community.general/pull/5349).
|
||||
- proxmox inventory plugin - added new flag ``qemu_extended_statuses`` and new groups ``<group_prefix>prelaunch``, ``<group_prefix>paused``. They will be populated only when ``want_facts=true``, ``qemu_extended_statuses=true`` and only for ``QEMU`` machines (https://github.com/ansible-collections/community.general/pull/4723).
|
||||
- proxmox inventory plugin - simplify option handling code (https://github.com/ansible-collections/community.general/pull/5437).
|
||||
- proxmox module utils, the proxmox* modules - add ``api_task_ok`` helper to standardize API task status checks across all proxmox modules (https://github.com/ansible-collections/community.general/pull/5274).
|
||||
- proxmox_kvm - allow ``agent`` argument to be a string (https://github.com/ansible-collections/community.general/pull/5107).
|
||||
- proxmox_snap - add ``unbind`` param to support snapshotting containers with configured mountpoints (https://github.com/ansible-collections/community.general/pull/5274).
|
||||
- puppet - adds ``confdir`` parameter to configure a custom confir location (https://github.com/ansible-collections/community.general/pull/4740).
|
||||
- redfish - added new command GetVirtualMedia, VirtualMediaInsert and VirtualMediaEject to Systems category due to Redfish spec changes the virtualMedia resource location from Manager to System (https://github.com/ansible-collections/community.general/pull/5124).
|
||||
- redfish_config - add ``SetSessionService`` to set default session timeout policy (https://github.com/ansible-collections/community.general/issues/5008).
|
||||
- redfish_info - add ``GetManagerInventory`` to report list of Manager inventory information (https://github.com/ansible-collections/community.general/issues/4899).
|
||||
- seport - added new argument ``local`` (https://github.com/ansible-collections/community.general/pull/5203)
|
||||
- snap - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- sudoers - will attempt to validate the proposed sudoers rule using visudo if available, optionally skipped, or required (https://github.com/ansible-collections/community.general/pull/4794, https://github.com/ansible-collections/community.general/issues/4745).
|
||||
- terraform - adds capability to handle complex variable structures for ``variables`` parameter in the module. This must be enabled with the new ``complex_vars`` parameter (https://github.com/ansible-collections/community.general/pull/4797).
|
||||
- terraform - run ``terraform init`` with ``-no-color`` not to mess up the stdout of the task (https://github.com/ansible-collections/community.general/pull/5147).
|
||||
- wdc_redfish_command - add ``IndicatorLedOn`` and ``IndicatorLedOff`` commands for ``Chassis`` category (https://github.com/ansible-collections/community.general/pull/5059).
|
||||
- wdc_redfish_command - add ``PowerModeLow`` and ``PowerModeNormal`` commands for ``Chassis`` category (https://github.com/ansible-collections/community.general/pull/5145).
|
||||
- xfconf - add ``stdout``, ``stderr`` and ``cmd`` to the module results (https://github.com/ansible-collections/community.general/pull/5037).
|
||||
- xfconf - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf - use ``do_raise()`` instead of defining custom exception class (https://github.com/ansible-collections/community.general/pull/4975).
|
||||
- xfconf - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- xfconf module utils - created new module util ``xfconf`` providing a ``cmd_runner`` specific for ``xfconf`` modules (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf_info - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf_info - use ``do_raise()`` instead of defining custom exception class (https://github.com/ansible-collections/community.general/pull/4975).
|
||||
- znode - possibility to use ZooKeeper ACL authentication (https://github.com/ansible-collections/community.general/pull/5306).
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- newrelic_deployment - ``revision`` is required for v2 API (https://github.com/ansible-collections/community.general/pull/5341).
|
||||
- scaleway_container_registry_info - no longer replace ``secret_environment_variables`` in the output by ``SENSITIVE_VALUE`` (https://github.com/ansible-collections/community.general/pull/5497).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- ArgFormat module utils - deprecated along ``CmdMixin``, in favor of the ``cmd_runner_fmt`` module util (https://github.com/ansible-collections/community.general/pull/5370).
|
||||
- CmdMixin module utils - deprecated in favor of the ``CmdRunner`` module util (https://github.com/ansible-collections/community.general/pull/5370).
|
||||
- CmdModuleHelper module utils - deprecated in favor of the ``CmdRunner`` module util (https://github.com/ansible-collections/community.general/pull/5370).
|
||||
- CmdStateModuleHelper module utils - deprecated in favor of the ``CmdRunner`` module util (https://github.com/ansible-collections/community.general/pull/5370).
|
||||
- cmd_runner module utils - deprecated ``fmt`` in favour of ``cmd_runner_fmt`` as the parameter format object (https://github.com/ansible-collections/community.general/pull/4777).
|
||||
- django_manage - support for Django releases older than 4.1 has been deprecated and will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5400).
|
||||
- django_manage - support for the commands ``cleanup``, ``syncdb`` and ``validate`` that have been deprecated in Django long time ago will be removed in community.general 9.0.0 (https://github.com/ansible-collections/community.general/pull/5400).
|
||||
- django_manage - the behavior of "creating the virtual environment when missing" is being deprecated and will be removed in community.general version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5405).
|
||||
- gconftool2 - deprecates ``state=get`` in favor of using the module ``gconftool2_info`` (https://github.com/ansible-collections/community.general/pull/4778).
|
||||
- lxc_container - the module will no longer make any effort to support Python 2 (https://github.com/ansible-collections/community.general/pull/5304).
|
||||
- newrelic_deployment - ``appname`` and ``environment`` are no longer valid options in the v2 API. They will be removed in community.general 7.0.0 (https://github.com/ansible-collections/community.general/pull/5341).
|
||||
- proxmox - deprecated the current ``unprivileged`` default value, will be changed to ``true`` in community.general 7.0.0 (https://github.com/pull/5224).
|
||||
- xfconf - deprecated parameter ``disable_facts``, as since version 4.0.0 it only allows value ``true`` (https://github.com/ansible-collections/community.general/pull/4520).
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- bitbucket* modules - ``username`` is no longer an alias of ``workspace``, but of ``user`` (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- gem - the default of the ``norc`` option changed from ``false`` to ``true`` (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- gitlab_group_members - ``gitlab_group`` must now always contain the full path, and no longer just the name or path (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- keycloak_authentication - the return value ``flow`` has been removed. Use ``end_state`` instead (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- keycloak_group - the return value ``group`` has been removed. Use ``end_state`` instead (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- lxd_container - the default of the ``ignore_volatile_options`` option changed from ``true`` to ``false`` (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- mail callback plugin - the ``sender`` option is now required (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- module_helper module utils - remove the ``VarDict`` attribute from ``ModuleHelper``. Import ``VarDict`` from ``ansible_collections.community.general.plugins.module_utils.mh.mixins.vars`` instead (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- proxmox inventory plugin - the default of the ``want_proxmox_nodes_ansible_host`` option changed from ``true`` to ``false`` (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
- vmadm - the ``debug`` option has been removed. It was not used anyway (https://github.com/ansible-collections/community.general/pull/5326).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_mount.py``.
|
||||
- Include ``simplified_bsd.txt`` license file for various module utils, the ``lxca_common`` docs fragment, and the ``utm_utils`` unit tests.
|
||||
- alternatives - do not set the priority if the priority was not set by the user (https://github.com/ansible-collections/community.general/pull/4810).
|
||||
- alternatives - only pass subcommands when they are specified as module arguments (https://github.com/ansible-collections/community.general/issues/4803, https://github.com/ansible-collections/community.general/issues/4804, https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- alternatives - when ``subcommands`` is specified, ``link`` must be given for every subcommand. This was already mentioned in the documentation, but not enforced by the code (https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- apache2_mod_proxy - avoid crash when reporting inability to parse balancer_member_page HTML caused by using an undefined variable in the error message (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- archive - avoid crash when ``lzma`` is not present and ``format`` is not ``xz`` (https://github.com/ansible-collections/community.general/pull/5393).
|
||||
- cmd_runner module utils - fix bug caused by using the ``command`` variable instead of ``self.command`` when looking for binary path (https://github.com/ansible-collections/community.general/pull/4903).
|
||||
- consul - fixed bug introduced in PR 4590 (https://github.com/ansible-collections/community.general/issues/4680).
|
||||
- credstash lookup plugin - pass plugin options to credstash for all terms, not just for the first (https://github.com/ansible-collections/community.general/pull/5440).
|
||||
- dig lookup plugin - add option to return empty result without empty strings, and return empty list instead of ``NXDOMAIN`` (https://github.com/ansible-collections/community.general/pull/5439, https://github.com/ansible-collections/community.general/issues/5428).
|
||||
- dig lookup plugin - fix evaluation of falsy values for boolean parameters ``fail_on_error`` and ``retry_servfail`` (https://github.com/ansible-collections/community.general/pull/5129).
|
||||
- dnsimple_info - correctly report missing library as ``requests`` and not ``another_library`` (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- dnstxt lookup plugin - add option to return empty result without empty strings, and return empty list instead of ``NXDOMAIN`` (https://github.com/ansible-collections/community.general/pull/5457, https://github.com/ansible-collections/community.general/issues/5428).
|
||||
- dsv lookup plugin - do not ignore the ``tld`` parameter (https://github.com/ansible-collections/community.general/pull/4911).
|
||||
- filesystem - handle ``fatresize --info`` output lines without ``:`` (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- filesystem - improve error messages when output cannot be parsed by including newlines in escaped form (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- funcd connection plugin - fix signature of ``exec_command`` (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- ini_file - minor refactor fixing a python lint error (https://github.com/ansible-collections/community.general/pull/5307).
|
||||
- iso_create - the module somtimes failed to add folders for Joliet and UDF formats (https://github.com/ansible-collections/community.general/issues/5275).
|
||||
- keycloak_realm - fix default groups and roles (https://github.com/ansible-collections/community.general/issues/4241).
|
||||
- keyring_info - fix the result from the keyring library never getting returned (https://github.com/ansible-collections/community.general/pull/4964).
|
||||
- ldap_attrs - fix bug which caused a ``Bad search filter`` error. The error was occuring when the ldap attribute value contained special characters such as ``(`` or ``*`` (https://github.com/ansible-collections/community.general/issues/5434, https://github.com/ansible-collections/community.general/pull/5435).
|
||||
- ldap_attrs - fix ordering issue by ignoring the ``{x}`` prefix on attribute values (https://github.com/ansible-collections/community.general/issues/977, https://github.com/ansible-collections/community.general/pull/5385).
|
||||
- listen_ports_facts - removed leftover ``EnvironmentError`` . The ``else`` clause had a wrong indentation. The check is now handled in the ``split_pid_name`` function (https://github.com/ansible-collections/community.general/pull/5202).
|
||||
- locale_gen - fix support for Ubuntu (https://github.com/ansible-collections/community.general/issues/5281).
|
||||
- lxc_container - the module has been updated to support Python 3 (https://github.com/ansible-collections/community.general/pull/5304).
|
||||
- lxd connection plugin - fix incorrect ``inventory_hostname`` in ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/issues/4886).
|
||||
- manageiq_alert_profiles - avoid crash when reporting unknown profile caused by trying to return an undefined variable (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- nmcli - avoid changed status for most cases with VPN connections (https://github.com/ansible-collections/community.general/pull/5126).
|
||||
- nmcli - fix error caused by adding undefined module arguments for list options (https://github.com/ansible-collections/community.general/issues/4373, https://github.com/ansible-collections/community.general/pull/4813).
|
||||
- nmcli - fix error when setting previously unset MAC address, ``gsm.apn`` or ``vpn.data``: current values were being normalized without checking if they might be ``None`` (https://github.com/ansible-collections/community.general/pull/5291).
|
||||
- nmcli - fix int options idempotence (https://github.com/ansible-collections/community.general/issues/4998).
|
||||
- nsupdate - compatibility with NS records (https://github.com/ansible-collections/community.general/pull/5112).
|
||||
- nsupdate - fix silent failures when updating ``NS`` entries from Bind9 managed DNS zones (https://github.com/ansible-collections/community.general/issues/4657).
|
||||
- opentelemetry callback plugin - support opentelemetry-api 1.13.0 that removed support for ``_time_ns`` (https://github.com/ansible-collections/community.general/pull/5342).
|
||||
- osx_defaults - no longer expand ``~`` in ``value`` to the user's home directory, or expand environment variables (https://github.com/ansible-collections/community.general/issues/5234, https://github.com/ansible-collections/community.general/pull/5243).
|
||||
- packet_ip_subnet - fix error reporting in case of invalid CIDR prefix lengths (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- pacman - fixed name resolution of URL packages (https://github.com/ansible-collections/community.general/pull/4959).
|
||||
- passwordstore lookup plugin - fix ``returnall`` for gopass (https://github.com/ansible-collections/community.general/pull/5027).
|
||||
- passwordstore lookup plugin - fix password store path detection for gopass (https://github.com/ansible-collections/community.general/pull/4955).
|
||||
- pfexec become plugin - remove superflous quotes preventing exe wrap from working as expected (https://github.com/ansible-collections/community.general/issues/3671, https://github.com/ansible-collections/community.general/pull/3889).
|
||||
- pip_package_info - remove usage of global variable (https://github.com/ansible-collections/community.general/pull/5111).
|
||||
- pkgng - fix case when ``pkg`` fails when trying to upgrade all packages (https://github.com/ansible-collections/community.general/issues/5363).
|
||||
- proxmox - fix error handling when getting VM by name when ``state=absent`` (https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- proxmox inventory plugin - fix crash when ``enabled=1`` is used in agent config string (https://github.com/ansible-collections/community.general/pull/4910).
|
||||
- proxmox inventory plugin - fixed extended status detection for qemu (https://github.com/ansible-collections/community.general/pull/4816).
|
||||
- proxmox_kvm - fix ``agent`` parameter when boolean value is specified (https://github.com/ansible-collections/community.general/pull/5198).
|
||||
- proxmox_kvm - fix error handling when getting VM by name when ``state=absent`` (https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- proxmox_kvm - fix exception when no ``agent`` argument is specified (https://github.com/ansible-collections/community.general/pull/5194).
|
||||
- proxmox_kvm - fix wrong condition (https://github.com/ansible-collections/community.general/pull/5108).
|
||||
- proxmox_kvm - replace new condition with proper condition to allow for using ``vmid`` on update (https://github.com/ansible-collections/community.general/pull/5206).
|
||||
- rax_clb_nodes - fix code to be compatible with Python 3 (https://github.com/ansible-collections/community.general/pull/4933).
|
||||
- redfish_command - fix the check if a virtual media is unmounted to just check for ``instered= false`` caused by Supermicro hardware that does not clear the ``ImageName`` (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- redfish_command - the Supermicro Redfish implementation only supports the ``image_url`` parameter in the underlying API calls to ``VirtualMediaInsert`` and ``VirtualMediaEject``. Any values set (or the defaults) for ``write_protected`` or ``inserted`` will be ignored (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- redfish_info - fix to ``GetChassisPower`` to correctly report power information when multiple chassis exist, but not all chassis report power information (https://github.com/ansible-collections/community.general/issues/4901).
|
||||
- redfish_utils module utils - centralize payload checking when performing modification requests to a Redfish service (https://github.com/ansible-collections/community.general/issues/5210/).
|
||||
- redhat_subscription - fix unsubscribing on RHEL 9 (https://github.com/ansible-collections/community.general/issues/4741).
|
||||
- redhat_subscription - make module idempotent when ``pool_ids`` are used (https://github.com/ansible-collections/community.general/issues/5313).
|
||||
- redis* modules - fix call to ``module.fail_json`` when failing because of missing Python libraries (https://github.com/ansible-collections/community.general/pull/4733).
|
||||
- slack - fix incorrect channel prefix ``#`` caused by incomplete pattern detection by adding ``G0`` and ``GF`` as channel ID patterns (https://github.com/ansible-collections/community.general/pull/5019).
|
||||
- slack - fix message update for channels which start with ``CP``. When ``message-id`` was passed it failed for channels which started with ``CP`` because the ``#`` symbol was added before the ``channel_id`` (https://github.com/ansible-collections/community.general/pull/5249).
|
||||
- snap - allow values in the ``options`` parameter to contain whitespaces (https://github.com/ansible-collections/community.general/pull/5475).
|
||||
- sudoers - ensure sudoers config files are created with the permissions requested by sudoers (0440) (https://github.com/ansible-collections/community.general/pull/4814).
|
||||
- sudoers - fix incorrect handling of ``state: absent`` (https://github.com/ansible-collections/community.general/issues/4852).
|
||||
- tss lookup plugin - adding support for updated Delinea library (https://github.com/DelineaXPM/python-tss-sdk/issues/9, https://github.com/ansible-collections/community.general/pull/5151).
|
||||
- virtualbox inventory plugin - skip parsing values with keys that have both a value and nested data. Skip parsing values that are nested more than two keys deep (https://github.com/ansible-collections/community.general/issues/5332, https://github.com/ansible-collections/community.general/pull/5348).
|
||||
- xcc_redfish_command - for compatibility due to Redfish spec changes the virtualMedia resource location changed from Manager to System (https://github.com/ansible-collections/community.general/pull/4682).
|
||||
- xenserver_facts - fix broken ``AnsibleModule`` call that prevented the module from working at all (https://github.com/ansible-collections/community.general/pull/5383).
|
||||
- xfconf - fix setting of boolean values (https://github.com/ansible-collections/community.general/issues/4999, https://github.com/ansible-collections/community.general/pull/5007).
|
||||
- zfs - fix wrong quoting of properties (https://github.com/ansible-collections/community.general/issues/4707, https://github.com/ansible-collections/community.general/pull/4726).
|
||||
|
||||
New Plugins
|
||||
-----------
|
||||
|
||||
Filter
|
||||
~~~~~~
|
||||
|
||||
- counter - Counts hashable elements in a sequence
|
||||
|
||||
Lookup
|
||||
~~~~~~
|
||||
|
||||
- bitwarden - Retrieve secrets from Bitwarden
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- gconftool2_info - Retrieve GConf configurations
|
||||
- iso_customize - Add/remove/change files in ISO file
|
||||
- keycloak_user_rolemapping - Allows administration of Keycloak user_rolemapping with the Keycloak API
|
||||
- keyring - Set or delete a passphrase using the Operating System's native keyring
|
||||
- keyring_info - Get a passphrase using the Operating System's native keyring
|
||||
- manageiq_policies_info - Listing of resource policy_profiles in ManageIQ
|
||||
- manageiq_tags_info - Retrieve resource tags in ManageIQ
|
||||
- pipx_info - Rretrieves information about applications installed with pipx
|
||||
- proxmox_disk - Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster.
|
||||
- scaleway_compute_private_network - Scaleway compute - private network management
|
||||
- scaleway_container - Scaleway Container management
|
||||
- scaleway_container_info - Retrieve information on Scaleway Container
|
||||
- scaleway_container_namespace - Scaleway Container namespace management
|
||||
- scaleway_container_namespace_info - Retrieve information on Scaleway Container namespace
|
||||
- scaleway_container_registry - Scaleway Container registry management module
|
||||
- scaleway_container_registry_info - Scaleway Container registry info module
|
||||
- scaleway_function - Scaleway Function management
|
||||
- scaleway_function_info - Retrieve information on Scaleway Function
|
||||
- scaleway_function_namespace - Scaleway Function namespace management
|
||||
- scaleway_function_namespace_info - Retrieve information on Scaleway Function namespace
|
||||
- wdc_redfish_command - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs
|
||||
- wdc_redfish_info - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs
|
||||
|
||||
@@ -31,7 +31,7 @@ Also, consider taking up a valuable, reviewed, but abandoned pull request which
|
||||
* Try committing your changes with an informative but short commit message.
|
||||
* Do not squash your commits and force-push to your branch if not needed. Reviews of your pull request are much easier with individual commits to comprehend the pull request history. All commits of your pull request branch will be squashed into one commit by GitHub upon merge.
|
||||
* Do not add merge commits to your PR. The bot will complain and you will have to rebase ([instructions for rebasing](https://docs.ansible.com/ansible/latest/dev_guide/developing_rebasing.html)) to remove them before your PR can be merged. To avoid that git automatically does merges during pulls, you can configure it to do rebases instead by running `git config pull.rebase true` inside the repository checkout.
|
||||
* Make sure your PR includes a [changelog fragment](https://docs.ansible.com/ansible/devel/community/development_process.html#creating-changelog-fragments). (You must not include a fragment for new modules or new plugins. Also you shouldn't include one for docs-only changes. If you're not sure, simply don't include one, we'll tell you whether one is needed or not :) )
|
||||
* Make sure your PR includes a [changelog fragment](https://docs.ansible.com/ansible/devel/community/development_process.html#creating-changelog-fragments). (You must not include a fragment for new modules or new plugins, except for test and filter plugins. Also you shouldn't include one for docs-only changes. If you're not sure, simply don't include one, we'll tell you whether one is needed or not :) )
|
||||
* Avoid reformatting unrelated parts of the codebase in your PR. These types of changes will likely be requested for reversion, create additional work for reviewers, and may cause approval to be delayed.
|
||||
|
||||
You can also read [our Quick-start development guide](https://github.com/ansible/community-docs/blob/main/create_pr_quick_start_guide.rst).
|
||||
|
||||
16
README.md
16
README.md
@@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Community General Collection
|
||||
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://github.com/ansible-collections/community.general/actions)
|
||||
[](https://codecov.io/gh/ansible-collections/community.general)
|
||||
|
||||
@@ -73,13 +73,13 @@ We are actively accepting new contributors.
|
||||
|
||||
All types of contributions are very welcome.
|
||||
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/stable-7/CONTRIBUTING.md)!
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/stable-6/CONTRIBUTING.md)!
|
||||
|
||||
The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/stable-7/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals.
|
||||
The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/stable-6/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals.
|
||||
|
||||
You can find more information in the [developer guide for collections](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections), and in the [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html).
|
||||
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/stable-7/CONTRIBUTING.md).
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/stable-6/CONTRIBUTING.md).
|
||||
|
||||
### Running tests
|
||||
|
||||
@@ -89,7 +89,7 @@ See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collectio
|
||||
|
||||
To learn how to maintain / become a maintainer of this collection, refer to:
|
||||
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/stable-7/commit-rights.md).
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/stable-6/commit-rights.md).
|
||||
* [Maintainer guidelines](https://github.com/ansible/community-docs/blob/main/maintaining.rst).
|
||||
|
||||
It is necessary for maintainers of this collection to be subscribed to:
|
||||
@@ -117,7 +117,7 @@ See the [Releasing guidelines](https://github.com/ansible/community-docs/blob/ma
|
||||
|
||||
## Release notes
|
||||
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-7/CHANGELOG.rst).
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-6/CHANGELOG.rst).
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -136,8 +136,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues
|
||||
|
||||
This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later.
|
||||
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-7/COPYING) for the full text.
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-6/COPYING) for the full text.
|
||||
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/PSF-2.0.txt).
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/PSF-2.0.txt).
|
||||
|
||||
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace: community
|
||||
name: general
|
||||
version: 7.0.0
|
||||
version: 6.4.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
128
meta/runtime.yml
128
meta/runtime.yml
@@ -22,120 +22,6 @@ plugin_routing:
|
||||
nios_next_network:
|
||||
redirect: infoblox.nios_modules.nios_next_network
|
||||
modules:
|
||||
rax_cbs_attachments:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_cbs:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_cdb_database:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_cdb_user:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_cdb:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_clb_nodes:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_clb_ssl:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_clb:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_dns_record:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_dns:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_facts:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_files_objects:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_files:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_identity:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_keypair:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_meta:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_mon_alarm:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_mon_check:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_mon_entity:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_mon_notification_plan:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_mon_notification:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_network:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_queue:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_scaling_group:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rax_scaling_policy:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relies on the deprecated package pyrax.
|
||||
rhn_channel:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL, please contact the community.general maintainers
|
||||
if still using this; see the module documentation for more details.
|
||||
rhn_register:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL, please contact the community.general maintainers
|
||||
if still using this; see the module documentation for more details.
|
||||
database.aerospike.aerospike_migrations:
|
||||
redirect: community.general.aerospike_migrations
|
||||
deprecation:
|
||||
@@ -1086,8 +972,6 @@ plugin_routing:
|
||||
warning_text: You are using an internal name to access the community.general.heroku_collaborator
|
||||
modules. This has never been supported or documented, and will stop working
|
||||
in community.general 9.0.0.
|
||||
hana_query:
|
||||
redirect: community.sap_libs.sap_hdbsql
|
||||
hetzner_failover_ip:
|
||||
redirect: community.hrobot.failover_ip
|
||||
hetzner_failover_ip_info:
|
||||
@@ -3505,10 +3389,6 @@ plugin_routing:
|
||||
warning_text: You are using an internal name to access the community.general.redfish_info
|
||||
modules. This has never been supported or documented, and will stop working
|
||||
in community.general 9.0.0.
|
||||
sapcar_extract:
|
||||
redirect: community.sap_libs.sapcar_extract
|
||||
sap_task_list_execute:
|
||||
redirect: community.sap_libs.sap_task_list_execute
|
||||
packaging.os.redhat_subscription:
|
||||
redirect: community.general.redhat_subscription
|
||||
deprecation:
|
||||
@@ -4613,10 +4493,6 @@ plugin_routing:
|
||||
modules. This has never been supported or documented, and will stop working
|
||||
in community.general 9.0.0.
|
||||
doc_fragments:
|
||||
rackspace:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This doc fragment is used by rax modules, that rely on the deprecated package pyrax.
|
||||
_gcp:
|
||||
redirect: community.google._gcp
|
||||
docker:
|
||||
@@ -4632,10 +4508,6 @@ plugin_routing:
|
||||
postgresql:
|
||||
redirect: community.postgresql.postgresql
|
||||
module_utils:
|
||||
rax:
|
||||
deprecation:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module util relies on the deprecated package pyrax.
|
||||
docker.common:
|
||||
redirect: community.docker.common
|
||||
docker.swarm:
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
||||
@@ -81,6 +80,13 @@ class ActionModule(ActionBase):
|
||||
getattr(self, default_value))))
|
||||
return value
|
||||
|
||||
def get_shutdown_command_args(self, distribution):
|
||||
args = self._get_value_from_facts('SHUTDOWN_COMMAND_ARGS', distribution, 'DEFAULT_SHUTDOWN_COMMAND_ARGS')
|
||||
# Convert seconds to minutes. If less that 60, set it to 0.
|
||||
delay_sec = self.delay
|
||||
shutdown_message = self._task.args.get('msg', self.DEFAULT_SHUTDOWN_MESSAGE)
|
||||
return args.format(delay_sec=delay_sec, delay_min=delay_sec // 60, message=shutdown_message)
|
||||
|
||||
def get_distribution(self, task_vars):
|
||||
# FIXME: only execute the module if we don't already have the facts we need
|
||||
distribution = {}
|
||||
@@ -95,8 +101,7 @@ class ActionModule(ActionBase):
|
||||
to_native(module_output['module_stdout']).strip(),
|
||||
to_native(module_output['module_stderr']).strip()))
|
||||
distribution['name'] = module_output['ansible_facts']['ansible_distribution'].lower()
|
||||
distribution['version'] = to_text(
|
||||
module_output['ansible_facts']['ansible_distribution_version'].split('.')[0])
|
||||
distribution['version'] = to_text(module_output['ansible_facts']['ansible_distribution_version'].split('.')[0])
|
||||
distribution['family'] = to_text(module_output['ansible_facts']['ansible_os_family'].lower())
|
||||
display.debug("{action}: distribution: {dist}".format(action=self._task.action, dist=distribution))
|
||||
return distribution
|
||||
@@ -104,23 +109,6 @@ class ActionModule(ActionBase):
|
||||
raise AnsibleError('Failed to get distribution information. Missing "{0}" in output.'.format(ke.args[0]))
|
||||
|
||||
def get_shutdown_command(self, task_vars, distribution):
|
||||
def find_command(command, find_search_paths):
|
||||
display.debug('{action}: running find module looking in {paths} to get path for "{command}"'.format(
|
||||
action=self._task.action,
|
||||
command=command,
|
||||
paths=find_search_paths))
|
||||
find_result = self._execute_module(
|
||||
task_vars=task_vars,
|
||||
# prevent collection search by calling with ansible.legacy (still allows library/ override of find)
|
||||
module_name='ansible.legacy.find',
|
||||
module_args={
|
||||
'paths': find_search_paths,
|
||||
'patterns': [command],
|
||||
'file_type': 'any'
|
||||
}
|
||||
)
|
||||
return [x['path'] for x in find_result['files']]
|
||||
|
||||
shutdown_bin = self._get_value_from_facts('SHUTDOWN_COMMANDS', distribution, 'DEFAULT_SHUTDOWN_COMMAND')
|
||||
default_search_paths = ['/sbin', '/usr/sbin', '/usr/local/sbin']
|
||||
search_paths = self._task.args.get('search_paths', default_search_paths)
|
||||
@@ -139,53 +127,45 @@ class ActionModule(ActionBase):
|
||||
except TypeError:
|
||||
raise AnsibleError(err_msg.format(search_paths))
|
||||
|
||||
full_path = find_command(shutdown_bin, search_paths) # find the path to the shutdown command
|
||||
if not full_path: # if we could not find the shutdown command
|
||||
display.vvv('Unable to find command "{0}" in search paths: {1}, will attempt a shutdown using systemd '
|
||||
'directly.'.format(shutdown_bin, search_paths)) # tell the user we will try with systemd
|
||||
systemctl_search_paths = ['/bin', '/usr/bin']
|
||||
full_path = find_command('systemctl', systemctl_search_paths) # find the path to the systemctl command
|
||||
if not full_path: # if we couldn't find systemctl
|
||||
raise AnsibleError(
|
||||
'Could not find command "{0}" in search paths: {1} or systemctl command in search paths: {2}, unable to shutdown.'.
|
||||
format(shutdown_bin, search_paths, systemctl_search_paths)) # we give up here
|
||||
else:
|
||||
return "{0} poweroff".format(full_path[0]) # done, since we cannot use args with systemd shutdown
|
||||
display.debug('{action}: running find module looking in {paths} to get path for "{command}"'.format(
|
||||
action=self._task.action,
|
||||
command=shutdown_bin,
|
||||
paths=search_paths))
|
||||
find_result = self._execute_module(
|
||||
task_vars=task_vars,
|
||||
# prevent collection search by calling with ansible.legacy (still allows library/ override of find)
|
||||
module_name='ansible.legacy.find',
|
||||
module_args={
|
||||
'paths': search_paths,
|
||||
'patterns': [shutdown_bin],
|
||||
'file_type': 'any'
|
||||
}
|
||||
)
|
||||
|
||||
# systemd case taken care of, here we add args to the command
|
||||
args = self._get_value_from_facts('SHUTDOWN_COMMAND_ARGS', distribution, 'DEFAULT_SHUTDOWN_COMMAND_ARGS')
|
||||
# Convert seconds to minutes. If less that 60, set it to 0.
|
||||
delay_sec = self.delay
|
||||
shutdown_message = self._task.args.get('msg', self.DEFAULT_SHUTDOWN_MESSAGE)
|
||||
return '{0} {1}'. \
|
||||
format(
|
||||
full_path[0],
|
||||
args.format(
|
||||
delay_sec=delay_sec,
|
||||
delay_min=delay_sec // 60,
|
||||
message=shutdown_message
|
||||
)
|
||||
)
|
||||
full_path = [x['path'] for x in find_result['files']]
|
||||
if not full_path:
|
||||
raise AnsibleError('Unable to find command "{0}" in search paths: {1}'.format(shutdown_bin, search_paths))
|
||||
self._shutdown_command = full_path[0]
|
||||
return self._shutdown_command
|
||||
|
||||
def perform_shutdown(self, task_vars, distribution):
|
||||
result = {}
|
||||
shutdown_result = {}
|
||||
shutdown_command_exec = self.get_shutdown_command(task_vars, distribution)
|
||||
shutdown_command = self.get_shutdown_command(task_vars, distribution)
|
||||
shutdown_command_args = self.get_shutdown_command_args(distribution)
|
||||
shutdown_command_exec = '{0} {1}'.format(shutdown_command, shutdown_command_args)
|
||||
|
||||
self.cleanup(force=True)
|
||||
try:
|
||||
display.vvv("{action}: shutting down server...".format(action=self._task.action))
|
||||
display.debug("{action}: shutting down server with command '{command}'".
|
||||
format(action=self._task.action, command=shutdown_command_exec))
|
||||
display.debug("{action}: shutting down server with command '{command}'".format(action=self._task.action, command=shutdown_command_exec))
|
||||
if self._play_context.check_mode:
|
||||
shutdown_result['rc'] = 0
|
||||
else:
|
||||
shutdown_result = self._low_level_execute_command(shutdown_command_exec, sudoable=self.DEFAULT_SUDOABLE)
|
||||
except AnsibleConnectionFailure as e:
|
||||
# If the connection is closed too quickly due to the system being shutdown, carry on
|
||||
display.debug(
|
||||
'{action}: AnsibleConnectionFailure caught and handled: {error}'.format(action=self._task.action,
|
||||
error=to_text(e)))
|
||||
display.debug('{action}: AnsibleConnectionFailure caught and handled: {error}'.format(action=self._task.action, error=to_text(e)))
|
||||
shutdown_result['rc'] = 0
|
||||
|
||||
if shutdown_result['rc'] != 0:
|
||||
|
||||
@@ -22,7 +22,6 @@ DOCUMENTATION = '''
|
||||
- Path to the jail
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: inventory_hostname
|
||||
- name: ansible_host
|
||||
- name: ansible_jail_host
|
||||
remote_user:
|
||||
|
||||
@@ -24,11 +24,6 @@ options:
|
||||
- The password to use with I(bind_dn).
|
||||
type: str
|
||||
default: ''
|
||||
ca_path:
|
||||
description:
|
||||
- Set the path to PEM file with CA certs.
|
||||
type: path
|
||||
version_added: "6.5.0"
|
||||
dn:
|
||||
required: true
|
||||
description:
|
||||
|
||||
@@ -105,10 +105,6 @@ options:
|
||||
- Whether or not to require SSL validation of API endpoints.
|
||||
type: bool
|
||||
aliases: [ verify_ssl ]
|
||||
deprecated:
|
||||
removed_in: 9.0.0
|
||||
why: This module relies on the deprecated package pyrax.
|
||||
alternative: Use the Openstack modules instead.
|
||||
requirements:
|
||||
- python >= 2.6
|
||||
- pyrax
|
||||
|
||||
@@ -30,27 +30,12 @@ DOCUMENTATION = '''
|
||||
address:
|
||||
description: Network IP or range of IPs to scan, you can use a simple range (10.2.2.15-25) or CIDR notation.
|
||||
required: true
|
||||
env:
|
||||
- name: ANSIBLE_NMAP_ADDRESS
|
||||
version_added: 6.6.0
|
||||
exclude:
|
||||
description:
|
||||
- List of addresses to exclude.
|
||||
- For example C(10.2.2.15-25) or C(10.2.2.15,10.2.2.16).
|
||||
description: list of addresses to exclude
|
||||
type: list
|
||||
elements: string
|
||||
env:
|
||||
- name: ANSIBLE_NMAP_EXCLUDE
|
||||
version_added: 6.6.0
|
||||
port:
|
||||
description:
|
||||
- Only scan specific port or port range (C(-p)).
|
||||
- For example, you could pass C(22) for a single port, C(1-65535) for a range of ports,
|
||||
or C(U:53,137,T:21-25,139,8080,S:9) to check port 53 with UDP, ports 21-25 with TCP, port 9 with SCTP, and ports 137, 139, and 8080 with all.
|
||||
type: string
|
||||
version_added: 6.5.0
|
||||
ports:
|
||||
description: Enable/disable scanning ports.
|
||||
description: Enable/disable scanning for open ports
|
||||
type: boolean
|
||||
default: true
|
||||
ipv4:
|
||||
@@ -75,11 +60,6 @@ DOCUMENTATION = '''
|
||||
type: boolean
|
||||
default: false
|
||||
version_added: 6.1.0
|
||||
open:
|
||||
description: Only scan for open (or possibly open) ports.
|
||||
type: boolean
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
dns_resolve:
|
||||
description: Whether to always (C(true)) or never (C(false)) do DNS resolution.
|
||||
type: boolean
|
||||
@@ -101,14 +81,6 @@ plugin: community.general.nmap
|
||||
sudo: true
|
||||
strict: false
|
||||
address: 192.168.0.0/24
|
||||
|
||||
# an nmap scan specifying ports and classifying results to an inventory group
|
||||
plugin: community.general.nmap
|
||||
address: 192.168.0.0/24
|
||||
exclude: 192.168.0.1, web.example.com
|
||||
port: 22, 443
|
||||
groups:
|
||||
web_servers: "ports | selectattr('port', 'equalto', '443')"
|
||||
'''
|
||||
|
||||
import os
|
||||
@@ -199,10 +171,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
if self._options['sudo']:
|
||||
cmd.insert(0, 'sudo')
|
||||
|
||||
if self._options['port']:
|
||||
cmd.append('-p')
|
||||
cmd.append(self._options['port'])
|
||||
|
||||
if not self._options['ports']:
|
||||
cmd.append('-sP')
|
||||
|
||||
@@ -226,9 +194,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
if self._options['icmp_timestamp']:
|
||||
cmd.append('-PP')
|
||||
|
||||
if self._options['open']:
|
||||
cmd.append('--open')
|
||||
|
||||
cmd.append(self._options['address'])
|
||||
try:
|
||||
# execute
|
||||
|
||||
@@ -78,7 +78,6 @@ compose:
|
||||
|
||||
import json
|
||||
import ssl
|
||||
from time import sleep
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
@@ -139,42 +138,21 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
self.conn = create_connection(
|
||||
'{0}://{1}/api/'.format(proto, xoa_api_host), sslopt=sslopt)
|
||||
|
||||
CALL_TIMEOUT = 100
|
||||
"""Number of 1/10ths of a second to wait before method call times out."""
|
||||
|
||||
def call(self, method, params):
|
||||
"""Calls a method on the XO server with the provided parameters."""
|
||||
id = self.pointer
|
||||
self.conn.send(json.dumps({
|
||||
'id': id,
|
||||
'jsonrpc': '2.0',
|
||||
'method': method,
|
||||
'params': params
|
||||
}))
|
||||
|
||||
waited = 0
|
||||
while waited < self.CALL_TIMEOUT:
|
||||
response = json.loads(self.conn.recv())
|
||||
if 'id' in response and response['id'] == id:
|
||||
return response
|
||||
else:
|
||||
sleep(0.1)
|
||||
waited += 1
|
||||
|
||||
raise AnsibleError(
|
||||
'Method call {method} timed out after {timeout} seconds.'.format(method=method, timeout=self.CALL_TIMEOUT / 10))
|
||||
|
||||
def login(self, user, password):
|
||||
result = self.call('session.signIn', {
|
||||
'username': user, 'password': password
|
||||
})
|
||||
payload = {'id': self.pointer, 'jsonrpc': '2.0', 'method': 'session.signIn', 'params': {
|
||||
'username': user, 'password': password}}
|
||||
self.conn.send(json.dumps(payload))
|
||||
result = json.loads(self.conn.recv())
|
||||
|
||||
if 'error' in result:
|
||||
raise AnsibleError(
|
||||
'Could not connect: {0}'.format(result['error']))
|
||||
|
||||
def get_object(self, name):
|
||||
answer = self.call('xo.getAllObjects', {'filter': {'type': name}})
|
||||
payload = {'id': self.pointer, 'jsonrpc': '2.0',
|
||||
'method': 'xo.getAllObjects', 'params': {'filter': {'type': name}}}
|
||||
self.conn.send(json.dumps(payload))
|
||||
answer = json.loads(self.conn.recv())
|
||||
|
||||
if 'error' in answer:
|
||||
raise AnsibleError(
|
||||
|
||||
@@ -61,7 +61,6 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- Return empty result without empty strings, and return empty list instead of C(NXDOMAIN).
|
||||
- The default for this option will likely change to C(true) in the future.
|
||||
- This option will be forced to C(true) if multiple domains to be queried are specified.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 6.0.0
|
||||
@@ -96,21 +95,6 @@ EXAMPLES = """
|
||||
msg: "MX record for gmail.com {{ item }}"
|
||||
with_items: "{{ lookup('community.general.dig', 'gmail.com./MX', wantlist=true) }}"
|
||||
|
||||
- name: Lookup multiple names at once
|
||||
ansible.builtin.debug:
|
||||
msg: "A record found {{ item }}"
|
||||
loop: "{{ query('community.general.dig', 'example.org.', 'example.com.', 'gmail.com.') }}"
|
||||
|
||||
- name: Lookup multiple names at once (from list variable)
|
||||
ansible.builtin.debug:
|
||||
msg: "A record found {{ item }}"
|
||||
loop: "{{ query('community.general.dig', *hosts) }}"
|
||||
vars:
|
||||
hosts:
|
||||
- example.org.
|
||||
- example.com.
|
||||
- gmail.com.
|
||||
|
||||
- ansible.builtin.debug:
|
||||
msg: "Reverse DNS for 192.0.2.5 is {{ lookup('community.general.dig', '192.0.2.5/PTR') }}"
|
||||
- ansible.builtin.debug:
|
||||
@@ -324,7 +308,7 @@ class LookupModule(LookupBase):
|
||||
edns_size = 4096
|
||||
myres.use_edns(0, ednsflags=dns.flags.DO, payload=edns_size)
|
||||
|
||||
domains = []
|
||||
domain = None
|
||||
qtype = self.get_option('qtype')
|
||||
flat = self.get_option('flat')
|
||||
fail_on_error = self.get_option('fail_on_error')
|
||||
@@ -381,71 +365,63 @@ class LookupModule(LookupBase):
|
||||
if '/' in t:
|
||||
try:
|
||||
domain, qtype = t.split('/')
|
||||
domains.append(domain)
|
||||
except Exception:
|
||||
domains.append(t)
|
||||
domain = t
|
||||
else:
|
||||
domains.append(t)
|
||||
domain = t
|
||||
|
||||
# print "--- domain = {0} qtype={1} rdclass={2}".format(domain, qtype, rdclass)
|
||||
|
||||
if qtype.upper() == 'PTR':
|
||||
reversed_domains = []
|
||||
for domain in domains:
|
||||
try:
|
||||
n = dns.reversename.from_address(domain)
|
||||
reversed_domains.append(n.to_text())
|
||||
except dns.exception.SyntaxError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise AnsibleError("dns.reversename unhandled exception %s" % to_native(e))
|
||||
domains = reversed_domains
|
||||
|
||||
if len(domains) > 1:
|
||||
real_empty = True
|
||||
|
||||
ret = []
|
||||
|
||||
for domain in domains:
|
||||
if qtype.upper() == 'PTR':
|
||||
try:
|
||||
answers = myres.query(domain, qtype, rdclass=rdclass)
|
||||
for rdata in answers:
|
||||
s = rdata.to_text()
|
||||
if qtype.upper() == 'TXT':
|
||||
s = s[1:-1] # Strip outside quotes on TXT rdata
|
||||
n = dns.reversename.from_address(domain)
|
||||
domain = n.to_text()
|
||||
except dns.exception.SyntaxError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise AnsibleError("dns.reversename unhandled exception %s" % to_native(e))
|
||||
|
||||
if flat:
|
||||
ret.append(s)
|
||||
else:
|
||||
try:
|
||||
rd = make_rdata_dict(rdata)
|
||||
rd['owner'] = answers.canonical_name.to_text()
|
||||
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
|
||||
rd['ttl'] = answers.rrset.ttl
|
||||
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
try:
|
||||
answers = myres.query(domain, qtype, rdclass=rdclass)
|
||||
for rdata in answers:
|
||||
s = rdata.to_text()
|
||||
if qtype.upper() == 'TXT':
|
||||
s = s[1:-1] # Strip outside quotes on TXT rdata
|
||||
|
||||
ret.append(rd)
|
||||
except Exception as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append(str(err))
|
||||
if flat:
|
||||
ret.append(s)
|
||||
else:
|
||||
try:
|
||||
rd = make_rdata_dict(rdata)
|
||||
rd['owner'] = answers.canonical_name.to_text()
|
||||
rd['type'] = dns.rdatatype.to_text(rdata.rdtype)
|
||||
rd['ttl'] = answers.rrset.ttl
|
||||
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
|
||||
except dns.resolver.NXDOMAIN as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append('NXDOMAIN')
|
||||
except dns.resolver.NoAnswer as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append("")
|
||||
except dns.resolver.Timeout as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append("")
|
||||
except dns.exception.DNSException as err:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
|
||||
ret.append(rd)
|
||||
except Exception as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append(str(err))
|
||||
|
||||
except dns.resolver.NXDOMAIN as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append('NXDOMAIN')
|
||||
except dns.resolver.NoAnswer as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append("")
|
||||
except dns.resolver.Timeout as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
if not real_empty:
|
||||
ret.append("")
|
||||
except dns.exception.DNSException as err:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Thales Netherlands
|
||||
# Copyright (c) 2021, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
author:
|
||||
- Roy Lenferink (@rlenferink)
|
||||
- Mark Ettema (@m-a-r-k-e)
|
||||
name: merge_variables
|
||||
short_description: merge variables with a certain suffix
|
||||
description:
|
||||
- This lookup returns the merged result of all variables in scope that match the given prefixes, suffixes, or
|
||||
regular expressions, optionally.
|
||||
version_added: 6.5.0
|
||||
options:
|
||||
_terms:
|
||||
description:
|
||||
- Depending on the value of I(pattern_type), this is a list of prefixes, suffixes, or regular expressions
|
||||
that will be used to match all variables that should be merged.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
pattern_type:
|
||||
description:
|
||||
- Change the way of searching for the specified pattern.
|
||||
type: str
|
||||
default: 'regex'
|
||||
choices:
|
||||
- prefix
|
||||
- suffix
|
||||
- regex
|
||||
env:
|
||||
- name: ANSIBLE_MERGE_VARIABLES_PATTERN_TYPE
|
||||
ini:
|
||||
- section: merge_variables_lookup
|
||||
key: pattern_type
|
||||
initial_value:
|
||||
description:
|
||||
- An initial value to start with.
|
||||
type: raw
|
||||
override:
|
||||
description:
|
||||
- Return an error, print a warning or ignore it when a key will be overwritten.
|
||||
- The default behavior C(error) makes the plugin fail when a key would be overwritten.
|
||||
- When C(warn) and C(ignore) are used, note that it is important to know that the variables
|
||||
are sorted by name before being merged. Keys for later variables in this order will overwrite
|
||||
keys of the same name for variables earlier in this order. To avoid potential confusion,
|
||||
better use I(override=error) whenever possible.
|
||||
type: str
|
||||
default: 'error'
|
||||
choices:
|
||||
- error
|
||||
- warn
|
||||
- ignore
|
||||
env:
|
||||
- name: ANSIBLE_MERGE_VARIABLES_OVERRIDE
|
||||
ini:
|
||||
- section: merge_variables_lookup
|
||||
key: override
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Some example variables, they can be defined anywhere as long as they are in scope
|
||||
test_init_list:
|
||||
- "list init item 1"
|
||||
- "list init item 2"
|
||||
|
||||
testa__test_list:
|
||||
- "test a item 1"
|
||||
|
||||
testb__test_list:
|
||||
- "test b item 1"
|
||||
|
||||
testa__test_dict:
|
||||
ports:
|
||||
- 1
|
||||
|
||||
testb__test_dict:
|
||||
ports:
|
||||
- 3
|
||||
|
||||
|
||||
# Merge variables that end with '__test_dict' and store the result in a variable 'example_a'
|
||||
example_a: "{{ lookup('community.general.merge_variables', '__test_dict', pattern_type='suffix') }}"
|
||||
|
||||
# The variable example_a now contains:
|
||||
# ports:
|
||||
# - 1
|
||||
# - 3
|
||||
|
||||
|
||||
# Merge variables that match the '^.+__test_list$' regular expression, starting with an initial value and store the
|
||||
# result in a variable 'example_b'
|
||||
example_b: "{{ lookup('community.general.merge_variables', '^.+__test_list$', initial_value=test_init_list) }}"
|
||||
|
||||
# The variable example_b now contains:
|
||||
# - "list init item 1"
|
||||
# - "list init item 2"
|
||||
# - "test a item 1"
|
||||
# - "test b item 1"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: In case the search matches list items, a list will be returned. In case the search matches dicts, a
|
||||
dict will be returned.
|
||||
type: raw
|
||||
elements: raw
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
def _verify_and_get_type(variable):
|
||||
if isinstance(variable, list):
|
||||
return "list"
|
||||
elif isinstance(variable, dict):
|
||||
return "dict"
|
||||
else:
|
||||
raise AnsibleError("Not supported type detected, variable must be a list or a dict")
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(direct=kwargs)
|
||||
initial_value = self.get_option("initial_value", None)
|
||||
self._override = self.get_option('override', 'error')
|
||||
self._pattern_type = self.get_option('pattern_type', 'regex')
|
||||
|
||||
ret = []
|
||||
for term in terms:
|
||||
if not isinstance(term, str):
|
||||
raise AnsibleError("Non-string type '{0}' passed, only 'str' types are allowed!".format(type(term)))
|
||||
|
||||
ret.append(self._merge_vars(term, initial_value, variables))
|
||||
|
||||
return ret
|
||||
|
||||
def _var_matches(self, key, search_pattern):
|
||||
if self._pattern_type == "prefix":
|
||||
return key.startswith(search_pattern)
|
||||
elif self._pattern_type == "suffix":
|
||||
return key.endswith(search_pattern)
|
||||
elif self._pattern_type == "regex":
|
||||
matcher = re.compile(search_pattern)
|
||||
return matcher.search(key)
|
||||
|
||||
return False
|
||||
|
||||
def _merge_vars(self, search_pattern, initial_value, variables):
|
||||
display.vvv("Merge variables with {0}: {1}".format(self._pattern_type, search_pattern))
|
||||
var_merge_names = sorted([key for key in variables.keys() if self._var_matches(key, search_pattern)])
|
||||
display.vvv("The following variables will be merged: {0}".format(var_merge_names))
|
||||
|
||||
prev_var_type = None
|
||||
result = None
|
||||
|
||||
if initial_value is not None:
|
||||
prev_var_type = _verify_and_get_type(initial_value)
|
||||
result = initial_value
|
||||
|
||||
for var_name in var_merge_names:
|
||||
var_value = self._templar.template(variables[var_name]) # Render jinja2 templates
|
||||
var_type = _verify_and_get_type(var_value)
|
||||
|
||||
if prev_var_type is None:
|
||||
prev_var_type = var_type
|
||||
elif prev_var_type != var_type:
|
||||
raise AnsibleError("Unable to merge, not all variables are of the same type")
|
||||
|
||||
if result is None:
|
||||
result = var_value
|
||||
continue
|
||||
|
||||
if var_type == "dict":
|
||||
result = self._merge_dict(var_value, result, [var_name])
|
||||
else: # var_type == "list"
|
||||
result += var_value
|
||||
|
||||
return result
|
||||
|
||||
def _merge_dict(self, src, dest, path):
|
||||
for key, value in src.items():
|
||||
if isinstance(value, dict):
|
||||
node = dest.setdefault(key, {})
|
||||
self._merge_dict(value, node, path + [key])
|
||||
elif isinstance(value, list) and key in dest:
|
||||
dest[key] += value
|
||||
else:
|
||||
if (key in dest) and dest[key] != value:
|
||||
msg = "The key '{0}' with value '{1}' will be overwritten with value '{2}' from '{3}.{0}'".format(
|
||||
key, dest[key], value, ".".join(path))
|
||||
|
||||
if self._override == "error":
|
||||
raise AnsibleError(msg)
|
||||
if self._override == "warn":
|
||||
display.warning(msg)
|
||||
|
||||
dest[key] = value
|
||||
|
||||
return dest
|
||||
@@ -209,6 +209,7 @@ import time
|
||||
import yaml
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleAssertionError
|
||||
from ansible.module_utils.common.file import FileLock
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.utils.display import Display
|
||||
@@ -216,8 +217,6 @@ from ansible.utils.encrypt import random_password
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible import constants as C
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils._filelock import FileLock
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
|
||||
@@ -26,18 +26,6 @@ options:
|
||||
description: The integer ID of the secret.
|
||||
required: true
|
||||
type: int
|
||||
fetch_attachments:
|
||||
description:
|
||||
- Boolean flag which indicates whether attached files will get downloaded or not.
|
||||
- The download will only happen if I(file_download_path) has been provided.
|
||||
required: false
|
||||
type: bool
|
||||
version_added: 7.0.0
|
||||
file_download_path:
|
||||
description: Indicate the file attachment download location.
|
||||
required: false
|
||||
type: path
|
||||
version_added: 7.0.0
|
||||
base_url:
|
||||
description: The base URL of the server, e.g. C(https://localhost/SecretServer).
|
||||
env:
|
||||
@@ -169,35 +157,10 @@ EXAMPLES = r"""
|
||||
tasks:
|
||||
- ansible.builtin.debug:
|
||||
msg: the password is {{ secret_password }}
|
||||
|
||||
# Private key stores into certificate file which is attached with secret.
|
||||
# If fetch_attachments=True then private key file will be download on specified path
|
||||
# and file content will display in debug message.
|
||||
- hosts: localhost
|
||||
vars:
|
||||
secret: >-
|
||||
{{
|
||||
lookup(
|
||||
'community.general.tss',
|
||||
102,
|
||||
fetch_attachments=True,
|
||||
file_download_path='/home/certs',
|
||||
base_url='https://secretserver.domain.com/SecretServer/',
|
||||
token='thycotic_access_token'
|
||||
)
|
||||
}}
|
||||
tasks:
|
||||
- ansible.builtin.debug:
|
||||
msg: >
|
||||
the private key is {{
|
||||
(secret['items']
|
||||
| items2dict(key_name='slug',
|
||||
value_name='itemValue'))['private-key']
|
||||
}}
|
||||
"""
|
||||
|
||||
import abc
|
||||
import os
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError
|
||||
from ansible.module_utils import six
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
@@ -248,27 +211,13 @@ class TSSClient(object):
|
||||
else:
|
||||
return TSSClientV0(**server_parameters)
|
||||
|
||||
def get_secret(self, term, fetch_file_attachments, file_download_path):
|
||||
def get_secret(self, term):
|
||||
display.debug("tss_lookup term: %s" % term)
|
||||
|
||||
secret_id = self._term_to_secret_id(term)
|
||||
display.vvv(u"Secret Server lookup of Secret with ID %d" % secret_id)
|
||||
|
||||
if fetch_file_attachments:
|
||||
obj = self._client.get_secret(secret_id, fetch_file_attachments)
|
||||
for i in obj['items']:
|
||||
if file_download_path and os.path.isdir(file_download_path):
|
||||
if i['isFile']:
|
||||
try:
|
||||
with open(os.path.join(file_download_path, str(obj['id']) + "_" + i['slug']), "w") as f:
|
||||
f.write(i['itemValue'].text)
|
||||
i['itemValue'] = "*** Not Valid For Display ***"
|
||||
except ValueError:
|
||||
raise AnsibleOptionsError("Failed to download {0}".format(str(i['slug'])))
|
||||
else:
|
||||
raise AnsibleOptionsError("File download path does not exist")
|
||||
return obj
|
||||
else:
|
||||
return self._client.get_secret_json(secret_id)
|
||||
return self._client.get_secret_json(secret_id)
|
||||
|
||||
@staticmethod
|
||||
def _term_to_secret_id(term):
|
||||
@@ -345,6 +294,6 @@ class LookupModule(LookupBase):
|
||||
)
|
||||
|
||||
try:
|
||||
return [tss.get_secret(term, self.get_option("fetch_attachments"), self.get_option("file_download_path")) for term in terms]
|
||||
return [tss.get_secret(term) for term in terms]
|
||||
except SecretServerError as error:
|
||||
raise AnsibleError("Secret Server lookup failure: %s" % error.message)
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
# NOTE:
|
||||
# This has been vendored from ansible.module_utils.common.file. This code has been removed from there for ansible-core 2.16.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import stat
|
||||
import time
|
||||
import fcntl
|
||||
import sys
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
class LockTimeout(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FileLock:
|
||||
'''
|
||||
Currently FileLock is implemented via fcntl.flock on a lock file, however this
|
||||
behaviour may change in the future. Avoid mixing lock types fcntl.flock,
|
||||
fcntl.lockf and module_utils.common.file.FileLock as it will certainly cause
|
||||
unwanted and/or unexpected behaviour
|
||||
'''
|
||||
def __init__(self):
|
||||
self.lockfd = None
|
||||
|
||||
@contextmanager
|
||||
def lock_file(self, path, tmpdir, lock_timeout=None):
|
||||
'''
|
||||
Context for lock acquisition
|
||||
'''
|
||||
try:
|
||||
self.set_lock(path, tmpdir, lock_timeout)
|
||||
yield
|
||||
finally:
|
||||
self.unlock()
|
||||
|
||||
def set_lock(self, path, tmpdir, lock_timeout=None):
|
||||
'''
|
||||
Create a lock file based on path with flock to prevent other processes
|
||||
using given path.
|
||||
Please note that currently file locking only works when it's executed by
|
||||
the same user, I.E single user scenarios
|
||||
|
||||
:kw path: Path (file) to lock
|
||||
:kw tmpdir: Path where to place the temporary .lock file
|
||||
:kw lock_timeout:
|
||||
Wait n seconds for lock acquisition, fail if timeout is reached.
|
||||
0 = Do not wait, fail if lock cannot be acquired immediately,
|
||||
Default is None, wait indefinitely until lock is released.
|
||||
:returns: True
|
||||
'''
|
||||
lock_path = os.path.join(tmpdir, 'ansible-{0}.lock'.format(os.path.basename(path)))
|
||||
l_wait = 0.1
|
||||
r_exception = IOError
|
||||
if sys.version_info[0] == 3:
|
||||
r_exception = BlockingIOError
|
||||
|
||||
self.lockfd = open(lock_path, 'w')
|
||||
|
||||
if lock_timeout <= 0:
|
||||
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
|
||||
return True
|
||||
|
||||
if lock_timeout:
|
||||
e_secs = 0
|
||||
while e_secs < lock_timeout:
|
||||
try:
|
||||
fcntl.flock(self.lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
|
||||
return True
|
||||
except r_exception:
|
||||
time.sleep(l_wait)
|
||||
e_secs += l_wait
|
||||
continue
|
||||
|
||||
self.lockfd.close()
|
||||
raise LockTimeout('{0} sec'.format(lock_timeout))
|
||||
|
||||
fcntl.flock(self.lockfd, fcntl.LOCK_EX)
|
||||
os.chmod(lock_path, stat.S_IWRITE | stat.S_IREAD)
|
||||
|
||||
return True
|
||||
|
||||
def unlock(self):
|
||||
'''
|
||||
Make sure lock file is available for everyone and Unlock the file descriptor
|
||||
locked by set_lock
|
||||
|
||||
:returns: True
|
||||
'''
|
||||
if not self.lockfd:
|
||||
return True
|
||||
|
||||
try:
|
||||
fcntl.flock(self.lockfd, fcntl.LOCK_UN)
|
||||
self.lockfd.close()
|
||||
except ValueError: # file wasn't opened, let context manager fail gracefully
|
||||
pass
|
||||
|
||||
return True
|
||||
@@ -1,258 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This code is part of Ansible, but is an independent component.
|
||||
# This particular file snippet, and this file snippet only, is based on
|
||||
# the config parser from here: https://github.com/emre/storm/blob/master/storm/parsers/ssh_config_parser.py
|
||||
# Copyright (C) <2013> <Emre Yilmaz>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
from operator import itemgetter
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
try:
|
||||
from paramiko.config import SSHConfig
|
||||
except ImportError:
|
||||
SSHConfig = object
|
||||
HAS_PARAMIKO = False
|
||||
PARAMIKO_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_PARAMIKO = True
|
||||
PARAMIKO_IMPORT_ERROR = None
|
||||
|
||||
|
||||
class StormConfig(SSHConfig):
|
||||
def parse(self, file_obj):
|
||||
"""
|
||||
Read an OpenSSH config from the given file object.
|
||||
@param file_obj: a file-like object to read the config file from
|
||||
@type file_obj: file
|
||||
"""
|
||||
order = 1
|
||||
host = {"host": ['*'], "config": {}, }
|
||||
for line in file_obj:
|
||||
line = line.rstrip('\n').lstrip()
|
||||
if line == '':
|
||||
self._config.append({
|
||||
'type': 'empty_line',
|
||||
'value': line,
|
||||
'host': '',
|
||||
'order': order,
|
||||
})
|
||||
order += 1
|
||||
continue
|
||||
|
||||
if line.startswith('#'):
|
||||
self._config.append({
|
||||
'type': 'comment',
|
||||
'value': line,
|
||||
'host': '',
|
||||
'order': order,
|
||||
})
|
||||
order += 1
|
||||
continue
|
||||
|
||||
if '=' in line:
|
||||
# Ensure ProxyCommand gets properly split
|
||||
if line.lower().strip().startswith('proxycommand'):
|
||||
proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I)
|
||||
match = proxy_re.match(line)
|
||||
key, value = match.group(1).lower(), match.group(2)
|
||||
else:
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip().lower()
|
||||
else:
|
||||
# find first whitespace, and split there
|
||||
i = 0
|
||||
while (i < len(line)) and not line[i].isspace():
|
||||
i += 1
|
||||
if i == len(line):
|
||||
raise Exception('Unparsable line: %r' % line)
|
||||
key = line[:i].lower()
|
||||
value = line[i:].lstrip()
|
||||
if key == 'host':
|
||||
self._config.append(host)
|
||||
value = value.split()
|
||||
host = {
|
||||
key: value,
|
||||
'config': {},
|
||||
'type': 'entry',
|
||||
'order': order
|
||||
}
|
||||
order += 1
|
||||
elif key in ['identityfile', 'localforward', 'remoteforward']:
|
||||
if key in host['config']:
|
||||
host['config'][key].append(value)
|
||||
else:
|
||||
host['config'][key] = [value]
|
||||
elif key not in host['config']:
|
||||
host['config'].update({key: value})
|
||||
self._config.append(host)
|
||||
|
||||
|
||||
class ConfigParser(object):
|
||||
"""
|
||||
Config parser for ~/.ssh/config files.
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_config_file=None):
|
||||
if not ssh_config_file:
|
||||
ssh_config_file = self.get_default_ssh_config_file()
|
||||
|
||||
self.defaults = {}
|
||||
|
||||
self.ssh_config_file = ssh_config_file
|
||||
|
||||
if not os.path.exists(self.ssh_config_file):
|
||||
if not os.path.exists(os.path.dirname(self.ssh_config_file)):
|
||||
os.makedirs(os.path.dirname(self.ssh_config_file))
|
||||
open(self.ssh_config_file, 'w+').close()
|
||||
os.chmod(self.ssh_config_file, 0o600)
|
||||
|
||||
self.config_data = []
|
||||
|
||||
def get_default_ssh_config_file(self):
|
||||
return os.path.expanduser("~/.ssh/config")
|
||||
|
||||
def load(self):
|
||||
config = StormConfig()
|
||||
|
||||
with open(self.ssh_config_file) as fd:
|
||||
config.parse(fd)
|
||||
|
||||
for entry in config.__dict__.get("_config"):
|
||||
if entry.get("host") == ["*"]:
|
||||
self.defaults.update(entry.get("config"))
|
||||
|
||||
if entry.get("type") in ["comment", "empty_line"]:
|
||||
self.config_data.append(entry)
|
||||
continue
|
||||
|
||||
host_item = {
|
||||
'host': entry["host"][0],
|
||||
'options': entry.get("config"),
|
||||
'type': 'entry',
|
||||
'order': entry.get("order", 0),
|
||||
}
|
||||
|
||||
if len(entry["host"]) > 1:
|
||||
host_item.update({
|
||||
'host': " ".join(entry["host"]),
|
||||
})
|
||||
# minor bug in paramiko.SSHConfig that duplicates
|
||||
# "Host *" entries.
|
||||
if entry.get("config") and len(entry.get("config")) > 0:
|
||||
self.config_data.append(host_item)
|
||||
|
||||
return self.config_data
|
||||
|
||||
def add_host(self, host, options):
|
||||
self.config_data.append({
|
||||
'host': host,
|
||||
'options': options,
|
||||
'order': self.get_last_index(),
|
||||
})
|
||||
|
||||
return self
|
||||
|
||||
def update_host(self, host, options, use_regex=False):
|
||||
for index, host_entry in enumerate(self.config_data):
|
||||
if host_entry.get("host") == host or \
|
||||
(use_regex and re.match(host, host_entry.get("host"))):
|
||||
|
||||
if 'deleted_fields' in options:
|
||||
deleted_fields = options.pop("deleted_fields")
|
||||
for deleted_field in deleted_fields:
|
||||
del self.config_data[index]["options"][deleted_field]
|
||||
|
||||
self.config_data[index]["options"].update(options)
|
||||
|
||||
return self
|
||||
|
||||
def search_host(self, search_string):
|
||||
results = []
|
||||
for host_entry in self.config_data:
|
||||
if host_entry.get("type") != 'entry':
|
||||
continue
|
||||
if host_entry.get("host") == "*":
|
||||
continue
|
||||
|
||||
searchable_information = host_entry.get("host")
|
||||
for key, value in host_entry.get("options").items():
|
||||
if isinstance(value, list):
|
||||
value = " ".join(value)
|
||||
if isinstance(value, int):
|
||||
value = str(value)
|
||||
|
||||
searchable_information += " " + value
|
||||
|
||||
if search_string in searchable_information:
|
||||
results.append(host_entry)
|
||||
|
||||
return results
|
||||
|
||||
def delete_host(self, host):
|
||||
found = 0
|
||||
for index, host_entry in enumerate(self.config_data):
|
||||
if host_entry.get("host") == host:
|
||||
del self.config_data[index]
|
||||
found += 1
|
||||
|
||||
if found == 0:
|
||||
raise ValueError('No host found')
|
||||
return self
|
||||
|
||||
def delete_all_hosts(self):
|
||||
self.config_data = []
|
||||
self.write_to_ssh_config()
|
||||
|
||||
return self
|
||||
|
||||
def dump(self):
|
||||
if len(self.config_data) < 1:
|
||||
return
|
||||
|
||||
file_content = ""
|
||||
self.config_data = sorted(self.config_data, key=itemgetter("order"))
|
||||
|
||||
for host_item in self.config_data:
|
||||
if host_item.get("type") in ['comment', 'empty_line']:
|
||||
file_content += host_item.get("value") + "\n"
|
||||
continue
|
||||
host_item_content = "Host {0}\n".format(host_item.get("host"))
|
||||
for key, value in host_item.get("options").items():
|
||||
if isinstance(value, list):
|
||||
sub_content = ""
|
||||
for value_ in value:
|
||||
sub_content += " {0} {1}\n".format(
|
||||
key, value_
|
||||
)
|
||||
host_item_content += sub_content
|
||||
else:
|
||||
host_item_content += " {0} {1}\n".format(
|
||||
key, value
|
||||
)
|
||||
file_content += host_item_content
|
||||
|
||||
return file_content
|
||||
|
||||
def write_to_ssh_config(self):
|
||||
with open(self.ssh_config_file, 'w+') as f:
|
||||
data = self.dump()
|
||||
if data:
|
||||
f.write(data)
|
||||
return self
|
||||
|
||||
def get_last_index(self):
|
||||
last_index = 0
|
||||
indexes = []
|
||||
for item in self.config_data:
|
||||
if item.get("order"):
|
||||
indexes.append(item.get("order"))
|
||||
if len(indexes) > 0:
|
||||
last_index = max(indexes)
|
||||
|
||||
return last_index
|
||||
@@ -1,464 +0,0 @@
|
||||
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
def normalize_subvolume_path(path):
|
||||
"""
|
||||
Normalizes btrfs subvolume paths to ensure exactly one leading slash, no trailing slashes and no consecutive slashes.
|
||||
In addition, if the path is prefixed with a leading <FS_TREE>, this value is removed.
|
||||
"""
|
||||
fstree_stripped = re.sub(r'^<FS_TREE>', '', path)
|
||||
result = re.sub(r'/+$', '', re.sub(r'/+', '/', '/' + fstree_stripped))
|
||||
return result if len(result) > 0 else '/'
|
||||
|
||||
|
||||
class BtrfsModuleException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BtrfsCommands(object):
|
||||
|
||||
"""
|
||||
Provides access to a subset of the Btrfs command line
|
||||
"""
|
||||
|
||||
def __init__(self, module):
|
||||
self.__module = module
|
||||
self.__btrfs = self.__module.get_bin_path("btrfs", required=True)
|
||||
|
||||
def filesystem_show(self):
|
||||
command = "%s filesystem show -d" % (self.__btrfs)
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
stdout = [x.strip() for x in result[1].splitlines()]
|
||||
filesystems = []
|
||||
current = None
|
||||
for line in stdout:
|
||||
if line.startswith('Label'):
|
||||
current = self.__parse_filesystem(line)
|
||||
filesystems.append(current)
|
||||
elif line.startswith('devid'):
|
||||
current['devices'].append(self.__parse_filesystem_device(line))
|
||||
return filesystems
|
||||
|
||||
def __parse_filesystem(self, line):
|
||||
label = re.sub(r'\s*uuid:.*$', '', re.sub(r'^Label:\s*', '', line))
|
||||
id = re.sub(r'^.*uuid:\s*', '', line)
|
||||
|
||||
filesystem = {}
|
||||
filesystem['label'] = label.strip("'") if label != 'none' else None
|
||||
filesystem['uuid'] = id
|
||||
filesystem['devices'] = []
|
||||
filesystem['mountpoints'] = []
|
||||
filesystem['subvolumes'] = []
|
||||
filesystem['default_subvolid'] = None
|
||||
return filesystem
|
||||
|
||||
def __parse_filesystem_device(self, line):
|
||||
return re.sub(r'^.*path\s', '', line)
|
||||
|
||||
def subvolumes_list(self, filesystem_path):
|
||||
command = "%s subvolume list -tap %s" % (self.__btrfs, filesystem_path)
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
stdout = [x.split('\t') for x in result[1].splitlines()]
|
||||
subvolumes = [{'id': 5, 'parent': None, 'path': '/'}]
|
||||
if len(stdout) > 2:
|
||||
subvolumes.extend([self.__parse_subvolume_list_record(x) for x in stdout[2:]])
|
||||
return subvolumes
|
||||
|
||||
def __parse_subvolume_list_record(self, item):
|
||||
return {
|
||||
'id': int(item[0]),
|
||||
'parent': int(item[2]),
|
||||
'path': normalize_subvolume_path(item[5]),
|
||||
}
|
||||
|
||||
def subvolume_get_default(self, filesystem_path):
|
||||
command = [self.__btrfs, "subvolume", "get-default", to_bytes(filesystem_path)]
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
# ID [n] ...
|
||||
return int(result[1].strip().split()[1])
|
||||
|
||||
def subvolume_set_default(self, filesystem_path, subvolume_id):
|
||||
command = [self.__btrfs, "subvolume", "set-default", str(subvolume_id), to_bytes(filesystem_path)]
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
|
||||
def subvolume_create(self, subvolume_path):
|
||||
command = [self.__btrfs, "subvolume", "create", to_bytes(subvolume_path)]
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
|
||||
def subvolume_snapshot(self, snapshot_source, snapshot_destination):
|
||||
command = [self.__btrfs, "subvolume", "snapshot", to_bytes(snapshot_source), to_bytes(snapshot_destination)]
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
|
||||
def subvolume_delete(self, subvolume_path):
|
||||
command = [self.__btrfs, "subvolume", "delete", to_bytes(subvolume_path)]
|
||||
result = self.__module.run_command(command, check_rc=True)
|
||||
|
||||
|
||||
class BtrfsInfoProvider(object):
|
||||
|
||||
"""
|
||||
Utility providing details of the currently available btrfs filesystems
|
||||
"""
|
||||
|
||||
def __init__(self, module):
|
||||
self.__module = module
|
||||
self.__btrfs_api = BtrfsCommands(module)
|
||||
self.__findmnt_path = self.__module.get_bin_path("findmnt", required=True)
|
||||
|
||||
def get_filesystems(self):
|
||||
filesystems = self.__btrfs_api.filesystem_show()
|
||||
mountpoints = self.__find_mountpoints()
|
||||
for filesystem in filesystems:
|
||||
device_mountpoints = self.__filter_mountpoints_for_devices(mountpoints, filesystem['devices'])
|
||||
filesystem['mountpoints'] = device_mountpoints
|
||||
|
||||
if len(device_mountpoints) > 0:
|
||||
|
||||
# any path within the filesystem can be used to query metadata
|
||||
mountpoint = device_mountpoints[0]['mountpoint']
|
||||
filesystem['subvolumes'] = self.get_subvolumes(mountpoint)
|
||||
filesystem['default_subvolid'] = self.get_default_subvolume_id(mountpoint)
|
||||
|
||||
return filesystems
|
||||
|
||||
def get_mountpoints(self, filesystem_devices):
|
||||
mountpoints = self.__find_mountpoints()
|
||||
return self.__filter_mountpoints_for_devices(mountpoints, filesystem_devices)
|
||||
|
||||
def get_subvolumes(self, filesystem_path):
|
||||
return self.__btrfs_api.subvolumes_list(filesystem_path)
|
||||
|
||||
def get_default_subvolume_id(self, filesystem_path):
|
||||
return self.__btrfs_api.subvolume_get_default(filesystem_path)
|
||||
|
||||
def __filter_mountpoints_for_devices(self, mountpoints, devices):
|
||||
return [m for m in mountpoints if (m['device'] in devices)]
|
||||
|
||||
def __find_mountpoints(self):
|
||||
command = "%s -t btrfs -nvP" % self.__findmnt_path
|
||||
result = self.__module.run_command(command)
|
||||
mountpoints = []
|
||||
if result[0] == 0:
|
||||
lines = result[1].splitlines()
|
||||
for line in lines:
|
||||
mountpoint = self.__parse_mountpoint_pairs(line)
|
||||
mountpoints.append(mountpoint)
|
||||
return mountpoints
|
||||
|
||||
def __parse_mountpoint_pairs(self, line):
|
||||
pattern = re.compile(r'^TARGET="(?P<target>.*)"\s+SOURCE="(?P<source>.*)"\s+FSTYPE="(?P<fstype>.*)"\s+OPTIONS="(?P<options>.*)"\s*$')
|
||||
match = pattern.search(line)
|
||||
if match is not None:
|
||||
groups = match.groupdict()
|
||||
|
||||
return {
|
||||
'mountpoint': groups['target'],
|
||||
'device': groups['source'],
|
||||
'subvolid': self.__extract_mount_subvolid(groups['options']),
|
||||
}
|
||||
else:
|
||||
raise BtrfsModuleException("Failed to parse findmnt result for line: '%s'" % line)
|
||||
|
||||
def __extract_mount_subvolid(self, mount_options):
|
||||
for option in mount_options.split(','):
|
||||
if option.startswith('subvolid='):
|
||||
return int(option[len('subvolid='):])
|
||||
raise BtrfsModuleException("Failed to find subvolid for mountpoint in options '%s'" % mount_options)
|
||||
|
||||
|
||||
class BtrfsSubvolume(object):
|
||||
|
||||
"""
|
||||
Wrapper class providing convenience methods for inspection of a btrfs subvolume
|
||||
"""
|
||||
|
||||
def __init__(self, filesystem, subvolume_id):
|
||||
self.__filesystem = filesystem
|
||||
self.__subvolume_id = subvolume_id
|
||||
|
||||
def get_filesystem(self):
|
||||
return self.__filesystem
|
||||
|
||||
def is_mounted(self):
|
||||
mountpoints = self.get_mountpoints()
|
||||
return mountpoints is not None and len(mountpoints) > 0
|
||||
|
||||
def is_filesystem_root(self):
|
||||
return 5 == self.__subvolume_id
|
||||
|
||||
def is_filesystem_default(self):
|
||||
return self.__filesystem.default_subvolid == self.__subvolume_id
|
||||
|
||||
def get_mounted_path(self):
|
||||
mountpoints = self.get_mountpoints()
|
||||
if mountpoints is not None and len(mountpoints) > 0:
|
||||
return mountpoints[0]
|
||||
elif self.parent is not None:
|
||||
parent = self.__filesystem.get_subvolume_by_id(self.parent)
|
||||
parent_path = parent.get_mounted_path()
|
||||
if parent_path is not None:
|
||||
return parent_path + os.path.sep + self.name
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_mountpoints(self):
|
||||
return self.__filesystem.get_mountpoints_by_subvolume_id(self.__subvolume_id)
|
||||
|
||||
def get_child_relative_path(self, absolute_child_path):
|
||||
"""
|
||||
Get the relative path from this subvolume to the named child subvolume.
|
||||
The provided parameter is expected to be normalized as by normalize_subvolume_path.
|
||||
"""
|
||||
path = self.path
|
||||
if absolute_child_path.startswith(path):
|
||||
relative = absolute_child_path[len(path):]
|
||||
return re.sub(r'^/*', '', relative)
|
||||
else:
|
||||
raise BtrfsModuleException("Path '%s' doesn't start with '%s'" % (absolute_child_path, path))
|
||||
|
||||
def get_parent_subvolume(self):
|
||||
parent_id = self.parent
|
||||
return self.__filesystem.get_subvolume_by_id(parent_id) if parent_id is not None else None
|
||||
|
||||
def get_child_subvolumes(self):
|
||||
return self.__filesystem.get_subvolume_children(self.__subvolume_id)
|
||||
|
||||
@property
|
||||
def __info(self):
|
||||
return self.__filesystem.get_subvolume_info_for_id(self.__subvolume_id)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.__subvolume_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.path.split('/').pop()
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.__info['path']
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return self.__info['parent']
|
||||
|
||||
|
||||
class BtrfsFilesystem(object):
|
||||
|
||||
"""
|
||||
Wrapper class providing convenience methods for inspection of a btrfs filesystem
|
||||
"""
|
||||
|
||||
def __init__(self, info, provider, module):
|
||||
self.__provider = provider
|
||||
|
||||
# constant for module execution
|
||||
self.__uuid = info['uuid']
|
||||
self.__label = info['label']
|
||||
self.__devices = info['devices']
|
||||
|
||||
# refreshable
|
||||
self.__default_subvolid = info['default_subvolid'] if 'default_subvolid' in info else None
|
||||
self.__update_mountpoints(info['mountpoints'] if 'mountpoints' in info else [])
|
||||
self.__update_subvolumes(info['subvolumes'] if 'subvolumes' in info else [])
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
return self.__uuid
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self.__label
|
||||
|
||||
@property
|
||||
def default_subvolid(self):
|
||||
return self.__default_subvolid
|
||||
|
||||
@property
|
||||
def devices(self):
|
||||
return list(self.__devices)
|
||||
|
||||
def refresh(self):
|
||||
self.refresh_mountpoints()
|
||||
self.refresh_subvolumes()
|
||||
self.refresh_default_subvolume()
|
||||
|
||||
def refresh_mountpoints(self):
|
||||
mountpoints = self.__provider.get_mountpoints(list(self.__devices))
|
||||
self.__update_mountpoints(mountpoints)
|
||||
|
||||
def __update_mountpoints(self, mountpoints):
|
||||
self.__mountpoints = dict()
|
||||
for i in mountpoints:
|
||||
subvolid = i['subvolid']
|
||||
mountpoint = i['mountpoint']
|
||||
if subvolid not in self.__mountpoints:
|
||||
self.__mountpoints[subvolid] = []
|
||||
self.__mountpoints[subvolid].append(mountpoint)
|
||||
|
||||
def refresh_subvolumes(self):
|
||||
filesystem_path = self.get_any_mountpoint()
|
||||
if filesystem_path is not None:
|
||||
subvolumes = self.__provider.get_subvolumes(filesystem_path)
|
||||
self.__update_subvolumes(subvolumes)
|
||||
|
||||
def __update_subvolumes(self, subvolumes):
|
||||
# TODO strategy for retaining information on deleted subvolumes?
|
||||
self.__subvolumes = dict()
|
||||
for subvolume in subvolumes:
|
||||
self.__subvolumes[subvolume['id']] = subvolume
|
||||
|
||||
def refresh_default_subvolume(self):
|
||||
filesystem_path = self.get_any_mountpoint()
|
||||
if filesystem_path is not None:
|
||||
self.__default_subvolid = self.__provider.get_default_subvolume_id(filesystem_path)
|
||||
|
||||
def contains_device(self, device):
|
||||
return device in self.__devices
|
||||
|
||||
def contains_subvolume(self, subvolume):
|
||||
return self.get_subvolume_by_name(subvolume) is not None
|
||||
|
||||
def get_subvolume_by_id(self, subvolume_id):
|
||||
return BtrfsSubvolume(self, subvolume_id) if subvolume_id in self.__subvolumes else None
|
||||
|
||||
def get_subvolume_info_for_id(self, subvolume_id):
|
||||
return self.__subvolumes[subvolume_id] if subvolume_id in self.__subvolumes else None
|
||||
|
||||
def get_subvolume_by_name(self, subvolume):
|
||||
for subvolume_info in self.__subvolumes.values():
|
||||
if subvolume_info['path'] == subvolume:
|
||||
return BtrfsSubvolume(self, subvolume_info['id'])
|
||||
return None
|
||||
|
||||
def get_any_mountpoint(self):
|
||||
for subvol_mountpoints in self.__mountpoints.values():
|
||||
if len(subvol_mountpoints) > 0:
|
||||
return subvol_mountpoints[0]
|
||||
# maybe error?
|
||||
return None
|
||||
|
||||
def get_any_mounted_subvolume(self):
|
||||
for subvolid, subvol_mountpoints in self.__mountpoints.items():
|
||||
if len(subvol_mountpoints) > 0:
|
||||
return self.get_subvolume_by_id(subvolid)
|
||||
return None
|
||||
|
||||
def get_mountpoints_by_subvolume_id(self, subvolume_id):
|
||||
return self.__mountpoints[subvolume_id] if subvolume_id in self.__mountpoints else []
|
||||
|
||||
def get_nearest_subvolume(self, subvolume):
|
||||
"""Return the identified subvolume if existing, else the closest matching parent"""
|
||||
subvolumes_by_path = self.__get_subvolumes_by_path()
|
||||
while len(subvolume) > 1:
|
||||
if subvolume in subvolumes_by_path:
|
||||
return BtrfsSubvolume(self, subvolumes_by_path[subvolume]['id'])
|
||||
else:
|
||||
subvolume = re.sub(r'/[^/]+$', '', subvolume)
|
||||
|
||||
return BtrfsSubvolume(self, 5)
|
||||
|
||||
def get_mountpath_as_child(self, subvolume_name):
|
||||
"""Find a path to the target subvolume through a mounted ancestor"""
|
||||
nearest = self.get_nearest_subvolume(subvolume_name)
|
||||
if nearest.path == subvolume_name:
|
||||
nearest = nearest.get_parent_subvolume()
|
||||
if nearest is None or nearest.get_mounted_path() is None:
|
||||
raise BtrfsModuleException("Failed to find a path '%s' through a mounted parent subvolume" % subvolume_name)
|
||||
else:
|
||||
return nearest.get_mounted_path() + os.path.sep + nearest.get_child_relative_path(subvolume_name)
|
||||
|
||||
def get_subvolume_children(self, subvolume_id):
|
||||
return [BtrfsSubvolume(self, x['id']) for x in self.__subvolumes.values() if x['parent'] == subvolume_id]
|
||||
|
||||
def __get_subvolumes_by_path(self):
|
||||
result = {}
|
||||
for s in self.__subvolumes.values():
|
||||
path = s['path']
|
||||
result[path] = s
|
||||
return result
|
||||
|
||||
def is_mounted(self):
|
||||
return self.__mountpoints is not None and len(self.__mountpoints) > 0
|
||||
|
||||
def get_summary(self):
|
||||
subvolumes = []
|
||||
sources = self.__subvolumes.values() if self.__subvolumes is not None else []
|
||||
for subvolume in sources:
|
||||
id = subvolume['id']
|
||||
subvolumes.append({
|
||||
'id': id,
|
||||
'path': subvolume['path'],
|
||||
'parent': subvolume['parent'],
|
||||
'mountpoints': self.get_mountpoints_by_subvolume_id(id),
|
||||
})
|
||||
|
||||
return {
|
||||
'default_subvolume': self.__default_subvolid,
|
||||
'devices': self.__devices,
|
||||
'label': self.__label,
|
||||
'uuid': self.__uuid,
|
||||
'subvolumes': subvolumes,
|
||||
}
|
||||
|
||||
|
||||
class BtrfsFilesystemsProvider(object):
|
||||
|
||||
"""
|
||||
Provides methods to query available btrfs filesystems
|
||||
"""
|
||||
|
||||
def __init__(self, module):
|
||||
self.__module = module
|
||||
self.__provider = BtrfsInfoProvider(module)
|
||||
self.__filesystems = None
|
||||
|
||||
def get_matching_filesystem(self, criteria):
|
||||
if criteria['device'] is not None:
|
||||
criteria['device'] = os.path.realpath(criteria['device'])
|
||||
|
||||
self.__check_init()
|
||||
matching = [f for f in self.__filesystems.values() if self.__filesystem_matches_criteria(f, criteria)]
|
||||
if len(matching) == 1:
|
||||
return matching[0]
|
||||
else:
|
||||
raise BtrfsModuleException("Found %d filesystems matching criteria uuid=%s label=%s device=%s" % (
|
||||
len(matching),
|
||||
criteria['uuid'],
|
||||
criteria['label'],
|
||||
criteria['device']
|
||||
))
|
||||
|
||||
def __filesystem_matches_criteria(self, filesystem, criteria):
|
||||
return ((criteria['uuid'] is None or filesystem.uuid == criteria['uuid']) and
|
||||
(criteria['label'] is None or filesystem.label == criteria['label']) and
|
||||
(criteria['device'] is None or filesystem.contains_device(criteria['device'])))
|
||||
|
||||
def get_filesystem_for_device(self, device):
|
||||
real_device = os.path.realpath(device)
|
||||
self.__check_init()
|
||||
for fs in self.__filesystems.values():
|
||||
if fs.contains_device(real_device):
|
||||
return fs
|
||||
return None
|
||||
|
||||
def get_filesystems(self):
|
||||
self.__check_init()
|
||||
return list(self.__filesystems.values())
|
||||
|
||||
def __check_init(self):
|
||||
if self.__filesystems is None:
|
||||
self.__filesystems = dict()
|
||||
for f in self.__provider.get_filesystems():
|
||||
uuid = f['uuid']
|
||||
self.__filesystems[uuid] = BtrfsFilesystem(f, self.__provider, self.__module)
|
||||
@@ -309,3 +309,11 @@ class _CmdRunnerContext(object):
|
||||
|
||||
|
||||
cmd_runner_fmt = _Format()
|
||||
|
||||
#
|
||||
# The fmt form is deprecated and will be removed in community.general 7.0.0
|
||||
# Please use:
|
||||
# cmd_runner_fmt
|
||||
# Or, to retain the same effect, use:
|
||||
# from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt as fmt
|
||||
fmt = cmd_runner_fmt
|
||||
|
||||
@@ -50,7 +50,7 @@ class _Dependency(object):
|
||||
def failed(self):
|
||||
return self.state == 1
|
||||
|
||||
def validate(self, module):
|
||||
def verify(self, module):
|
||||
if self.failed:
|
||||
module.fail_json(msg=self.message, exception=self.trace)
|
||||
|
||||
@@ -71,28 +71,20 @@ def declare(name, *args, **kwargs):
|
||||
_deps[name] = dep
|
||||
|
||||
|
||||
def _select_names(spec):
|
||||
def validate(module, spec=None):
|
||||
dep_names = sorted(_deps)
|
||||
|
||||
if spec:
|
||||
if spec is not None:
|
||||
if spec.startswith("-"):
|
||||
spec_split = spec[1:].split(":")
|
||||
for d in spec_split:
|
||||
dep_names.remove(d)
|
||||
else:
|
||||
spec_split = spec.split(":")
|
||||
spec_split = spec[1:].split(":")
|
||||
dep_names = []
|
||||
for d in spec_split:
|
||||
_deps[d] # ensure it exists
|
||||
dep_names.append(d)
|
||||
|
||||
return dep_names
|
||||
|
||||
|
||||
def validate(module, spec=None):
|
||||
for dep in _select_names(spec):
|
||||
_deps[dep].validate(module)
|
||||
|
||||
|
||||
def failed(spec=None):
|
||||
return any(_deps[d].failed for d in _select_names(spec))
|
||||
for dep in dep_names:
|
||||
_deps[dep].verify(module)
|
||||
|
||||
@@ -10,7 +10,6 @@ __metaclass__ = type
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.six import integer_types, string_types
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
@@ -122,38 +121,3 @@ def filter_returned_variables(gitlab_variables):
|
||||
if key not in KNOWN:
|
||||
item.pop(key)
|
||||
return existing_variables
|
||||
|
||||
|
||||
def vars_to_variables(vars, module):
|
||||
# transform old vars to new variables structure
|
||||
variables = list()
|
||||
for item, value in vars.items():
|
||||
if isinstance(value, (string_types, integer_types, float)):
|
||||
variables.append(
|
||||
{
|
||||
"name": item,
|
||||
"value": str(value),
|
||||
"masked": False,
|
||||
"protected": False,
|
||||
"variable_type": "env_var",
|
||||
}
|
||||
)
|
||||
|
||||
elif isinstance(value, dict):
|
||||
new_item = {
|
||||
"name": item,
|
||||
"value": value.get('value'),
|
||||
"masked": value.get('masked'),
|
||||
"protected": value.get('protected'),
|
||||
"variable_type": value.get('variable_type'),
|
||||
}
|
||||
|
||||
if value.get('environment_scope'):
|
||||
new_item['environment_scope'] = value.get('environment_scope')
|
||||
|
||||
variables.append(new_item)
|
||||
|
||||
else:
|
||||
module.fail_json(msg="value must be of type string, integer, float or dict")
|
||||
|
||||
return variables
|
||||
|
||||
@@ -49,16 +49,6 @@ URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}"
|
||||
URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models"
|
||||
URL_CLIENTSCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}"
|
||||
|
||||
URL_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes"
|
||||
URL_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}"
|
||||
URL_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes"
|
||||
URL_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}"
|
||||
|
||||
URL_CLIENT_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes"
|
||||
URL_CLIENT_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}"
|
||||
URL_CLIENT_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes"
|
||||
URL_CLIENT_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}"
|
||||
|
||||
URL_CLIENT_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}"
|
||||
URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/available"
|
||||
URL_CLIENT_GROUP_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/composite"
|
||||
@@ -90,9 +80,6 @@ URL_IDENTITY_PROVIDER_MAPPER = "{url}/admin/realms/{realm}/identity-provider/ins
|
||||
URL_COMPONENTS = "{url}/admin/realms/{realm}/components"
|
||||
URL_COMPONENT = "{url}/admin/realms/{realm}/components/{id}"
|
||||
|
||||
URL_AUTHZ_AUTHORIZATION_SCOPE = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope/{id}"
|
||||
URL_AUTHZ_AUTHORIZATION_SCOPES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/scope"
|
||||
|
||||
|
||||
def keycloak_argument_spec():
|
||||
"""
|
||||
@@ -1176,131 +1163,6 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='Could not update protocolmappers for clientscope %s in realm %s: %s'
|
||||
% (mapper_rep, realm, str(e)))
|
||||
|
||||
def get_default_clientscopes(self, realm, client_id=None):
|
||||
"""Fetch the name and ID of all clientscopes on the Keycloak server.
|
||||
|
||||
To fetch the full data of the client scope, make a subsequent call to
|
||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
||||
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
:return The default clientscopes of this realm or client
|
||||
"""
|
||||
url = URL_DEFAULT_CLIENTSCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENTSCOPES
|
||||
return self._get_clientscopes_of_type(realm, url, 'default', client_id)
|
||||
|
||||
def get_optional_clientscopes(self, realm, client_id=None):
|
||||
"""Fetch the name and ID of all clientscopes on the Keycloak server.
|
||||
|
||||
To fetch the full data of the client scope, make a subsequent call to
|
||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
||||
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
:return The optinal clientscopes of this realm or client
|
||||
"""
|
||||
url = URL_OPTIONAL_CLIENTSCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENTSCOPES
|
||||
return self._get_clientscopes_of_type(realm, url, 'optional', client_id)
|
||||
|
||||
def _get_clientscopes_of_type(self, realm, url_template, scope_type, client_id=None):
|
||||
"""Fetch the name and ID of all clientscopes on the Keycloak server.
|
||||
|
||||
To fetch the full data of the client scope, make a subsequent call to
|
||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
||||
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param url_template the template for the right type
|
||||
:param scope_type this can be either optinal or default
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
:return The clientscopes of the specified type of this realm
|
||||
"""
|
||||
if client_id is None:
|
||||
clientscopes_url = url_template.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of %s clientscopes in realm %s: %s" % (scope_type, realm, str(e)))
|
||||
else:
|
||||
cid = self.get_client_id(client_id=client_id, realm=realm)
|
||||
clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of %s clientscopes in client %s: %s" % (scope_type, client_id, clientscopes_url))
|
||||
|
||||
def _decide_url_type_clientscope(self, client_id=None, scope_type="default"):
|
||||
"""Decides which url to use.
|
||||
:param scope_type this can be either optinal or default
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
"""
|
||||
if client_id is None:
|
||||
if scope_type == "default":
|
||||
return URL_DEFAULT_CLIENTSCOPE
|
||||
if scope_type == "optional":
|
||||
return URL_OPTIONAL_CLIENTSCOPE
|
||||
else:
|
||||
if scope_type == "default":
|
||||
return URL_CLIENT_DEFAULT_CLIENTSCOPE
|
||||
if scope_type == "optional":
|
||||
return URL_CLIENT_OPTIONAL_CLIENTSCOPE
|
||||
|
||||
def add_default_clientscope(self, id, realm="master", client_id=None):
|
||||
"""Add a client scope as default either on realm or client level.
|
||||
|
||||
:param id: Client scope Id.
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
"""
|
||||
self._action_type_clientscope(id, client_id, "default", realm, 'add')
|
||||
|
||||
def add_optional_clientscope(self, id, realm="master", client_id=None):
|
||||
"""Add a client scope as optional either on realm or client level.
|
||||
|
||||
:param id: Client scope Id.
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
"""
|
||||
self._action_type_clientscope(id, client_id, "optional", realm, 'add')
|
||||
|
||||
def delete_default_clientscope(self, id, realm="master", client_id=None):
|
||||
"""Remove a client scope as default either on realm or client level.
|
||||
|
||||
:param id: Client scope Id.
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
"""
|
||||
self._action_type_clientscope(id, client_id, "default", realm, 'delete')
|
||||
|
||||
def delete_optional_clientscope(self, id, realm="master", client_id=None):
|
||||
"""Remove a client scope as optional either on realm or client level.
|
||||
|
||||
:param id: Client scope Id.
|
||||
:param realm: Realm in which the clientscope resides.
|
||||
:param client_id: The client in which the clientscope resides.
|
||||
"""
|
||||
self._action_type_clientscope(id, client_id, "optional", realm, 'delete')
|
||||
|
||||
def _action_type_clientscope(self, id=None, client_id=None, scope_type="default", realm="master", action='add'):
|
||||
""" Delete or add a clientscope of type.
|
||||
:param name: The name of the clientscope. A lookup will be performed to retrieve the clientscope ID.
|
||||
:param client_id: The ID of the clientscope (preferred to name).
|
||||
:param scope_type 'default' or 'optional'
|
||||
:param realm: The realm in which this group resides, default "master".
|
||||
"""
|
||||
cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm)
|
||||
# should have a good cid by here.
|
||||
clientscope_type_url = self._decide_url_type_clientscope(client_id, scope_type).format(realm=realm, id=id, cid=cid, url=self.baseurl)
|
||||
try:
|
||||
method = 'PUT' if action == "add" else 'DELETE'
|
||||
return open_url(clientscope_type_url, method=method, http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
place = 'realm' if client_id is None else 'client ' + client_id
|
||||
self.module.fail_json(msg="Unable to %s %s clientscope %s @ %s : %s" % (action, scope_type, id, place, str(e)))
|
||||
|
||||
def create_clientsecret(self, id, realm="master"):
|
||||
""" Generate a new client secret by id
|
||||
|
||||
@@ -1933,9 +1795,6 @@ class KeycloakAPI(object):
|
||||
data=json.dumps(updatedExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except HTTPError as e:
|
||||
self.module.fail_json(msg="Unable to update execution '%s': %s: %s %s" %
|
||||
(flowAlias, repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(updatedExec)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
|
||||
|
||||
@@ -1960,7 +1819,7 @@ class KeycloakAPI(object):
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
|
||||
def create_subflow(self, subflowName, flowAlias, realm='master', flowType='basic-flow'):
|
||||
def create_subflow(self, subflowName, flowAlias, realm='master'):
|
||||
""" Create new sublow on the flow
|
||||
|
||||
:param subflowName: name of the subflow to create
|
||||
@@ -1971,7 +1830,7 @@ class KeycloakAPI(object):
|
||||
newSubFlow = {}
|
||||
newSubFlow["alias"] = subflowName
|
||||
newSubFlow["provider"] = "registration-page-form"
|
||||
newSubFlow["type"] = flowType
|
||||
newSubFlow["type"] = "basic-flow"
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOW_EXECUTIONS_FLOW.format(
|
||||
url=self.baseurl,
|
||||
@@ -2006,11 +1865,8 @@ class KeycloakAPI(object):
|
||||
data=json.dumps(newExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except HTTPError as e:
|
||||
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s: %s %s" %
|
||||
(flowAlias, execution["providerId"], repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(newExec)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s" % (flowAlias, execution["providerId"], repr(e)))
|
||||
self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e)))
|
||||
|
||||
def change_execution_priority(self, executionId, diff, realm='master'):
|
||||
""" Raise or lower execution priority of diff time
|
||||
@@ -2334,44 +2190,3 @@ class KeycloakAPI(object):
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
|
||||
% (cid, realm, str(e)))
|
||||
|
||||
def get_authz_authorization_scope_by_name(self, name, client_id, realm):
|
||||
url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm)
|
||||
search_url = "%s/search?name=%s" % (url, quote(name))
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(search_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def create_authz_authorization_scope(self, payload, client_id, realm):
|
||||
"""Create an authorization scope for a Keycloak client"""
|
||||
url = URL_AUTHZ_AUTHORIZATION_SCOPES.format(url=self.baseurl, client_id=client_id, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create authorization scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def update_authz_authorization_scope(self, payload, id, client_id, realm):
|
||||
"""Update an authorization scope for a Keycloak client"""
|
||||
url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create update scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def remove_authz_authorization_scope(self, id, client_id, realm):
|
||||
"""Remove an authorization scope from a Keycloak client"""
|
||||
url = URL_AUTHZ_AUTHORIZATION_SCOPE.format(url=self.baseurl, id=id, client_id=client_id, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete scope %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
|
||||
@@ -8,7 +8,6 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
|
||||
import time
|
||||
|
||||
|
||||
class iLORedfishUtils(RedfishUtils):
|
||||
@@ -229,79 +228,3 @@ class iLORedfishUtils(RedfishUtils):
|
||||
if not response['ret']:
|
||||
return response
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % mgrattr['mgr_attr_name']}
|
||||
|
||||
def get_server_poststate(self):
|
||||
# Get server details
|
||||
response = self.get_request(self.root_uri + self.systems_uri)
|
||||
if not response["ret"]:
|
||||
return response
|
||||
server_data = response["data"]
|
||||
|
||||
if "Hpe" in server_data["Oem"]:
|
||||
return {
|
||||
"ret": True,
|
||||
"server_poststate": server_data["Oem"]["Hpe"]["PostState"]
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"ret": True,
|
||||
"server_poststate": server_data["Oem"]["Hp"]["PostState"]
|
||||
}
|
||||
|
||||
def wait_for_ilo_reboot_completion(self, polling_interval=60, max_polling_time=1800):
|
||||
# This method checks if OOB controller reboot is completed
|
||||
time.sleep(10)
|
||||
|
||||
# Check server poststate
|
||||
state = self.get_server_poststate()
|
||||
if not state["ret"]:
|
||||
return state
|
||||
|
||||
count = int(max_polling_time / polling_interval)
|
||||
times = 0
|
||||
|
||||
# When server is powered OFF
|
||||
pcount = 0
|
||||
while state["server_poststate"] in ["PowerOff", "Off"] and pcount < 5:
|
||||
time.sleep(10)
|
||||
state = self.get_server_poststate()
|
||||
if not state["ret"]:
|
||||
return state
|
||||
|
||||
if state["server_poststate"] not in ["PowerOff", "Off"]:
|
||||
break
|
||||
pcount = pcount + 1
|
||||
if state["server_poststate"] in ["PowerOff", "Off"]:
|
||||
return {
|
||||
"ret": False,
|
||||
"changed": False,
|
||||
"msg": "Server is powered OFF"
|
||||
}
|
||||
|
||||
# When server is not rebooting
|
||||
if state["server_poststate"] in ["InPostDiscoveryComplete", "FinishedPost"]:
|
||||
return {
|
||||
"ret": True,
|
||||
"changed": False,
|
||||
"msg": "Server is not rebooting"
|
||||
}
|
||||
|
||||
while state["server_poststate"] not in ["InPostDiscoveryComplete", "FinishedPost"] and count > times:
|
||||
state = self.get_server_poststate()
|
||||
if not state["ret"]:
|
||||
return state
|
||||
|
||||
if state["server_poststate"] in ["InPostDiscoveryComplete", "FinishedPost"]:
|
||||
return {
|
||||
"ret": True,
|
||||
"changed": True,
|
||||
"msg": "Server reboot is completed"
|
||||
}
|
||||
time.sleep(polling_interval)
|
||||
times = times + 1
|
||||
|
||||
return {
|
||||
"ret": False,
|
||||
"changed": False,
|
||||
"msg": "Server Reboot has failed, server state: {state} ".format(state=state)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ def gen_specs(**specs):
|
||||
specs.update({
|
||||
'bind_dn': dict(),
|
||||
'bind_pw': dict(default='', no_log=True),
|
||||
'ca_path': dict(type='path'),
|
||||
'dn': dict(required=True),
|
||||
'referrals_chasing': dict(type='str', default='anonymous', choices=['disabled', 'anonymous']),
|
||||
'server_uri': dict(default='ldapi:///'),
|
||||
@@ -53,7 +52,6 @@ class LdapGeneric(object):
|
||||
self.module = module
|
||||
self.bind_dn = self.module.params['bind_dn']
|
||||
self.bind_pw = self.module.params['bind_pw']
|
||||
self.ca_path = self.module.params['ca_path']
|
||||
self.referrals_chasing = self.module.params['referrals_chasing']
|
||||
self.server_uri = self.module.params['server_uri']
|
||||
self.start_tls = self.module.params['start_tls']
|
||||
@@ -99,9 +97,6 @@ class LdapGeneric(object):
|
||||
if not self.verify_cert:
|
||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||
|
||||
if self.ca_path:
|
||||
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca_path)
|
||||
|
||||
connection = ldap.initialize(self.server_uri)
|
||||
|
||||
if self.referrals_chasing == 'disabled':
|
||||
|
||||
@@ -26,7 +26,6 @@ class Response(object):
|
||||
def __init__(self):
|
||||
self.content = None
|
||||
self.status_code = None
|
||||
self.stderr = None
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
@@ -76,10 +75,6 @@ def memset_api_call(api_key, api_method, payload=None):
|
||||
msg = "Memset API returned a {0} response ({1}, {2})." . format(response.status_code, response.json()['error_type'], response.json()['error'])
|
||||
else:
|
||||
msg = "Memset API returned an error ({0}, {1})." . format(response.json()['error_type'], response.json()['error'])
|
||||
except urllib_error.URLError as e:
|
||||
has_failed = True
|
||||
msg = "An URLError occured ({0})." . format(type(e))
|
||||
response.stderr = "{0}" . format(e)
|
||||
|
||||
if msg is None:
|
||||
msg = response.json()
|
||||
|
||||
@@ -38,12 +38,6 @@ class DependencyCtxMgr(object):
|
||||
|
||||
|
||||
class DependencyMixin(ModuleHelperBase):
|
||||
"""
|
||||
THIS CLASS IS BEING DEPRECATED.
|
||||
See the deprecation notice in ``DependencyMixin.fail_on_missing_deps()`` below.
|
||||
|
||||
Mixin for mapping module options to running a CLI command with its arguments.
|
||||
"""
|
||||
_dependencies = []
|
||||
|
||||
@classmethod
|
||||
@@ -52,12 +46,6 @@ class DependencyMixin(ModuleHelperBase):
|
||||
return cls._dependencies[-1]
|
||||
|
||||
def fail_on_missing_deps(self):
|
||||
self.module.deprecate(
|
||||
'The DependencyMixin is being deprecated. '
|
||||
'Modules should use community.general.plugins.module_utils.deps instead.',
|
||||
version='9.0.0',
|
||||
collection_name='community.general',
|
||||
)
|
||||
for d in self._dependencies:
|
||||
if not d.has_it:
|
||||
self.module.fail_json(changed=False,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
|
||||
# (TODO: remove AnsibleModule!) pylint: disable-next=unused-import
|
||||
|
||||
@@ -7,17 +7,14 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
# pylint: disable=unused-import
|
||||
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.module_helper import (
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.module_helper import ( # noqa: F401, pylint: disable=unused-import
|
||||
ModuleHelper, StateModuleHelper, CmdModuleHelper, CmdStateModuleHelper, AnsibleModule
|
||||
)
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr, DependencyMixin # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.deco import (
|
||||
cause_changes, module_fails_on_exception, check_mode_skip, check_mode_skip_returns,
|
||||
)
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarMeta, VarDict, VarsMixin # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat # noqa: F401, pylint: disable=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin # noqa: F401, pylint: disable=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr # noqa: F401, pylint: disable=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException # noqa: F401, pylint: disable=unused-import
|
||||
# pylint: disable-next=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.deco import cause_changes, module_fails_on_exception # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarMeta, VarDict # noqa: F401, pylint: disable=unused-import
|
||||
|
||||
@@ -45,8 +45,6 @@ def render(to_render):
|
||||
"""Converts dictionary to OpenNebula template."""
|
||||
def recurse(to_render):
|
||||
for key, value in sorted(to_render.items()):
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, dict):
|
||||
yield '{0:}=[{1:}]'.format(key, ','.join(recurse(value)))
|
||||
continue
|
||||
@@ -54,9 +52,6 @@ def render(to_render):
|
||||
for item in value:
|
||||
yield '{0:}=[{1:}]'.format(key, ','.join(recurse(item)))
|
||||
continue
|
||||
if isinstance(value, str):
|
||||
yield '{0:}="{1:}"'.format(key, value.replace('\\', '\\\\').replace('"', '\\"'))
|
||||
continue
|
||||
yield '{0:}="{1:}"'.format(key, value)
|
||||
return '\n'.join(recurse(to_render))
|
||||
|
||||
|
||||
@@ -32,14 +32,12 @@ def pipx_runner(module, command, **kwargs):
|
||||
state=fmt.as_map(_state_map),
|
||||
name=fmt.as_list(),
|
||||
name_source=fmt.as_func(fmt.unpack_args(lambda n, s: [s] if s else [n])),
|
||||
install_apps=fmt.as_bool("--include-apps"),
|
||||
install_deps=fmt.as_bool("--include-deps"),
|
||||
inject_packages=fmt.as_list(),
|
||||
force=fmt.as_bool("--force"),
|
||||
include_injected=fmt.as_bool("--include-injected"),
|
||||
index_url=fmt.as_opt_val('--index-url'),
|
||||
python=fmt.as_opt_val('--python'),
|
||||
system_site_packages=fmt.as_bool("--system-site-packages"),
|
||||
_list=fmt.as_fixed(['list', '--include-injected', '--json']),
|
||||
editable=fmt.as_bool("--editable"),
|
||||
pip_args=fmt.as_opt_val('--pip-args'),
|
||||
|
||||
@@ -63,7 +63,11 @@ def puppet_runner(module):
|
||||
return cmd
|
||||
|
||||
def noop_func(v):
|
||||
return ["--noop"] if module.check_mode or v else ["--no-noop"]
|
||||
_noop = cmd_runner_fmt.as_map({
|
||||
True: "--noop",
|
||||
False: "--no-noop",
|
||||
})
|
||||
return _noop(module.check_mode or v)
|
||||
|
||||
_logdest_map = {
|
||||
"syslog": ["--logdest", "syslog"],
|
||||
@@ -92,7 +96,6 @@ def puppet_runner(module):
|
||||
confdir=cmd_runner_fmt.as_opt_val("--confdir"),
|
||||
environment=cmd_runner_fmt.as_opt_val("--environment"),
|
||||
tags=cmd_runner_fmt.as_func(lambda v: ["--tags", ",".join(v)]),
|
||||
skip_tags=cmd_runner_fmt.as_func(lambda v: ["--skip_tags", ",".join(v)]),
|
||||
certname=cmd_runner_fmt.as_opt_eq_val("--certname"),
|
||||
noop=cmd_runner_fmt.as_func(noop_func),
|
||||
use_srv_records=cmd_runner_fmt.as_map({
|
||||
|
||||
@@ -3217,35 +3217,3 @@ class RedfishUtils(object):
|
||||
body["SecureBootEnable"] = True
|
||||
|
||||
return self.patch_request(self.root_uri + secure_boot_url, body, check_pyld=True)
|
||||
|
||||
def get_hpe_thermal_config(self):
|
||||
result = {}
|
||||
key = "Thermal"
|
||||
# Go through list
|
||||
for chassis_uri in self.chassis_uri_list:
|
||||
response = self.get_request(self.root_uri + chassis_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
oem = data.get['Oem']
|
||||
hpe = oem.get['Hpe']
|
||||
thermal_config = hpe.get('ThermalConfiguration')
|
||||
result["current_thermal_config"] = thermal_config
|
||||
return result
|
||||
|
||||
def get_hpe_fan_percent_min(self):
|
||||
result = {}
|
||||
key = "Thermal"
|
||||
# Go through list
|
||||
for chassis_uri in self.chassis_uri_list:
|
||||
response = self.get_request(self.root_uri + chassis_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
oem = data.get['Oem']
|
||||
hpe = oem.get['Hpe']
|
||||
fan_percent_min_config = hpe.get('FanPercentMinimum')
|
||||
result["fan_percent_min"] = fan_percent_min_config
|
||||
return result
|
||||
|
||||
@@ -81,18 +81,12 @@ def api_request(module, endpoint, data=None, method="GET"):
|
||||
|
||||
try:
|
||||
content = response.read()
|
||||
|
||||
if not content:
|
||||
return None, info
|
||||
else:
|
||||
json_response = json.loads(content)
|
||||
return json_response, info
|
||||
json_response = json.loads(content)
|
||||
return json_response, info
|
||||
except AttributeError as error:
|
||||
module.fail_json(
|
||||
msg="Rundeck API request error",
|
||||
exception=to_native(error),
|
||||
execution_info=info
|
||||
)
|
||||
module.fail_json(msg="Rundeck API request error",
|
||||
exception=to_native(error),
|
||||
execution_info=info)
|
||||
except ValueError as error:
|
||||
module.fail_json(
|
||||
msg="No valid JSON response",
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2023, Alexei Znamensky <russoz@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
|
||||
|
||||
|
||||
_alias_state_map = dict(
|
||||
present='alias',
|
||||
absent='unalias',
|
||||
info='aliases',
|
||||
)
|
||||
|
||||
_state_map = dict(
|
||||
present='install',
|
||||
absent='remove',
|
||||
enabled='enable',
|
||||
disabled='disable',
|
||||
)
|
||||
|
||||
|
||||
def snap_runner(module, **kwargs):
|
||||
runner = CmdRunner(
|
||||
module,
|
||||
"snap",
|
||||
arg_formats=dict(
|
||||
state_alias=cmd_runner_fmt.as_map(_alias_state_map), # snap_alias only
|
||||
name=cmd_runner_fmt.as_list(),
|
||||
alias=cmd_runner_fmt.as_list(), # snap_alias only
|
||||
state=cmd_runner_fmt.as_map(_state_map),
|
||||
_list=cmd_runner_fmt.as_fixed("list"),
|
||||
_set=cmd_runner_fmt.as_fixed("set"),
|
||||
get=cmd_runner_fmt.as_fixed(["get", "-d"]),
|
||||
classic=cmd_runner_fmt.as_bool("--classic"),
|
||||
channel=cmd_runner_fmt.as_func(lambda v: [] if v == 'stable' else ['--channel', '{0}'.format(v)]),
|
||||
options=cmd_runner_fmt.as_list(),
|
||||
),
|
||||
check_rc=False,
|
||||
**kwargs
|
||||
)
|
||||
return runner
|
||||
@@ -108,7 +108,7 @@ EXAMPLES = r'''
|
||||
device: en1
|
||||
attributes:
|
||||
mtu: 900
|
||||
arp: 'off'
|
||||
arp: off
|
||||
state: available
|
||||
|
||||
- name: Configure IP, netmask and set en1 up.
|
||||
|
||||
@@ -27,7 +27,8 @@ attributes:
|
||||
options:
|
||||
package:
|
||||
description:
|
||||
- List of packages to install, upgrade, or remove.
|
||||
- list of packages to install, upgrade or remove.
|
||||
required: true
|
||||
aliases: [ name, pkg ]
|
||||
type: list
|
||||
elements: str
|
||||
@@ -39,30 +40,9 @@ options:
|
||||
type: str
|
||||
update_cache:
|
||||
description:
|
||||
- Run the equivalent of C(apt-get update) before the operation. Can be run as part of the package installation or as a separate step.
|
||||
- Default is not to update the cache.
|
||||
- update the package database first C(apt-get update).
|
||||
type: bool
|
||||
default: false
|
||||
clean:
|
||||
description:
|
||||
- Run the equivalent of C(apt-get clean) to clear out the local repository of retrieved package files. It removes everything but
|
||||
the lock file from C(/var/cache/apt/archives/) and C(/var/cache/apt/archives/partial/).
|
||||
- Can be run as part of the package installation (clean runs before install) or as a separate step.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
dist_upgrade:
|
||||
description:
|
||||
- If true performs an C(apt-get dist-upgrade) to upgrade system.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
update_kernel:
|
||||
description:
|
||||
- If true performs an C(update-kernel) to upgrade kernel packages.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
author:
|
||||
- Evgenii Terechkov (@evgkrsk)
|
||||
'''
|
||||
@@ -96,16 +76,6 @@ EXAMPLES = '''
|
||||
name: bar
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Run the equivalent of "apt-get clean" as a separate step
|
||||
community.general.apt_rpm:
|
||||
clean: true
|
||||
|
||||
- name: Perform cache update and complete system upgrade (includes kernel)
|
||||
community.general.apt_rpm:
|
||||
update_cache: true
|
||||
dist_upgrade: true
|
||||
update_kernel: true
|
||||
'''
|
||||
|
||||
import os
|
||||
@@ -114,8 +84,6 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
APT_PATH = "/usr/bin/apt-get"
|
||||
RPM_PATH = "/usr/bin/rpm"
|
||||
APT_GET_ZERO = "\n0 upgraded, 0 newly installed"
|
||||
UPDATE_KERNEL_ZERO = "\nTry to install new kernel "
|
||||
|
||||
|
||||
def query_package(module, name):
|
||||
@@ -136,39 +104,14 @@ def query_package_provides(module, name):
|
||||
|
||||
|
||||
def update_package_db(module):
|
||||
rc, update_out, err = module.run_command([APT_PATH, "update"], check_rc=True, environ_update={"LANG": "C"})
|
||||
return (False, update_out)
|
||||
rc, out, err = module.run_command("%s update" % APT_PATH)
|
||||
|
||||
|
||||
def dir_size(module, path):
|
||||
total_size = 0
|
||||
for path, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
total_size += os.path.getsize(os.path.join(path, f))
|
||||
return total_size
|
||||
|
||||
|
||||
def clean(module):
|
||||
t = dir_size(module, "/var/cache/apt/archives")
|
||||
rc, out, err = module.run_command([APT_PATH, "clean"], check_rc=True)
|
||||
return (t != dir_size(module, "/var/cache/apt/archives"), out)
|
||||
|
||||
|
||||
def dist_upgrade(module):
|
||||
rc, out, err = module.run_command([APT_PATH, "-y", "dist-upgrade"], check_rc=True, environ_update={"LANG": "C"})
|
||||
return (APT_GET_ZERO not in out, out)
|
||||
|
||||
|
||||
def update_kernel(module):
|
||||
rc, out, err = module.run_command(["/usr/sbin/update-kernel", "-y"], check_rc=True, environ_update={"LANG": "C"})
|
||||
return (UPDATE_KERNEL_ZERO not in out, out)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="could not update package db: %s" % err)
|
||||
|
||||
|
||||
def remove_packages(module, packages):
|
||||
|
||||
if packages is None:
|
||||
return (False, "Empty package list")
|
||||
|
||||
remove_c = 0
|
||||
# Using a for loop in case of error, we can report the package that failed
|
||||
for package in packages:
|
||||
@@ -176,7 +119,7 @@ def remove_packages(module, packages):
|
||||
if not query_package(module, package):
|
||||
continue
|
||||
|
||||
rc, out, err = module.run_command("%s -y remove %s" % (APT_PATH, package), environ_update={"LANG": "C"})
|
||||
rc, out, err = module.run_command("%s -y remove %s" % (APT_PATH, package))
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg="failed to remove %s: %s" % (package, err))
|
||||
@@ -184,16 +127,13 @@ def remove_packages(module, packages):
|
||||
remove_c += 1
|
||||
|
||||
if remove_c > 0:
|
||||
return (True, "removed %s package(s)" % remove_c)
|
||||
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
|
||||
|
||||
return (False, "package(s) already absent")
|
||||
module.exit_json(changed=False, msg="package(s) already absent")
|
||||
|
||||
|
||||
def install_packages(module, pkgspec):
|
||||
|
||||
if pkgspec is None:
|
||||
return (False, "Empty package list")
|
||||
|
||||
packages = ""
|
||||
for package in pkgspec:
|
||||
if not query_package_provides(module, package):
|
||||
@@ -201,7 +141,7 @@ def install_packages(module, pkgspec):
|
||||
|
||||
if len(packages) != 0:
|
||||
|
||||
rc, out, err = module.run_command("%s -y install %s" % (APT_PATH, packages), environ_update={"LANG": "C"})
|
||||
rc, out, err = module.run_command("%s -y install %s" % (APT_PATH, packages))
|
||||
|
||||
installed = True
|
||||
for packages in pkgspec:
|
||||
@@ -212,9 +152,9 @@ def install_packages(module, pkgspec):
|
||||
if rc or not installed:
|
||||
module.fail_json(msg="'apt-get -y install %s' failed: %s" % (packages, err))
|
||||
else:
|
||||
return (True, "%s present(s)" % packages)
|
||||
module.exit_json(changed=True, msg="%s present(s)" % packages)
|
||||
else:
|
||||
return (False, "Nothing to install")
|
||||
module.exit_json(changed=False)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -222,10 +162,7 @@ def main():
|
||||
argument_spec=dict(
|
||||
state=dict(type='str', default='present', choices=['absent', 'installed', 'present', 'removed']),
|
||||
update_cache=dict(type='bool', default=False),
|
||||
clean=dict(type='bool', default=False),
|
||||
dist_upgrade=dict(type='bool', default=False),
|
||||
update_kernel=dict(type='bool', default=False),
|
||||
package=dict(type='list', elements='str', aliases=['name', 'pkg']),
|
||||
package=dict(type='list', elements='str', required=True, aliases=['name', 'pkg']),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -233,39 +170,17 @@ def main():
|
||||
module.fail_json(msg="cannot find /usr/bin/apt-get and/or /usr/bin/rpm")
|
||||
|
||||
p = module.params
|
||||
modified = False
|
||||
output = ""
|
||||
|
||||
if p['update_cache']:
|
||||
update_package_db(module)
|
||||
|
||||
if p['clean']:
|
||||
(m, out) = clean(module)
|
||||
modified = modified or m
|
||||
|
||||
if p['dist_upgrade']:
|
||||
(m, out) = dist_upgrade(module)
|
||||
modified = modified or m
|
||||
output += out
|
||||
|
||||
if p['update_kernel']:
|
||||
(m, out) = update_kernel(module)
|
||||
modified = modified or m
|
||||
output += out
|
||||
|
||||
packages = p['package']
|
||||
|
||||
if p['state'] in ['installed', 'present']:
|
||||
(m, out) = install_packages(module, packages)
|
||||
modified = modified or m
|
||||
output += out
|
||||
install_packages(module, packages)
|
||||
|
||||
if p['state'] in ['absent', 'removed']:
|
||||
(m, out) = remove_packages(module, packages)
|
||||
modified = modified or m
|
||||
output += out
|
||||
|
||||
# Return total modification status and output of all commands
|
||||
module.exit_json(changed=modified, msg=output)
|
||||
elif p['state'] in ['absent', 'removed']:
|
||||
remove_packages(module, packages)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -198,10 +198,6 @@ from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native
|
||||
from ansible.module_utils import six
|
||||
|
||||
try: # python 3.2+
|
||||
from zipfile import BadZipFile # type: ignore[attr-defined]
|
||||
except ImportError: # older python
|
||||
from zipfile import BadZipfile as BadZipFile
|
||||
|
||||
LZMA_IMP_ERR = None
|
||||
if six.PY3:
|
||||
@@ -538,7 +534,7 @@ class ZipArchive(Archive):
|
||||
archive = zipfile.ZipFile(_to_native_ascii(path), 'r')
|
||||
checksums = set((info.filename, info.CRC) for info in archive.infolist())
|
||||
archive.close()
|
||||
except BadZipFile:
|
||||
except zipfile.BadZipfile:
|
||||
checksums = set()
|
||||
return checksums
|
||||
|
||||
@@ -608,13 +604,7 @@ class TarArchive(Archive):
|
||||
# The python implementations of gzip, bz2, and lzma do not support restoring compressed files
|
||||
# to their original names so only file checksum is returned
|
||||
f = self._open_compressed_file(_to_native_ascii(path), 'r')
|
||||
checksum = 0
|
||||
while True:
|
||||
chunk = f.read(16 * 1024 * 1024)
|
||||
if not chunk:
|
||||
break
|
||||
checksum = crc32(chunk, checksum)
|
||||
checksums = set([(b'', checksum)])
|
||||
checksums = set([(b'', crc32(f.read()))])
|
||||
f.close()
|
||||
except Exception:
|
||||
checksums = set()
|
||||
|
||||
@@ -15,7 +15,7 @@ module: awall
|
||||
short_description: Manage awall policies
|
||||
author: Ted Trask (@tdtrask) <ttrask01@yahoo.com>
|
||||
description:
|
||||
- This modules allows for enable/disable/activate of C(awall) policies.
|
||||
- This modules allows for enable/disable/activate of I(awall) policies.
|
||||
- Alpine Wall (I(awall)) generates a firewall configuration from the enabled policy files
|
||||
and activates the configuration on the system.
|
||||
extends_documentation_fragment:
|
||||
@@ -41,11 +41,8 @@ options:
|
||||
description:
|
||||
- Activate the new firewall rules.
|
||||
- Can be run with other steps or on its own.
|
||||
- Idempotency is affected if I(activate=true), as the module will always report a changed state.
|
||||
type: bool
|
||||
default: false
|
||||
notes:
|
||||
- At least one of I(name) and I(activate) is required.
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: btrfs_info
|
||||
short_description: Query btrfs filesystem info
|
||||
version_added: "6.6.0"
|
||||
description: Query status of available btrfs filesystems, including uuid, label, subvolumes and mountpoints.
|
||||
|
||||
author:
|
||||
- Gregory Furlong (@gnfzdz)
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
||||
- name: Query information about mounted btrfs filesystems
|
||||
community.general.btrfs_info:
|
||||
register: my_btrfs_info
|
||||
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
|
||||
filesystems:
|
||||
description: Summaries of the current state for all btrfs filesystems found on the target host.
|
||||
type: list
|
||||
elements: dict
|
||||
returned: success
|
||||
contains:
|
||||
uuid:
|
||||
description: A unique identifier assigned to the filesystem.
|
||||
type: str
|
||||
sample: 96c9c605-1454-49b8-a63a-15e2584c208e
|
||||
label:
|
||||
description: An optional label assigned to the filesystem.
|
||||
type: str
|
||||
sample: Tank
|
||||
devices:
|
||||
description: A list of devices assigned to the filesystem.
|
||||
type: list
|
||||
sample:
|
||||
- /dev/sda1
|
||||
- /dev/sdb1
|
||||
default_subvolume:
|
||||
description: The id of the filesystem's default subvolume.
|
||||
type: int
|
||||
sample: 5
|
||||
subvolumes:
|
||||
description: A list of dicts containing metadata for all of the filesystem's subvolumes.
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
id:
|
||||
description: An identifier assigned to the subvolume, unique within the containing filesystem.
|
||||
type: int
|
||||
sample: 256
|
||||
mountpoints:
|
||||
description: Paths where the subvolume is mounted on the targeted host.
|
||||
type: list
|
||||
sample: ['/home']
|
||||
parent:
|
||||
description: The identifier of this subvolume's parent.
|
||||
type: int
|
||||
sample: 5
|
||||
path:
|
||||
description: The full path of the subvolume relative to the btrfs fileystem's root.
|
||||
type: str
|
||||
sample: /@home
|
||||
|
||||
'''
|
||||
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.btrfs import BtrfsFilesystemsProvider
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def run_module():
|
||||
module_args = dict()
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
provider = BtrfsFilesystemsProvider(module)
|
||||
filesystems = [x.get_summary() for x in provider.get_filesystems()]
|
||||
result = {
|
||||
"filesystems": filesystems,
|
||||
}
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,682 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: btrfs_subvolume
|
||||
short_description: Manage btrfs subvolumes
|
||||
version_added: "6.6.0"
|
||||
|
||||
description: Creates, updates and deletes btrfs subvolumes and snapshots.
|
||||
|
||||
options:
|
||||
automount:
|
||||
description:
|
||||
- Allow the module to temporarily mount the targeted btrfs filesystem in order to validate the current state and make any required changes.
|
||||
type: bool
|
||||
default: false
|
||||
default:
|
||||
description:
|
||||
- Make the subvolume specified by I(name) the filesystem's default subvolume.
|
||||
type: bool
|
||||
default: false
|
||||
filesystem_device:
|
||||
description:
|
||||
- A block device contained within the btrfs filesystem to be targeted.
|
||||
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
|
||||
type: path
|
||||
filesystem_label:
|
||||
description:
|
||||
- A descriptive label assigned to the btrfs filesystem to be targeted.
|
||||
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
|
||||
type: str
|
||||
filesystem_uuid:
|
||||
description:
|
||||
- A unique identifier assigned to the btrfs filesystem to be targeted.
|
||||
- Useful when multiple btrfs filesystems are present to specify which filesystem should be targeted.
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Name of the subvolume/snapshot to be targeted.
|
||||
required: true
|
||||
type: str
|
||||
recursive:
|
||||
description:
|
||||
- When true, indicates that parent/child subvolumes should be created/removedas necessary
|
||||
to complete the operation (for I(state=present) and I(state=absent) respectively).
|
||||
type: bool
|
||||
default: false
|
||||
snapshot_source:
|
||||
description:
|
||||
- Identifies the source subvolume for the created snapshot.
|
||||
- Infers that the created subvolume is a snapshot.
|
||||
type: str
|
||||
snapshot_conflict:
|
||||
description:
|
||||
- Policy defining behavior when a subvolume already exists at the path of the requested snapshot.
|
||||
- C(skip) - Create a snapshot only if a subvolume does not yet exist at the target location, otherwise indicate that no change is required.
|
||||
Warning, this option does not yet verify that the target subvolume was generated from a snapshot of the requested source.
|
||||
- C(clobber) - If a subvolume already exists at the requested location, delete it first.
|
||||
This option is not idempotent and will result in a new snapshot being generated on every execution.
|
||||
- C(error) - If a subvolume already exists at the requested location, return an error.
|
||||
This option is not idempotent and will result in an error on replay of the module.
|
||||
type: str
|
||||
choices: [ skip, clobber, error ]
|
||||
default: skip
|
||||
state:
|
||||
description:
|
||||
- Indicates the current state of the targeted subvolume.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
|
||||
notes:
|
||||
- If any or all of the options I(filesystem_device), I(filesystem_label) or I(filesystem_uuid) parameters are provided, there is expected
|
||||
to be a matching btrfs filesystem. If none are provided and only a single btrfs filesystem exists or only a single
|
||||
btrfs filesystem is mounted, that filesystem will be used; otherwise, the module will take no action and return an error.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: partial
|
||||
details:
|
||||
- In some scenarios it may erroneously report intermediate subvolumes being created.
|
||||
After mounting, if a directory like file is found where the subvolume would have been created, the operation is skipped.
|
||||
diff_mode:
|
||||
support: none
|
||||
|
||||
author:
|
||||
- Gregory Furlong (@gnfzdz)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
||||
- name: Create a @home subvolume under the root subvolume
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@home
|
||||
device: /dev/vda2
|
||||
|
||||
- name: Remove the @home subvolume if it exists
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@home
|
||||
state: absent
|
||||
device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the root subvolume named @
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@
|
||||
snapshot_source: /
|
||||
device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the root subvolume and make it the new default subvolume
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@
|
||||
snapshot_source: /
|
||||
default: Yes
|
||||
device: /dev/vda2
|
||||
|
||||
- name: Create a snapshot of the /@ subvolume and recursively creating intermediate subvolumes as required
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@snapshots/@2022_06_09
|
||||
snapshot_source: /@
|
||||
recursive: True
|
||||
device: /dev/vda2
|
||||
|
||||
- name: Remove the /@ subvolume and recursively delete child subvolumes as required
|
||||
community.general.btrfs_subvolume:
|
||||
name: /@snapshots/@2022_06_09
|
||||
snapshot_source: /@
|
||||
recursive: True
|
||||
device: /dev/vda2
|
||||
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
|
||||
filesystem:
|
||||
description:
|
||||
- A summary of the final state of the targeted btrfs filesystem.
|
||||
type: dict
|
||||
returned: success
|
||||
contains:
|
||||
uuid:
|
||||
description: A unique identifier assigned to the filesystem.
|
||||
returned: success
|
||||
type: str
|
||||
sample: 96c9c605-1454-49b8-a63a-15e2584c208e
|
||||
label:
|
||||
description: An optional label assigned to the filesystem.
|
||||
returned: success
|
||||
type: str
|
||||
sample: Tank
|
||||
devices:
|
||||
description: A list of devices assigned to the filesystem.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- /dev/sda1
|
||||
- /dev/sdb1
|
||||
default_subvolume:
|
||||
description: The ID of the filesystem's default subvolume.
|
||||
returned: success and if filesystem is mounted
|
||||
type: int
|
||||
sample: 5
|
||||
subvolumes:
|
||||
description: A list of dicts containing metadata for all of the filesystem's subvolumes.
|
||||
returned: success and if filesystem is mounted
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
id:
|
||||
description: An identifier assigned to the subvolume, unique within the containing filesystem.
|
||||
type: int
|
||||
sample: 256
|
||||
mountpoints:
|
||||
description: Paths where the subvolume is mounted on the targeted host.
|
||||
type: list
|
||||
sample: ['/home']
|
||||
parent:
|
||||
description: The identifier of this subvolume's parent.
|
||||
type: int
|
||||
sample: 5
|
||||
path:
|
||||
description: The full path of the subvolume relative to the btrfs fileystem's root.
|
||||
type: str
|
||||
sample: /@home
|
||||
|
||||
modifications:
|
||||
description:
|
||||
- A list where each element describes a change made to the target btrfs filesystem.
|
||||
type: list
|
||||
returned: Success
|
||||
elements: str
|
||||
|
||||
target_subvolume_id:
|
||||
description:
|
||||
- The ID of the subvolume specified with the I(name) parameter, either pre-existing or created as part of module execution.
|
||||
type: int
|
||||
sample: 257
|
||||
returned: Success and subvolume exists after module execution
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.btrfs import BtrfsFilesystemsProvider, BtrfsCommands, BtrfsModuleException
|
||||
from ansible_collections.community.general.plugins.module_utils.btrfs import normalize_subvolume_path
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
|
||||
class BtrfsSubvolumeModule(object):
|
||||
|
||||
__BTRFS_ROOT_SUBVOLUME = '/'
|
||||
__BTRFS_ROOT_SUBVOLUME_ID = 5
|
||||
__BTRFS_SUBVOLUME_INODE_NUMBER = 256
|
||||
|
||||
__CREATE_SUBVOLUME_OPERATION = 'create'
|
||||
__CREATE_SNAPSHOT_OPERATION = 'snapshot'
|
||||
__DELETE_SUBVOLUME_OPERATION = 'delete'
|
||||
__SET_DEFAULT_SUBVOLUME_OPERATION = 'set-default'
|
||||
|
||||
__UNKNOWN_SUBVOLUME_ID = '?'
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.__btrfs_api = BtrfsCommands(module)
|
||||
self.__provider = BtrfsFilesystemsProvider(module)
|
||||
|
||||
# module parameters
|
||||
name = self.module.params['name']
|
||||
self.__name = normalize_subvolume_path(name) if name is not None else None
|
||||
self.__state = self.module.params['state']
|
||||
|
||||
self.__automount = self.module.params['automount']
|
||||
self.__default = self.module.params['default']
|
||||
self.__filesystem_device = self.module.params['filesystem_device']
|
||||
self.__filesystem_label = self.module.params['filesystem_label']
|
||||
self.__filesystem_uuid = self.module.params['filesystem_uuid']
|
||||
self.__recursive = self.module.params['recursive']
|
||||
self.__snapshot_conflict = self.module.params['snapshot_conflict']
|
||||
snapshot_source = self.module.params['snapshot_source']
|
||||
self.__snapshot_source = normalize_subvolume_path(snapshot_source) if snapshot_source is not None else None
|
||||
|
||||
# execution state
|
||||
self.__filesystem = None
|
||||
self.__required_mounts = []
|
||||
self.__unit_of_work = []
|
||||
self.__completed_work = []
|
||||
self.__temporary_mounts = dict()
|
||||
|
||||
def run(self):
|
||||
error = None
|
||||
try:
|
||||
self.__load_filesystem()
|
||||
self.__prepare_unit_of_work()
|
||||
|
||||
if not self.module.check_mode:
|
||||
# check required mounts & mount
|
||||
if len(self.__unit_of_work) > 0:
|
||||
self.__execute_unit_of_work()
|
||||
self.__filesystem.refresh()
|
||||
else:
|
||||
# check required mounts
|
||||
self.__completed_work.extend(self.__unit_of_work)
|
||||
except Exception as e:
|
||||
error = e
|
||||
finally:
|
||||
self.__cleanup_mounts()
|
||||
if self.__filesystem is not None:
|
||||
self.__filesystem.refresh_mountpoints()
|
||||
|
||||
return (error, self.get_results())
|
||||
|
||||
# Identify the targeted filesystem and obtain the current state
|
||||
def __load_filesystem(self):
|
||||
if self.__has_filesystem_criteria():
|
||||
filesystem = self.__find_matching_filesytem()
|
||||
else:
|
||||
filesystem = self.__find_default_filesystem()
|
||||
|
||||
# The filesystem must be mounted to obtain the current state (subvolumes, default, etc)
|
||||
if not filesystem.is_mounted():
|
||||
if not self.__automount:
|
||||
raise BtrfsModuleException(
|
||||
"Target filesystem uuid=%s is not currently mounted and automount=False."
|
||||
"Mount explicitly before module execution or pass automount=True" % filesystem.uuid)
|
||||
elif self.module.check_mode:
|
||||
# TODO is failing the module an appropriate outcome in this scenario?
|
||||
raise BtrfsModuleException(
|
||||
"Target filesystem uuid=%s is not currently mounted. Unable to validate the current"
|
||||
"state while running with check_mode=True" % filesystem.uuid)
|
||||
else:
|
||||
self.__mount_subvolume_id_to_tempdir(filesystem, self.__BTRFS_ROOT_SUBVOLUME_ID)
|
||||
filesystem.refresh()
|
||||
self.__filesystem = filesystem
|
||||
|
||||
def __has_filesystem_criteria(self):
|
||||
return self.__filesystem_uuid is not None or self.__filesystem_label is not None or self.__filesystem_device is not None
|
||||
|
||||
def __find_matching_filesytem(self):
|
||||
criteria = {
|
||||
'uuid': self.__filesystem_uuid,
|
||||
'label': self.__filesystem_label,
|
||||
'device': self.__filesystem_device,
|
||||
}
|
||||
return self.__provider.get_matching_filesystem(criteria)
|
||||
|
||||
def __find_default_filesystem(self):
|
||||
filesystems = self.__provider.get_filesystems()
|
||||
filesystem = None
|
||||
|
||||
if len(filesystems) == 1:
|
||||
filesystem = filesystems[0]
|
||||
else:
|
||||
mounted_filesystems = [x for x in filesystems if x.is_mounted()]
|
||||
if len(mounted_filesystems) == 1:
|
||||
filesystem = mounted_filesystems[0]
|
||||
|
||||
if filesystem is not None:
|
||||
return filesystem
|
||||
else:
|
||||
raise BtrfsModuleException(
|
||||
"Failed to automatically identify targeted filesystem. "
|
||||
"No explicit device indicated and found %d available filesystems." % len(filesystems)
|
||||
)
|
||||
|
||||
# Prepare unit of work
|
||||
def __prepare_unit_of_work(self):
|
||||
if self.__state == "present":
|
||||
if self.__snapshot_source is None:
|
||||
self.__prepare_subvolume_present()
|
||||
else:
|
||||
self.__prepare_snapshot_present()
|
||||
|
||||
if self.__default:
|
||||
self.__prepare_set_default()
|
||||
elif self.__state == "absent":
|
||||
self.__prepare_subvolume_absent()
|
||||
|
||||
def __prepare_subvolume_present(self):
|
||||
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
|
||||
if subvolume is None:
|
||||
self.__prepare_before_create_subvolume(self.__name)
|
||||
self.__stage_create_subvolume(self.__name)
|
||||
|
||||
def __prepare_before_create_subvolume(self, subvolume_name):
|
||||
closest_parent = self.__filesystem.get_nearest_subvolume(subvolume_name)
|
||||
self.__stage_required_mount(closest_parent)
|
||||
if self.__recursive:
|
||||
self.__prepare_create_intermediates(closest_parent, subvolume_name)
|
||||
|
||||
def __prepare_create_intermediates(self, closest_subvolume, subvolume_name):
|
||||
relative_path = closest_subvolume.get_child_relative_path(self.__name)
|
||||
missing_subvolumes = [x for x in relative_path.split(os.path.sep) if len(x) > 0]
|
||||
if len(missing_subvolumes) > 1:
|
||||
current = closest_subvolume.path
|
||||
for s in missing_subvolumes[:-1]:
|
||||
separator = os.path.sep if current[-1] != os.path.sep else ""
|
||||
current = current + separator + s
|
||||
self.__stage_create_subvolume(current, True)
|
||||
|
||||
def __prepare_snapshot_present(self):
|
||||
source_subvolume = self.__filesystem.get_subvolume_by_name(self.__snapshot_source)
|
||||
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
|
||||
subvolume_exists = subvolume is not None
|
||||
|
||||
if subvolume_exists:
|
||||
if self.__snapshot_conflict == "skip":
|
||||
# No change required
|
||||
return
|
||||
elif self.__snapshot_conflict == "error":
|
||||
raise BtrfsModuleException("Target subvolume=%s already exists and snapshot_conflict='error'" % self.__name)
|
||||
|
||||
if source_subvolume is None:
|
||||
raise BtrfsModuleException("Source subvolume %s does not exist" % self.__snapshot_source)
|
||||
elif subvolume is not None and source_subvolume.id == subvolume.id:
|
||||
raise BtrfsModuleException("Snapshot source and target are the same.")
|
||||
else:
|
||||
self.__stage_required_mount(source_subvolume)
|
||||
|
||||
if subvolume_exists and self.__snapshot_conflict == "clobber":
|
||||
self.__prepare_delete_subvolume_tree(subvolume)
|
||||
elif not subvolume_exists:
|
||||
self.__prepare_before_create_subvolume(self.__name)
|
||||
|
||||
self.__stage_create_snapshot(source_subvolume, self.__name)
|
||||
|
||||
def __prepare_subvolume_absent(self):
|
||||
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
|
||||
if subvolume is not None:
|
||||
self.__prepare_delete_subvolume_tree(subvolume)
|
||||
|
||||
def __prepare_delete_subvolume_tree(self, subvolume):
|
||||
if subvolume.is_filesystem_root():
|
||||
raise BtrfsModuleException("Can not delete the filesystem's root subvolume")
|
||||
if not self.__recursive and len(subvolume.get_child_subvolumes()) > 0:
|
||||
raise BtrfsModuleException("Subvolume targeted for deletion %s has children and recursive=False."
|
||||
"Either explicitly delete the child subvolumes first or pass "
|
||||
"parameter recursive=True." % subvolume.path)
|
||||
|
||||
self.__stage_required_mount(subvolume.get_parent_subvolume())
|
||||
queue = self.__prepare_recursive_delete_order(subvolume) if self.__recursive else [subvolume]
|
||||
# prepare unit of work
|
||||
for s in queue:
|
||||
if s.is_mounted():
|
||||
# TODO potentially unmount the subvolume if automount=True ?
|
||||
raise BtrfsModuleException("Can not delete mounted subvolume=%s" % s.path)
|
||||
if s.is_filesystem_default():
|
||||
self.__stage_set_default_subvolume(self.__BTRFS_ROOT_SUBVOLUME, self.__BTRFS_ROOT_SUBVOLUME_ID)
|
||||
self.__stage_delete_subvolume(s)
|
||||
|
||||
def __prepare_recursive_delete_order(self, subvolume):
|
||||
"""Return the subvolume and all descendents as a list, ordered so that descendents always occur before their ancestors"""
|
||||
pending = [subvolume]
|
||||
ordered = []
|
||||
while len(pending) > 0:
|
||||
next = pending.pop()
|
||||
ordered.append(next)
|
||||
pending.extend(next.get_child_subvolumes())
|
||||
ordered.reverse() # reverse to ensure children are deleted before their parent
|
||||
return ordered
|
||||
|
||||
def __prepare_set_default(self):
|
||||
subvolume = self.__filesystem.get_subvolume_by_name(self.__name)
|
||||
subvolume_id = subvolume.id if subvolume is not None else None
|
||||
|
||||
if self.__filesystem.default_subvolid != subvolume_id:
|
||||
self.__stage_set_default_subvolume(self.__name, subvolume_id)
|
||||
|
||||
# Stage operations to the unit of work
|
||||
def __stage_required_mount(self, subvolume):
|
||||
if subvolume.get_mounted_path() is None:
|
||||
if self.__automount:
|
||||
self.__required_mounts.append(subvolume)
|
||||
else:
|
||||
raise BtrfsModuleException("The requested changes will require the subvolume '%s' to be mounted, but automount=False" % subvolume.path)
|
||||
|
||||
def __stage_create_subvolume(self, subvolume_path, intermediate=False):
|
||||
"""
|
||||
Add required creation of an intermediate subvolume to the unit of work
|
||||
If intermediate is true, the action will be skipped if a directory like file is found at target
|
||||
after mounting a parent subvolume
|
||||
"""
|
||||
self.__unit_of_work.append({
|
||||
'action': self.__CREATE_SUBVOLUME_OPERATION,
|
||||
'target': subvolume_path,
|
||||
'intermediate': intermediate,
|
||||
})
|
||||
|
||||
def __stage_create_snapshot(self, source_subvolume, target_subvolume_path):
|
||||
"""Add creation of a snapshot from source to target to the unit of work"""
|
||||
self.__unit_of_work.append({
|
||||
'action': self.__CREATE_SNAPSHOT_OPERATION,
|
||||
'source': source_subvolume.path,
|
||||
'source_id': source_subvolume.id,
|
||||
'target': target_subvolume_path,
|
||||
})
|
||||
|
||||
def __stage_delete_subvolume(self, subvolume):
|
||||
"""Add deletion of the target subvolume to the unit of work"""
|
||||
self.__unit_of_work.append({
|
||||
'action': self.__DELETE_SUBVOLUME_OPERATION,
|
||||
'target': subvolume.path,
|
||||
'target_id': subvolume.id,
|
||||
})
|
||||
|
||||
def __stage_set_default_subvolume(self, subvolume_path, subvolume_id=None):
|
||||
"""Add update of the filesystem's default subvolume to the unit of work"""
|
||||
self.__unit_of_work.append({
|
||||
'action': self.__SET_DEFAULT_SUBVOLUME_OPERATION,
|
||||
'target': subvolume_path,
|
||||
'target_id': subvolume_id,
|
||||
})
|
||||
|
||||
# Execute the unit of work
|
||||
def __execute_unit_of_work(self):
|
||||
self.__check_required_mounts()
|
||||
for op in self.__unit_of_work:
|
||||
if op['action'] == self.__CREATE_SUBVOLUME_OPERATION:
|
||||
self.__execute_create_subvolume(op)
|
||||
elif op['action'] == self.__CREATE_SNAPSHOT_OPERATION:
|
||||
self.__execute_create_snapshot(op)
|
||||
elif op['action'] == self.__DELETE_SUBVOLUME_OPERATION:
|
||||
self.__execute_delete_subvolume(op)
|
||||
elif op['action'] == self.__SET_DEFAULT_SUBVOLUME_OPERATION:
|
||||
self.__execute_set_default_subvolume(op)
|
||||
else:
|
||||
raise ValueError("Unknown operation type '%s'" % op['action'])
|
||||
|
||||
def __execute_create_subvolume(self, operation):
|
||||
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
|
||||
if not self.__is_existing_directory_like(target_mounted_path):
|
||||
self.__btrfs_api.subvolume_create(target_mounted_path)
|
||||
self.__completed_work.append(operation)
|
||||
|
||||
def __execute_create_snapshot(self, operation):
|
||||
source_subvolume = self.__filesystem.get_subvolume_by_name(operation['source'])
|
||||
source_mounted_path = source_subvolume.get_mounted_path()
|
||||
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
|
||||
|
||||
self.__btrfs_api.subvolume_snapshot(source_mounted_path, target_mounted_path)
|
||||
self.__completed_work.append(operation)
|
||||
|
||||
def __execute_delete_subvolume(self, operation):
|
||||
target_mounted_path = self.__filesystem.get_mountpath_as_child(operation['target'])
|
||||
self.__btrfs_api.subvolume_delete(target_mounted_path)
|
||||
self.__completed_work.append(operation)
|
||||
|
||||
def __execute_set_default_subvolume(self, operation):
|
||||
target = operation['target']
|
||||
target_id = operation['target_id']
|
||||
|
||||
if target_id is None:
|
||||
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
|
||||
|
||||
if target_subvolume is None:
|
||||
self.__filesystem.refresh() # the target may have been created earlier in module execution
|
||||
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
|
||||
|
||||
if target_subvolume is None:
|
||||
raise BtrfsModuleException("Failed to find existing subvolume '%s'" % target)
|
||||
else:
|
||||
target_id = target_subvolume.id
|
||||
|
||||
self.__btrfs_api.subvolume_set_default(self.__filesystem.get_any_mountpoint(), target_id)
|
||||
self.__completed_work.append(operation)
|
||||
|
||||
def __is_existing_directory_like(self, path):
|
||||
return os.path.exists(path) and (
|
||||
os.path.isdir(path) or
|
||||
os.stat(path).st_ino == self.__BTRFS_SUBVOLUME_INODE_NUMBER
|
||||
)
|
||||
|
||||
def __check_required_mounts(self):
|
||||
filtered = self.__filter_child_subvolumes(self.__required_mounts)
|
||||
if len(filtered) > 0:
|
||||
for subvolume in filtered:
|
||||
self.__mount_subvolume_id_to_tempdir(self.__filesystem, subvolume.id)
|
||||
self.__filesystem.refresh_mountpoints()
|
||||
|
||||
def __filter_child_subvolumes(self, subvolumes):
|
||||
"""Filter the provided list of subvolumes to remove any that are a child of another item in the list"""
|
||||
filtered = []
|
||||
last = None
|
||||
ordered = sorted(subvolumes, key=lambda x: x.path)
|
||||
for next in ordered:
|
||||
if last is None or not next.path[0:len(last)] == last:
|
||||
filtered.append(next)
|
||||
last = next.path
|
||||
return filtered
|
||||
|
||||
# Create/cleanup temporary mountpoints
|
||||
def __mount_subvolume_id_to_tempdir(self, filesystem, subvolid):
|
||||
# this check should be redundant
|
||||
if self.module.check_mode or not self.__automount:
|
||||
raise BtrfsModuleException("Unable to temporarily mount required subvolumes"
|
||||
"with automount=%s and check_mode=%s" % (self.__automount, self.module.check_mode))
|
||||
|
||||
cache_key = "%s:%d" % (filesystem.uuid, subvolid)
|
||||
# The subvolume was already mounted, so return the current path
|
||||
if cache_key in self.__temporary_mounts:
|
||||
return self.__temporary_mounts[cache_key]
|
||||
|
||||
device = filesystem.devices[0]
|
||||
mountpoint = tempfile.mkdtemp(dir="/tmp")
|
||||
self.__temporary_mounts[cache_key] = mountpoint
|
||||
|
||||
mount = self.module.get_bin_path("mount", required=True)
|
||||
command = "%s -o noatime,subvolid=%d %s %s " % (mount,
|
||||
subvolid,
|
||||
device,
|
||||
mountpoint)
|
||||
result = self.module.run_command(command, check_rc=True)
|
||||
|
||||
return mountpoint
|
||||
|
||||
def __cleanup_mounts(self):
|
||||
for key in self.__temporary_mounts.keys():
|
||||
self.__cleanup_mount(self.__temporary_mounts[key])
|
||||
|
||||
def __cleanup_mount(self, mountpoint):
|
||||
umount = self.module.get_bin_path("umount", required=True)
|
||||
result = self.module.run_command("%s %s" % (umount, mountpoint))
|
||||
if result[0] == 0:
|
||||
rmdir = self.module.get_bin_path("rmdir", required=True)
|
||||
self.module.run_command("%s %s" % (rmdir, mountpoint))
|
||||
|
||||
# Format and return results
|
||||
def get_results(self):
|
||||
target = self.__filesystem.get_subvolume_by_name(self.__name)
|
||||
return dict(
|
||||
changed=len(self.__completed_work) > 0,
|
||||
filesystem=self.__filesystem.get_summary(),
|
||||
modifications=self.__get_formatted_modifications(),
|
||||
target_subvolume_id=(target.id if target is not None else None)
|
||||
)
|
||||
|
||||
def __get_formatted_modifications(self):
|
||||
return [self.__format_operation_result(op) for op in self.__completed_work]
|
||||
|
||||
def __format_operation_result(self, operation):
|
||||
action_type = operation['action']
|
||||
if action_type == self.__CREATE_SUBVOLUME_OPERATION:
|
||||
return self.__format_create_subvolume_result(operation)
|
||||
elif action_type == self.__CREATE_SNAPSHOT_OPERATION:
|
||||
return self.__format_create_snapshot_result(operation)
|
||||
elif action_type == self.__DELETE_SUBVOLUME_OPERATION:
|
||||
return self.__format_delete_subvolume_result(operation)
|
||||
elif action_type == self.__SET_DEFAULT_SUBVOLUME_OPERATION:
|
||||
return self.__format_set_default_subvolume_result(operation)
|
||||
else:
|
||||
raise ValueError("Unknown operation type '%s'" % operation['action'])
|
||||
|
||||
def __format_create_subvolume_result(self, operation):
|
||||
target = operation['target']
|
||||
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
|
||||
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
|
||||
return "Created subvolume '%s' (%s)" % (target, target_id)
|
||||
|
||||
def __format_create_snapshot_result(self, operation):
|
||||
source = operation['source']
|
||||
source_id = operation['source_id']
|
||||
|
||||
target = operation['target']
|
||||
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
|
||||
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
|
||||
return "Created snapshot '%s' (%s) from '%s' (%s)" % (target, target_id, source, source_id)
|
||||
|
||||
def __format_delete_subvolume_result(self, operation):
|
||||
target = operation['target']
|
||||
target_id = operation['target_id']
|
||||
return "Deleted subvolume '%s' (%s)" % (target, target_id)
|
||||
|
||||
def __format_set_default_subvolume_result(self, operation):
|
||||
target = operation['target']
|
||||
if 'target_id' in operation:
|
||||
target_id = operation['target_id']
|
||||
else:
|
||||
target_subvolume = self.__filesystem.get_subvolume_by_name(target)
|
||||
target_id = target_subvolume.id if target_subvolume is not None else self.__UNKNOWN_SUBVOLUME_ID
|
||||
return "Updated default subvolume to '%s' (%s)" % (target, target_id)
|
||||
|
||||
|
||||
def run_module():
|
||||
module_args = dict(
|
||||
automount=dict(type='bool', required=False, default=False),
|
||||
default=dict(type='bool', required=False, default=False),
|
||||
filesystem_device=dict(type='path', required=False),
|
||||
filesystem_label=dict(type='str', required=False),
|
||||
filesystem_uuid=dict(type='str', required=False),
|
||||
name=dict(type='str', required=True),
|
||||
recursive=dict(type='bool', default=False),
|
||||
state=dict(type='str', required=False, default='present', choices=['present', 'absent']),
|
||||
snapshot_source=dict(type='str', required=False),
|
||||
snapshot_conflict=dict(type='str', required=False, default='skip', choices=['skip', 'clobber', 'error'])
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
subvolume = BtrfsSubvolumeModule(module)
|
||||
error, result = subvolume.run()
|
||||
if error is not None:
|
||||
module.fail_json(str(error), **result)
|
||||
else:
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -183,7 +183,7 @@ class CPANMinus(ModuleHelper):
|
||||
if v.name and v.from_path:
|
||||
self.do_raise("Parameters 'name' and 'from_path' are mutually exclusive when 'mode=new'")
|
||||
|
||||
self.command = self.get_bin_path(v.executable if v.executable else self.command)
|
||||
self.command = self.module.get_bin_path(v.executable if v.executable else self.command)
|
||||
self.vars.set("binary", self.command)
|
||||
|
||||
def _is_package_installed(self, name, locallib, version):
|
||||
|
||||
@@ -21,23 +21,11 @@ description:
|
||||
- Since C(dconf) requires a running D-Bus session to change values, the module
|
||||
will try to detect an existing session and reuse it, or run the tool via
|
||||
C(dbus-run-session).
|
||||
requirements:
|
||||
- Optionally the C(gi.repository) Python library (usually included in the OS
|
||||
on hosts which have C(dconf)); this will become a non-optional requirement
|
||||
in a future major release of community.general.
|
||||
notes:
|
||||
- This module depends on C(psutil) Python library (version 4.0.0 and upwards),
|
||||
C(dconf), C(dbus-send), and C(dbus-run-session) binaries. Depending on
|
||||
distribution you are using, you may need to install additional packages to
|
||||
have these available.
|
||||
- This module uses the C(gi.repository) Python library when available for
|
||||
accurate comparison of values in C(dconf) to values specified in Ansible
|
||||
code. C(gi.repository) is likely to be present on most systems which have
|
||||
C(dconf) but may not be present everywhere. When it is missing, a simple
|
||||
string comparison between values is used, and there may be false positives,
|
||||
that is, Ansible may think that a value is being changed when it is not.
|
||||
This fallback will be removed in a future version of this module, at which
|
||||
point the module will stop working on hosts without C(gi.repository).
|
||||
- Detection of existing, running D-Bus session, required to change settings
|
||||
via C(dconf), is not 100% reliable due to implementation details of D-Bus
|
||||
daemon itself. This might lead to running applications not picking-up
|
||||
@@ -70,18 +58,13 @@ options:
|
||||
description:
|
||||
- A dconf key to modify or read from the dconf database.
|
||||
value:
|
||||
type: raw
|
||||
type: str
|
||||
required: false
|
||||
description:
|
||||
- Value to set for the specified dconf key. Value should be specified in
|
||||
GVariant format. Due to complexity of this format, it is best to have a
|
||||
look at existing values in the dconf database.
|
||||
- Required for I(state=present).
|
||||
- Although the type is specified as "raw", it should typically be
|
||||
specified as a string. However, boolean values in particular are
|
||||
handled properly even when specified as booleans rather than strings
|
||||
(in fact, handling booleans properly is why the type of this parameter
|
||||
is "raw").
|
||||
state:
|
||||
type: str
|
||||
required: false
|
||||
@@ -143,27 +126,17 @@ EXAMPLES = r"""
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.respawn import (
|
||||
has_respawned,
|
||||
probe_interpreters_for_module,
|
||||
respawn_module,
|
||||
)
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.community.general.plugins.module_utils import deps
|
||||
|
||||
glib_module_name = 'gi.repository.GLib'
|
||||
import traceback
|
||||
|
||||
PSUTIL_IMP_ERR = None
|
||||
try:
|
||||
from gi.repository.GLib import Variant, GError
|
||||
except ImportError:
|
||||
Variant = None
|
||||
GError = AttributeError
|
||||
|
||||
with deps.declare("psutil"):
|
||||
import psutil
|
||||
HAS_PSUTIL = True
|
||||
except ImportError:
|
||||
PSUTIL_IMP_ERR = traceback.format_exc()
|
||||
HAS_PSUTIL = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
|
||||
class DBusWrapper(object):
|
||||
@@ -285,29 +258,6 @@ class DconfPreference(object):
|
||||
# Check if dconf binary exists
|
||||
self.dconf_bin = self.module.get_bin_path('dconf', required=True)
|
||||
|
||||
@staticmethod
|
||||
def variants_are_equal(canonical_value, user_value):
|
||||
"""Compare two string GVariant representations for equality.
|
||||
|
||||
Assumes `canonical_value` is "canonical" in the sense that the type of
|
||||
the variant is specified explicitly if it cannot be inferred; this is
|
||||
true for textual representations of variants generated by the `dconf`
|
||||
command. The type of `canonical_value` is used to parse `user_value`,
|
||||
so the latter does not need to be explicitly typed.
|
||||
|
||||
Returns True if the two values are equal.
|
||||
"""
|
||||
if canonical_value is None:
|
||||
# It's unset in dconf database, so anything the user is trying to
|
||||
# set is a change.
|
||||
return False
|
||||
try:
|
||||
variant1 = Variant.parse(None, canonical_value)
|
||||
variant2 = Variant.parse(variant1.get_type(), user_value)
|
||||
return variant1 == variant2
|
||||
except GError:
|
||||
return canonical_value == user_value
|
||||
|
||||
def read(self, key):
|
||||
"""
|
||||
Retrieves current value associated with the dconf key.
|
||||
@@ -348,7 +298,7 @@ class DconfPreference(object):
|
||||
"""
|
||||
# If no change is needed (or won't be done due to check_mode), notify
|
||||
# caller straight away.
|
||||
if self.variants_are_equal(self.read(key), value):
|
||||
if value == self.read(key):
|
||||
return False
|
||||
elif self.check_mode:
|
||||
return True
|
||||
@@ -362,7 +312,7 @@ class DconfPreference(object):
|
||||
rc, out, err = dbus_wrapper.run_command(command)
|
||||
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg='dconf failed while writing key %s, value %s with error: %s' % (key, value, err),
|
||||
self.module.fail_json(msg='dconf failed while write the value with error: %s' % err,
|
||||
out=out,
|
||||
err=err)
|
||||
|
||||
@@ -414,62 +364,17 @@ def main():
|
||||
argument_spec=dict(
|
||||
state=dict(default='present', choices=['present', 'absent', 'read']),
|
||||
key=dict(required=True, type='str', no_log=False),
|
||||
# Converted to str below after special handling of bool.
|
||||
value=dict(required=False, default=None, type='raw'),
|
||||
value=dict(required=False, default=None, type='str'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
('state', 'present', ['value']),
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if Variant is None:
|
||||
# This interpreter can't see the GLib module. To try to fix that, we'll
|
||||
# look in common locations for system-owned interpreters that can see
|
||||
# it; if we find one, we'll respawn under it. Otherwise we'll proceed
|
||||
# with degraded performance, without the ability to parse GVariants.
|
||||
# Later (in a different PR) we'll actually deprecate this degraded
|
||||
# performance level and fail with an error if the library can't be
|
||||
# found.
|
||||
if not HAS_PSUTIL:
|
||||
module.fail_json(msg=missing_required_lib("psutil"), exception=PSUTIL_IMP_ERR)
|
||||
|
||||
if has_respawned():
|
||||
# This shouldn't be possible; short-circuit early if it happens.
|
||||
module.fail_json(
|
||||
msg="%s must be installed and visible from %s." %
|
||||
(glib_module_name, sys.executable))
|
||||
|
||||
interpreters = ['/usr/bin/python3', '/usr/bin/python2',
|
||||
'/usr/bin/python']
|
||||
|
||||
interpreter = probe_interpreters_for_module(
|
||||
interpreters, glib_module_name)
|
||||
|
||||
if interpreter:
|
||||
# Found the Python bindings; respawn this module under the
|
||||
# interpreter where we found them.
|
||||
respawn_module(interpreter)
|
||||
# This is the end of the line for this process, it will exit here
|
||||
# once the respawned module has completed.
|
||||
|
||||
# Try to be forgiving about the user specifying a boolean as the value, or
|
||||
# more accurately about the fact that YAML and Ansible are quite insistent
|
||||
# about converting strings that look like booleans into booleans. Convert
|
||||
# the boolean into a string of the type dconf will understand. Any type for
|
||||
# the value other than boolean is just converted into a string directly.
|
||||
if module.params['value'] is not None:
|
||||
if isinstance(module.params['value'], bool):
|
||||
module.params['value'] = 'true' if module.params['value'] else 'false'
|
||||
else:
|
||||
module.params['value'] = to_native(
|
||||
module.params['value'], errors='surrogate_or_strict')
|
||||
|
||||
if Variant is None:
|
||||
module.warn(
|
||||
'WARNING: The gi.repository Python library is not available; '
|
||||
'using string comparison to check value equality. This fallback '
|
||||
'will be deprecated in a future version of community.general.')
|
||||
|
||||
deps.validate(module)
|
||||
# If present state was specified, value must be provided.
|
||||
if module.params['state'] == 'present' and module.params['value'] is None:
|
||||
module.fail_json(msg='State "present" requires "value" to be set.')
|
||||
|
||||
# Create wrapper instance.
|
||||
dconf = DconfPreference(module, module.check_mode)
|
||||
|
||||
@@ -239,9 +239,9 @@ with deps.declare("requests"):
|
||||
|
||||
def build_url(account, key, is_sandbox):
|
||||
headers = {'Accept': 'application/json',
|
||||
'Authorization': 'Bearer {0}'.format(key)}
|
||||
sandbox = '.sandbox' if is_sandbox else ''
|
||||
url = 'https://api{sandbox}.dnsimple.com/v2/{account}'.format(sandbox=sandbox, account=account)
|
||||
'Authorization': 'Bearer ' + key}
|
||||
url = 'https://api{sandbox}.dnsimple.com/'.format(
|
||||
sandbox=".sandbox" if is_sandbox else "") + 'v2/' + account
|
||||
req = Request(url=url, headers=headers)
|
||||
prepped_request = req.prepare()
|
||||
return prepped_request
|
||||
@@ -250,21 +250,19 @@ def build_url(account, key, is_sandbox):
|
||||
def iterate_data(module, request_object):
|
||||
base_url = request_object.url
|
||||
response = Session().send(request_object)
|
||||
if 'pagination' not in response.json():
|
||||
if 'pagination' in response.json():
|
||||
data = response.json()["data"]
|
||||
pages = response.json()["pagination"]["total_pages"]
|
||||
if int(pages) > 1:
|
||||
for page in range(1, pages):
|
||||
page = page + 1
|
||||
request_object.url = base_url + '&page=' + str(page)
|
||||
new_results = Session().send(request_object)
|
||||
data = data + new_results.json()["data"]
|
||||
return data
|
||||
else:
|
||||
module.fail_json('API Call failed, check ID, key and sandbox values')
|
||||
|
||||
data = response.json()["data"]
|
||||
total_pages = response.json()["pagination"]["total_pages"]
|
||||
page = 1
|
||||
|
||||
while page < total_pages:
|
||||
page = page + 1
|
||||
request_object.url = '{url}&page={page}'.format(url=base_url, page=page)
|
||||
new_results = Session().send(request_object)
|
||||
data = data + new_results.json()['data']
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def record_info(dnsimple_mod, req_obj):
|
||||
req_obj.url, req_obj.method = req_obj.url + '/zones/' + dnsimple_mod.params["name"] + '/records?name=' + dnsimple_mod.params["record"], 'GET'
|
||||
|
||||
@@ -215,7 +215,7 @@ def uninstall_flat(module, binary, names, method):
|
||||
|
||||
def flatpak_exists(module, binary, names, method):
|
||||
"""Check if the flatpaks are installed."""
|
||||
command = [binary, "list", "--{0}".format(method)]
|
||||
command = [binary, "list", "--{0}".format(method), "--app"]
|
||||
output = _flatpak_command(module, False, command)
|
||||
installed = []
|
||||
not_installed = []
|
||||
|
||||
@@ -35,13 +35,12 @@ options:
|
||||
type: str
|
||||
description:
|
||||
- Preference keys typically have simple values such as strings,
|
||||
integers, or lists of strings and integers.
|
||||
This is ignored unless I(state=present). See man gconftool-2(1).
|
||||
integers, or lists of strings and integers. This is ignored if the state
|
||||
is "get". See man gconftool-2(1).
|
||||
value_type:
|
||||
type: str
|
||||
description:
|
||||
- The type of value being set.
|
||||
This is ignored unless I(state=present). See man gconftool-2(1).
|
||||
- The type of value being set. This is ignored if the state is "get".
|
||||
choices: [ bool, float, int, string ]
|
||||
state:
|
||||
type: str
|
||||
@@ -57,8 +56,8 @@ options:
|
||||
See man gconftool-2(1).
|
||||
direct:
|
||||
description:
|
||||
- Access the config database directly, bypassing server. If I(direct) is
|
||||
specified then the I(config_source) must be specified as well.
|
||||
- Access the config database directly, bypassing server. If direct is
|
||||
specified then the config_source must be specified as well.
|
||||
See man gconftool-2(1).
|
||||
type: bool
|
||||
default: false
|
||||
@@ -74,26 +73,17 @@ EXAMPLES = """
|
||||
|
||||
RETURN = '''
|
||||
key:
|
||||
description: The key specified in the module parameters.
|
||||
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.
|
||||
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 C(null) if key is removed.
|
||||
- From community.general 7.0.0 onwards it returns C(null) for a non-existent I(key), and returns C("") 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 C(null) for a non-existent I(key), and returns C("") before that.
|
||||
description: The value of the preference key after executing the module
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Serif 12"
|
||||
@@ -105,6 +95,7 @@ from ansible_collections.community.general.plugins.module_utils.gconftool2 impor
|
||||
|
||||
|
||||
class GConftool(StateModuleHelper):
|
||||
change_params = ('value', )
|
||||
diff_params = ('value', )
|
||||
output_params = ('key', 'value_type')
|
||||
facts_params = ('key', 'value_type')
|
||||
@@ -120,6 +111,7 @@ class GConftool(StateModuleHelper):
|
||||
),
|
||||
required_if=[
|
||||
('state', 'present', ['value', 'value_type']),
|
||||
('state', 'absent', ['value']),
|
||||
('direct', True, ['config_source']),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
@@ -133,7 +125,6 @@ class GConftool(StateModuleHelper):
|
||||
|
||||
self.vars.set('previous_value', self._get(), fact=True)
|
||||
self.vars.set('value_type', self.vars.value_type)
|
||||
self.vars.set('_value', self.vars.previous_value, output=False, change=True)
|
||||
self.vars.set_meta('value', initial_value=self.vars.previous_value)
|
||||
self.vars.set('playbook_value', self.vars.value, fact=True)
|
||||
|
||||
@@ -141,8 +132,7 @@ class GConftool(StateModuleHelper):
|
||||
def process(rc, out, err):
|
||||
if err and fail_on_err:
|
||||
self.ansible.fail_json(msg='gconftool-2 failed with error: %s' % (str(err)))
|
||||
out = out.rstrip()
|
||||
self.vars.value = None if out == "" else out
|
||||
self.vars.value = out.rstrip()
|
||||
return self.vars.value
|
||||
return process
|
||||
|
||||
@@ -158,18 +148,11 @@ class GConftool(StateModuleHelper):
|
||||
def state_absent(self):
|
||||
with self.runner("state key", output_process=self._make_process(False)) as ctx:
|
||||
ctx.run()
|
||||
if self.verbosity >= 4:
|
||||
self.vars.run_info = ctx.run_info
|
||||
self.vars.set('new_value', None, fact=True)
|
||||
self.vars._value = None
|
||||
|
||||
def state_present(self):
|
||||
with self.runner("direct config_source value_type state key value", output_process=self._make_process(True)) as ctx:
|
||||
ctx.run()
|
||||
if self.verbosity >= 4:
|
||||
self.vars.run_info = ctx.run_info
|
||||
self.vars.set('new_value', self._get(), fact=True)
|
||||
self.vars._value = self.vars.new_value
|
||||
self.vars.set('new_value', ctx.run(), fact=True)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -166,11 +166,52 @@ group_variable:
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.six import integer_types
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables, vars_to_variables
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables
|
||||
)
|
||||
|
||||
|
||||
def vars_to_variables(vars, module):
|
||||
# transform old vars to new variables structure
|
||||
variables = list()
|
||||
for item, value in vars.items():
|
||||
if (isinstance(value, string_types) or
|
||||
isinstance(value, (integer_types, float))):
|
||||
variables.append(
|
||||
{
|
||||
"name": item,
|
||||
"value": str(value),
|
||||
"masked": False,
|
||||
"protected": False,
|
||||
"variable_type": "env_var",
|
||||
}
|
||||
)
|
||||
|
||||
elif isinstance(value, dict):
|
||||
new_item = {"name": item, "value": value.get('value')}
|
||||
|
||||
new_item = {
|
||||
"name": item,
|
||||
"value": value.get('value'),
|
||||
"masked": value.get('masked'),
|
||||
"protected": value.get('protected'),
|
||||
"variable_type": value.get('variable_type'),
|
||||
}
|
||||
|
||||
if value.get('environment_scope'):
|
||||
new_item['environment_scope'] = value.get('environment_scope')
|
||||
|
||||
variables.append(new_item)
|
||||
|
||||
else:
|
||||
module.fail_json(msg="value must be of type string, integer, float or dict")
|
||||
|
||||
return variables
|
||||
|
||||
|
||||
class GitlabGroupVariables(object):
|
||||
|
||||
def __init__(self, module, gitlab_instance):
|
||||
|
||||
@@ -251,13 +251,6 @@ options:
|
||||
type: str
|
||||
choices: ["private", "disabled", "enabled"]
|
||||
version_added: "6.4.0"
|
||||
topics:
|
||||
description:
|
||||
- A topic or list of topics to be assigned to a project.
|
||||
- It is compatible with old GitLab server releases (versions before 14, correspond to C(tag_list)).
|
||||
type: list
|
||||
elements: str
|
||||
version_added: "6.6.0"
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -341,8 +334,6 @@ from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_group, find_project, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
)
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
|
||||
class GitLabProject(object):
|
||||
def __init__(self, module, gitlab_instance):
|
||||
@@ -385,14 +376,6 @@ class GitLabProject(object):
|
||||
'monitor_access_level': options['monitor_access_level'],
|
||||
'security_and_compliance_access_level': options['security_and_compliance_access_level'],
|
||||
}
|
||||
|
||||
# topics was introduced on gitlab >=14 and replace tag_list. We get current gitlab version
|
||||
# and check if less than 14. If yes we use tag_list instead topics
|
||||
if LooseVersion(self._gitlab.version()[0]) < LooseVersion("14"):
|
||||
project_options['tag_list'] = options['topics']
|
||||
else:
|
||||
project_options['topics'] = options['topics']
|
||||
|
||||
# Because we have already call userExists in main()
|
||||
if self.project_object is None:
|
||||
project_options.update({
|
||||
@@ -531,7 +514,6 @@ def main():
|
||||
infrastructure_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
|
||||
monitor_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
|
||||
security_and_compliance_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']),
|
||||
topics=dict(type='list', elements='str'),
|
||||
))
|
||||
|
||||
module = AnsibleModule(
|
||||
@@ -588,7 +570,6 @@ def main():
|
||||
infrastructure_access_level = module.params['infrastructure_access_level']
|
||||
monitor_access_level = module.params['monitor_access_level']
|
||||
security_and_compliance_access_level = module.params['security_and_compliance_access_level']
|
||||
topics = module.params['topics']
|
||||
|
||||
if default_branch and not initialize_with_readme:
|
||||
module.fail_json(msg="Param default_branch need param initialize_with_readme set to true")
|
||||
@@ -667,7 +648,6 @@ def main():
|
||||
"infrastructure_access_level": infrastructure_access_level,
|
||||
"monitor_access_level": monitor_access_level,
|
||||
"security_and_compliance_access_level": security_and_compliance_access_level,
|
||||
"topics": topics,
|
||||
}):
|
||||
|
||||
module.exit_json(changed=True, msg="Successfully created or updated the project %s" % project_name, project=gitlab_project.project_object._attrs)
|
||||
|
||||
@@ -184,6 +184,8 @@ project_variable:
|
||||
import traceback
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.six import integer_types
|
||||
|
||||
GITLAB_IMP_ERR = None
|
||||
try:
|
||||
@@ -194,10 +196,47 @@ except Exception:
|
||||
HAS_GITLAB_PACKAGE = False
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables, vars_to_variables
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables
|
||||
)
|
||||
|
||||
|
||||
def vars_to_variables(vars, module):
|
||||
# transform old vars to new variables structure
|
||||
variables = list()
|
||||
for item, value in vars.items():
|
||||
if (isinstance(value, string_types) or
|
||||
isinstance(value, (integer_types, float))):
|
||||
variables.append(
|
||||
{
|
||||
"name": item,
|
||||
"value": str(value),
|
||||
"masked": False,
|
||||
"protected": False,
|
||||
"variable_type": "env_var",
|
||||
}
|
||||
)
|
||||
|
||||
elif isinstance(value, dict):
|
||||
|
||||
new_item = {
|
||||
"name": item,
|
||||
"value": value.get('value'),
|
||||
"masked": value.get('masked'),
|
||||
"protected": value.get('protected'),
|
||||
"variable_type": value.get('variable_type'),
|
||||
}
|
||||
|
||||
if value.get('environment_scope'):
|
||||
new_item['environment_scope'] = value.get('environment_scope')
|
||||
|
||||
variables.append(new_item)
|
||||
|
||||
else:
|
||||
module.fail_json(msg="value must be of type string, integer, float or dict")
|
||||
|
||||
return variables
|
||||
|
||||
|
||||
class GitlabProjectVariables(object):
|
||||
|
||||
def __init__(self, module, gitlab_instance):
|
||||
@@ -283,7 +322,7 @@ def compare(requested_variables, existing_variables, state):
|
||||
def native_python_main(this_gitlab, purge, requested_variables, state, module):
|
||||
|
||||
change = False
|
||||
return_value = dict(added=[], updated=[], removed=[], untouched=[])
|
||||
return_value = dict(added=list(), updated=list(), removed=list(), untouched=list())
|
||||
|
||||
gitlab_keys = this_gitlab.list_all_project_variables()
|
||||
before = [x.attributes for x in gitlab_keys]
|
||||
@@ -352,7 +391,7 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module):
|
||||
if module.check_mode:
|
||||
return_value = dict(added=added, updated=updated, removed=return_value['removed'], untouched=untouched)
|
||||
|
||||
if any(return_value[x] for x in ['added', 'removed', 'updated']):
|
||||
if return_value['added'] or return_value['removed'] or return_value['updated']:
|
||||
change = True
|
||||
|
||||
gitlab_keys = this_gitlab.list_all_project_variables()
|
||||
@@ -413,7 +452,7 @@ def main():
|
||||
|
||||
if state == 'present':
|
||||
if any(x['value'] is None for x in variables):
|
||||
module.fail_json(msg='value parameter is required for all variables in state present')
|
||||
module.fail_json(msg='value parameter is required in state present')
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
|
||||
@@ -44,17 +44,10 @@ attributes:
|
||||
support: none
|
||||
|
||||
options:
|
||||
group:
|
||||
description:
|
||||
- ID or full path of the group in the form group/subgroup.
|
||||
- Mutually exclusive with I(owned) and I(project).
|
||||
type: str
|
||||
version_added: '6.5.0'
|
||||
project:
|
||||
description:
|
||||
- ID or full path of the project in the form of group/name.
|
||||
- Mutually exclusive with I(owned) since community.general 4.5.0.
|
||||
- Mutually exclusive with I(group).
|
||||
type: str
|
||||
version_added: '3.7.0'
|
||||
description:
|
||||
@@ -80,7 +73,6 @@ options:
|
||||
description:
|
||||
- Searches only runners available to the user when searching for existing, when false admin token required.
|
||||
- Mutually exclusive with I(project) since community.general 4.5.0.
|
||||
- Mutually exclusive with I(group).
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 2.0.0
|
||||
@@ -114,9 +106,8 @@ options:
|
||||
- Whether the runner should be registered with an access level or not.
|
||||
- If set to C(true), the value of I(access_level) is used for runner registration.
|
||||
- If set to C(false), GitLab registers the runner with the default access level.
|
||||
- The default of this option changed to C(true) in community.general 7.0.0. Before, it was C(false).
|
||||
- The current default of this option is C(false). This default is deprecated and will change to C(true) in commuinty.general 7.0.0.
|
||||
required: false
|
||||
default: true
|
||||
type: bool
|
||||
version_added: 6.3.0
|
||||
maximum_timeout:
|
||||
@@ -218,23 +209,21 @@ except NameError:
|
||||
|
||||
|
||||
class GitLabRunner(object):
|
||||
def __init__(self, module, gitlab_instance, group=None, project=None):
|
||||
def __init__(self, module, gitlab_instance, project=None):
|
||||
self._module = module
|
||||
self._gitlab = gitlab_instance
|
||||
self.runner_object = None
|
||||
|
||||
# Whether to operate on GitLab-instance-wide or project-wide runners
|
||||
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/60774
|
||||
# for group runner token access
|
||||
if project:
|
||||
self._runners_endpoint = project.runners.list
|
||||
elif group:
|
||||
self._runners_endpoint = group.runners.list
|
||||
elif module.params['owned']:
|
||||
self._runners_endpoint = gitlab_instance.runners.list
|
||||
else:
|
||||
self._runners_endpoint = gitlab_instance.runners.all
|
||||
|
||||
self.runner_object = None
|
||||
|
||||
def create_or_update_runner(self, description, options):
|
||||
changed = False
|
||||
|
||||
@@ -245,16 +234,24 @@ class GitLabRunner(object):
|
||||
'maximum_timeout': options['maximum_timeout'],
|
||||
'tag_list': options['tag_list'],
|
||||
}
|
||||
if options.get('access_level') is not None:
|
||||
if arguments['access_level'] is not None:
|
||||
arguments['access_level'] = options['access_level']
|
||||
|
||||
# Because we have already call userExists in main()
|
||||
if self.runner_object is None:
|
||||
arguments['description'] = description
|
||||
arguments['token'] = options['registration_token']
|
||||
|
||||
access_level_on_creation = self._module.params['access_level_on_creation']
|
||||
if access_level_on_creation is None:
|
||||
message = "The option 'access_level_on_creation' is unspecified, so 'false' is assumed. "\
|
||||
"That means any value of 'access_level' is ignored and GitLab registers the runner with its default value. "\
|
||||
"The option 'access_level_on_creation' will switch to 'true' in community.general 7.0.0"
|
||||
self._module.deprecate(message, version='7.0.0', collection_name='community.general')
|
||||
access_level_on_creation = False
|
||||
|
||||
if not access_level_on_creation:
|
||||
arguments.pop('access_level', None)
|
||||
del arguments['access_level']
|
||||
|
||||
runner = self.create_runner(arguments)
|
||||
changed = True
|
||||
@@ -360,11 +357,10 @@ def main():
|
||||
run_untagged=dict(type='bool', default=True),
|
||||
locked=dict(type='bool', default=False),
|
||||
access_level=dict(type='str', choices=["not_protected", "ref_protected"]),
|
||||
access_level_on_creation=dict(type='bool', default=True),
|
||||
access_level_on_creation=dict(type='bool'),
|
||||
maximum_timeout=dict(type='int', default=3600),
|
||||
registration_token=dict(type='str', no_log=True),
|
||||
project=dict(type='str'),
|
||||
group=dict(type='str'),
|
||||
state=dict(type='str', default="present", choices=["absent", "present"]),
|
||||
))
|
||||
|
||||
@@ -377,8 +373,6 @@ def main():
|
||||
['api_token', 'api_oauth_token'],
|
||||
['api_token', 'api_job_token'],
|
||||
['project', 'owned'],
|
||||
['group', 'owned'],
|
||||
['project', 'group'],
|
||||
],
|
||||
required_together=[
|
||||
['api_username', 'api_password'],
|
||||
@@ -403,7 +397,6 @@ def main():
|
||||
maximum_timeout = module.params['maximum_timeout']
|
||||
registration_token = module.params['registration_token']
|
||||
project = module.params['project']
|
||||
group = module.params['group']
|
||||
|
||||
if access_level is None:
|
||||
message = "The option 'access_level' is unspecified, so 'ref_protected' is assumed. "\
|
||||
@@ -416,20 +409,13 @@ def main():
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
gitlab_project = None
|
||||
gitlab_group = None
|
||||
|
||||
if project:
|
||||
try:
|
||||
gitlab_project = gitlab_instance.projects.get(project)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
module.fail_json(msg='No such a project %s' % project, exception=to_native(e))
|
||||
elif group:
|
||||
try:
|
||||
gitlab_group = gitlab_instance.groups.get(group)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
module.fail_json(msg='No such a group %s' % group, exception=to_native(e))
|
||||
|
||||
gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_group, gitlab_project)
|
||||
gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_project)
|
||||
runner_exists = gitlab_runner.exists_runner(runner_description)
|
||||
|
||||
if state == 'absent':
|
||||
|
||||
219
plugins/modules/hana_query.py
Normal file
219
plugins/modules/hana_query.py
Normal file
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021, Rainer Leber <rainerleber@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: hana_query
|
||||
short_description: Execute SQL on HANA
|
||||
version_added: 3.2.0
|
||||
description: This module executes SQL statements on HANA with hdbsql.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
sid:
|
||||
description: The system ID.
|
||||
type: str
|
||||
required: true
|
||||
instance:
|
||||
description: The instance number.
|
||||
type: str
|
||||
required: true
|
||||
user:
|
||||
description: A dedicated username. The user could be also in hdbuserstore. Defaults to C(SYSTEM).
|
||||
type: str
|
||||
default: SYSTEM
|
||||
userstore:
|
||||
description: If C(true) the user must be in hdbuserstore.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 3.5.0
|
||||
password:
|
||||
description:
|
||||
- The password to connect to the database.
|
||||
- "B(Note:) Since the passwords have to be passed as command line arguments, I(userstore=true) should
|
||||
be used whenever possible, as command line arguments can be seen by other users
|
||||
on the same machine."
|
||||
type: str
|
||||
autocommit:
|
||||
description: Autocommit the statement.
|
||||
type: bool
|
||||
default: true
|
||||
host:
|
||||
description: The Host IP address. The port can be defined as well.
|
||||
type: str
|
||||
database:
|
||||
description: Define the database on which to connect.
|
||||
type: str
|
||||
encrypted:
|
||||
description: Use encrypted connection. Defaults to C(false).
|
||||
type: bool
|
||||
default: false
|
||||
filepath:
|
||||
description:
|
||||
- One or more files each containing one SQL query to run.
|
||||
- Must be a string or list containing strings.
|
||||
type: list
|
||||
elements: path
|
||||
query:
|
||||
description:
|
||||
- SQL query to run.
|
||||
- Must be a string or list containing strings. Please note that if you supply a string, it will be split by commas (C(,)) to a list.
|
||||
It is better to supply a one-element list instead to avoid mangled input.
|
||||
type: list
|
||||
elements: str
|
||||
author:
|
||||
- Rainer Leber (@rainerleber)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Simple select query
|
||||
community.general.hana_query:
|
||||
sid: "hdb"
|
||||
instance: "01"
|
||||
password: "Test123"
|
||||
query: "select user_name from users"
|
||||
|
||||
- name: Run several queries
|
||||
community.general.hana_query:
|
||||
sid: "hdb"
|
||||
instance: "01"
|
||||
password: "Test123"
|
||||
query:
|
||||
- "select user_name from users;"
|
||||
- select * from SYSTEM;
|
||||
host: "localhost"
|
||||
autocommit: false
|
||||
|
||||
- name: Run several queries from file
|
||||
community.general.hana_query:
|
||||
sid: "hdb"
|
||||
instance: "01"
|
||||
password: "Test123"
|
||||
filepath:
|
||||
- /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt
|
||||
- /tmp/HANA.txt
|
||||
host: "localhost"
|
||||
|
||||
- name: Run several queries from user store
|
||||
community.general.hana_query:
|
||||
sid: "hdb"
|
||||
instance: "01"
|
||||
user: hdbstoreuser
|
||||
userstore: true
|
||||
query:
|
||||
- "select user_name from users;"
|
||||
- select * from users;
|
||||
autocommit: false
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
query_result:
|
||||
description: List containing results of all queries executed (one sublist for every query).
|
||||
returned: on success
|
||||
type: list
|
||||
elements: list
|
||||
sample: [[{"Column": "Value1"}, {"Column": "Value2"}], [{"Column": "Value1"}, {"Column": "Value2"}]]
|
||||
'''
|
||||
|
||||
import csv
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import StringIO
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
def csv_to_list(rawcsv):
|
||||
reader_raw = csv.DictReader(StringIO(rawcsv))
|
||||
reader = [dict((k, v.strip()) for k, v in row.items()) for row in reader_raw]
|
||||
return list(reader)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
sid=dict(type='str', required=True),
|
||||
instance=dict(type='str', required=True),
|
||||
encrypted=dict(type='bool', default=False),
|
||||
host=dict(type='str', required=False),
|
||||
user=dict(type='str', default="SYSTEM"),
|
||||
userstore=dict(type='bool', default=False),
|
||||
password=dict(type='str', no_log=True),
|
||||
database=dict(type='str', required=False),
|
||||
query=dict(type='list', elements='str', required=False),
|
||||
filepath=dict(type='list', elements='path', required=False),
|
||||
autocommit=dict(type='bool', default=True),
|
||||
),
|
||||
required_one_of=[('query', 'filepath')],
|
||||
required_if=[('userstore', False, ['password'])],
|
||||
supports_check_mode=False,
|
||||
)
|
||||
rc, out, err, out_raw = [0, [], "", ""]
|
||||
|
||||
params = module.params
|
||||
|
||||
sid = (params['sid']).upper()
|
||||
instance = params['instance']
|
||||
user = params['user']
|
||||
userstore = params['userstore']
|
||||
password = params['password']
|
||||
autocommit = params['autocommit']
|
||||
host = params['host']
|
||||
database = params['database']
|
||||
encrypted = params['encrypted']
|
||||
|
||||
filepath = params['filepath']
|
||||
query = params['query']
|
||||
|
||||
bin_path = "/usr/sap/{sid}/HDB{instance}/exe/hdbsql".format(sid=sid, instance=instance)
|
||||
|
||||
try:
|
||||
command = [module.get_bin_path(bin_path, required=True)]
|
||||
except Exception as e:
|
||||
module.fail_json(msg='Failed to find hdbsql at the expected path "{0}". Please check SID and instance number: "{1}"'.format(bin_path, to_native(e)))
|
||||
|
||||
if encrypted is True:
|
||||
command.extend(['-attemptencrypt'])
|
||||
if autocommit is False:
|
||||
command.extend(['-z'])
|
||||
if host is not None:
|
||||
command.extend(['-n', host])
|
||||
if database is not None:
|
||||
command.extend(['-d', database])
|
||||
# -x Suppresses additional output, such as the number of selected rows in a result set.
|
||||
if userstore:
|
||||
command.extend(['-x', '-U', user])
|
||||
else:
|
||||
command.extend(['-x', '-i', instance, '-u', user, '-p', password])
|
||||
|
||||
if filepath is not None:
|
||||
command.extend(['-I'])
|
||||
for p in filepath:
|
||||
# makes a command like hdbsql -i 01 -u SYSTEM -p secret123# -I /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt,
|
||||
# iterates through files and append the output to var out.
|
||||
query_command = command + [p]
|
||||
(rc, out_raw, err) = module.run_command(query_command)
|
||||
out.append(csv_to_list(out_raw))
|
||||
if query is not None:
|
||||
for q in query:
|
||||
# makes a command like hdbsql -i 01 -u SYSTEM -p secret123# "select user_name from users",
|
||||
# iterates through multiple commands and append the output to var out.
|
||||
query_command = command + [q]
|
||||
(rc, out_raw, err) = module.run_command(query_command)
|
||||
out.append(csv_to_list(out_raw))
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed, rc=rc, query_result=out, stderr=err)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -78,9 +78,8 @@ options:
|
||||
greedy:
|
||||
description:
|
||||
- Upgrade casks that auto update.
|
||||
- Passes C(--greedy) to C(brew outdated --cask) when checking
|
||||
if an installed cask has a newer version available,
|
||||
or to C(brew upgrade --cask) when upgrading all casks.
|
||||
- Passes --greedy to brew cask outdated when checking
|
||||
if an installed cask has a newer version available.
|
||||
type: bool
|
||||
default: false
|
||||
'''
|
||||
@@ -129,11 +128,6 @@ EXAMPLES = '''
|
||||
community.general.homebrew_cask:
|
||||
upgrade_all: true
|
||||
|
||||
- name: Upgrade all casks with greedy option
|
||||
community.general.homebrew_cask:
|
||||
upgrade_all: true
|
||||
greedy: true
|
||||
|
||||
- name: Upgrade given cask with force option
|
||||
community.general.homebrew_cask:
|
||||
name: alfred
|
||||
@@ -587,9 +581,6 @@ class HomebrewCask(object):
|
||||
else:
|
||||
cmd = [self.brew_path, 'cask', 'upgrade']
|
||||
|
||||
if self.greedy:
|
||||
cmd = cmd + ['--greedy']
|
||||
|
||||
rc, out, err = '', '', ''
|
||||
|
||||
if self.sudo_password:
|
||||
|
||||
@@ -256,9 +256,9 @@ def main():
|
||||
state = module.params["state"]
|
||||
name = module.params["name"]
|
||||
zone = module.params["zone"]
|
||||
template = []
|
||||
template = [name]
|
||||
if module.params["template"]:
|
||||
template = [module.params["template"]]
|
||||
template.append(module.params["template"])
|
||||
check_command = module.params["check_command"]
|
||||
ip = module.params["ip"]
|
||||
display_name = module.params["display_name"]
|
||||
@@ -273,18 +273,20 @@ def main():
|
||||
module.fail_json(msg="unable to connect to Icinga. Exception message: %s" % (e))
|
||||
|
||||
data = {
|
||||
'templates': template,
|
||||
'attrs': {
|
||||
'address': ip,
|
||||
'display_name': display_name,
|
||||
'check_command': check_command,
|
||||
'zone': zone,
|
||||
'vars.made_by': "ansible"
|
||||
'vars': {
|
||||
'made_by': "ansible",
|
||||
},
|
||||
'templates': template,
|
||||
}
|
||||
}
|
||||
|
||||
for key, value in variables.items():
|
||||
data['attrs']['vars.' + key] = value
|
||||
if variables:
|
||||
data['attrs']['vars'].update(variables)
|
||||
|
||||
changed = False
|
||||
if icinga.exists(name):
|
||||
|
||||
@@ -85,14 +85,6 @@ msg:
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Action was successful"
|
||||
return_values:
|
||||
description: Dictionary containing command-specific response data from the action.
|
||||
returned: on success
|
||||
type: dict
|
||||
version_added: 6.6.0
|
||||
sample: {
|
||||
"job_id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/JID_471269252011"
|
||||
}
|
||||
'''
|
||||
|
||||
import re
|
||||
@@ -136,9 +128,10 @@ class IdracRedfishUtils(RedfishUtils):
|
||||
return response
|
||||
|
||||
response_output = response['resp'].__dict__
|
||||
job_id_full = response_output["headers"]["Location"]
|
||||
job_id = re.search("JID_.+", job_id_full).group()
|
||||
return {'ret': True, 'msg': "Config job %s created" % job_id, 'job_id': job_id_full}
|
||||
job_id = response_output["headers"]["Location"]
|
||||
job_id = re.search("JID_.+", job_id).group()
|
||||
# Currently not passing job_id back to user but patch is coming
|
||||
return {'ret': True, 'msg': "Config job %s created" % job_id}
|
||||
|
||||
|
||||
CATEGORY_COMMANDS_ALL = {
|
||||
@@ -150,7 +143,6 @@ CATEGORY_COMMANDS_ALL = {
|
||||
|
||||
def main():
|
||||
result = {}
|
||||
return_values = {}
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
category=dict(required=True),
|
||||
@@ -207,20 +199,7 @@ def main():
|
||||
|
||||
if category == "Systems":
|
||||
# execute only if we find a System resource
|
||||
# NOTE: Currently overriding the usage of 'data_modification' due to
|
||||
# how 'resource_id' is processed. In the case of CreateBiosConfigJob,
|
||||
# we interact with BOTH systems and managers, so you currently cannot
|
||||
# specify a single 'resource_id' to make both '_find_systems_resource'
|
||||
# and '_find_managers_resource' return success. Since
|
||||
# CreateBiosConfigJob doesn't use the matched 'resource_id' for a
|
||||
# system regardless of what's specified, disabling the 'resource_id'
|
||||
# inspection for the next call allows a specific manager to be
|
||||
# specified with 'resource_id'. If we ever need to expand the input
|
||||
# to inspect a specific system and manager in parallel, this will need
|
||||
# updates.
|
||||
rf_utils.data_modification = False
|
||||
result = rf_utils._find_systems_resource()
|
||||
rf_utils.data_modification = True
|
||||
if result['ret'] is False:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
|
||||
@@ -231,13 +210,11 @@ def main():
|
||||
if result['ret'] is False:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
result = rf_utils.create_bios_config_job()
|
||||
if 'job_id' in result:
|
||||
return_values['job_id'] = result['job_id']
|
||||
|
||||
# Return data back or fail with proper message
|
||||
if result['ret'] is True:
|
||||
del result['ret']
|
||||
module.exit_json(changed=True, msg='Action was successful', return_values=return_values)
|
||||
module.exit_json(changed=True, msg='Action was successful')
|
||||
else:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ilo_redfish_command
|
||||
short_description: Manages Out-Of-Band controllers using Redfish APIs
|
||||
version_added: 6.6.0
|
||||
description:
|
||||
- Builds Redfish URIs locally and sends them to remote OOB controllers to
|
||||
perform an action.
|
||||
attributes:
|
||||
check_mode:
|
||||
support: none
|
||||
diff_mode:
|
||||
support: none
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
options:
|
||||
category:
|
||||
required: true
|
||||
description:
|
||||
- Category to execute on OOB controller.
|
||||
type: str
|
||||
choices: ['Systems']
|
||||
command:
|
||||
required: true
|
||||
description:
|
||||
- List of commands to execute on OOB controller.
|
||||
type: list
|
||||
elements: str
|
||||
baseuri:
|
||||
required: true
|
||||
description:
|
||||
- Base URI of OOB controller.
|
||||
type: str
|
||||
username:
|
||||
required: false
|
||||
description:
|
||||
- Username for authenticating to iLO.
|
||||
type: str
|
||||
password:
|
||||
required: false
|
||||
description:
|
||||
- Password for authenticating to iLO.
|
||||
type: str
|
||||
auth_token:
|
||||
required: false
|
||||
description:
|
||||
- Security token for authenticating to iLO.
|
||||
type: str
|
||||
timeout:
|
||||
required: false
|
||||
description:
|
||||
- Timeout in seconds for HTTP requests to iLO.
|
||||
default: 60
|
||||
type: int
|
||||
author:
|
||||
- Varni H P (@varini-hp)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Wait for iLO Reboot Completion
|
||||
community.general.ilo_redfish_command:
|
||||
category: Systems
|
||||
command: WaitforiLORebootCompletion
|
||||
baseuri: "{{ baseuri }}"
|
||||
username: "{{ username }}"
|
||||
password: "{{ password }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
ilo_redfish_command:
|
||||
description: Returns the status of the operation performed on the iLO.
|
||||
type: dict
|
||||
contains:
|
||||
WaitforiLORebootCompletion:
|
||||
description: Returns the output msg and whether the function executed successfully.
|
||||
type: dict
|
||||
contains:
|
||||
ret:
|
||||
description: Return True/False based on whether the operation was performed succesfully.
|
||||
type: bool
|
||||
msg:
|
||||
description: Status of the operation performed on the iLO.
|
||||
type: str
|
||||
returned: always
|
||||
'''
|
||||
|
||||
# More will be added as module features are expanded
|
||||
CATEGORY_COMMANDS_ALL = {
|
||||
"Systems": ["WaitforiLORebootCompletion"]
|
||||
}
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.ilo_redfish_utils import iLORedfishUtils
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
def main():
|
||||
result = {}
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
category=dict(required=True, choices=list(CATEGORY_COMMANDS_ALL.keys())),
|
||||
command=dict(required=True, type='list', elements='str'),
|
||||
baseuri=dict(required=True),
|
||||
timeout=dict(type="int", default=60),
|
||||
username=dict(),
|
||||
password=dict(no_log=True),
|
||||
auth_token=dict(no_log=True)
|
||||
),
|
||||
required_together=[
|
||||
('username', 'password'),
|
||||
],
|
||||
required_one_of=[
|
||||
('username', 'auth_token'),
|
||||
],
|
||||
mutually_exclusive=[
|
||||
('username', 'auth_token'),
|
||||
],
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
category = module.params['category']
|
||||
command_list = module.params['command']
|
||||
|
||||
# admin credentials used for authentication
|
||||
creds = {'user': module.params['username'],
|
||||
'pswd': module.params['password'],
|
||||
'token': module.params['auth_token']}
|
||||
|
||||
timeout = module.params['timeout']
|
||||
|
||||
# Build root URI
|
||||
root_uri = "https://" + module.params['baseuri']
|
||||
rf_utils = iLORedfishUtils(creds, root_uri, timeout, module)
|
||||
|
||||
# Check that Category is valid
|
||||
if category not in CATEGORY_COMMANDS_ALL:
|
||||
module.fail_json(msg=to_native(
|
||||
"Invalid Category '%s'. Valid Categories = %s" % (category, list(CATEGORY_COMMANDS_ALL.keys()))))
|
||||
|
||||
# Check that all commands are valid
|
||||
for cmd in command_list:
|
||||
# Fail if even one command given is invalid
|
||||
if cmd not in CATEGORY_COMMANDS_ALL[category]:
|
||||
module.fail_json(
|
||||
msg=to_native("Invalid Command '%s'. Valid Commands = %s" % (cmd, CATEGORY_COMMANDS_ALL[category])))
|
||||
|
||||
if category == "Systems":
|
||||
# execute only if we find a System resource
|
||||
|
||||
result = rf_utils._find_systems_resource()
|
||||
if result['ret'] is False:
|
||||
module.fail_json(msg=to_native(result['msg']))
|
||||
|
||||
for command in command_list:
|
||||
if command == "WaitforiLORebootCompletion":
|
||||
result[command] = rf_utils.wait_for_ilo_reboot_completion()
|
||||
|
||||
# Return data back or fail with proper message
|
||||
if not result[command]['ret']:
|
||||
module.fail_json(msg=result)
|
||||
|
||||
changed = result[command].get('changed', False)
|
||||
module.exit_json(ilo_redfish_command=result, changed=changed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -174,14 +174,8 @@ def drop_user(module, client, user_name):
|
||||
def set_user_grants(module, client, user_name, grants):
|
||||
changed = False
|
||||
|
||||
current_grants = []
|
||||
try:
|
||||
current_grants = client.get_list_privileges(user_name)
|
||||
except influx.exceptions.InfluxDBClientError as e:
|
||||
if not module.check_mode or 'user not found' not in e.content:
|
||||
module.fail_json(msg=e.content)
|
||||
|
||||
try:
|
||||
parsed_grants = []
|
||||
# Fix privileges wording
|
||||
for i, v in enumerate(current_grants):
|
||||
|
||||
@@ -42,9 +42,10 @@ options:
|
||||
description:
|
||||
- Section name in INI file. This is added if I(state=present) automatically when
|
||||
a single value is being set.
|
||||
- If left empty, being omitted, or being set to C(null), the I(option) will be placed before the first I(section).
|
||||
- If left empty or set to C(null), the I(option) will be placed before the first I(section).
|
||||
- Using C(null) is also required if the config format does not support sections.
|
||||
type: str
|
||||
required: true
|
||||
option:
|
||||
description:
|
||||
- If set (required for changing a I(value)), this is the name of the option.
|
||||
@@ -429,7 +430,7 @@ def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
path=dict(type='path', required=True, aliases=['dest']),
|
||||
section=dict(type='str'),
|
||||
section=dict(type='str', required=True),
|
||||
option=dict(type='str'),
|
||||
value=dict(type='str'),
|
||||
values=dict(type='list', elements='str'),
|
||||
|
||||
@@ -156,22 +156,20 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
|
||||
|
||||
def lineDict(line):
|
||||
def line_dict(line):
|
||||
return {'line': line, 'line_type': 'unknown'}
|
||||
|
||||
|
||||
def optionDict(line, iface, option, value, address_family):
|
||||
def make_option_dict(line, iface, option, value, address_family):
|
||||
return {'line': line, 'iface': iface, 'option': option, 'value': value, 'line_type': 'option', 'address_family': address_family}
|
||||
|
||||
|
||||
def getValueFromLine(s):
|
||||
spaceRe = re.compile(r'\s+')
|
||||
m = list(spaceRe.finditer(s))[-1]
|
||||
valueEnd = m.start()
|
||||
option = s.split()[0]
|
||||
optionStart = s.find(option)
|
||||
optionLen = len(option)
|
||||
return s[optionLen + optionStart:].strip()
|
||||
def get_option_value(line):
|
||||
patt = re.compile(r'^\s+(?P<option>\S+)\s+(?P<value>\S?.*\S)\s*$')
|
||||
match = patt.match(line)
|
||||
if not match:
|
||||
return None, None
|
||||
return match.group("option"), match.group("value")
|
||||
|
||||
|
||||
def read_interfaces_file(module, filename):
|
||||
@@ -179,32 +177,27 @@ def read_interfaces_file(module, filename):
|
||||
return read_interfaces_lines(module, f)
|
||||
|
||||
|
||||
def _is_line_processing_none(first_word):
|
||||
return first_word in ("source", "source-dir", "source-directory", "auto", "no-auto-down", "no-scripts") or first_word.startswith("auto-")
|
||||
|
||||
|
||||
def read_interfaces_lines(module, line_strings):
|
||||
lines = []
|
||||
ifaces = {}
|
||||
iface_name = None
|
||||
address_family = None
|
||||
currif = {}
|
||||
currently_processing = None
|
||||
i = 0
|
||||
for line in line_strings:
|
||||
i += 1
|
||||
for i, line in enumerate(line_strings):
|
||||
words = line.split()
|
||||
if len(words) < 1:
|
||||
lines.append(lineDict(line))
|
||||
continue
|
||||
if words[0][0] == "#":
|
||||
lines.append(lineDict(line))
|
||||
if not words or words[0].startswith("#"):
|
||||
lines.append(line_dict(line))
|
||||
continue
|
||||
if words[0] == "mapping":
|
||||
# currmap = calloc(1, sizeof *currmap);
|
||||
lines.append(lineDict(line))
|
||||
lines.append(line_dict(line))
|
||||
currently_processing = "MAPPING"
|
||||
elif words[0] == "source":
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0] == "source-dir":
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0] == "source-directory":
|
||||
lines.append(lineDict(line))
|
||||
elif _is_line_processing_none(words[0]):
|
||||
lines.append(line_dict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0] == "iface":
|
||||
currif = {
|
||||
@@ -227,57 +220,25 @@ def read_interfaces_lines(module, line_strings):
|
||||
ifaces[iface_name] = currif
|
||||
lines.append({'line': line, 'iface': iface_name, 'line_type': 'iface', 'params': currif, 'address_family': address_family})
|
||||
currently_processing = "IFACE"
|
||||
elif words[0] == "auto":
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0].startswith("allow-"):
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0] == "no-auto-down":
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
elif words[0] == "no-scripts":
|
||||
lines.append(lineDict(line))
|
||||
currently_processing = "NONE"
|
||||
else:
|
||||
if currently_processing == "IFACE":
|
||||
option_name = words[0]
|
||||
value = getValueFromLine(line)
|
||||
lines.append(optionDict(line, iface_name, option_name, value, address_family))
|
||||
option_name, value = get_option_value(line)
|
||||
# TODO: if option_name in currif.options
|
||||
lines.append(make_option_dict(line, iface_name, option_name, value, address_family))
|
||||
if option_name in ["pre-up", "up", "down", "post-up"]:
|
||||
currif[option_name].append(value)
|
||||
else:
|
||||
currif[option_name] = value
|
||||
elif currently_processing == "MAPPING":
|
||||
lines.append(lineDict(line))
|
||||
lines.append(line_dict(line))
|
||||
elif currently_processing == "NONE":
|
||||
lines.append(lineDict(line))
|
||||
lines.append(line_dict(line))
|
||||
else:
|
||||
module.fail_json(msg="misplaced option %s in line %d" % (line, i))
|
||||
return None, None
|
||||
module.fail_json(msg="misplaced option %s in line %d" % (line, i + 1))
|
||||
|
||||
return lines, ifaces
|
||||
|
||||
|
||||
def get_interface_options(iface_lines):
|
||||
return [i for i in iface_lines if i['line_type'] == 'option']
|
||||
|
||||
|
||||
def get_target_options(iface_options, option):
|
||||
return [i for i in iface_options if i['option'] == option]
|
||||
|
||||
|
||||
def update_existing_option_line(target_option, value):
|
||||
old_line = target_option['line']
|
||||
old_value = target_option['value']
|
||||
prefix_start = old_line.find(target_option["option"])
|
||||
optionLen = len(target_option["option"])
|
||||
old_value_position = re.search(r"\s+".join(map(re.escape, old_value.split())), old_line[prefix_start + optionLen:])
|
||||
start = old_value_position.start() + prefix_start + optionLen
|
||||
end = old_value_position.end() + prefix_start + optionLen
|
||||
line = old_line[:start] + value + old_line[end:]
|
||||
return line
|
||||
|
||||
|
||||
def set_interface_option(module, lines, iface, option, raw_value, state, address_family=None):
|
||||
value = str(raw_value)
|
||||
changed = False
|
||||
@@ -287,35 +248,41 @@ def set_interface_option(module, lines, iface, option, raw_value, state, address
|
||||
iface_lines = [item for item in iface_lines
|
||||
if "address_family" in item and item["address_family"] == address_family]
|
||||
|
||||
if len(iface_lines) < 1:
|
||||
if not iface_lines:
|
||||
# interface not found
|
||||
module.fail_json(msg="Error: interface %s not found" % iface)
|
||||
return changed, None
|
||||
|
||||
iface_options = get_interface_options(iface_lines)
|
||||
target_options = get_target_options(iface_options, option)
|
||||
iface_options = [il for il in iface_lines if il['line_type'] == 'option']
|
||||
target_options = [io for io in iface_options if io['option'] == option]
|
||||
|
||||
if state == "present":
|
||||
if len(target_options) < 1:
|
||||
changed = True
|
||||
if not target_options:
|
||||
# add new option
|
||||
last_line_dict = iface_lines[-1]
|
||||
changed, lines = addOptionAfterLine(option, value, iface, lines, last_line_dict, iface_options, address_family)
|
||||
else:
|
||||
if option in ["pre-up", "up", "down", "post-up"]:
|
||||
if len(list(filter(lambda i: i['value'] == value, target_options))) < 1:
|
||||
changed, lines = addOptionAfterLine(option, value, iface, lines, target_options[-1], iface_options, address_family)
|
||||
else:
|
||||
# if more than one option found edit the last one
|
||||
if target_options[-1]['value'] != value:
|
||||
changed = True
|
||||
target_option = target_options[-1]
|
||||
line = update_existing_option_line(target_option, value)
|
||||
address_family = target_option['address_family']
|
||||
index = len(lines) - lines[::-1].index(target_option) - 1
|
||||
lines[index] = optionDict(line, iface, option, value, address_family)
|
||||
elif state == "absent":
|
||||
if len(target_options) >= 1:
|
||||
return add_option_after_line(option, value, iface, lines, last_line_dict, iface_options, address_family)
|
||||
|
||||
if option in ["pre-up", "up", "down", "post-up"] and all(ito['value'] != value for ito in target_options):
|
||||
return add_option_after_line(option, value, iface, lines, target_options[-1], iface_options, address_family)
|
||||
|
||||
# if more than one option found edit the last one
|
||||
if target_options[-1]['value'] != value:
|
||||
changed = True
|
||||
target_option = target_options[-1]
|
||||
old_line = target_option['line']
|
||||
old_value = target_option['value']
|
||||
address_family = target_option['address_family']
|
||||
prefix_start = old_line.find(option)
|
||||
option_len = len(option)
|
||||
old_value_position = re.search(r"\s+".join(map(re.escape, old_value.split())), old_line[prefix_start + option_len:])
|
||||
start = old_value_position.start() + prefix_start + option_len
|
||||
end = old_value_position.end() + prefix_start + option_len
|
||||
line = old_line[:start] + value + old_line[end:]
|
||||
index = len(lines) - lines[::-1].index(target_option) - 1
|
||||
lines[index] = make_option_dict(line, iface, option, value, address_family)
|
||||
return changed, lines
|
||||
|
||||
if state == "absent":
|
||||
if target_options:
|
||||
if option in ["pre-up", "up", "down", "post-up"] and value is not None and value != "None":
|
||||
for target_option in [ito for ito in target_options if ito['value'] == value]:
|
||||
changed = True
|
||||
@@ -324,13 +291,11 @@ def set_interface_option(module, lines, iface, option, raw_value, state, address
|
||||
changed = True
|
||||
for target_option in target_options:
|
||||
lines = [ln for ln in lines if ln != target_option]
|
||||
else:
|
||||
module.fail_json(msg="Error: unsupported state %s, has to be either present or absent" % state)
|
||||
|
||||
return changed, lines
|
||||
|
||||
|
||||
def addOptionAfterLine(option, value, iface, lines, last_line_dict, iface_options, address_family):
|
||||
def add_option_after_line(option, value, iface, lines, last_line_dict, iface_options, address_family):
|
||||
# Changing method of interface is not an addition
|
||||
if option == 'method':
|
||||
changed = False
|
||||
@@ -346,19 +311,18 @@ def addOptionAfterLine(option, value, iface, lines, last_line_dict, iface_option
|
||||
suffix_start = last_line.rfind(last_line.split()[-1]) + len(last_line.split()[-1])
|
||||
prefix = last_line[:prefix_start]
|
||||
|
||||
if len(iface_options) < 1:
|
||||
if not iface_options:
|
||||
# interface has no options, ident
|
||||
prefix += " "
|
||||
|
||||
line = prefix + "%s %s" % (option, value) + last_line[suffix_start:]
|
||||
option_dict = optionDict(line, iface, option, value, address_family)
|
||||
option_dict = make_option_dict(line, iface, option, value, address_family)
|
||||
index = len(lines) - lines[::-1].index(last_line_dict)
|
||||
lines.insert(index, option_dict)
|
||||
return True, lines
|
||||
|
||||
|
||||
def write_changes(module, lines, dest):
|
||||
|
||||
tmpfd, tmpfile = tempfile.mkstemp()
|
||||
with os.fdopen(tmpfd, 'wb') as f:
|
||||
f.write(to_bytes(''.join(lines), errors='surrogate_or_strict'))
|
||||
|
||||
@@ -20,13 +20,6 @@ attributes:
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
append:
|
||||
description:
|
||||
- If C(true), add the listed I(host) to the I(hostgroup).
|
||||
- If C(false), only the listed I(host) will be in I(hostgroup), removing any other hosts.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 6.6.0
|
||||
cn:
|
||||
description:
|
||||
- Name of host-group.
|
||||
@@ -154,7 +147,6 @@ def ensure(module, client):
|
||||
state = module.params['state']
|
||||
host = module.params['host']
|
||||
hostgroup = module.params['hostgroup']
|
||||
append = module.params['append']
|
||||
|
||||
ipa_hostgroup = client.hostgroup_find(name=name)
|
||||
module_hostgroup = get_hostgroup_dict(description=module.params['description'])
|
||||
@@ -176,18 +168,14 @@ def ensure(module, client):
|
||||
client.hostgroup_mod(name=name, item=data)
|
||||
|
||||
if host is not None:
|
||||
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_host', []),
|
||||
[item.lower() for item in host],
|
||||
client.hostgroup_add_host,
|
||||
client.hostgroup_remove_host,
|
||||
append=append) or changed
|
||||
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_host', []), [item.lower() for item in host],
|
||||
client.hostgroup_add_host, client.hostgroup_remove_host) or changed
|
||||
|
||||
if hostgroup is not None:
|
||||
changed = client.modify_if_diff(name, ipa_hostgroup.get('member_hostgroup', []),
|
||||
[item.lower() for item in hostgroup],
|
||||
client.hostgroup_add_hostgroup,
|
||||
client.hostgroup_remove_hostgroup,
|
||||
append=append) or changed
|
||||
client.hostgroup_remove_hostgroup) or changed
|
||||
|
||||
else:
|
||||
if ipa_hostgroup:
|
||||
@@ -204,8 +192,7 @@ def main():
|
||||
description=dict(type='str'),
|
||||
host=dict(type='list', elements='str'),
|
||||
hostgroup=dict(type='list', elements='str'),
|
||||
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
append=dict(type='bool', default=False))
|
||||
state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']))
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2023, Dominik Kukacka <dominik.kukacka@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: "ipbase_info"
|
||||
version_added: "7.0.0"
|
||||
short_description: "Retrieve IP geolocation and other facts of a host's IP address using the ipbase.com API"
|
||||
description:
|
||||
- "Retrieve IP geolocation and other facts of a host's IP address using the ipbase.com API"
|
||||
author: "Dominik Kukacka (@dominikkukacka)"
|
||||
extends_documentation_fragment:
|
||||
- "community.general.attributes"
|
||||
- "community.general.attributes.info_module"
|
||||
options:
|
||||
ip:
|
||||
description:
|
||||
- "The IP you want to get the info for. If not specified the API will detect the IP automatically."
|
||||
required: false
|
||||
type: str
|
||||
apikey:
|
||||
description:
|
||||
- "The API key for the request if you need more requests."
|
||||
required: false
|
||||
type: str
|
||||
hostname:
|
||||
description:
|
||||
- "If the hostname parameter is set to C(true), the API response will contain the hostname of the IP."
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
language:
|
||||
description:
|
||||
- "An ISO Alpha 2 Language Code for localizing the IP data"
|
||||
required: false
|
||||
type: str
|
||||
default: "en"
|
||||
notes:
|
||||
- "Check U(https://ipbase.com/) for more information."
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: "Get IP geolocation information of the primary outgoing IP"
|
||||
community.general.ipbase_info:
|
||||
register: my_ip_info
|
||||
|
||||
- name: "Get IP geolocation information of a specific IP"
|
||||
community.general.ipbase_info:
|
||||
ip: "8.8.8.8"
|
||||
register: my_ip_info
|
||||
|
||||
|
||||
- name: "Get IP geolocation information of a specific IP with all other possible parameters"
|
||||
community.general.ipbase_info:
|
||||
ip: "8.8.8.8"
|
||||
apikey: "xxxxxxxxxxxxxxxxxxxxxx"
|
||||
hostname: true
|
||||
language: "de"
|
||||
register: my_ip_info
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
data:
|
||||
description: "JSON parsed response from ipbase.com. Please refer to U(https://ipbase.com/docs/info) for the detailled structure of the response."
|
||||
returned: success
|
||||
type: dict
|
||||
sample: {
|
||||
"ip": "1.1.1.1",
|
||||
"hostname": "one.one.one.one",
|
||||
"type": "v4",
|
||||
"range_type": {
|
||||
"type": "PUBLIC",
|
||||
"description": "Public address"
|
||||
},
|
||||
"connection": {
|
||||
"asn": 13335,
|
||||
"organization": "Cloudflare, Inc.",
|
||||
"isp": "APNIC Research and Development",
|
||||
"range": "1.1.1.1/32"
|
||||
},
|
||||
"location": {
|
||||
"geonames_id": 5332870,
|
||||
"latitude": 34.053611755371094,
|
||||
"longitude": -118.24549865722656,
|
||||
"zip": "90012",
|
||||
"continent": {
|
||||
"code": "NA",
|
||||
"name": "North America",
|
||||
"name_translated": "North America"
|
||||
},
|
||||
"country": {
|
||||
"alpha2": "US",
|
||||
"alpha3": "USA",
|
||||
"calling_codes": [
|
||||
"+1"
|
||||
],
|
||||
"currencies": [
|
||||
{
|
||||
"symbol": "$",
|
||||
"name": "US Dollar",
|
||||
"symbol_native": "$",
|
||||
"decimal_digits": 2,
|
||||
"rounding": 0,
|
||||
"code": "USD",
|
||||
"name_plural": "US dollars"
|
||||
}
|
||||
],
|
||||
"emoji": "...",
|
||||
"ioc": "USA",
|
||||
"languages": [
|
||||
{
|
||||
"name": "English",
|
||||
"name_native": "English"
|
||||
}
|
||||
],
|
||||
"name": "United States",
|
||||
"name_translated": "United States",
|
||||
"timezones": [
|
||||
"America/New_York",
|
||||
"America/Detroit",
|
||||
"America/Kentucky/Louisville",
|
||||
"America/Kentucky/Monticello",
|
||||
"America/Indiana/Indianapolis",
|
||||
"America/Indiana/Vincennes",
|
||||
"America/Indiana/Winamac",
|
||||
"America/Indiana/Marengo",
|
||||
"America/Indiana/Petersburg",
|
||||
"America/Indiana/Vevay",
|
||||
"America/Chicago",
|
||||
"America/Indiana/Tell_City",
|
||||
"America/Indiana/Knox",
|
||||
"America/Menominee",
|
||||
"America/North_Dakota/Center",
|
||||
"America/North_Dakota/New_Salem",
|
||||
"America/North_Dakota/Beulah",
|
||||
"America/Denver",
|
||||
"America/Boise",
|
||||
"America/Phoenix",
|
||||
"America/Los_Angeles",
|
||||
"America/Anchorage",
|
||||
"America/Juneau",
|
||||
"America/Sitka",
|
||||
"America/Metlakatla",
|
||||
"America/Yakutat",
|
||||
"America/Nome",
|
||||
"America/Adak",
|
||||
"Pacific/Honolulu"
|
||||
],
|
||||
"is_in_european_union": false,
|
||||
"fips": "US",
|
||||
"geonames_id": 6252001,
|
||||
"hasc_id": "US",
|
||||
"wikidata_id": "Q30"
|
||||
},
|
||||
"city": {
|
||||
"fips": "644000",
|
||||
"alpha2": null,
|
||||
"geonames_id": 5368753,
|
||||
"hasc_id": null,
|
||||
"wikidata_id": "Q65",
|
||||
"name": "Los Angeles",
|
||||
"name_translated": "Los Angeles"
|
||||
},
|
||||
"region": {
|
||||
"fips": "US06",
|
||||
"alpha2": "US-CA",
|
||||
"geonames_id": 5332921,
|
||||
"hasc_id": "US.CA",
|
||||
"wikidata_id": "Q99",
|
||||
"name": "California",
|
||||
"name_translated": "California"
|
||||
}
|
||||
},
|
||||
"tlds": [
|
||||
".us"
|
||||
],
|
||||
"timezone": {
|
||||
"id": "America/Los_Angeles",
|
||||
"current_time": "2023-05-04T04:30:28-07:00",
|
||||
"code": "PDT",
|
||||
"is_daylight_saving": true,
|
||||
"gmt_offset": -25200
|
||||
},
|
||||
"security": {
|
||||
"is_anonymous": false,
|
||||
"is_datacenter": false,
|
||||
"is_vpn": false,
|
||||
"is_bot": false,
|
||||
"is_abuser": true,
|
||||
"is_known_attacker": true,
|
||||
"is_proxy": false,
|
||||
"is_spam": false,
|
||||
"is_tor": false,
|
||||
"is_icloud_relay": false,
|
||||
"threat_score": 100
|
||||
},
|
||||
"domains": {
|
||||
"count": 10943,
|
||||
"domains": [
|
||||
"eliwise.academy",
|
||||
"accountingprose.academy",
|
||||
"pistola.academy",
|
||||
"1and1-test-ntlds-fr.accountant",
|
||||
"omnergy.africa"
|
||||
]
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
|
||||
|
||||
USER_AGENT = 'ansible-community.general.ipbase_info/0.1.0'
|
||||
BASE_URL = 'https://api.ipbase.com/v2/info'
|
||||
|
||||
|
||||
class IpbaseInfo(object):
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
|
||||
def _get_url_data(self, url):
|
||||
response, info = fetch_url(
|
||||
self.module,
|
||||
url,
|
||||
force=True,
|
||||
timeout=10,
|
||||
headers={
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': USER_AGENT,
|
||||
})
|
||||
|
||||
if info['status'] != 200:
|
||||
self.module.fail_json(msg='The API request to ipbase.com returned an error status code {0}'.format(info['status']))
|
||||
else:
|
||||
try:
|
||||
content = response.read()
|
||||
result = self.module.from_json(content.decode('utf8'))
|
||||
except ValueError:
|
||||
self.module.fail_json(
|
||||
msg='Failed to parse the ipbase.com response: '
|
||||
'{0} {1}'.format(url, content))
|
||||
else:
|
||||
return result
|
||||
|
||||
def info(self):
|
||||
|
||||
ip = self.module.params['ip']
|
||||
apikey = self.module.params['apikey']
|
||||
hostname = self.module.params['hostname']
|
||||
language = self.module.params['language']
|
||||
|
||||
url = BASE_URL
|
||||
|
||||
params = {}
|
||||
if ip:
|
||||
params['ip'] = ip
|
||||
|
||||
if apikey:
|
||||
params['apikey'] = apikey
|
||||
|
||||
if hostname:
|
||||
params['hostname'] = 1
|
||||
|
||||
if language:
|
||||
params['language'] = language
|
||||
|
||||
if params:
|
||||
url += '?' + urlencode(params)
|
||||
|
||||
return self._get_url_data(url)
|
||||
|
||||
|
||||
def main():
|
||||
module_args = dict(
|
||||
ip=dict(type='str', required=False, no_log=False),
|
||||
apikey=dict(type='str', required=False, no_log=True),
|
||||
hostname=dict(type='bool', required=False, no_log=False, default=False),
|
||||
language=dict(type='str', required=False, no_log=False, default='en'),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ipbase = IpbaseInfo(module)
|
||||
module.exit_json(**ipbase.info())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -100,7 +100,7 @@ powerstate:
|
||||
description: The current power state of the machine.
|
||||
returned: success and I(machine) is not provided
|
||||
type: str
|
||||
sample: 'on'
|
||||
sample: on
|
||||
status:
|
||||
description: The current power state of the machine when the machine option is set.
|
||||
returned: success and I(machine) is provided
|
||||
@@ -132,14 +132,14 @@ EXAMPLES = '''
|
||||
name: test.testdomain.com
|
||||
user: admin
|
||||
password: password
|
||||
state: 'on'
|
||||
state: on
|
||||
|
||||
- name: Ensure machines of which remote target address is 48 and 50 are powered off
|
||||
community.general.ipmi_power:
|
||||
name: test.testdomain.com
|
||||
user: admin
|
||||
password: password
|
||||
state: 'off'
|
||||
state: off
|
||||
machine:
|
||||
- targetAddress: 48
|
||||
- targetAddress: 50
|
||||
@@ -151,9 +151,9 @@ EXAMPLES = '''
|
||||
password: password
|
||||
machine:
|
||||
- targetAddress: 48
|
||||
state: 'on'
|
||||
state: on
|
||||
- targetAddress: 50
|
||||
state: 'off'
|
||||
state: off
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#
|
||||
# Copyright (c) 2020, Per Abildgaard Toft <per@minfejl.dk> Search and update function
|
||||
# Copyright (c) 2021, Brandon McNama <brandonmcnama@outlook.com> Issue attachment functionality
|
||||
# Copyright (c) 2022, Hugo Prudente <hugo.kenshin+oss@gmail.com> Worklog functionality
|
||||
#
|
||||
# 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
|
||||
@@ -41,10 +40,9 @@ options:
|
||||
type: str
|
||||
required: true
|
||||
aliases: [ command ]
|
||||
choices: [ attach, comment, create, edit, fetch, link, search, transition, update, worklog ]
|
||||
choices: [ attach, comment, create, edit, fetch, link, search, transition, update ]
|
||||
description:
|
||||
- The operation to perform.
|
||||
- C(worklog) was added in community.genereal 6.5.0.
|
||||
|
||||
username:
|
||||
type: str
|
||||
@@ -173,6 +171,7 @@ options:
|
||||
- When passed to comment, the data structure is merged at the first level since community.general 4.6.0. Useful to add JIRA properties for example.
|
||||
- Note that JIRA may not allow changing field values on specific transitions or states.
|
||||
default: {}
|
||||
|
||||
jql:
|
||||
required: false
|
||||
description:
|
||||
@@ -288,47 +287,6 @@ EXAMPLES = r"""
|
||||
value:
|
||||
internal: true
|
||||
|
||||
# Add an workog to an existing issue
|
||||
- name: Worklog on issue
|
||||
community.general.jira:
|
||||
uri: '{{ server }}'
|
||||
username: '{{ user }}'
|
||||
password: '{{ pass }}'
|
||||
issue: '{{ issue.meta.key }}'
|
||||
operation: worklog
|
||||
comment: A worklog added by Ansible
|
||||
fields:
|
||||
timeSpentSeconds: 12000
|
||||
|
||||
- name: Workflow on issue with comment restricted visibility
|
||||
community.general.jira:
|
||||
uri: '{{ server }}'
|
||||
username: '{{ user }}'
|
||||
password: '{{ pass }}'
|
||||
issue: '{{ issue.meta.key }}'
|
||||
operation: worklog
|
||||
comment: A worklog added by Ansible
|
||||
comment_visibility:
|
||||
type: role
|
||||
value: Developers
|
||||
fields:
|
||||
timeSpentSeconds: 12000
|
||||
|
||||
- name: Workflow on issue with comment property to mark it internal
|
||||
community.general.jira:
|
||||
uri: '{{ server }}'
|
||||
username: '{{ user }}'
|
||||
password: '{{ pass }}'
|
||||
issue: '{{ issue.meta.key }}'
|
||||
operation: worklog
|
||||
comment: A worklog added by Ansible
|
||||
fields:
|
||||
properties:
|
||||
- key: 'sd.public.comment'
|
||||
value:
|
||||
internal: true
|
||||
timeSpentSeconds: 12000
|
||||
|
||||
# Assign an existing issue using edit
|
||||
- name: Assign an issue using free-form fields
|
||||
community.general.jira:
|
||||
@@ -480,7 +438,7 @@ class JIRA(StateModuleHelper):
|
||||
uri=dict(type='str', required=True),
|
||||
operation=dict(
|
||||
type='str',
|
||||
choices=['attach', 'create', 'comment', 'edit', 'update', 'fetch', 'transition', 'link', 'search', 'worklog'],
|
||||
choices=['attach', 'create', 'comment', 'edit', 'update', 'fetch', 'transition', 'link', 'search'],
|
||||
aliases=['command'], required=True
|
||||
),
|
||||
username=dict(type='str'),
|
||||
@@ -523,7 +481,6 @@ class JIRA(StateModuleHelper):
|
||||
('operation', 'attach', ['issue', 'attachment']),
|
||||
('operation', 'create', ['project', 'issuetype', 'summary']),
|
||||
('operation', 'comment', ['issue', 'comment']),
|
||||
('operation', 'workflow', ['issue', 'comment']),
|
||||
('operation', 'fetch', ['issue']),
|
||||
('operation', 'transition', ['issue', 'status']),
|
||||
('operation', 'link', ['linktype', 'inwardissue', 'outwardissue']),
|
||||
@@ -578,22 +535,6 @@ class JIRA(StateModuleHelper):
|
||||
url = self.vars.restbase + '/issue/' + self.vars.issue + '/comment'
|
||||
self.vars.meta = self.post(url, data)
|
||||
|
||||
@cause_changes(on_success=True)
|
||||
def operation_worklog(self):
|
||||
data = {
|
||||
'comment': self.vars.comment
|
||||
}
|
||||
# if comment_visibility is specified restrict visibility
|
||||
if self.vars.comment_visibility is not None:
|
||||
data['visibility'] = self.vars.comment_visibility
|
||||
|
||||
# Use 'fields' to merge in any additional data
|
||||
if self.vars.fields:
|
||||
data.update(self.vars.fields)
|
||||
|
||||
url = self.vars.restbase + '/issue/' + self.vars.issue + '/worklog'
|
||||
self.vars.meta = self.post(url, data)
|
||||
|
||||
@cause_changes(on_success=True)
|
||||
def operation_edit(self):
|
||||
data = {
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright (c) 2023, Salvatore Mesoraca <s.mesoraca16@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: kdeconfig
|
||||
short_description: Manage KDE configuration files
|
||||
version_added: "6.5.0"
|
||||
description:
|
||||
- Add or change individual settings in KDE configuration files.
|
||||
- It uses B(kwriteconfig) under the hood.
|
||||
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- Path to the config file. If the file does not exist it will be created.
|
||||
type: path
|
||||
required: true
|
||||
kwriteconfig_path:
|
||||
description:
|
||||
- Path to the kwriteconfig executable. If not specified, Ansible will try
|
||||
to discover it.
|
||||
type: path
|
||||
values:
|
||||
description:
|
||||
- List of values to set.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
group:
|
||||
description:
|
||||
- The option's group. One between this and I(groups) is required.
|
||||
type: str
|
||||
groups:
|
||||
description:
|
||||
- List of the option's groups. One between this and I(group) is required.
|
||||
type: list
|
||||
elements: str
|
||||
key:
|
||||
description:
|
||||
- The option's name.
|
||||
type: str
|
||||
required: true
|
||||
value:
|
||||
description:
|
||||
- The option's value. One between this and I(bool_value) is required.
|
||||
type: str
|
||||
bool_value:
|
||||
description:
|
||||
- Boolean value.
|
||||
- One between this and I(value) is required.
|
||||
type: bool
|
||||
required: true
|
||||
backup:
|
||||
description:
|
||||
- Create a backup file.
|
||||
type: bool
|
||||
default: false
|
||||
extends_documentation_fragment:
|
||||
- files
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
requirements:
|
||||
- kwriteconfig
|
||||
author:
|
||||
- Salvatore Mesoraca (@smeso)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Ensure "Homepage=https://www.ansible.com/" in group "Branding"
|
||||
community.general.kdeconfig:
|
||||
path: /etc/xdg/kickoffrc
|
||||
values:
|
||||
- group: Branding
|
||||
key: Homepage
|
||||
value: https://www.ansible.com/
|
||||
mode: '0644'
|
||||
|
||||
- name: Ensure "KEY=true" in groups "Group" and "Subgroup", and "KEY=VALUE" in Group2
|
||||
community.general.kdeconfig:
|
||||
path: /etc/xdg/someconfigrc
|
||||
values:
|
||||
- groups: [Group, Subgroup]
|
||||
key: KEY
|
||||
bool_value: true
|
||||
- group: Group2
|
||||
key: KEY
|
||||
value: VALUE
|
||||
backup: true
|
||||
'''
|
||||
|
||||
RETURN = r''' # '''
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
|
||||
|
||||
class TemporaryDirectory(object):
|
||||
"""Basic backport of tempfile.TemporaryDirectory"""
|
||||
|
||||
def __init__(self, suffix="", prefix="tmp", dir=None):
|
||||
self.name = None
|
||||
self.name = tempfile.mkdtemp(suffix, prefix, dir)
|
||||
|
||||
def __enter__(self):
|
||||
return self.name
|
||||
|
||||
def rm(self):
|
||||
if self.name:
|
||||
shutil.rmtree(self.name, ignore_errors=True)
|
||||
self.name = None
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
self.rm()
|
||||
|
||||
def __del__(self):
|
||||
self.rm()
|
||||
|
||||
|
||||
def run_kwriteconfig(module, cmd, path, groups, key, value):
|
||||
"""Invoke kwriteconfig with arguments"""
|
||||
args = [cmd, '--file', path, '--key', key]
|
||||
for group in groups:
|
||||
args.extend(['--group', group])
|
||||
if isinstance(value, bool):
|
||||
args.extend(['--type', 'bool'])
|
||||
if value:
|
||||
args.append('true')
|
||||
else:
|
||||
args.append('false')
|
||||
else:
|
||||
args.append(value)
|
||||
module.run_command(args, check_rc=True)
|
||||
|
||||
|
||||
def run_module(module, tmpdir, kwriteconfig):
|
||||
result = dict(changed=False, msg='OK', path=module.params['path'])
|
||||
b_path = to_bytes(module.params['path'])
|
||||
tmpfile = os.path.join(tmpdir, 'file')
|
||||
b_tmpfile = to_bytes(tmpfile)
|
||||
diff = dict(
|
||||
before='',
|
||||
after='',
|
||||
before_header=result['path'],
|
||||
after_header=result['path'],
|
||||
)
|
||||
try:
|
||||
with open(b_tmpfile, 'wb') as dst:
|
||||
try:
|
||||
with open(b_path, 'rb') as src:
|
||||
b_data = src.read()
|
||||
except IOError:
|
||||
result['changed'] = True
|
||||
else:
|
||||
dst.write(b_data)
|
||||
try:
|
||||
diff['before'] = to_text(b_data)
|
||||
except UnicodeError:
|
||||
diff['before'] = repr(b_data)
|
||||
except IOError:
|
||||
module.fail_json(msg='Unable to create temporary file', traceback=traceback.format_exc())
|
||||
|
||||
for row in module.params['values']:
|
||||
groups = row['groups']
|
||||
if groups is None:
|
||||
groups = [row['group']]
|
||||
key = row['key']
|
||||
value = row['bool_value']
|
||||
if value is None:
|
||||
value = row['value']
|
||||
run_kwriteconfig(module, kwriteconfig, tmpfile, groups, key, value)
|
||||
|
||||
with open(b_tmpfile, 'rb') as tmpf:
|
||||
b_data = tmpf.read()
|
||||
try:
|
||||
diff['after'] = to_text(b_data)
|
||||
except UnicodeError:
|
||||
diff['after'] = repr(b_data)
|
||||
|
||||
result['changed'] = result['changed'] or diff['after'] != diff['before']
|
||||
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
|
||||
if module.check_mode:
|
||||
if not result['changed']:
|
||||
shutil.copystat(b_path, b_tmpfile)
|
||||
uid, gid = module.user_and_group(b_path)
|
||||
os.chown(b_tmpfile, uid, gid)
|
||||
if module._diff:
|
||||
diff = {}
|
||||
else:
|
||||
diff = None
|
||||
result['changed'] = module.set_fs_attributes_if_different(file_args, result['changed'], diff=diff)
|
||||
if module._diff:
|
||||
result['diff'] = diff
|
||||
module.exit_json(**result)
|
||||
|
||||
if result['changed']:
|
||||
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)
|
||||
except IOError:
|
||||
module.ansible.fail_json(msg='Unable to move temporary file %s to %s, IOError' % (tmpfile, result['path']), traceback=traceback.format_exc())
|
||||
|
||||
if result['changed']:
|
||||
module.set_fs_attributes_if_different(file_args, result['changed'])
|
||||
else:
|
||||
if module._diff:
|
||||
diff = {}
|
||||
else:
|
||||
diff = None
|
||||
result['changed'] = module.set_fs_attributes_if_different(file_args, result['changed'], diff=diff)
|
||||
if module._diff:
|
||||
result['diff'] = diff
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
single_value_arg = dict(group=dict(type='str'),
|
||||
groups=dict(type='list', elements='str'),
|
||||
key=dict(type='str', required=True, no_log=False),
|
||||
value=dict(type='str'),
|
||||
bool_value=dict(type='bool'))
|
||||
required_alternatives = [('group', 'groups'), ('value', 'bool_value')]
|
||||
module_args = dict(
|
||||
values=dict(type='list',
|
||||
elements='dict',
|
||||
options=single_value_arg,
|
||||
mutually_exclusive=required_alternatives,
|
||||
required_one_of=required_alternatives,
|
||||
required=True),
|
||||
path=dict(type='path', required=True),
|
||||
kwriteconfig_path=dict(type='path'),
|
||||
backup=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
add_file_common_args=True,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
kwriteconfig = None
|
||||
if module.params['kwriteconfig_path'] is not None:
|
||||
kwriteconfig = module.get_bin_path(module.params['kwriteconfig_path'], required=True)
|
||||
else:
|
||||
for progname in ('kwriteconfig5', 'kwriteconfig', 'kwriteconfig4'):
|
||||
kwriteconfig = module.get_bin_path(progname)
|
||||
if kwriteconfig is not None:
|
||||
break
|
||||
if kwriteconfig is None:
|
||||
module.fail_json(msg='kwriteconfig is not installed')
|
||||
for v in module.params['values']:
|
||||
if not v['key']:
|
||||
module.fail_json(msg="'key' cannot be empty")
|
||||
with TemporaryDirectory(dir=module.tmpdir) as tmpdir:
|
||||
run_module(module, tmpdir, kwriteconfig)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -79,14 +79,6 @@ options:
|
||||
description:
|
||||
- Priority order of the execution.
|
||||
type: int
|
||||
subFlowType:
|
||||
description:
|
||||
- For new subflows, optionally specify the type.
|
||||
- Is only used at creation.
|
||||
choices: ["basic-flow", "form-flow"]
|
||||
default: "basic-flow"
|
||||
type: str
|
||||
version_added: 6.6.0
|
||||
state:
|
||||
description:
|
||||
- Control if the authentication flow must exists or not.
|
||||
@@ -272,7 +264,7 @@ def create_or_update_executions(kc, config, realm='master'):
|
||||
exec_index = find_exec_in_executions(new_exec, existing_executions)
|
||||
if exec_index != -1:
|
||||
# Remove key that doesn't need to be compared with existing_exec
|
||||
exclude_key = ["flowAlias", "subFlowType"]
|
||||
exclude_key = ["flowAlias"]
|
||||
for index_key, key in enumerate(new_exec, start=0):
|
||||
if new_exec[key] is None:
|
||||
exclude_key.append(key)
|
||||
@@ -290,7 +282,7 @@ def create_or_update_executions(kc, config, realm='master'):
|
||||
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
|
||||
after += str(new_exec) + '\n'
|
||||
elif new_exec["displayName"] is not None:
|
||||
kc.create_subflow(new_exec["displayName"], flow_alias_parent, realm=realm, flowType=new_exec["subFlowType"])
|
||||
kc.create_subflow(new_exec["displayName"], flow_alias_parent, realm=realm)
|
||||
exec_found = True
|
||||
exec_index = new_exec_index
|
||||
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
|
||||
@@ -307,7 +299,7 @@ def create_or_update_executions(kc, config, realm='master'):
|
||||
kc.add_authenticationConfig_to_execution(updated_exec["id"], new_exec["authenticationConfig"], realm=realm)
|
||||
for key in new_exec:
|
||||
# remove unwanted key for the next API call
|
||||
if key not in ("flowAlias", "authenticationConfig", "subFlowType"):
|
||||
if key != "flowAlias" and key != "authenticationConfig":
|
||||
updated_exec[key] = new_exec[key]
|
||||
if new_exec["requirement"] is not None:
|
||||
kc.update_authentication_executions(flow_alias_parent, updated_exec, realm=realm)
|
||||
@@ -342,7 +334,6 @@ def main():
|
||||
flowAlias=dict(type='str'),
|
||||
authenticationConfig=dict(type='dict'),
|
||||
index=dict(type='int'),
|
||||
subFlowType=dict(choices=["basic-flow", "form-flow"], default='basic-flow', type='str'),
|
||||
)),
|
||||
state=dict(choices=["absent", "present"], default='present'),
|
||||
force=dict(type='bool', default=False),
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
|
||||
# Copyright (c) 2021, Christophe Gilles <christophe.gilles54@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: keycloak_authz_authorization_scope
|
||||
|
||||
short_description: Allows administration of Keycloak client authorization scopes via Keycloak API
|
||||
|
||||
version_added: 6.6.0
|
||||
|
||||
description:
|
||||
- This module allows the administration of Keycloak client Authorization Scopes via the Keycloak REST
|
||||
API. Authorization Scopes are only available if a client has Authorization enabled.
|
||||
|
||||
- This module requires access to the REST API via OpenID Connect; the user connecting and the realm
|
||||
being used must have the requisite access rights. In a default Keycloak installation, admin-cli
|
||||
and an admin user would work, as would a separate realm definition with the scope tailored
|
||||
to your needs and a user having the expected roles.
|
||||
|
||||
- The names of module options are snake_cased versions of the camelCase options used by Keycloak.
|
||||
The Authorization Services paths and payloads have not officially been documented by the Keycloak project.
|
||||
U(https://www.puppeteers.net/blog/keycloak-authorization-services-rest-api-paths-and-payload/)
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- State of the authorization scope.
|
||||
- On C(present), the authorization scope will be created (or updated if it exists already).
|
||||
- On C(absent), the authorization scope will be removed if it exists.
|
||||
choices: ['present', 'absent']
|
||||
default: 'present'
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Name of the authorization scope to create.
|
||||
type: str
|
||||
required: true
|
||||
display_name:
|
||||
description:
|
||||
- The display name of the authorization scope.
|
||||
type: str
|
||||
required: false
|
||||
icon_uri:
|
||||
description:
|
||||
- The icon URI for the authorization scope.
|
||||
type: str
|
||||
required: false
|
||||
client_id:
|
||||
description:
|
||||
- The C(clientId) of the Keycloak client that should have the authorization scope.
|
||||
- This is usually a human-readable name of the Keycloak client.
|
||||
type: str
|
||||
required: true
|
||||
realm:
|
||||
description:
|
||||
- The name of the Keycloak realm the Keycloak client is in.
|
||||
type: str
|
||||
required: true
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
- community.general.attributes
|
||||
|
||||
author:
|
||||
- Samuli Seppänen (@mattock)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Manage Keycloak file:delete authorization scope
|
||||
keycloak_authz_authorization_scope:
|
||||
name: file:delete
|
||||
state: present
|
||||
display_name: File delete
|
||||
client_id: myclient
|
||||
realm: myrealm
|
||||
auth_keycloak_url: http://localhost:8080/auth
|
||||
auth_username: keycloak
|
||||
auth_password: keycloak
|
||||
auth_realm: master
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
|
||||
end_state:
|
||||
description: Representation of the authorization scope after module execution.
|
||||
returned: on success
|
||||
type: complex
|
||||
contains:
|
||||
id:
|
||||
description: ID of the authorization scope.
|
||||
type: str
|
||||
returned: when I(state=present)
|
||||
sample: a6ab1cf2-1001-40ec-9f39-48f23b6a0a41
|
||||
name:
|
||||
description: Name of the authorization scope.
|
||||
type: str
|
||||
returned: when I(state=present)
|
||||
sample: file:delete
|
||||
display_name:
|
||||
description: Display name of the authorization scope.
|
||||
type: str
|
||||
returned: when I(state=present)
|
||||
sample: File delete
|
||||
icon_uri:
|
||||
description: Icon URI for the authorization scope.
|
||||
type: str
|
||||
returned: when I(state=present)
|
||||
sample: http://localhost/icon.png
|
||||
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, \
|
||||
keycloak_argument_spec, get_token, KeycloakError
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Module execution
|
||||
|
||||
:return:
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
state=dict(type='str', default='present',
|
||||
choices=['present', 'absent']),
|
||||
name=dict(type='str', required=True),
|
||||
display_name=dict(type='str', required=False),
|
||||
icon_uri=dict(type='str', required=False),
|
||||
client_id=dict(type='str', required=True),
|
||||
realm=dict(type='str', required=True)
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=(
|
||||
[['token', 'auth_realm', 'auth_username', 'auth_password']]),
|
||||
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
|
||||
|
||||
result = dict(changed=False, msg='', end_state={}, diff=dict(before={}, after={}))
|
||||
|
||||
# Obtain access token, initialize API
|
||||
try:
|
||||
connection_header = get_token(module.params)
|
||||
except KeycloakError as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
kc = KeycloakAPI(module, connection_header)
|
||||
|
||||
# Convenience variables
|
||||
state = module.params.get('state')
|
||||
name = module.params.get('name')
|
||||
display_name = module.params.get('display_name')
|
||||
icon_uri = module.params.get('icon_uri')
|
||||
client_id = module.params.get('client_id')
|
||||
realm = module.params.get('realm')
|
||||
|
||||
# Get the "id" of the client based on the usually more human-readable
|
||||
# "clientId"
|
||||
cid = kc.get_client_id(client_id, realm=realm)
|
||||
if not cid:
|
||||
module.fail_json(msg='Invalid client %s for realm %s' %
|
||||
(client_id, realm))
|
||||
|
||||
# Get current state of the Authorization Scope using its name as the search
|
||||
# filter. This returns False if it is not found.
|
||||
before_authz_scope = kc.get_authz_authorization_scope_by_name(
|
||||
name=name, client_id=cid, realm=realm)
|
||||
|
||||
# Generate a JSON payload for Keycloak Admin API. This is needed for
|
||||
# "create" and "update" operations.
|
||||
desired_authz_scope = {}
|
||||
desired_authz_scope['name'] = name
|
||||
desired_authz_scope['displayName'] = display_name
|
||||
desired_authz_scope['iconUri'] = icon_uri
|
||||
|
||||
# Add "id" to payload for modify operations
|
||||
if before_authz_scope:
|
||||
desired_authz_scope['id'] = before_authz_scope['id']
|
||||
|
||||
# Ensure that undefined (null) optional parameters are presented as empty
|
||||
# strings in the desired state. This makes comparisons with current state
|
||||
# much easier.
|
||||
for k, v in desired_authz_scope.items():
|
||||
if not v:
|
||||
desired_authz_scope[k] = ''
|
||||
|
||||
# Do the above for the current state
|
||||
if before_authz_scope:
|
||||
for k in ['displayName', 'iconUri']:
|
||||
if k not in before_authz_scope:
|
||||
before_authz_scope[k] = ''
|
||||
|
||||
if before_authz_scope and state == 'present':
|
||||
changes = False
|
||||
for k, v in desired_authz_scope.items():
|
||||
if before_authz_scope[k] != v:
|
||||
changes = True
|
||||
# At this point we know we have to update the object anyways,
|
||||
# so there's no need to do more work.
|
||||
break
|
||||
|
||||
if changes:
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_authz_scope, after=desired_authz_scope)
|
||||
|
||||
if module.check_mode:
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope would be updated'
|
||||
module.exit_json(**result)
|
||||
else:
|
||||
kc.update_authz_authorization_scope(
|
||||
payload=desired_authz_scope, id=before_authz_scope['id'], client_id=cid, realm=realm)
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope updated'
|
||||
else:
|
||||
result['changed'] = False
|
||||
result['msg'] = 'Authorization scope not updated'
|
||||
|
||||
result['end_state'] = desired_authz_scope
|
||||
elif not before_authz_scope and state == 'present':
|
||||
if module._diff:
|
||||
result['diff'] = dict(before={}, after=desired_authz_scope)
|
||||
|
||||
if module.check_mode:
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope would be created'
|
||||
module.exit_json(**result)
|
||||
else:
|
||||
kc.create_authz_authorization_scope(
|
||||
payload=desired_authz_scope, client_id=cid, realm=realm)
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope created'
|
||||
result['end_state'] = desired_authz_scope
|
||||
elif before_authz_scope and state == 'absent':
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_authz_scope, after={})
|
||||
|
||||
if module.check_mode:
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope would be removed'
|
||||
module.exit_json(**result)
|
||||
else:
|
||||
kc.remove_authz_authorization_scope(
|
||||
id=before_authz_scope['id'], client_id=cid, realm=realm)
|
||||
result['changed'] = True
|
||||
result['msg'] = 'Authorization scope removed'
|
||||
elif not before_authz_scope and state == 'absent':
|
||||
result['changed'] = False
|
||||
else:
|
||||
module.fail_json(msg='Unable to determine what to do with authorization scope %s of client %s in realm %s' % (
|
||||
name, client_id, realm))
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,285 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) Ansible project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: keycloak_clientscope_type
|
||||
|
||||
short_description: Set the type of aclientscope in realm or client via Keycloak API
|
||||
|
||||
version_added: 6.6.0
|
||||
|
||||
description:
|
||||
- This module allows you to set the type (optional, default) of clientscopes
|
||||
via the Keycloak REST API. It requires access to the REST API via OpenID
|
||||
Connect; the user connecting and the client being used must have the
|
||||
requisite access rights. In a default Keycloak installation, admin-cli and
|
||||
an admin user would work, as would a separate client definition with the
|
||||
scope tailored to your needs and a user having the expected roles.
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
|
||||
options:
|
||||
realm:
|
||||
type: str
|
||||
description:
|
||||
- The Keycloak realm.
|
||||
default: 'master'
|
||||
|
||||
client_id:
|
||||
description:
|
||||
- The I(client_id) of the client. If not set the clientscop types are set as a default for the realm.
|
||||
aliases:
|
||||
- clientId
|
||||
type: str
|
||||
|
||||
default_clientscopes:
|
||||
description:
|
||||
- Client scopes that should be of type default.
|
||||
type: list
|
||||
elements: str
|
||||
|
||||
optional_clientscopes:
|
||||
description:
|
||||
- Client scopes that should be of type optional.
|
||||
type: list
|
||||
elements: str
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
- community.general.attributes
|
||||
|
||||
author:
|
||||
- Simon Pahl (@simonpahl)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Set default client scopes on realm level
|
||||
community.general.keycloak_clientsecret_info:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
auth_realm: master
|
||||
auth_username: USERNAME
|
||||
auth_password: PASSWORD
|
||||
realm: "MyCustomRealm"
|
||||
default_clientscopes: ['profile', 'roles']
|
||||
delegate_to: localhost
|
||||
|
||||
|
||||
- name: Set default and optional client scopes on client level with token auth
|
||||
community.general.keycloak_clientsecret_info:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
token: TOKEN
|
||||
realm: "MyCustomRealm"
|
||||
client_id: "MyCustomClient"
|
||||
default_clientscopes: ['profile', 'roles']
|
||||
optional_clientscopes: ['phone']
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: ""
|
||||
proposed:
|
||||
description: Representation of proposed client-scope types mapping.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
default_clientscopes: ["profile", "role"],
|
||||
optional_clientscopes: []
|
||||
}
|
||||
existing:
|
||||
description:
|
||||
- Representation of client scopes before module execution.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
default_clientscopes: ["profile", "role"],
|
||||
optional_clientscopes: ["phone"]
|
||||
}
|
||||
end_state:
|
||||
description:
|
||||
- Representation of client scopes after module execution.
|
||||
- The sample is truncated.
|
||||
returned: on success
|
||||
type: dict
|
||||
sample: {
|
||||
default_clientscopes: ["profile", "role"],
|
||||
optional_clientscopes: []
|
||||
}
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import (
|
||||
KeycloakAPI, KeycloakError, get_token)
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import \
|
||||
keycloak_argument_spec
|
||||
|
||||
|
||||
def keycloak_clientscope_type_module():
|
||||
"""
|
||||
Returns an AnsibleModule definition.
|
||||
|
||||
:return: argument_spec dict
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
realm=dict(default='master'),
|
||||
client_id=dict(type='str', aliases=['clientId']),
|
||||
default_clientscopes=dict(type='list', elements='str'),
|
||||
optional_clientscopes=dict(type='list', elements='str'),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=([
|
||||
['token', 'auth_realm', 'auth_username', 'auth_password'],
|
||||
['default_clientscopes', 'optional_clientscopes']
|
||||
]),
|
||||
required_together=([['auth_realm', 'auth_username', 'auth_password']]),
|
||||
mutually_exclusive=[
|
||||
['token', 'auth_realm'],
|
||||
['token', 'auth_username'],
|
||||
['token', 'auth_password']
|
||||
])
|
||||
|
||||
return module
|
||||
|
||||
|
||||
def clientscopes_to_add(existing, proposed):
|
||||
to_add = []
|
||||
existing_clientscope_ids = extract_field(existing, 'id')
|
||||
for clientscope in proposed:
|
||||
if not clientscope['id'] in existing_clientscope_ids:
|
||||
to_add.append(clientscope)
|
||||
return to_add
|
||||
|
||||
|
||||
def clientscopes_to_delete(existing, proposed):
|
||||
to_delete = []
|
||||
proposed_clientscope_ids = extract_field(proposed, 'id')
|
||||
for clientscope in existing:
|
||||
if not clientscope['id'] in proposed_clientscope_ids:
|
||||
to_delete.append(clientscope)
|
||||
return to_delete
|
||||
|
||||
|
||||
def extract_field(dictionary, field='name'):
|
||||
return [cs[field] for cs in dictionary]
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Module keycloak_clientscope_type
|
||||
|
||||
:return:
|
||||
"""
|
||||
|
||||
module = keycloak_clientscope_type_module()
|
||||
|
||||
# Obtain access token, initialize API
|
||||
try:
|
||||
connection_header = get_token(module.params)
|
||||
except KeycloakError as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
kc = KeycloakAPI(module, connection_header)
|
||||
|
||||
realm = module.params.get('realm')
|
||||
client_id = module.params.get('client_id')
|
||||
default_clientscopes = module.params.get('default_clientscopes')
|
||||
optional_clientscopes = module.params.get('optional_clientscopes')
|
||||
|
||||
result = dict(changed=False, msg='', proposed={}, existing={}, end_state={})
|
||||
|
||||
all_clientscopes = kc.get_clientscopes(realm)
|
||||
default_clientscopes_real = []
|
||||
optional_clientscopes_real = []
|
||||
|
||||
for client_scope in all_clientscopes:
|
||||
if default_clientscopes is not None and client_scope["name"] in default_clientscopes:
|
||||
default_clientscopes_real.append(client_scope)
|
||||
if optional_clientscopes is not None and client_scope["name"] in optional_clientscopes:
|
||||
optional_clientscopes_real.append(client_scope)
|
||||
|
||||
if default_clientscopes is not None and len(default_clientscopes_real) != len(default_clientscopes):
|
||||
module.fail_json(msg='At least one of the default_clientscopes does not exist!')
|
||||
|
||||
if optional_clientscopes is not None and len(optional_clientscopes_real) != len(optional_clientscopes):
|
||||
module.fail_json(msg='At least one of the optional_clientscopes does not exist!')
|
||||
|
||||
result['proposed'].update({
|
||||
'default_clientscopes': 'no-change' if default_clientscopes is None else default_clientscopes,
|
||||
'optional_clientscopes': 'no-change' if optional_clientscopes is None else optional_clientscopes
|
||||
})
|
||||
|
||||
default_clientscopes_existing = kc.get_default_clientscopes(realm, client_id)
|
||||
optional_clientscopes_existing = kc.get_optional_clientscopes(realm, client_id)
|
||||
|
||||
result['existing'].update({
|
||||
'default_clientscopes': extract_field(default_clientscopes_existing),
|
||||
'optional_clientscopes': extract_field(optional_clientscopes_existing)
|
||||
})
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=result['existing'], after=result['proposed'])
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
default_clientscopes_add = clientscopes_to_add(default_clientscopes_existing, default_clientscopes_real)
|
||||
optional_clientscopes_add = clientscopes_to_add(optional_clientscopes_existing, optional_clientscopes_real)
|
||||
|
||||
default_clientscopes_delete = clientscopes_to_delete(default_clientscopes_existing, default_clientscopes_real)
|
||||
optional_clientscopes_delete = clientscopes_to_delete(optional_clientscopes_existing, optional_clientscopes_real)
|
||||
|
||||
# first delete so clientscopes can change type
|
||||
for clientscope in default_clientscopes_delete:
|
||||
kc.delete_default_clientscope(clientscope['id'], realm, client_id)
|
||||
for clientscope in optional_clientscopes_delete:
|
||||
kc.delete_optional_clientscope(clientscope['id'], realm, client_id)
|
||||
|
||||
for clientscope in default_clientscopes_add:
|
||||
kc.add_default_clientscope(clientscope['id'], realm, client_id)
|
||||
for clientscope in optional_clientscopes_add:
|
||||
kc.add_optional_clientscope(clientscope['id'], realm, client_id)
|
||||
|
||||
result["changed"] = (
|
||||
len(default_clientscopes_add) > 0
|
||||
or len(optional_clientscopes_add) > 0
|
||||
or len(default_clientscopes_delete) > 0
|
||||
or len(optional_clientscopes_delete) > 0
|
||||
)
|
||||
|
||||
result['end_state'].update({
|
||||
'default_clientscopes': extract_field(kc.get_default_clientscopes(realm, client_id)),
|
||||
'optional_clientscopes': extract_field(kc.get_optional_clientscopes(realm, client_id))
|
||||
})
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -62,15 +62,8 @@ options:
|
||||
required: true
|
||||
type: dict
|
||||
description:
|
||||
- The attribute(s) and value(s) to add or remove.
|
||||
- Each attribute value can be a string for single-valued attributes or
|
||||
a list of strings for multi-valued attributes.
|
||||
- If you specify values for this option in YAML, please note that you can improve
|
||||
readability for long string values by using YAML block modifiers as seen in the
|
||||
examples for this module.
|
||||
- Note that when using values that YAML/ansible-core interprets as other types,
|
||||
like C(yes), C(no) (booleans), or C(2.10) (float), make sure to quote them if
|
||||
these are meant to be strings. Otherwise the wrong values may be sent to LDAP.
|
||||
- The attribute(s) and value(s) to add or remove. The complex argument format is required in order to pass
|
||||
a list of strings (see examples).
|
||||
ordered:
|
||||
required: false
|
||||
type: bool
|
||||
|
||||
@@ -41,14 +41,6 @@ options:
|
||||
- If I(state=present), attributes necessary to create an entry. Existing
|
||||
entries are never modified. To assert specific attribute values on an
|
||||
existing entry, use M(community.general.ldap_attrs) module instead.
|
||||
- Each attribute value can be a string for single-valued attributes or
|
||||
a list of strings for multi-valued attributes.
|
||||
- If you specify values for this option in YAML, please note that you can improve
|
||||
readability for long string values by using YAML block modifiers as seen in the
|
||||
examples for this module.
|
||||
- Note that when using values that YAML/ansible-core interprets as other types,
|
||||
like C(yes), C(no) (booleans), or C(2.10) (float), make sure to quote them if
|
||||
these are meant to be strings. Otherwise the wrong values may be sent to LDAP.
|
||||
type: dict
|
||||
default: {}
|
||||
objectClass:
|
||||
@@ -94,29 +86,6 @@ EXAMPLES = """
|
||||
description: An LDAP administrator
|
||||
userPassword: "{SSHA}tabyipcHzhwESzRaGA7oQ/SDoBZQOGND"
|
||||
|
||||
- name: Set possible values for attributes elements
|
||||
community.general.ldap_entry:
|
||||
dn: cn=admin,dc=example,dc=com
|
||||
objectClass:
|
||||
- simpleSecurityObject
|
||||
- organizationalRole
|
||||
attributes:
|
||||
description: An LDAP Administrator
|
||||
roleOccupant:
|
||||
- cn=Chocs Puddington,ou=Information Technology,dc=example,dc=com
|
||||
- cn=Alice Stronginthebrain,ou=Information Technology,dc=example,dc=com
|
||||
olcAccess:
|
||||
- >-
|
||||
{0}to attrs=userPassword,shadowLastChange
|
||||
by self write
|
||||
by anonymous auth
|
||||
by dn="cn=admin,dc=example,dc=com" write
|
||||
by * none'
|
||||
- >-
|
||||
{1}to dn.base="dc=example,dc=com"
|
||||
by dn="cn=admin,dc=example,dc=com" write
|
||||
by * read
|
||||
|
||||
- name: Get rid of an old entry
|
||||
community.general.ldap_entry:
|
||||
dn: ou=stuff,dc=example,dc=com
|
||||
|
||||
@@ -61,17 +61,6 @@ options:
|
||||
description:
|
||||
- Set to C(true) to return the full attribute schema of entries, not
|
||||
their attribute values. Overrides I(attrs) when provided.
|
||||
base64_attributes:
|
||||
description:
|
||||
- If provided, all attribute values returned that are listed in this option
|
||||
will be Base64 encoded.
|
||||
- If the special value C(*) appears in this list, all attributes will be
|
||||
Base64 encoded.
|
||||
- All other attribute values will be converted to UTF-8 strings. If they
|
||||
contain binary data, please note that invalid UTF-8 bytes will be omitted.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 7.0.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.ldap.documentation
|
||||
- community.general.attributes
|
||||
@@ -92,27 +81,10 @@ EXAMPLES = r"""
|
||||
register: ldap_group_gids
|
||||
"""
|
||||
|
||||
RESULTS = """
|
||||
results:
|
||||
description:
|
||||
- For every entry found, one dictionary will be returned.
|
||||
- Every dictionary contains a key C(dn) with the entry's DN as a value.
|
||||
- Every attribute of the entry found is added to the dictionary. If the key
|
||||
has precisely one value, that value is taken directly, otherwise the key's
|
||||
value is a list.
|
||||
- Note that all values (for single-element lists) and list elements (for multi-valued
|
||||
lists) will be UTF-8 strings. Some might contain Base64-encoded binary data; which
|
||||
ones is determined by the I(base64_attributes) option.
|
||||
type: list
|
||||
elements: dict
|
||||
"""
|
||||
|
||||
import base64
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.six import string_types, text_type
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs
|
||||
|
||||
LDAP_IMP_ERR = None
|
||||
@@ -133,7 +105,6 @@ def main():
|
||||
filter=dict(type='str', default='(objectClass=*)'),
|
||||
attrs=dict(type='list', elements='str'),
|
||||
schema=dict(type='bool', default=False),
|
||||
base64_attributes=dict(type='list', elements='str'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -147,30 +118,16 @@ def main():
|
||||
except Exception as exception:
|
||||
module.fail_json(msg="Attribute action failed.", details=to_native(exception))
|
||||
|
||||
|
||||
def _normalize_string(val, convert_to_base64):
|
||||
if isinstance(val, string_types):
|
||||
if isinstance(val, text_type):
|
||||
val = to_bytes(val, encoding='utf-8')
|
||||
if convert_to_base64:
|
||||
val = to_text(base64.b64encode(val))
|
||||
else:
|
||||
# See https://github.com/ansible/ansible/issues/80258#issuecomment-1477038952 for details.
|
||||
# We want to make sure that all strings are properly UTF-8 encoded, even if they were not,
|
||||
# or happened to be byte strings.
|
||||
val = to_text(val, 'utf-8', errors='replace')
|
||||
# See also https://github.com/ansible-collections/community.general/issues/5704.
|
||||
return val
|
||||
module.exit_json(changed=False)
|
||||
|
||||
|
||||
def _extract_entry(dn, attrs, base64_attributes):
|
||||
def _extract_entry(dn, attrs):
|
||||
extracted = {'dn': dn}
|
||||
for attr, val in list(attrs.items()):
|
||||
convert_to_base64 = '*' in base64_attributes or attr in base64_attributes
|
||||
if len(val) == 1:
|
||||
extracted[attr] = _normalize_string(val[0], convert_to_base64)
|
||||
extracted[attr] = val[0]
|
||||
else:
|
||||
extracted[attr] = [_normalize_string(v, convert_to_base64) for v in val]
|
||||
extracted[attr] = val
|
||||
return extracted
|
||||
|
||||
|
||||
@@ -183,10 +140,9 @@ class LdapSearch(LdapGeneric):
|
||||
self._load_scope()
|
||||
self._load_attrs()
|
||||
self._load_schema()
|
||||
self._base64_attributes = set(self.module.params['base64_attributes'] or [])
|
||||
|
||||
def _load_schema(self):
|
||||
self.schema = self.module.params['schema']
|
||||
self.schema = self.module.boolean(self.module.params['schema'])
|
||||
if self.schema:
|
||||
self.attrsonly = 1
|
||||
else:
|
||||
@@ -223,7 +179,7 @@ class LdapSearch(LdapGeneric):
|
||||
if self.schema:
|
||||
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
|
||||
else:
|
||||
ldap_entries.append(_extract_entry(result[0], result[1], self._base64_attributes))
|
||||
ldap_entries.append(_extract_entry(result[0], result[1]))
|
||||
return ldap_entries
|
||||
except ldap.NO_SUCH_OBJECT:
|
||||
self.module.fail_json(msg="Base not found: {0}".format(self.dn))
|
||||
|
||||
@@ -25,6 +25,15 @@ attributes:
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
target:
|
||||
description:
|
||||
- The target to run.
|
||||
- Typically this would be something like C(install),C(test) or C(all)."
|
||||
type: str
|
||||
params:
|
||||
description:
|
||||
- Any extra parameters to pass to make.
|
||||
type: dict
|
||||
chdir:
|
||||
description:
|
||||
- Change to this directory before running make.
|
||||
@@ -34,6 +43,11 @@ options:
|
||||
description:
|
||||
- Use a custom Makefile.
|
||||
type: path
|
||||
make:
|
||||
description:
|
||||
- Use a specific make binary.
|
||||
type: path
|
||||
version_added: '0.2.0'
|
||||
jobs:
|
||||
description:
|
||||
- Set the number of make jobs to run concurrently.
|
||||
@@ -41,20 +55,6 @@ options:
|
||||
- This is not supported by all make implementations.
|
||||
type: int
|
||||
version_added: 2.0.0
|
||||
make:
|
||||
description:
|
||||
- Use a specific make binary.
|
||||
type: path
|
||||
version_added: '0.2.0'
|
||||
params:
|
||||
description:
|
||||
- Any extra parameters to pass to make.
|
||||
type: dict
|
||||
target:
|
||||
description:
|
||||
- The target to run.
|
||||
- Typically this would be something like C(install), C(test), or C(all).
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -83,42 +83,9 @@ EXAMPLES = r'''
|
||||
file: /some-project/Makefile
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
chdir:
|
||||
description:
|
||||
- The value of the module parameter I(chdir).
|
||||
type: str
|
||||
returned: success
|
||||
command:
|
||||
description:
|
||||
- The command built and executed by the module.
|
||||
type: str
|
||||
returned: success
|
||||
version_added: 6.5.0
|
||||
file:
|
||||
description:
|
||||
- The value of the module parameter I(file).
|
||||
type: str
|
||||
returned: success
|
||||
jobs:
|
||||
description:
|
||||
- The value of the module parameter I(jobs).
|
||||
type: int
|
||||
returned: success
|
||||
params:
|
||||
description:
|
||||
- The value of the module parameter I(params).
|
||||
type: dict
|
||||
returned: success
|
||||
target:
|
||||
description:
|
||||
- The value of the module parameter I(target).
|
||||
type: str
|
||||
returned: success
|
||||
'''
|
||||
RETURN = r'''# '''
|
||||
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
@@ -225,7 +192,6 @@ def main():
|
||||
chdir=module.params['chdir'],
|
||||
file=module.params['file'],
|
||||
jobs=module.params['jobs'],
|
||||
command=' '.join([shlex_quote(part) for part in base_command]),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -92,6 +92,17 @@ EXAMPLES = '''
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: false
|
||||
|
||||
- name: List current policy_profile and policies for a provider in ManageIQ
|
||||
community.general.manageiq_policies:
|
||||
state: list
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: false
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
|
||||
@@ -34,10 +34,7 @@ options:
|
||||
description:
|
||||
- C(absent) - tags should not exist.
|
||||
- C(present) - tags should exist.
|
||||
- >
|
||||
C(list) - list current tags.
|
||||
This state is deprecated and will be removed 8.0.0.
|
||||
Please use the module M(community.general.manageiq_tags_info) instead.
|
||||
- C(list) - list current tags.
|
||||
choices: ['absent', 'present', 'list']
|
||||
default: 'present'
|
||||
tags:
|
||||
@@ -113,6 +110,17 @@ EXAMPLES = '''
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: false
|
||||
|
||||
- name: List current tags for a provider in ManageIQ.
|
||||
community.general.manageiq_tags:
|
||||
state: list
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: false
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
@@ -154,13 +162,6 @@ def main():
|
||||
resource_name = module.params['resource_name']
|
||||
state = module.params['state']
|
||||
|
||||
if state == "list":
|
||||
module.deprecate(
|
||||
'The value "list" for "state" is deprecated. Please use community.general.manageiq_tags_info instead.',
|
||||
version='8.0.0',
|
||||
collection_name='community.general'
|
||||
)
|
||||
|
||||
# get the action and resource type
|
||||
action = actions[state]
|
||||
resource_type = manageiq_entities()[resource_type_key]
|
||||
|
||||
@@ -139,10 +139,7 @@ def reload_dns(args=None):
|
||||
# manifest themselves at this point so we need to ensure the user is
|
||||
# informed of the reason.
|
||||
retvals['failed'] = has_failed
|
||||
if response.status_code is not None:
|
||||
retvals['memset_api'] = response.json()
|
||||
else:
|
||||
retvals['stderr'] = response.stderr
|
||||
retvals['memset_api'] = response.json()
|
||||
retvals['msg'] = msg
|
||||
return retvals
|
||||
|
||||
|
||||
@@ -134,10 +134,7 @@ def get_facts(args=None):
|
||||
# informed of the reason.
|
||||
retvals['failed'] = has_failed
|
||||
retvals['msg'] = msg
|
||||
if response.status_code is not None:
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
else:
|
||||
retvals['stderr'] = "{0}" . format(response.stderr)
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
return retvals
|
||||
|
||||
# we don't want to return the same thing twice
|
||||
|
||||
@@ -259,10 +259,7 @@ def get_facts(args=None):
|
||||
# informed of the reason.
|
||||
retvals['failed'] = has_failed
|
||||
retvals['msg'] = msg
|
||||
if response.status_code is not None:
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
else:
|
||||
retvals['stderr'] = "{0}" . format(response.stderr)
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
return retvals
|
||||
|
||||
# we don't want to return the same thing twice
|
||||
|
||||
@@ -264,9 +264,6 @@ def create_or_delete(args=None):
|
||||
retvals['failed'] = _has_failed
|
||||
retvals['msg'] = _msg
|
||||
|
||||
if response.stderr is not None:
|
||||
retvals['stderr'] = response.stderr
|
||||
|
||||
return retvals
|
||||
|
||||
zone_exists, _msg, counter, _zone_id = get_zone_id(zone_name=args['name'], current_zones=response.json())
|
||||
|
||||
@@ -195,10 +195,7 @@ def create_or_delete_domain(args=None):
|
||||
# informed of the reason.
|
||||
retvals['failed'] = has_failed
|
||||
retvals['msg'] = msg
|
||||
if response.status_code is not None:
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
else:
|
||||
retvals['stderr'] = response.stderr
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
return retvals
|
||||
|
||||
zone_exists, msg, counter, zone_id = get_zone_id(zone_name=args['zone'], current_zones=response.json())
|
||||
|
||||
@@ -313,10 +313,7 @@ def create_or_delete(args=None):
|
||||
# informed of the reason.
|
||||
retvals['failed'] = _has_failed
|
||||
retvals['msg'] = msg
|
||||
if response.status_code is not None:
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
else:
|
||||
retvals['stderr'] = response.stderr
|
||||
retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
|
||||
return retvals
|
||||
|
||||
zone_exists, _msg, counter, zone_id = get_zone_id(zone_name=args['zone'], current_zones=response.json())
|
||||
|
||||
@@ -146,7 +146,8 @@ class MkSysB(ModuleHelper):
|
||||
def __run__(self):
|
||||
def process(rc, out, err):
|
||||
if rc != 0:
|
||||
self.do_raise("mksysb failed: {0}".format(out))
|
||||
self.do_raise("mksysb failed.")
|
||||
self.vars.msg = out
|
||||
|
||||
runner = CmdRunner(
|
||||
self.module,
|
||||
@@ -157,8 +158,6 @@ class MkSysB(ModuleHelper):
|
||||
'extended_attrs', 'backup_crypt_files', 'backup_dmapi_fs', 'new_image_data', 'combined_path'],
|
||||
output_process=process, check_mode_skip=True) as ctx:
|
||||
ctx.run(combined_path=[self.vars.storage_path, self.vars.name])
|
||||
if self.verbosity >= 4:
|
||||
self.vars.run_info = ctx.run_info
|
||||
|
||||
self.changed = True
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ options:
|
||||
- If C(present), adds module name to C(/etc/modules-load.d/) and params to C(/etc/modprobe.d/) so the module will be loaded on next reboot.
|
||||
- If C(absent), will comment out module name from C(/etc/modules-load.d/) and comment out params from C(/etc/modprobe.d/) so the module will not be
|
||||
loaded on next reboot.
|
||||
- If C(disabled), will not touch anything and leave C(/etc/modules-load.d/) and C(/etc/modprobe.d/) as it is.
|
||||
- If C(disabled), will not toch anything and leave C(/etc/modules-load.d/) and C(/etc/modprobe.d/) as it is.
|
||||
- Note that it is usually a better idea to rely on the automatic module loading by PCI IDs, USB IDs, DMI IDs or similar triggers encoded in the
|
||||
kernel modules themselves instead of configuration like this.
|
||||
- In fact, most modern kernel modules are prepared for automatic loading already.
|
||||
|
||||
@@ -280,32 +280,14 @@ def run_module():
|
||||
cursor = conn.cursor(as_dict=True)
|
||||
query_results_key = 'query_results_dict'
|
||||
|
||||
# Process the script into batches
|
||||
queries = []
|
||||
current_batch = []
|
||||
for statement in script.splitlines(keepends=True):
|
||||
# Ignore the Byte Order Mark, if found
|
||||
if statement.strip() == '\uFEFF':
|
||||
continue
|
||||
|
||||
# Assume each 'GO' is on its own line but may have leading/trailing whitespace
|
||||
# and be of mixed-case
|
||||
if statement.strip().upper() != 'GO':
|
||||
current_batch.append(statement)
|
||||
else:
|
||||
queries.append(''.join(current_batch))
|
||||
current_batch = []
|
||||
if len(current_batch) > 0:
|
||||
queries.append(''.join(current_batch))
|
||||
|
||||
queries = script.split('\nGO\n')
|
||||
result['changed'] = True
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
query_results = []
|
||||
for query in queries:
|
||||
# Catch and exit on any bad query errors
|
||||
try:
|
||||
try:
|
||||
for query in queries:
|
||||
cursor.execute(query, sql_params)
|
||||
qry_result = []
|
||||
rows = cursor.fetchall()
|
||||
@@ -313,17 +295,8 @@ def run_module():
|
||||
qry_result.append(rows)
|
||||
rows = cursor.fetchall()
|
||||
query_results.append(qry_result)
|
||||
except Exception as e:
|
||||
# We know we executed the statement so this error just means we have no resultset
|
||||
# which is ok (eg UPDATE/INSERT)
|
||||
if (
|
||||
type(e).__name__ == 'OperationalError' and
|
||||
str(e) == 'Statement not executed or executed statement has no resultset'
|
||||
):
|
||||
query_results.append([])
|
||||
else:
|
||||
error_msg = '%s: %s' % (type(e).__name__, str(e))
|
||||
module.fail_json(msg="query failed", query=query, error=error_msg, **result)
|
||||
except Exception as e:
|
||||
return module.fail_json(msg="query failed", query=query, error=str(e), **result)
|
||||
|
||||
# ensure that the result is json serializable
|
||||
qry_results = json.loads(json.dumps(query_results, default=clean_output))
|
||||
|
||||
@@ -61,6 +61,18 @@ options:
|
||||
description:
|
||||
- The name of the user/process that triggered this deployment
|
||||
required: false
|
||||
appname:
|
||||
type: str
|
||||
description:
|
||||
- Name of the application.
|
||||
- This option has been deprecated and will be removed in community.general 7.0.0. Please do not use.
|
||||
required: false
|
||||
environment:
|
||||
type: str
|
||||
description:
|
||||
- The environment for this deployment.
|
||||
- This option has been deprecated and will be removed community.general 7.0.0. Please do not use.
|
||||
required: false
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(false), SSL certificates will not be validated. This should only be used
|
||||
@@ -101,6 +113,8 @@ def main():
|
||||
description=dict(required=False),
|
||||
revision=dict(required=True),
|
||||
user=dict(required=False),
|
||||
appname=dict(required=False, removed_in_version='7.0.0', removed_from_collection='community.general'),
|
||||
environment=dict(required=False, removed_in_version='7.0.0', removed_from_collection='community.general'),
|
||||
validate_certs=dict(default=True, type='bool'),
|
||||
),
|
||||
required_one_of=[['app_name', 'application_id']],
|
||||
|
||||
@@ -63,14 +63,11 @@ options:
|
||||
- Type C(generic) is added in Ansible 2.5.
|
||||
- Type C(infiniband) is added in community.general 2.0.0.
|
||||
- Type C(gsm) is added in community.general 3.7.0.
|
||||
- Type C(macvlan) is added in community.general 6.6.0.
|
||||
- Type C(wireguard) is added in community.general 4.3.0.
|
||||
- Type C(vpn) is added in community.general 5.1.0.
|
||||
- Using C(bond-slave), C(bridge-slave) or C(team-slave) implies C(ethernet) connection type with corresponding I(slave_type) option.
|
||||
- If you want to control non-ethernet connection attached to C(bond), C(bridge) or C(team) consider using C(slave_type) option.
|
||||
type: str
|
||||
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, macvlan, sit, team, team-slave, vlan, vxlan,
|
||||
wifi, gsm, wireguard, vpn ]
|
||||
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, sit, team, team-slave, vlan, vxlan, wifi, gsm,
|
||||
wireguard, vpn ]
|
||||
mode:
|
||||
description:
|
||||
- This is the type of device or network connection that you wish to create for a bond or bridge.
|
||||
@@ -83,16 +80,9 @@ options:
|
||||
type: str
|
||||
choices: [ datagram, connected ]
|
||||
version_added: 5.8.0
|
||||
slave_type:
|
||||
description:
|
||||
- Type of the device of this slave's master connection (for example C(bond)).
|
||||
type: str
|
||||
choices: [ 'bond', 'bridge', 'team' ]
|
||||
version_added: 7.0.0
|
||||
master:
|
||||
description:
|
||||
- Master <master (ifname, or connection UUID or conn_name) of bridge, team, bond master connection profile.
|
||||
- Mandatory if I(slave_type) is defined.
|
||||
type: str
|
||||
ip4:
|
||||
description:
|
||||
@@ -207,7 +197,6 @@ options:
|
||||
may_fail4:
|
||||
description:
|
||||
- If you need I(ip4) configured before C(network-online.target) is reached, set this option to C(false).
|
||||
- This option applies when C(method4) is not C(disabled).
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 3.3.0
|
||||
@@ -313,9 +302,8 @@ options:
|
||||
addr_gen_mode6:
|
||||
description:
|
||||
- Configure method for creating the address for use with IPv6 Stateless Address Autoconfiguration.
|
||||
- C(default) and C(deafult-or-eui64) have been added in community.general 6.5.0.
|
||||
type: str
|
||||
choices: [default, default-or-eui64, eui64, stable-privacy]
|
||||
choices: [eui64, stable-privacy]
|
||||
version_added: 4.2.0
|
||||
mtu:
|
||||
description:
|
||||
@@ -406,9 +394,9 @@ options:
|
||||
description:
|
||||
- This is only used with 'bridge-slave' - 'hairpin mode' for the slave, which allows frames to be sent back out through the slave the
|
||||
frame was received on.
|
||||
- The default change to C(false) in community.general 7.0.0. It used to be C(true) before.
|
||||
- The default value is C(true), but that is being deprecated
|
||||
and it will be changed to C(false) in community.general 7.0.0.
|
||||
type: bool
|
||||
default: false
|
||||
runner:
|
||||
description:
|
||||
- This is the type of device or network connection that you wish to create for a team.
|
||||
@@ -423,14 +411,6 @@ options:
|
||||
type: str
|
||||
choices: [ same_all, by_active, only_active ]
|
||||
version_added: 3.4.0
|
||||
runner_fast_rate:
|
||||
description:
|
||||
- Option specifies the rate at which our link partner is asked to transmit LACPDU
|
||||
packets. If this is C(true) then packets will be sent once per second. Otherwise they
|
||||
will be sent every 30 seconds.
|
||||
- Only allowed for C(lacp) runner.
|
||||
type: bool
|
||||
version_added: 6.5.0
|
||||
vlanid:
|
||||
description:
|
||||
- This is only used with VLAN - VLAN ID in range <0-4095>.
|
||||
@@ -889,38 +869,6 @@ options:
|
||||
- The username used to authenticate with the network, if required.
|
||||
- Many providers do not require a username, or accept any username.
|
||||
- But if a username is required, it is specified here.
|
||||
macvlan:
|
||||
description:
|
||||
- The configuration of the MAC VLAN connection.
|
||||
- Note the list of suboption attributes may vary depending on which version of NetworkManager/nmcli is installed on the host.
|
||||
- 'An up-to-date list of supported attributes can be found here:
|
||||
U(https://networkmanager.dev/docs/api/latest/settings-macvlan.html).'
|
||||
type: dict
|
||||
version_added: 6.6.0
|
||||
suboptions:
|
||||
mode:
|
||||
description:
|
||||
- The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device.
|
||||
- 'Following choices are allowed: C(1) B(vepa), C(2) B(bridge), C(3) B(private), C(4) B(passthru)
|
||||
and C(5) B(source)'
|
||||
type: int
|
||||
choices: [ 1, 2, 3, 4, 5 ]
|
||||
required: true
|
||||
parent:
|
||||
description:
|
||||
- If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should
|
||||
be created. If this property is not specified, the connection must contain an "802-3-ethernet" setting with a
|
||||
"mac-address" property.
|
||||
type: str
|
||||
required: true
|
||||
promiscuous:
|
||||
description:
|
||||
- Whether the interface should be put in promiscuous mode.
|
||||
type: bool
|
||||
tap:
|
||||
description:
|
||||
- Whether the interface should be a MACVTAP.
|
||||
type: bool
|
||||
wireguard:
|
||||
description:
|
||||
- The configuration of the Wireguard connection.
|
||||
@@ -1020,6 +968,7 @@ options:
|
||||
- Enable or disable IPSec tunnel to L2TP host.
|
||||
- This option is need when C(service-type) is C(org.freedesktop.NetworkManager.l2tp).
|
||||
type: bool
|
||||
choices: [ yes, no ]
|
||||
ipsec-psk:
|
||||
description:
|
||||
- The pre-shared key in base64 encoding.
|
||||
@@ -1399,17 +1348,6 @@ EXAMPLES = r'''
|
||||
autoconnect: true
|
||||
state: present
|
||||
|
||||
- name: Create a macvlan connection
|
||||
community.general.nmcli:
|
||||
type: macvlan
|
||||
conn_name: my-macvlan-connection
|
||||
ifname: mymacvlan0
|
||||
macvlan:
|
||||
mode: 2
|
||||
parent: eth1
|
||||
autoconnect: true
|
||||
state: present
|
||||
|
||||
- name: Create a wireguard connection
|
||||
community.general.nmcli:
|
||||
type: wireguard
|
||||
@@ -1438,39 +1376,6 @@ EXAMPLES = r'''
|
||||
autoconnect: false
|
||||
state: present
|
||||
|
||||
## Creating bond attached to bridge example
|
||||
- name: Create bond attached to bridge
|
||||
community.general.nmcli:
|
||||
type: bond
|
||||
conn_name: bond0
|
||||
slave_type: bridge
|
||||
master: br0
|
||||
state: present
|
||||
|
||||
- name: Create master bridge
|
||||
community.general.nmcli:
|
||||
type: bridge
|
||||
conn_name: br0
|
||||
method4: disabled
|
||||
method6: disabled
|
||||
state: present
|
||||
|
||||
## Creating vlan connection attached to bridge
|
||||
- name: Create master bridge
|
||||
community.general.nmcli:
|
||||
type: bridge
|
||||
conn_name: br0
|
||||
state: present
|
||||
|
||||
- name: Create VLAN 5
|
||||
community.general.nmcli:
|
||||
type: vlan
|
||||
conn_name: eth0.5
|
||||
slave_type: bridge
|
||||
master: br0
|
||||
vlandev: eth0
|
||||
vlanid: 5
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = r"""#
|
||||
@@ -1517,7 +1422,6 @@ 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.slave_type = module.params['slave_type']
|
||||
self.master = module.params['master']
|
||||
self.ifname = module.params['ifname']
|
||||
self.type = module.params['type']
|
||||
@@ -1562,12 +1466,12 @@ class Nmcli(object):
|
||||
self.hellotime = module.params['hellotime']
|
||||
self.maxage = module.params['maxage']
|
||||
self.ageingtime = module.params['ageingtime']
|
||||
self.hairpin = module.params['hairpin']
|
||||
# hairpin should be back to normal in 7.0.0
|
||||
self._hairpin = module.params['hairpin']
|
||||
self.path_cost = module.params['path_cost']
|
||||
self.mac = module.params['mac']
|
||||
self.runner = module.params['runner']
|
||||
self.runner_hwaddr_policy = module.params['runner_hwaddr_policy']
|
||||
self.runner_fast_rate = module.params['runner_fast_rate']
|
||||
self.vlanid = module.params['vlanid']
|
||||
self.vlandev = module.params['vlandev']
|
||||
self.flags = module.params['flags']
|
||||
@@ -1588,14 +1492,13 @@ class Nmcli(object):
|
||||
self.wifi = module.params['wifi']
|
||||
self.wifi_sec = module.params['wifi_sec']
|
||||
self.gsm = module.params['gsm']
|
||||
self.macvlan = module.params['macvlan']
|
||||
self.wireguard = module.params['wireguard']
|
||||
self.vpn = module.params['vpn']
|
||||
self.transport_mode = module.params['transport_mode']
|
||||
|
||||
if self.method4:
|
||||
self.ipv4_method = self.method4
|
||||
elif self.type in ('dummy', 'macvlan', 'wireguard') and not self.ip4:
|
||||
elif self.type in ('dummy', 'wireguard') and not self.ip4:
|
||||
self.ipv4_method = 'disabled'
|
||||
elif self.ip4:
|
||||
self.ipv4_method = 'manual'
|
||||
@@ -1604,7 +1507,7 @@ class Nmcli(object):
|
||||
|
||||
if self.method6:
|
||||
self.ipv6_method = self.method6
|
||||
elif self.type in ('dummy', 'macvlan', 'wireguard') and not self.ip6:
|
||||
elif self.type in ('dummy', 'wireguard') and not self.ip6:
|
||||
self.ipv6_method = 'disabled'
|
||||
elif self.ip6:
|
||||
self.ipv6_method = 'manual'
|
||||
@@ -1613,13 +1516,17 @@ class Nmcli(object):
|
||||
|
||||
self.edit_commands = []
|
||||
|
||||
self.extra_options_validation()
|
||||
|
||||
def extra_options_validation(self):
|
||||
""" Additional validation of options set passed to module that cannot be implemented in module's argspecs. """
|
||||
if self.type not in ("bridge-slave", "team-slave", "bond-slave"):
|
||||
if self.master is None and self.slave_type is not None:
|
||||
self.module.fail_json(msg="'master' option is required when 'slave_type' is specified.")
|
||||
@property
|
||||
def hairpin(self):
|
||||
if self._hairpin is None:
|
||||
self.module.deprecate(
|
||||
"Parameter 'hairpin' default value will change from true to false in community.general 7.0.0. "
|
||||
"Set the value explicitly to suppress this warning.",
|
||||
version='7.0.0', collection_name='community.general',
|
||||
)
|
||||
# Should be False in 7.0.0 but then that should be in argument_specs
|
||||
self._hairpin = True
|
||||
return self._hairpin
|
||||
|
||||
def execute_command(self, cmd, use_unsafe_shell=False, data=None):
|
||||
if isinstance(cmd, list):
|
||||
@@ -1669,10 +1576,6 @@ class Nmcli(object):
|
||||
'ipv6.ip6-privacy': self.ip_privacy6,
|
||||
'ipv6.addr-gen-mode': self.addr_gen_mode6
|
||||
})
|
||||
# when 'method' is disabled the 'may_fail' no make sense but accepted by nmcli with keeping 'yes'
|
||||
# force ignoring to save idempotency
|
||||
if self.ipv4_method and self.ipv4_method != 'disabled':
|
||||
options.update({'ipv4.may-fail': self.may_fail4})
|
||||
|
||||
# Layer 2 options.
|
||||
if self.mac:
|
||||
@@ -1685,7 +1588,6 @@ class Nmcli(object):
|
||||
if self.slave_conn_type:
|
||||
options.update({
|
||||
'connection.master': self.master,
|
||||
'connection.slave-type': self.slave_type,
|
||||
})
|
||||
|
||||
# Options specific to a connection type.
|
||||
@@ -1701,17 +1603,9 @@ class Nmcli(object):
|
||||
'xmit_hash_policy': self.xmit_hash_policy,
|
||||
})
|
||||
elif self.type == 'bond-slave':
|
||||
if self.slave_type and self.slave_type != 'bond':
|
||||
self.module.fail_json(msg="Connection type '%s' cannot be combined with '%s' slave-type. "
|
||||
"Allowed slave-type for '%s' is 'bond'."
|
||||
% (self.type, self.slave_type, self.type)
|
||||
)
|
||||
if not self.slave_type:
|
||||
self.module.warn("Connection 'slave-type' property automatically set to 'bond' "
|
||||
"because of using 'bond-slave' connection type.")
|
||||
options.update({
|
||||
'connection.slave-type': 'bond',
|
||||
})
|
||||
options.update({
|
||||
'connection.slave-type': 'bond',
|
||||
})
|
||||
elif self.type == 'bridge':
|
||||
options.update({
|
||||
'bridge.ageing-time': self.ageingtime,
|
||||
@@ -1721,50 +1615,22 @@ class Nmcli(object):
|
||||
'bridge.priority': self.priority,
|
||||
'bridge.stp': self.stp,
|
||||
})
|
||||
# priority make sense when stp enabed, otherwise nmcli keeps bridge-priority to 32768 regrdless of input.
|
||||
# force ignoring to save idempotency
|
||||
if self.stp:
|
||||
options.update({'bridge.priority': self.priority})
|
||||
elif self.type == 'team':
|
||||
options.update({
|
||||
'team.runner': self.runner,
|
||||
'team.runner-hwaddr-policy': self.runner_hwaddr_policy,
|
||||
})
|
||||
if self.runner_fast_rate is not None:
|
||||
options.update({
|
||||
'team.runner-fast-rate': self.runner_fast_rate,
|
||||
})
|
||||
elif self.type == 'bridge-slave':
|
||||
if self.slave_type and self.slave_type != 'bridge':
|
||||
self.module.fail_json(msg="Connection type '%s' cannot be combined with '%s' slave-type. "
|
||||
"Allowed slave-type for '%s' is 'bridge'."
|
||||
% (self.type, self.slave_type, self.type)
|
||||
)
|
||||
if not self.slave_type:
|
||||
self.module.warn("Connection 'slave-type' property automatically set to 'bridge' "
|
||||
"because of using 'bridge-slave' connection type.")
|
||||
options.update({'connection.slave-type': 'bridge'})
|
||||
self.module.warn(
|
||||
"Connection type as 'bridge-slave' implies 'ethernet' connection with 'bridge' slave-type. "
|
||||
"Consider using slave_type='bridge' with necessary type."
|
||||
)
|
||||
options.update({
|
||||
'connection.slave-type': 'bridge',
|
||||
'bridge-port.path-cost': self.path_cost,
|
||||
'bridge-port.hairpin-mode': self.hairpin,
|
||||
'bridge-port.priority': self.slavepriority,
|
||||
})
|
||||
elif self.type == 'team-slave':
|
||||
if self.slave_type and self.slave_type != 'team':
|
||||
self.module.fail_json(msg="Connection type '%s' cannot be combined with '%s' slave-type. "
|
||||
"Allowed slave-type for '%s' is 'team'."
|
||||
% (self.type, self.slave_type, self.type)
|
||||
)
|
||||
if not self.slave_type:
|
||||
self.module.warn("Connection 'slave-type' property automatically set to 'team' "
|
||||
"because of using 'team-slave' connection type.")
|
||||
options.update({
|
||||
'connection.slave-type': 'team',
|
||||
})
|
||||
options.update({
|
||||
'connection.slave-type': 'team',
|
||||
})
|
||||
elif self.tunnel_conn_type:
|
||||
options.update({
|
||||
'ip-tunnel.local': self.ip_tunnel_local,
|
||||
@@ -1812,14 +1678,6 @@ class Nmcli(object):
|
||||
options.update({
|
||||
'gsm.%s' % name: value,
|
||||
})
|
||||
elif self.type == 'macvlan':
|
||||
if self.macvlan:
|
||||
for name, value in self.macvlan.items():
|
||||
options.update({
|
||||
'macvlan.%s' % name: value,
|
||||
})
|
||||
elif self.state == 'present':
|
||||
raise NmcliModuleError('type is macvlan but all of the following are missing: macvlan')
|
||||
elif self.type == 'wireguard':
|
||||
if self.wireguard:
|
||||
for name, value in self.wireguard.items():
|
||||
@@ -1897,7 +1755,6 @@ class Nmcli(object):
|
||||
'wifi',
|
||||
'802-11-wireless',
|
||||
'gsm',
|
||||
'macvlan',
|
||||
'wireguard',
|
||||
'vpn',
|
||||
)
|
||||
@@ -1915,7 +1772,6 @@ class Nmcli(object):
|
||||
'dummy',
|
||||
'ethernet',
|
||||
'team-slave',
|
||||
'vlan',
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -1949,12 +1805,6 @@ class Nmcli(object):
|
||||
@property
|
||||
def slave_conn_type(self):
|
||||
return self.type in (
|
||||
'ethernet',
|
||||
'bridge',
|
||||
'bond',
|
||||
'vlan',
|
||||
'team',
|
||||
'wifi',
|
||||
'bond-slave',
|
||||
'bridge-slave',
|
||||
'team-slave',
|
||||
@@ -2029,8 +1879,7 @@ class Nmcli(object):
|
||||
'ipv4.may-fail',
|
||||
'ipv6.ignore-auto-dns',
|
||||
'ipv6.ignore-auto-routes',
|
||||
'802-11-wireless.hidden',
|
||||
'team.runner-fast-rate'):
|
||||
'802-11-wireless.hidden'):
|
||||
return bool
|
||||
elif setting in ('ipv4.addresses',
|
||||
'ipv6.addresses',
|
||||
@@ -2346,7 +2195,6 @@ def main():
|
||||
state=dict(type='str', required=True, choices=['absent', 'present']),
|
||||
conn_name=dict(type='str', required=True),
|
||||
master=dict(type='str'),
|
||||
slave_type=dict(type='str', choices=['bond', 'bridge', 'team']),
|
||||
ifname=dict(type='str'),
|
||||
type=dict(type='str',
|
||||
choices=[
|
||||
@@ -2367,7 +2215,6 @@ def main():
|
||||
'vxlan',
|
||||
'wifi',
|
||||
'gsm',
|
||||
'macvlan',
|
||||
'wireguard',
|
||||
'vpn',
|
||||
]),
|
||||
@@ -2417,7 +2264,7 @@ def main():
|
||||
route_metric6=dict(type='int'),
|
||||
method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared', 'disabled']),
|
||||
ip_privacy6=dict(type='str', choices=['disabled', 'prefer-public-addr', 'prefer-temp-addr', 'unknown']),
|
||||
addr_gen_mode6=dict(type='str', choices=['default', 'default-or-eui64', 'eui64', 'stable-privacy']),
|
||||
addr_gen_mode6=dict(type='str', choices=['eui64', 'stable-privacy']),
|
||||
# Bond Specific vars
|
||||
mode=dict(type='str', default='balance-rr',
|
||||
choices=['802.3ad', 'active-backup', 'balance-alb', 'balance-rr', 'balance-tlb', 'balance-xor', 'broadcast']),
|
||||
@@ -2440,15 +2287,13 @@ def main():
|
||||
hellotime=dict(type='int', default=2),
|
||||
maxage=dict(type='int', default=20),
|
||||
ageingtime=dict(type='int', default=300),
|
||||
hairpin=dict(type='bool', default=False),
|
||||
hairpin=dict(type='bool'),
|
||||
path_cost=dict(type='int', default=100),
|
||||
# team specific vars
|
||||
runner=dict(type='str', default='roundrobin',
|
||||
choices=['broadcast', 'roundrobin', 'activebackup', 'loadbalance', 'lacp']),
|
||||
# team active-backup runner specific options
|
||||
runner_hwaddr_policy=dict(type='str', choices=['same_all', 'by_active', 'only_active']),
|
||||
# team lacp runner specific options
|
||||
runner_fast_rate=dict(type='bool'),
|
||||
# vlan specific vars
|
||||
vlanid=dict(type='int'),
|
||||
vlandev=dict(type='str'),
|
||||
@@ -2471,11 +2316,6 @@ def main():
|
||||
wifi=dict(type='dict'),
|
||||
wifi_sec=dict(type='dict', no_log=True),
|
||||
gsm=dict(type='dict'),
|
||||
macvlan=dict(type='dict', options=dict(
|
||||
mode=dict(type='int', choices=[1, 2, 3, 4, 5], required=True),
|
||||
parent=dict(type='str', required=True),
|
||||
promiscuous=dict(type='bool'),
|
||||
tap=dict(type='bool'))),
|
||||
wireguard=dict(type='dict'),
|
||||
vpn=dict(type='dict'),
|
||||
transport_mode=dict(type='str', choices=['datagram', 'connected']),
|
||||
@@ -2500,10 +2340,8 @@ def main():
|
||||
if nmcli.type == "team":
|
||||
if nmcli.runner_hwaddr_policy and not nmcli.runner == "activebackup":
|
||||
nmcli.module.fail_json(msg="Runner-hwaddr-policy is only allowed for runner activebackup")
|
||||
if nmcli.runner_fast_rate is not None and nmcli.runner != "lacp":
|
||||
nmcli.module.fail_json(msg="runner-fast-rate is only allowed for runner lacp")
|
||||
# team-slave checks
|
||||
if nmcli.type == 'team-slave' or nmcli.slave_type == 'team':
|
||||
if nmcli.type == 'team-slave':
|
||||
if nmcli.master is None:
|
||||
nmcli.module.fail_json(msg="Please specify a name for the master when type is %s" % nmcli.type)
|
||||
if nmcli.ifname is None:
|
||||
|
||||
@@ -13,8 +13,7 @@ module: office_365_connector_card
|
||||
short_description: Use webhooks to create Connector Card messages within an Office 365 group
|
||||
description:
|
||||
- Creates Connector Card messages through
|
||||
Office 365 Connectors
|
||||
U(https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-reference#connector-card-for-microsoft-365-groups).
|
||||
- Office 365 Connectors U(https://dev.outlook.com/Connectors)
|
||||
author: "Marc Sensenich (@marc-sensenich)"
|
||||
notes:
|
||||
- This module is not idempotent, therefore if the same task is run twice
|
||||
@@ -63,7 +62,7 @@ options:
|
||||
elements: dict
|
||||
description:
|
||||
- Contains a list of sections to display in the card.
|
||||
- For more information see U(https://learn.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#section-fields).
|
||||
- For more information see https://dev.outlook.com/Connectors/reference.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
|
||||
@@ -152,11 +152,7 @@ def execute_command(cmd, module):
|
||||
# This makes run_command() use shell=False which we need to not cause shell
|
||||
# expansion of special characters like '*'.
|
||||
cmd_args = shlex.split(cmd)
|
||||
|
||||
# We set TERM to 'dumb' to keep pkg_add happy if the machine running
|
||||
# ansible is using a TERM that the managed machine does not know about,
|
||||
# e.g.: "No progress meter: failed termcap lookup on xterm-kitty".
|
||||
return module.run_command(cmd_args, environ_update={'TERM': 'dumb'})
|
||||
return module.run_command(cmd_args)
|
||||
|
||||
|
||||
# Function used to find out if a package is currently installed.
|
||||
|
||||
@@ -148,11 +148,6 @@ class Opkg(StateModuleHelper):
|
||||
),
|
||||
)
|
||||
|
||||
if self.vars.update_cache:
|
||||
rc, dummy, dummy = self.runner("update_cache").run()
|
||||
if rc != 0:
|
||||
self.do_raise("could not update package db")
|
||||
|
||||
@staticmethod
|
||||
def split_name_and_version(package):
|
||||
""" Split the name and the version when using the NAME=VERSION syntax """
|
||||
@@ -169,6 +164,10 @@ class Opkg(StateModuleHelper):
|
||||
return want_installed == has_package
|
||||
|
||||
def state_present(self):
|
||||
if self.vars.update_cache:
|
||||
dummy, rc, dummy = self.runner("update_cache").run()
|
||||
if rc != 0:
|
||||
self.do_raise("could not update package db")
|
||||
with self.runner("state force package") as ctx:
|
||||
for package in self.vars.name:
|
||||
pkg_name, pkg_version = self.split_name_and_version(package)
|
||||
@@ -177,14 +176,16 @@ class Opkg(StateModuleHelper):
|
||||
if not self._package_in_desired_state(pkg_name, want_installed=True, version=pkg_version):
|
||||
self.do_raise("failed to install %s" % package)
|
||||
self.vars.install_c += 1
|
||||
if self.verbosity >= 4:
|
||||
self.vars.run_info = ctx.run_info
|
||||
if self.vars.install_c > 0:
|
||||
self.vars.msg = "installed %s package(s)" % (self.vars.install_c)
|
||||
else:
|
||||
self.vars.msg = "package(s) already present"
|
||||
|
||||
def state_absent(self):
|
||||
if self.vars.update_cache:
|
||||
dummy, rc, dummy = self.runner("update_cache").run()
|
||||
if rc != 0:
|
||||
self.do_raise("could not update package db")
|
||||
with self.runner("state force package") as ctx:
|
||||
for package in self.vars.name:
|
||||
package, dummy = self.split_name_and_version(package)
|
||||
@@ -193,8 +194,6 @@ class Opkg(StateModuleHelper):
|
||||
if not self._package_in_desired_state(package, want_installed=False):
|
||||
self.do_raise("failed to remove %s" % package)
|
||||
self.vars.remove_c += 1
|
||||
if self.verbosity >= 4:
|
||||
self.vars.run_info = ctx.run_info
|
||||
if self.vars.remove_c > 0:
|
||||
self.vars.msg = "removed %s package(s)" % (self.vars.remove_c)
|
||||
else:
|
||||
|
||||
@@ -20,11 +20,10 @@ description:
|
||||
command line tool. For a full description of the fields and the options
|
||||
check the GNU parted manual.
|
||||
requirements:
|
||||
- This module requires C(parted) version 1.8.3 and above.
|
||||
- Option I(align) (except C(undefined)) requires C(parted) 2.1 or above.
|
||||
- If the version of C(parted) is below 3.1, it requires a Linux version running
|
||||
the C(sysfs) file system C(/sys/).
|
||||
- Requires the C(resizepart) command when using the I(resize) parameter.
|
||||
- This module requires parted version 1.8.3 and above
|
||||
- align option (except 'undefined') requires parted 2.1 and above
|
||||
- If the version of parted is below 3.1, it requires a Linux version running
|
||||
the sysfs file system C(/sys/).
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
@@ -34,70 +33,66 @@ attributes:
|
||||
support: none
|
||||
options:
|
||||
device:
|
||||
description:
|
||||
- The block device (disk) where to operate.
|
||||
- Regular files can also be partitioned, but it is recommended to create a
|
||||
loopback device using C(losetup) to easily access its partitions.
|
||||
description: The block device (disk) where to operate.
|
||||
type: str
|
||||
required: true
|
||||
align:
|
||||
description:
|
||||
- Set alignment for newly created partitions. Use C(undefined) for parted default aligment.
|
||||
description: Set alignment for newly created partitions. Use 'undefined' for parted default aligment.
|
||||
type: str
|
||||
choices: [ cylinder, minimal, none, optimal, undefined ]
|
||||
default: optimal
|
||||
number:
|
||||
description:
|
||||
- The partition number being affected.
|
||||
- Required when performing any action on the disk, except fetching information.
|
||||
- The number of the partition to work with or the number of the partition
|
||||
that will be created.
|
||||
- Required when performing any action on the disk, except fetching information.
|
||||
type: int
|
||||
unit:
|
||||
description:
|
||||
- Selects the current default unit that Parted will use to display
|
||||
locations and capacities on the disk and to interpret those given by the
|
||||
user if they are not suffixed by an unit.
|
||||
- When fetching information about a disk, it is recommended to always specify a unit.
|
||||
- Selects the current default unit that Parted will use to display
|
||||
locations and capacities on the disk and to interpret those given by the
|
||||
user if they are not suffixed by an unit.
|
||||
- When fetching information about a disk, it is always recommended to specify a unit.
|
||||
type: str
|
||||
choices: [ s, B, KB, KiB, MB, MiB, GB, GiB, TB, TiB, '%', cyl, chs, compact ]
|
||||
default: KiB
|
||||
label:
|
||||
description:
|
||||
- Disk label type or partition table to use.
|
||||
- If I(device) already contains a different label, it will be changed to I(label)
|
||||
and any previous partitions will be lost.
|
||||
- A I(name) must be specified for a C(gpt) partition table.
|
||||
- Disk label type to use.
|
||||
- If C(device) already contains different label, it will be changed to C(label) and any previous partitions will be lost.
|
||||
type: str
|
||||
choices: [ aix, amiga, bsd, dvh, gpt, loop, mac, msdos, pc98, sun ]
|
||||
default: msdos
|
||||
part_type:
|
||||
description:
|
||||
- May be specified only with I(label=msdos) or I(label=dvh).
|
||||
- Neither I(part_type) nor I(name) may be used with I(label=sun).
|
||||
- May be specified only with 'msdos' or 'dvh' partition tables.
|
||||
- A C(name) must be specified for a 'gpt' partition table.
|
||||
- Neither C(part_type) nor C(name) may be used with a 'sun' partition table.
|
||||
type: str
|
||||
choices: [ extended, logical, primary ]
|
||||
default: primary
|
||||
part_start:
|
||||
description:
|
||||
- Where the partition will start as offset from the beginning of the disk,
|
||||
that is, the "distance" from the start of the disk. Negative numbers
|
||||
specify distance from the end of the disk.
|
||||
- The distance can be specified with all the units supported by parted
|
||||
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
|
||||
- Using negative values may require setting of I(fs_type) (see notes).
|
||||
- Where the partition will start as offset from the beginning of the disk,
|
||||
that is, the "distance" from the start of the disk. Negative numbers
|
||||
specify distance from the end of the disk.
|
||||
- The distance can be specified with all the units supported by parted
|
||||
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
|
||||
- Using negative values may require setting of C(fs_type) (see notes).
|
||||
type: str
|
||||
default: 0%
|
||||
part_end:
|
||||
description:
|
||||
- Where the partition will end as offset from the beginning of the disk,
|
||||
that is, the "distance" from the start of the disk. Negative numbers
|
||||
specify distance from the end of the disk.
|
||||
- The distance can be specified with all the units supported by parted
|
||||
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
|
||||
- Where the partition will end as offset from the beginning of the disk,
|
||||
that is, the "distance" from the start of the disk. Negative numbers
|
||||
specify distance from the end of the disk.
|
||||
- The distance can be specified with all the units supported by parted
|
||||
(except compat) and it is case sensitive, e.g. C(10GiB), C(15%).
|
||||
type: str
|
||||
default: 100%
|
||||
name:
|
||||
description:
|
||||
- Sets the name for the partition number (GPT, Mac, MIPS and PC98 only).
|
||||
- Sets the name for the partition number (GPT, Mac, MIPS and PC98 only).
|
||||
type: str
|
||||
flags:
|
||||
description: A list of the flags that has to be set on the partition.
|
||||
@@ -105,15 +100,15 @@ options:
|
||||
elements: str
|
||||
state:
|
||||
description:
|
||||
- Whether to create or delete a partition.
|
||||
- If set to C(info) the module will only return the device information.
|
||||
- Whether to create or delete a partition.
|
||||
- If set to C(info) the module will only return the device information.
|
||||
type: str
|
||||
choices: [ absent, present, info ]
|
||||
default: info
|
||||
fs_type:
|
||||
description:
|
||||
- If specified and the partition does not exist, will set filesystem type to given partition.
|
||||
- Parameter optional, but see notes below about negative I(part_start) values.
|
||||
- If specified and the partition does not exist, will set filesystem type to given partition.
|
||||
- Parameter optional, but see notes below about negative C(part_start) values.
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
resize:
|
||||
@@ -128,9 +123,9 @@ notes:
|
||||
installed on the system is before version 3.1, the module queries the kernel
|
||||
through C(/sys/) to obtain disk information. In this case the units CHS and
|
||||
CYL are not supported.
|
||||
- Negative I(part_start) start values were rejected if I(fs_type) was not given.
|
||||
This bug was fixed in parted 3.2.153. If you want to use negative I(part_start),
|
||||
specify I(fs_type) as well or make sure your system contains newer parted.
|
||||
- Negative C(part_start) start values were rejected if C(fs_type) was not given.
|
||||
This bug was fixed in parted 3.2.153. If you want to use negative C(part_start),
|
||||
specify C(fs_type) as well or make sure your system contains newer parted.
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
|
||||
@@ -47,17 +47,10 @@ options:
|
||||
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 I(source) instead of I(name).
|
||||
install_apps:
|
||||
description:
|
||||
- Add apps from the injected packages.
|
||||
- Only used when I(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
install_deps:
|
||||
description:
|
||||
- Include applications of dependent packages.
|
||||
- Only used when I(state=install), I(state=latest), or I(state=inject).
|
||||
- Only used when I(state=install) or I(state=upgrade).
|
||||
type: bool
|
||||
default: false
|
||||
inject_packages:
|
||||
@@ -69,33 +62,25 @@ options:
|
||||
force:
|
||||
description:
|
||||
- Force modification of the application's virtual environment. See C(pipx) for details.
|
||||
- Only used when I(state=install), I(state=upgrade), I(state=upgrade_all), I(state=latest), or I(state=inject).
|
||||
- Only used when I(state=install), I(state=upgrade), I(state=upgrade_all), or I(state=inject).
|
||||
type: bool
|
||||
default: false
|
||||
include_injected:
|
||||
description:
|
||||
- Upgrade the injected packages along with the application.
|
||||
- Only used when I(state=upgrade), I(state=upgrade_all), or I(state=latest).
|
||||
- This is used with I(state=upgrade) and I(state=latest) since community.general 6.6.0.
|
||||
- Only used when I(state=upgrade) or I(state=upgrade_all).
|
||||
type: bool
|
||||
default: false
|
||||
index_url:
|
||||
description:
|
||||
- Base URL of Python Package Index.
|
||||
- Only used when I(state=install), I(state=upgrade), I(state=latest), or I(state=inject).
|
||||
- Only used when I(state=install), I(state=upgrade), or I(state=inject).
|
||||
type: str
|
||||
python:
|
||||
description:
|
||||
- Python version to be used when creating the application virtual environment. Must be 3.6+.
|
||||
- Only used when I(state=install), I(state=latest), I(state=reinstall), or I(state=reinstall_all).
|
||||
- Only used when I(state=install), I(state=reinstall), or I(state=reinstall_all).
|
||||
type: str
|
||||
system_site_packages:
|
||||
description:
|
||||
- Give application virtual environment access to the system site-packages directory.
|
||||
- Only used when I(state=install) or I(state=latest).
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.6.0
|
||||
executable:
|
||||
description:
|
||||
- Path to the C(pipx) installed in the system.
|
||||
@@ -120,7 +105,6 @@ notes:
|
||||
- >
|
||||
This module will honor C(pipx) environment variables such as but not limited to C(PIPX_HOME) and C(PIPX_BIN_DIR)
|
||||
passed using the R(environment Ansible keyword, playbooks_environment).
|
||||
- This module requires C(pipx) version 0.16.2.1 or above.
|
||||
- Please note that C(pipx) requires Python 3.6 or above.
|
||||
- >
|
||||
This first implementation does not verify whether a specified version constraint has been installed or not.
|
||||
@@ -177,14 +161,12 @@ class PipX(StateModuleHelper):
|
||||
'inject', 'upgrade', 'upgrade_all', 'reinstall', 'reinstall_all', 'latest']),
|
||||
name=dict(type='str'),
|
||||
source=dict(type='str'),
|
||||
install_apps=dict(type='bool', default=False),
|
||||
install_deps=dict(type='bool', default=False),
|
||||
inject_packages=dict(type='list', elements='str'),
|
||||
force=dict(type='bool', default=False),
|
||||
include_injected=dict(type='bool', default=False),
|
||||
index_url=dict(type='str'),
|
||||
python=dict(type='str'),
|
||||
system_site_packages=dict(type='bool', default=False),
|
||||
executable=dict(type='path'),
|
||||
editable=dict(type='bool', default=False),
|
||||
pip_args=dict(type='str'),
|
||||
@@ -252,7 +234,7 @@ class PipX(StateModuleHelper):
|
||||
def state_install(self):
|
||||
if not self.vars.application or self.vars.force:
|
||||
self.changed = True
|
||||
with self.runner('state index_url install_deps force python system_site_packages editable pip_args name_source', check_mode_skip=True) as ctx:
|
||||
with self.runner('state index_url install_deps force python editable pip_args name_source', check_mode_skip=True) as ctx:
|
||||
ctx.run(name_source=[self.vars.name, self.vars.source])
|
||||
self._capture_results(ctx)
|
||||
|
||||
@@ -264,7 +246,7 @@ class PipX(StateModuleHelper):
|
||||
if self.vars.force:
|
||||
self.changed = True
|
||||
|
||||
with self.runner('state include_injected index_url force editable pip_args name', check_mode_skip=True) as ctx:
|
||||
with self.runner('state index_url install_deps force editable pip_args name', check_mode_skip=True) as ctx:
|
||||
ctx.run()
|
||||
self._capture_results(ctx)
|
||||
|
||||
@@ -289,7 +271,7 @@ class PipX(StateModuleHelper):
|
||||
self.do_raise("Trying to inject packages into a non-existent application: {0}".format(self.vars.name))
|
||||
if self.vars.force:
|
||||
self.changed = True
|
||||
with self.runner('state index_url install_apps install_deps force editable pip_args name inject_packages', check_mode_skip=True) as ctx:
|
||||
with self.runner('state index_url force editable pip_args name inject_packages', check_mode_skip=True) as ctx:
|
||||
ctx.run()
|
||||
self._capture_results(ctx)
|
||||
|
||||
@@ -313,11 +295,11 @@ class PipX(StateModuleHelper):
|
||||
def state_latest(self):
|
||||
if not self.vars.application or self.vars.force:
|
||||
self.changed = True
|
||||
with self.runner('state index_url install_deps force python system_site_packages editable pip_args name_source', check_mode_skip=True) as ctx:
|
||||
with self.runner('state index_url install_deps force python editable pip_args name_source', check_mode_skip=True) as ctx:
|
||||
ctx.run(state='install', name_source=[self.vars.name, self.vars.source])
|
||||
self._capture_results(ctx)
|
||||
|
||||
with self.runner('state include_injected index_url force editable pip_args name', check_mode_skip=True) as ctx:
|
||||
with self.runner('state index_url install_deps force editable pip_args name', check_mode_skip=True) as ctx:
|
||||
ctx.run(state='upgrade')
|
||||
self._capture_results(ctx)
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ notes:
|
||||
- >
|
||||
This module will honor C(pipx) environment variables such as but not limited to C(PIPX_HOME) and C(PIPX_BIN_DIR)
|
||||
passed using the R(environment Ansible keyword, playbooks_environment).
|
||||
- This module requires C(pipx) version 0.16.2.1 or above.
|
||||
- Please note that C(pipx) requires Python 3.6 or above.
|
||||
- See also the C(pipx) documentation at U(https://pypa.github.io/pipx/).
|
||||
author:
|
||||
|
||||
@@ -383,12 +383,14 @@ def emerge_packages(module, packages):
|
||||
"""Fallback to default: don't use this argument at all."""
|
||||
continue
|
||||
|
||||
"""Add the --flag=value pair."""
|
||||
if isinstance(flag_val, bool):
|
||||
args.extend((arg, to_native('y' if flag_val else 'n')))
|
||||
elif not flag_val:
|
||||
if not flag_val:
|
||||
"""If the value is 0 or 0.0: add the flag, but not the value."""
|
||||
args.append(arg)
|
||||
continue
|
||||
|
||||
"""Add the --flag=value pair."""
|
||||
if isinstance(p[flag], bool):
|
||||
args.extend((arg, to_native('y' if flag_val else 'n')))
|
||||
else:
|
||||
args.extend((arg, to_native(flag_val)))
|
||||
|
||||
|
||||
@@ -154,9 +154,10 @@ options:
|
||||
unprivileged:
|
||||
description:
|
||||
- Indicate if the container should be unprivileged.
|
||||
- The default change to C(true) in community.general 7.0.0. It used to be C(false) before.
|
||||
- >
|
||||
The default value for this parameter is C(false) but that is deprecated
|
||||
and it will be replaced with C(true) in community.general 7.0.0.
|
||||
type: bool
|
||||
default: true
|
||||
description:
|
||||
description:
|
||||
- Specify the description for the container. Only used on the configuration web interface.
|
||||
@@ -599,7 +600,7 @@ def main():
|
||||
purge=dict(type='bool', default=False),
|
||||
state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted']),
|
||||
pubkey=dict(type='str'),
|
||||
unprivileged=dict(type='bool', default=True),
|
||||
unprivileged=dict(type='bool'),
|
||||
description=dict(type='str'),
|
||||
hookscript=dict(type='str'),
|
||||
proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']),
|
||||
@@ -642,6 +643,14 @@ def main():
|
||||
timeout = module.params['timeout']
|
||||
clone = module.params['clone']
|
||||
|
||||
if module.params['unprivileged'] is None:
|
||||
module.params['unprivileged'] = False
|
||||
module.deprecate(
|
||||
'The default value `false` for the parameter "unprivileged" is deprecated and it will be replaced with `true`',
|
||||
version='7.0.0',
|
||||
collection_name='community.general'
|
||||
)
|
||||
|
||||
if module.params['proxmox_default_behavior'] == 'compatibility':
|
||||
old_default_values = dict(
|
||||
disk="3",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user