mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-29 09:56:53 +00:00
Compare commits
462 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27c094a095 | ||
|
|
86a5b4f28c | ||
|
|
4e14c429c7 | ||
|
|
c34fb01462 | ||
|
|
2086977af6 | ||
|
|
587d221376 | ||
|
|
5ed6b38477 | ||
|
|
2c0cfe4d16 | ||
|
|
c344d20a9a | ||
|
|
53480b25c8 | ||
|
|
cfffaa5b6f | ||
|
|
726918930b | ||
|
|
614a84d0f2 | ||
|
|
a11022e896 | ||
|
|
491196937d | ||
|
|
af7a6dc29f | ||
|
|
16ffb4ba10 | ||
|
|
31c3865251 | ||
|
|
53e0bf8297 | ||
|
|
9b80b14956 | ||
|
|
be763e6ed2 | ||
|
|
4375280497 | ||
|
|
ebda14ba41 | ||
|
|
c16a5f3780 | ||
|
|
f6c1566924 | ||
|
|
bffed2fda5 | ||
|
|
440804fd62 | ||
|
|
a915a4b7c5 | ||
|
|
ed69bde7a9 | ||
|
|
77700e7110 | ||
|
|
91d445ab35 | ||
|
|
19c2af03b7 | ||
|
|
58a5463ddb | ||
|
|
84941d0a7f | ||
|
|
87880da6da | ||
|
|
7acc0b897a | ||
|
|
5174fc98d2 | ||
|
|
d9ad386a13 | ||
|
|
739719a3b1 | ||
|
|
311b618016 | ||
|
|
70820cab5d | ||
|
|
a75a12227f | ||
|
|
6959847701 | ||
|
|
ad93c40d40 | ||
|
|
5bfbd65115 | ||
|
|
71de1ee1d5 | ||
|
|
ad4efaeb31 | ||
|
|
786ea68016 | ||
|
|
dd878f931f | ||
|
|
8f03511d9c | ||
|
|
004e6d06c3 | ||
|
|
25f46caefb | ||
|
|
0a733c60ca | ||
|
|
f006aa4cf6 | ||
|
|
72e0d8c310 | ||
|
|
b96aaffeae | ||
|
|
5bd5de4281 | ||
|
|
4aebefcf9e | ||
|
|
62f9a5b0a9 | ||
|
|
3d03eda99e | ||
|
|
c01ce10b4b | ||
|
|
16aa776c93 | ||
|
|
d7d1659e34 | ||
|
|
5b9b99384f | ||
|
|
f898279c8c | ||
|
|
2215c6d360 | ||
|
|
ca3948858a | ||
|
|
f14e566cc7 | ||
|
|
a2c93f5e99 | ||
|
|
67a2abcab2 | ||
|
|
2e4864db7f | ||
|
|
1f0b2a5173 | ||
|
|
25482000f0 | ||
|
|
c0f3aa14cf | ||
|
|
1ef104be61 | ||
|
|
773df88a41 | ||
|
|
d77e256088 | ||
|
|
2917389779 | ||
|
|
59af80235b | ||
|
|
aec52198e3 | ||
|
|
cbe4490c9e | ||
|
|
9de059b44d | ||
|
|
c72a23a5f1 | ||
|
|
0b9d9c0fdb | ||
|
|
67eaf9405f | ||
|
|
5de05a6243 | ||
|
|
46b4b9a6de | ||
|
|
10146aae1c | ||
|
|
d2ec7053c5 | ||
|
|
51fcacae08 | ||
|
|
29211b970c | ||
|
|
5c1fa53558 | ||
|
|
2348f3d439 | ||
|
|
46a051d168 | ||
|
|
b2212bc8ef | ||
|
|
e05e3aed67 | ||
|
|
a13541299e | ||
|
|
221067e708 | ||
|
|
db6458bd93 | ||
|
|
f342243fb0 | ||
|
|
37f2b06c3c | ||
|
|
0a8a41966d | ||
|
|
263c5ba9de | ||
|
|
cfc28a3f6a | ||
|
|
b495035923 | ||
|
|
d4637e9b1c | ||
|
|
7842dc0dea | ||
|
|
314a0bc553 | ||
|
|
34b7876e4f | ||
|
|
65c10de630 | ||
|
|
a86f31ac0f | ||
|
|
bc82fe36be | ||
|
|
7c810a6186 | ||
|
|
9d468fb078 | ||
|
|
2c79d42eb4 | ||
|
|
d95c3a738f | ||
|
|
839880d711 | ||
|
|
bc0edf7d55 | ||
|
|
68458fd8aa | ||
|
|
4aa70ab48f | ||
|
|
739210c6b9 | ||
|
|
8c23d0e345 | ||
|
|
cde4a1a099 | ||
|
|
10b3381f21 | ||
|
|
0ccd52b63a | ||
|
|
c76e598d61 | ||
|
|
ad3efa9719 | ||
|
|
b9c8d2bee5 | ||
|
|
2c167547f6 | ||
|
|
73de447489 | ||
|
|
134f6132ce | ||
|
|
f229c800da | ||
|
|
52a0970ef8 | ||
|
|
11e0797650 | ||
|
|
e6bbbac6a0 | ||
|
|
d78f3dd7c4 | ||
|
|
737f8340e4 | ||
|
|
f87ab7046d | ||
|
|
4c100aef47 | ||
|
|
2b6bbd9f91 | ||
|
|
0484abdddd | ||
|
|
3f119aa9b6 | ||
|
|
277329a6fe | ||
|
|
955eb531a3 | ||
|
|
9ac2918d49 | ||
|
|
740180d4a5 | ||
|
|
5d6c539373 | ||
|
|
09e2f77289 | ||
|
|
7aaf8cf496 | ||
|
|
664bd70294 | ||
|
|
9651bca396 | ||
|
|
f3375c638e | ||
|
|
14d663029a | ||
|
|
9297802089 | ||
|
|
5b4dc4ace2 | ||
|
|
15a72418ac | ||
|
|
7278bdcf9d | ||
|
|
b9aab568f7 | ||
|
|
e01d014c36 | ||
|
|
e5bdc028c4 | ||
|
|
67f7184234 | ||
|
|
b42ab6b45d | ||
|
|
67eafdd20b | ||
|
|
3cc11bfd42 | ||
|
|
a367fba315 | ||
|
|
00583448e2 | ||
|
|
c0cae2b27e | ||
|
|
07a9efd54f | ||
|
|
9dc8f2b05d | ||
|
|
baf726b389 | ||
|
|
2d7302ba12 | ||
|
|
7caefbd420 | ||
|
|
1a3c221995 | ||
|
|
0eecd48ea8 | ||
|
|
d71c10da27 | ||
|
|
664a09b277 | ||
|
|
e11bf7d788 | ||
|
|
889989aa96 | ||
|
|
0feb38f2b1 | ||
|
|
e6323433ff | ||
|
|
0a9a853abf | ||
|
|
3da1119e41 | ||
|
|
4255c0d2fc | ||
|
|
066975e5d1 | ||
|
|
8a59d6306c | ||
|
|
c4dc911d26 | ||
|
|
e30bb0958a | ||
|
|
67640e5431 | ||
|
|
9e7bcae370 | ||
|
|
c461e3cf71 | ||
|
|
3495823a72 | ||
|
|
f493110651 | ||
|
|
548758a878 | ||
|
|
f915cf5df2 | ||
|
|
6d147d748f | ||
|
|
1349d38c73 | ||
|
|
552207ea13 | ||
|
|
9cbe572c22 | ||
|
|
b485d23a05 | ||
|
|
a59a15e56c | ||
|
|
98251abfa9 | ||
|
|
e16bd2d015 | ||
|
|
bdafa31851 | ||
|
|
231f9c0283 | ||
|
|
0a5b29a744 | ||
|
|
405b4f34c0 | ||
|
|
c51b10eb9b | ||
|
|
6e172f37af | ||
|
|
c31424a924 | ||
|
|
6f0be41e1e | ||
|
|
8d035be233 | ||
|
|
35de2377f2 | ||
|
|
ad8cd8efb3 | ||
|
|
1dc03685b5 | ||
|
|
c81ea00a97 | ||
|
|
ca39c45bd4 | ||
|
|
2326d72cf7 | ||
|
|
9bd3627796 | ||
|
|
8a8e6c8058 | ||
|
|
d85d31ba3c | ||
|
|
15998c9f72 | ||
|
|
93027a33b9 | ||
|
|
c5f17f2184 | ||
|
|
172e8bb161 | ||
|
|
8da1ff3c90 | ||
|
|
7aa1c1a338 | ||
|
|
378687503c | ||
|
|
90be1cc838 | ||
|
|
8cee29b8f6 | ||
|
|
5a71909770 | ||
|
|
9d0af30702 | ||
|
|
9dc21447cc | ||
|
|
940130c959 | ||
|
|
0b239199e7 | ||
|
|
f0d6fcb3fa | ||
|
|
e1aad0db30 | ||
|
|
7701ea0293 | ||
|
|
9afb84c8f3 | ||
|
|
1746d11749 | ||
|
|
ea3b8eeee7 | ||
|
|
8c9add3d15 | ||
|
|
9244d0ae47 | ||
|
|
22591fb6e1 | ||
|
|
166fa1a7fa | ||
|
|
9e541a6f11 | ||
|
|
dbb37194d4 | ||
|
|
3cd7b0ec25 | ||
|
|
1c84389f50 | ||
|
|
61de9ce51c | ||
|
|
7ccd5c9116 | ||
|
|
e3cea35f2c | ||
|
|
94f58d1920 | ||
|
|
0f884bbadc | ||
|
|
6ca3e78d11 | ||
|
|
a09d70daa0 | ||
|
|
c2a3cf35c7 | ||
|
|
ee5ff3b31b | ||
|
|
18b7333f93 | ||
|
|
3197ef2e38 | ||
|
|
5f971e677a | ||
|
|
1b5d91153b | ||
|
|
2ce9ea8c54 | ||
|
|
eec4861c36 | ||
|
|
82e7e931a8 | ||
|
|
4b59174063 | ||
|
|
58d8469759 | ||
|
|
6d5dbfd455 | ||
|
|
6357048068 | ||
|
|
a861149a0e | ||
|
|
9a9b0b04a5 | ||
|
|
0a4e9379e2 | ||
|
|
7d1abf5d6a | ||
|
|
7ef25be10c | ||
|
|
0d0194fdf8 | ||
|
|
36f64367cf | ||
|
|
d827601c95 | ||
|
|
2efd31bacf | ||
|
|
6eaf047739 | ||
|
|
80268b0828 | ||
|
|
1e848c56f2 | ||
|
|
f74756d7fc | ||
|
|
138b57230a | ||
|
|
d32193afef | ||
|
|
ef8aa73dab | ||
|
|
28007079a4 | ||
|
|
f0b7233e8d | ||
|
|
48cc39a2b1 | ||
|
|
c34dc24d3a | ||
|
|
0760f60ca5 | ||
|
|
48b1bc7d47 | ||
|
|
769233808d | ||
|
|
7361ca5430 | ||
|
|
2322937a4a | ||
|
|
82225e5850 | ||
|
|
2d237987ae | ||
|
|
dc14070e08 | ||
|
|
feb1c1081e | ||
|
|
20bda07aaf | ||
|
|
1f6aa62210 | ||
|
|
5308f61b78 | ||
|
|
29636c1cc8 | ||
|
|
830734d6cf | ||
|
|
0296c200c7 | ||
|
|
2b435a591d | ||
|
|
ec2c793b08 | ||
|
|
a6bffa274c | ||
|
|
b653a9a84a | ||
|
|
8c209bdedc | ||
|
|
d4b4370ec4 | ||
|
|
e34276fa92 | ||
|
|
59d7850900 | ||
|
|
151b482fe6 | ||
|
|
ea04bb97cb | ||
|
|
5dd64a45de | ||
|
|
4df33d26b1 | ||
|
|
766f2dfe46 | ||
|
|
8a7128997d | ||
|
|
b598ca28f9 | ||
|
|
c943f7aa56 | ||
|
|
9565be5e50 | ||
|
|
725e670b47 | ||
|
|
ffdef00a6a | ||
|
|
92ccc6f013 | ||
|
|
f0c1b1065a | ||
|
|
a44356c966 | ||
|
|
33f9f0b05f | ||
|
|
f0f0704d64 | ||
|
|
55fe140230 | ||
|
|
ac543f5ef0 | ||
|
|
dbc0fe8859 | ||
|
|
42a1318fe3 | ||
|
|
d25352dc06 | ||
|
|
55682c52df | ||
|
|
46781d9fd1 | ||
|
|
4545d1c91e | ||
|
|
6570dfeb7d | ||
|
|
94c368f7df | ||
|
|
4cba1e60d9 | ||
|
|
321fb6c974 | ||
|
|
eb4d7a4199 | ||
|
|
4b07d45b7e | ||
|
|
d4a33433b4 | ||
|
|
e30b91cb8d | ||
|
|
b2b65c431b | ||
|
|
9ade4f6dd6 | ||
|
|
635d4f2138 | ||
|
|
6549e41ab8 | ||
|
|
6faface39e | ||
|
|
3b893ec421 | ||
|
|
65805e2dd6 | ||
|
|
297b50fb96 | ||
|
|
2edadb42fb | ||
|
|
4e1bf2d4ba | ||
|
|
b1a4a0ff21 | ||
|
|
e74ea7c8b8 | ||
|
|
6590f5e082 | ||
|
|
7483f71d31 | ||
|
|
6b215e3a9c | ||
|
|
3723e458d3 | ||
|
|
0f8bb43723 | ||
|
|
f33530dd61 | ||
|
|
8f3043058e | ||
|
|
3987b8a291 | ||
|
|
f7403a0b34 | ||
|
|
0a676406b3 | ||
|
|
5a7d234d80 | ||
|
|
fb9730f75e | ||
|
|
928aeafe1d | ||
|
|
5b68665571 | ||
|
|
e6b84acd1e | ||
|
|
c242993291 | ||
|
|
4f3de5658e | ||
|
|
301fcc3b7e | ||
|
|
0f0e9b2dca | ||
|
|
ed0636dc27 | ||
|
|
057321c6c6 | ||
|
|
1a4814de53 | ||
|
|
89b67a014b | ||
|
|
57bfbdc407 | ||
|
|
e19dffbf29 | ||
|
|
113e7cdfa0 | ||
|
|
c12be67a69 | ||
|
|
3a076fd585 | ||
|
|
4ef05a6483 | ||
|
|
936dd28395 | ||
|
|
e3b47899c5 | ||
|
|
fd8193e0bd | ||
|
|
fa477ebb35 | ||
|
|
43e766dd44 | ||
|
|
b25e0f360c | ||
|
|
658e95c5ca | ||
|
|
26c2876f50 | ||
|
|
62043463f3 | ||
|
|
f1dab6d4a7 | ||
|
|
d43764da79 | ||
|
|
de2feb2567 | ||
|
|
6e56bae0f3 | ||
|
|
1f7047e725 | ||
|
|
b2e4485567 | ||
|
|
b78254fe24 | ||
|
|
38aa0ec8ad | ||
|
|
42f28048a8 | ||
|
|
b699aaff7b | ||
|
|
af85b6c203 | ||
|
|
ec2e7cad3e | ||
|
|
7753fa4219 | ||
|
|
69ea487005 | ||
|
|
048f15fe68 | ||
|
|
aa1aa1d540 | ||
|
|
e78517ca93 | ||
|
|
bf185573a6 | ||
|
|
145435cdd9 | ||
|
|
6013c77c2b | ||
|
|
ad5482f63d | ||
|
|
f5594aefd5 | ||
|
|
ab5b379b30 | ||
|
|
1c5e44c649 | ||
|
|
23da67cc72 | ||
|
|
4032dd6b08 | ||
|
|
4cb6f39a80 | ||
|
|
3539957bac | ||
|
|
e05769d4bf | ||
|
|
19c03cff96 | ||
|
|
703660c81d | ||
|
|
fd32af1ac3 | ||
|
|
80fbcf2f98 | ||
|
|
a722e038cc | ||
|
|
19c8d2164d | ||
|
|
d4656ffca2 | ||
|
|
b49607f12d | ||
|
|
af0ce4284f | ||
|
|
f5f862617a | ||
|
|
a1a4ba4337 | ||
|
|
b0b783f8ff | ||
|
|
e670ca666a | ||
|
|
49b991527e | ||
|
|
e6cc671a0d | ||
|
|
797ea23e50 | ||
|
|
4d23b7a48b | ||
|
|
020b47a1a9 | ||
|
|
0da9d956a0 | ||
|
|
5691e3aff3 | ||
|
|
007333dbfe | ||
|
|
05666b0e4d | ||
|
|
c934d9aeb5 | ||
|
|
5b15e4089a | ||
|
|
a6379e45ce | ||
|
|
b95176dbc8 | ||
|
|
b752fea121 | ||
|
|
cf50990fed | ||
|
|
45343e6bc0 | ||
|
|
51540f6345 | ||
|
|
74eba52028 | ||
|
|
b920e8abf2 | ||
|
|
75c0004e1e | ||
|
|
be42fd4af7 | ||
|
|
1c05908ff6 | ||
|
|
ea42b75378 | ||
|
|
0330f4b52c | ||
|
|
1d8c659ba2 | ||
|
|
e784254679 | ||
|
|
d5e1edd284 |
58
.github/BOTMETA.yml
vendored
58
.github/BOTMETA.yml
vendored
@@ -163,8 +163,6 @@ files:
|
||||
keywords: opennebula dynamic inventory script
|
||||
$inventories/proxmox.py:
|
||||
maintainers: $team_virt ilijamt
|
||||
$inventories/xen_orchestra.py:
|
||||
maintainers: shinuza
|
||||
$inventories/icinga2.py:
|
||||
maintainers: bongoeadgc6
|
||||
$inventories/scaleway.py:
|
||||
@@ -177,8 +175,6 @@ files:
|
||||
labels: lookups
|
||||
$lookups/cartesian.py: {}
|
||||
$lookups/chef_databag.py: {}
|
||||
$lookups/collection_version.py:
|
||||
maintainers: felixfontein
|
||||
$lookups/consul_kv.py: {}
|
||||
$lookups/credstash.py: {}
|
||||
$lookups/cyberarkpassword.py:
|
||||
@@ -209,6 +205,9 @@ files:
|
||||
$lookups/manifold.py:
|
||||
maintainers: galanoff
|
||||
labels: manifold
|
||||
$lookups/nios:
|
||||
maintainers: $team_networking sganesh-infoblox
|
||||
labels: infoblox networking
|
||||
$lookups/onepass:
|
||||
maintainers: samdoran
|
||||
labels: onepassword
|
||||
@@ -221,12 +220,8 @@ files:
|
||||
maintainers: Akasurde
|
||||
$lookups/random_string.py:
|
||||
maintainers: Akasurde
|
||||
$lookups/random_words.py:
|
||||
maintainers: konstruktoid
|
||||
$lookups/redis.py:
|
||||
maintainers: $team_ansible_core jpmens
|
||||
$lookups/revbitspss.py:
|
||||
maintainers: RevBits
|
||||
$lookups/shelvefile.py: {}
|
||||
$lookups/tss.py:
|
||||
maintainers: amigus endlesstrax
|
||||
@@ -258,6 +253,9 @@ files:
|
||||
$module_utils/module_helper.py:
|
||||
maintainers: russoz
|
||||
labels: module_helper
|
||||
$module_utils/net_tools/nios/api.py:
|
||||
maintainers: $team_networking sganesh-infoblox
|
||||
labels: infoblox networking
|
||||
$module_utils/oracle/oci_utils.py:
|
||||
maintainers: $team_oracle
|
||||
labels: cloud
|
||||
@@ -479,16 +477,11 @@ files:
|
||||
maintainers: paginabianca
|
||||
$modules/database/misc/redis_data.py:
|
||||
maintainers: paginabianca
|
||||
$modules/database/misc/redis_data_incr.py:
|
||||
maintainers: paginabianca
|
||||
$modules/database/misc/riak.py:
|
||||
maintainers: drewkerrigan jsmartin
|
||||
$modules/database/mssql/mssql_db.py:
|
||||
maintainers: vedit Jmainguy kenichi-ogawa-1988
|
||||
labels: mssql_db
|
||||
$modules/database/mssql/mssql_script.py:
|
||||
maintainers: kbudde
|
||||
labels: mssql_script
|
||||
$modules/database/saphana/hana_query.py:
|
||||
maintainers: rainerleber
|
||||
$modules/database/vertica/:
|
||||
@@ -619,8 +612,6 @@ files:
|
||||
labels: cloudflare_dns
|
||||
$modules/net_tools/dnsimple.py:
|
||||
maintainers: drcapulet
|
||||
$modules/net_tools/dnsimple_info.py:
|
||||
maintainers: edhilgendorf
|
||||
$modules/net_tools/dnsmadeeasy.py:
|
||||
maintainers: briceburg
|
||||
$modules/net_tools/gandi_livedns.py:
|
||||
@@ -656,6 +647,31 @@ files:
|
||||
maintainers: amasolov nerzhul
|
||||
$modules/net_tools/pritunl/:
|
||||
maintainers: Lowess
|
||||
$modules/net_tools/nios/:
|
||||
maintainers: $team_networking
|
||||
labels: infoblox networking
|
||||
$modules/net_tools/nios/nios_a_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_aaaa_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_cname_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_fixed_address.py:
|
||||
maintainers: sjaiswal
|
||||
$modules/net_tools/nios/nios_member.py:
|
||||
maintainers: krisvasudevan
|
||||
$modules/net_tools/nios/nios_mx_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_naptr_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_nsgroup.py:
|
||||
maintainers: ebirn sjaiswal
|
||||
$modules/net_tools/nios/nios_ptr_record.py:
|
||||
maintainers: clementtrebuchet
|
||||
$modules/net_tools/nios/nios_srv_record.py:
|
||||
maintainers: brampling
|
||||
$modules/net_tools/nios/nios_txt_record.py:
|
||||
maintainers: coreywan
|
||||
$modules/net_tools/nmcli.py:
|
||||
maintainers: alcamie101
|
||||
$modules/net_tools/snmp_facts.py:
|
||||
@@ -759,8 +775,6 @@ files:
|
||||
maintainers: evgkrsk
|
||||
$modules/packaging/os/copr.py:
|
||||
maintainers: schlupov
|
||||
$modules/packaging/os/dnf_versionlock.py:
|
||||
maintainers: moreda
|
||||
$modules/packaging/os/flatpak.py:
|
||||
maintainers: $team_flatpak
|
||||
$modules/packaging/os/flatpak_remote.py:
|
||||
@@ -857,9 +871,6 @@ files:
|
||||
$modules/packaging/os/snap.py:
|
||||
maintainers: angristan vcarceler
|
||||
labels: snap
|
||||
$modules/packaging/os/snap_alias.py:
|
||||
maintainers: russoz
|
||||
labels: snap
|
||||
$modules/packaging/os/sorcery.py:
|
||||
maintainers: vaygr
|
||||
$modules/packaging/os/svr4pkg.py:
|
||||
@@ -953,8 +964,6 @@ files:
|
||||
maintainers: SamyCoenen
|
||||
$modules/source_control/gitlab/gitlab_user.py:
|
||||
maintainers: LennertMertens stgrace
|
||||
$modules/source_control/gitlab/gitlab_branch.py:
|
||||
maintainers: paytroff
|
||||
$modules/source_control/hg.py:
|
||||
maintainers: yeukhon
|
||||
$modules/storage/emc/emc_vnx_sg_member.py:
|
||||
@@ -1179,8 +1188,6 @@ files:
|
||||
maintainers: inetfuture mattupstate
|
||||
$modules/web_infrastructure/taiga_issue.py:
|
||||
maintainers: lekum
|
||||
$tests/a_module.py:
|
||||
maintainers: felixfontein
|
||||
#########################
|
||||
tests/:
|
||||
labels: tests
|
||||
@@ -1207,7 +1214,6 @@ macros:
|
||||
module_utils: plugins/module_utils
|
||||
modules: plugins/modules
|
||||
terminals: plugins/terminal
|
||||
tests: plugins/test
|
||||
team_ansible_core:
|
||||
team_aix: MorrisA bcoca d-little flynn1973 gforster kairoaraujo marvin-sinister mator molekuul ramooncamacho wtcross
|
||||
team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo
|
||||
@@ -1228,7 +1234,7 @@ macros:
|
||||
team_opennebula: ilicmilan meerkampdvv rsmontero xorel nilsding
|
||||
team_oracle: manojmeda mross22 nalsaber
|
||||
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
|
||||
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06
|
||||
team_redfish: mraineri tomasg2012 xmadsen renxulei
|
||||
team_rhn: FlossWare alikins barnabycourt vritant
|
||||
team_scaleway: remyleone abarbare
|
||||
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
14
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -62,20 +62,6 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
14
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
@@ -62,20 +62,6 @@ body:
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -21,7 +21,7 @@ body:
|
||||
placeholder: >-
|
||||
I am trying to do X with the collection from the main branch on GitHub and
|
||||
I think that implementing a feature Y would be very helpful for me and
|
||||
every other user of community.general because of Z.
|
||||
every other user of ansible-core because of Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
interval:
|
||||
schedule: "weekly"
|
||||
1661
CHANGELOG.rst
1661
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
||||
# Community General Collection
|
||||
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://codecov.io/gh/ansible-collections/community.general)
|
||||
|
||||
This repository contains the `community.general` Ansible Collection. The collection is a part of the Ansible package and includes many modules and plugins supported by Ansible community which are not part of more specialized community collections.
|
||||
@@ -64,13 +64,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-4/CONTRIBUTING.md)!
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md)!
|
||||
|
||||
The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/stable-4/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/main/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-4/CONTRIBUTING.md).
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md).
|
||||
|
||||
### Running tests
|
||||
|
||||
@@ -80,7 +80,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-4/commit-rights.md).
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/main/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:
|
||||
@@ -108,7 +108,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-4/CHANGELOG.rst).
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-3/CHANGELOG.rst).
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,4 +3,3 @@ sections:
|
||||
- title: Guides
|
||||
toctree:
|
||||
- filter_guide
|
||||
- test_guide
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
.. _ansible_collections.community.general.docsite.test_guide:
|
||||
|
||||
community.general Test (Plugin) Guide
|
||||
=====================================
|
||||
|
||||
The :ref:`community.general collection <plugins_in_community.general>` offers currently one test plugin.
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
Feature Tests
|
||||
-------------
|
||||
|
||||
The ``a_module`` test allows to check whether a given string refers to an existing module or action plugin. This can be useful in roles, which can use this to ensure that required modules are present ahead of time.
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
- name: Make sure that community.aws.route53 is available
|
||||
assert:
|
||||
that:
|
||||
- >
|
||||
'community.aws.route53' is community.general.a_module
|
||||
|
||||
- name: Make sure that community.general.does_not_exist is not a module or action plugin
|
||||
assert:
|
||||
that:
|
||||
- "'community.general.does_not_exist' is not community.general.a_module"
|
||||
|
||||
.. versionadded:: 4.0.0
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: community
|
||||
name: general
|
||||
version: 4.2.0
|
||||
version: 3.8.3
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
105
meta/runtime.yml
105
meta/runtime.yml
@@ -12,11 +12,20 @@ plugin_routing:
|
||||
hashi_vault:
|
||||
redirect: community.hashi_vault.hashi_vault
|
||||
nios:
|
||||
redirect: infoblox.nios_modules.nios_lookup
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios lookup plugin has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_lookup instead.
|
||||
nios_next_ip:
|
||||
redirect: infoblox.nios_modules.nios_next_ip
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_next_ip lookup plugin has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_next_ip instead.
|
||||
nios_next_network:
|
||||
redirect: infoblox.nios_modules.nios_next_network
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_next_network lookup plugin has been
|
||||
deprecated. Please use infoblox.nios_modules.nios_next_network instead.
|
||||
modules:
|
||||
ali_instance_facts:
|
||||
tombstone:
|
||||
@@ -257,37 +266,85 @@ plugin_routing:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.nginx_status_info instead.
|
||||
nios_a_record:
|
||||
redirect: infoblox.nios_modules.nios_a_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_a_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_a_record instead.
|
||||
nios_aaaa_record:
|
||||
redirect: infoblox.nios_modules.nios_aaaa_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_aaaa_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_aaaa_record instead.
|
||||
nios_cname_record:
|
||||
redirect: infoblox.nios_modules.nios_cname_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_cname_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_cname_record instead.
|
||||
nios_dns_view:
|
||||
redirect: infoblox.nios_modules.nios_dns_view
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_dns_view module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_dns_view instead.
|
||||
nios_fixed_address:
|
||||
redirect: infoblox.nios_modules.nios_fixed_address
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_fixed_address module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_fixed_address instead.
|
||||
nios_host_record:
|
||||
redirect: infoblox.nios_modules.nios_host_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_host_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_host_record instead.
|
||||
nios_member:
|
||||
redirect: infoblox.nios_modules.nios_member
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_member module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_member instead.
|
||||
nios_mx_record:
|
||||
redirect: infoblox.nios_modules.nios_mx_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_mx_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_mx_record instead.
|
||||
nios_naptr_record:
|
||||
redirect: infoblox.nios_modules.nios_naptr_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_naptr_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_naptr_record instead.
|
||||
nios_network:
|
||||
redirect: infoblox.nios_modules.nios_network
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_network module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_network instead.
|
||||
nios_network_view:
|
||||
redirect: infoblox.nios_modules.nios_network_view
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_network_view module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_network_view instead.
|
||||
nios_nsgroup:
|
||||
redirect: infoblox.nios_modules.nios_nsgroup
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_nsgroup module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_nsgroup instead.
|
||||
nios_ptr_record:
|
||||
redirect: infoblox.nios_modules.nios_ptr_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_ptr_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_ptr_record instead.
|
||||
nios_srv_record:
|
||||
redirect: infoblox.nios_modules.nios_srv_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_srv_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_srv_record instead.
|
||||
nios_txt_record:
|
||||
redirect: infoblox.nios_modules.nios_txt_record
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_txt_record module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_txt_record instead.
|
||||
nios_zone:
|
||||
redirect: infoblox.nios_modules.nios_zone
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios_zone module has been deprecated.
|
||||
Please use infoblox.nios_modules.nios_zone instead.
|
||||
ome_device_info:
|
||||
redirect: dellemc.openmanage.ome_device_info
|
||||
one_image_facts:
|
||||
@@ -571,7 +628,10 @@ plugin_routing:
|
||||
kubevirt_vm_options:
|
||||
redirect: community.kubevirt.kubevirt_vm_options
|
||||
nios:
|
||||
redirect: infoblox.nios_modules.nios
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.nios document fragment has been deprecated.
|
||||
Please use infoblox.nios_modules.nios instead.
|
||||
postgresql:
|
||||
redirect: community.postgresql.postgresql
|
||||
module_utils:
|
||||
@@ -590,7 +650,10 @@ plugin_routing:
|
||||
kubevirt:
|
||||
redirect: community.kubevirt.kubevirt
|
||||
net_tools.nios.api:
|
||||
redirect: infoblox.nios_modules.api
|
||||
deprecation:
|
||||
removal_version: 5.0.0
|
||||
warning_text: The community.general.net_tools.nios.api module_utils has been
|
||||
deprecated. Please use infoblox.nios_modules.api instead.
|
||||
postgresql:
|
||||
redirect: community.postgresql.postgresql
|
||||
remote_management.dellemc.dellemc_idrac:
|
||||
|
||||
@@ -226,15 +226,18 @@ class ElasticSource(object):
|
||||
|
||||
message = "success"
|
||||
status = "success"
|
||||
enriched_error_message = None
|
||||
if host_data.status == 'included':
|
||||
rc = 0
|
||||
else:
|
||||
res = host_data.result._result
|
||||
rc = res.get('rc', 0)
|
||||
if host_data.status == 'failed':
|
||||
message = self.get_error_message(res)
|
||||
enriched_error_message = self.enrich_error_message(res)
|
||||
if res.get('exception') is not None:
|
||||
message = res['exception'].strip().split('\n')[-1]
|
||||
elif 'msg' in res:
|
||||
message = res['msg']
|
||||
else:
|
||||
message = 'failed'
|
||||
status = "failure"
|
||||
elif host_data.status == 'skipped':
|
||||
if 'skip_reason' in res:
|
||||
@@ -256,7 +259,7 @@ class ElasticSource(object):
|
||||
"ansible.task.host.status": host_data.status}) as span:
|
||||
span.outcome = status
|
||||
if 'failure' in status:
|
||||
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, enriched_error_message))
|
||||
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, message))
|
||||
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
|
||||
|
||||
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
|
||||
@@ -269,24 +272,6 @@ class ElasticSource(object):
|
||||
use_elastic_traceparent_header=True,
|
||||
debug=True)
|
||||
|
||||
@staticmethod
|
||||
def get_error_message(result):
|
||||
if result.get('exception') is not None:
|
||||
return ElasticSource._last_line(result['exception'])
|
||||
return result.get('msg', 'failed')
|
||||
|
||||
@staticmethod
|
||||
def _last_line(text):
|
||||
lines = text.strip().split('\n')
|
||||
return lines[-1]
|
||||
|
||||
@staticmethod
|
||||
def enrich_error_message(result):
|
||||
message = result.get('msg', 'failed')
|
||||
exception = result.get('exception')
|
||||
stderr = result.get('stderr')
|
||||
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
"""
|
||||
|
||||
@@ -70,7 +70,6 @@ import os
|
||||
import json
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
@@ -144,7 +143,7 @@ class CallbackModule(CallbackBase):
|
||||
body = {
|
||||
'cmd': 'submitcheck',
|
||||
'token': self.token,
|
||||
'XMLDATA': to_bytes(xmldata)
|
||||
'XMLDATA': bytes(xmldata)
|
||||
}
|
||||
|
||||
try:
|
||||
|
||||
@@ -80,7 +80,6 @@ from os.path import basename
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.six import raise_from
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
try:
|
||||
@@ -92,6 +91,8 @@ try:
|
||||
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import (
|
||||
ConsoleSpanExporter,
|
||||
SimpleSpanProcessor,
|
||||
BatchSpanProcessor
|
||||
)
|
||||
from opentelemetry.util._time import _time_ns
|
||||
@@ -179,7 +180,7 @@ class OpenTelemetrySource(object):
|
||||
args = None
|
||||
|
||||
if not task.no_log and not hide_task_arguments:
|
||||
args = task.args
|
||||
args = ', '.join(('%s=%s' % a for a in task.args.items()))
|
||||
|
||||
tasks_data[uuid] = TaskData(uuid, name, path, play_name, action, args)
|
||||
|
||||
@@ -246,45 +247,34 @@ class OpenTelemetrySource(object):
|
||||
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
|
||||
|
||||
message = 'success'
|
||||
res = {}
|
||||
rc = 0
|
||||
status = Status(status_code=StatusCode.OK)
|
||||
if host_data.status != 'included':
|
||||
# Support loops
|
||||
if 'results' in host_data.result._result:
|
||||
if host_data.status == 'failed':
|
||||
message = self.get_error_message_from_results(host_data.result._result['results'], task_data.action)
|
||||
enriched_error_message = self.enrich_error_message_from_results(host_data.result._result['results'], task_data.action)
|
||||
else:
|
||||
res = host_data.result._result
|
||||
rc = res.get('rc', 0)
|
||||
message = self.get_error_message(res)
|
||||
enriched_error_message = self.enrich_error_message(res)
|
||||
|
||||
if host_data.status == 'included':
|
||||
rc = 0
|
||||
else:
|
||||
res = host_data.result._result
|
||||
rc = res.get('rc', 0)
|
||||
if host_data.status == 'failed':
|
||||
message = self.get_error_message(res)
|
||||
status = Status(status_code=StatusCode.ERROR, description=message)
|
||||
# Record an exception with the task message
|
||||
span.record_exception(BaseException(enriched_error_message))
|
||||
span.record_exception(BaseException(self.enrich_error_message(res)))
|
||||
elif host_data.status == 'skipped':
|
||||
message = res['skip_reason'] if 'skip_reason' in res else 'skipped'
|
||||
if 'skip_reason' in res:
|
||||
message = res['skip_reason']
|
||||
else:
|
||||
message = 'skipped'
|
||||
status = Status(status_code=StatusCode.UNSET)
|
||||
elif host_data.status == 'ignored':
|
||||
status = Status(status_code=StatusCode.UNSET)
|
||||
|
||||
span.set_status(status)
|
||||
if isinstance(task_data.args, dict) and "gather_facts" not in task_data.action:
|
||||
names = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.keys())
|
||||
values = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.values())
|
||||
self.set_span_attribute(span, ("ansible.task.args.name"), names)
|
||||
self.set_span_attribute(span, ("ansible.task.args.value"), values)
|
||||
self.set_span_attribute(span, "ansible.task.args", task_data.args)
|
||||
self.set_span_attribute(span, "ansible.task.module", task_data.action)
|
||||
self.set_span_attribute(span, "ansible.task.message", message)
|
||||
self.set_span_attribute(span, "ansible.task.name", name)
|
||||
self.set_span_attribute(span, "ansible.task.result", rc)
|
||||
self.set_span_attribute(span, "ansible.task.host.name", host_data.name)
|
||||
self.set_span_attribute(span, "ansible.task.host.status", host_data.status)
|
||||
# This will allow to enrich the service map
|
||||
self.add_attributes_for_service_map_if_possible(span, task_data)
|
||||
span.end(end_time=host_data.finish)
|
||||
|
||||
def set_span_attribute(self, span, attributeName, attributeValue):
|
||||
@@ -296,64 +286,12 @@ class OpenTelemetrySource(object):
|
||||
if attributeValue is not None:
|
||||
span.set_attribute(attributeName, attributeValue)
|
||||
|
||||
def add_attributes_for_service_map_if_possible(self, span, task_data):
|
||||
"""Update the span attributes with the service that the task interacted with, if possible."""
|
||||
|
||||
redacted_url = self.parse_and_redact_url_if_possible(task_data.args)
|
||||
if redacted_url:
|
||||
self.set_span_attribute(span, "http.url", redacted_url.geturl())
|
||||
|
||||
@staticmethod
|
||||
def parse_and_redact_url_if_possible(args):
|
||||
"""Parse and redact the url, if possible."""
|
||||
|
||||
try:
|
||||
parsed_url = urlparse(OpenTelemetrySource.url_from_args(args))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
if OpenTelemetrySource.is_valid_url(parsed_url):
|
||||
return OpenTelemetrySource.redact_user_password(parsed_url)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def url_from_args(args):
|
||||
# the order matters
|
||||
url_args = ("url", "api_url", "baseurl", "repo", "server_url", "chart_repo_url")
|
||||
for arg in url_args:
|
||||
if args.get(arg):
|
||||
return args.get(arg)
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def redact_user_password(url):
|
||||
return url._replace(netloc=url.hostname) if url.password else url
|
||||
|
||||
@staticmethod
|
||||
def is_valid_url(url):
|
||||
if all([url.scheme, url.netloc, url.hostname]):
|
||||
return "{{" not in url.hostname
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def transform_ansible_unicode_to_str(value):
|
||||
parsed_url = urlparse(str(value))
|
||||
if OpenTelemetrySource.is_valid_url(parsed_url):
|
||||
return OpenTelemetrySource.redact_user_password(parsed_url).geturl()
|
||||
return str(value)
|
||||
|
||||
@staticmethod
|
||||
def get_error_message(result):
|
||||
if result.get('exception') is not None:
|
||||
return OpenTelemetrySource._last_line(result['exception'])
|
||||
return result.get('msg', 'failed')
|
||||
|
||||
@staticmethod
|
||||
def get_error_message_from_results(results, action):
|
||||
for result in results:
|
||||
if result.get('failed', False):
|
||||
return ('{0}({1}) - {2}').format(action, result.get('item', 'none'), OpenTelemetrySource.get_error_message(result))
|
||||
|
||||
@staticmethod
|
||||
def _last_line(text):
|
||||
lines = text.strip().split('\n')
|
||||
@@ -366,14 +304,6 @@ class OpenTelemetrySource(object):
|
||||
stderr = result.get('stderr')
|
||||
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||
|
||||
@staticmethod
|
||||
def enrich_error_message_from_results(results, action):
|
||||
message = ""
|
||||
for result in results:
|
||||
if result.get('failed', False):
|
||||
message = ('{0}({1}) - {2}\n{3}').format(action, result.get('item', 'none'), OpenTelemetrySource.enrich_error_message(result), message)
|
||||
return message
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
"""
|
||||
|
||||
138
plugins/doc_fragments/_netapp.py
Normal file
138
plugins/doc_fragments/_netapp.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2018, Sumit Kumar <sumit4@netapp.com>, chris Archibald <carchi@netapp.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
- See respective platform section for more details
|
||||
requirements:
|
||||
- See respective platform section for more details
|
||||
notes:
|
||||
- Ansible modules are available for the following NetApp Storage Platforms: E-Series, ONTAP, SolidFire
|
||||
'''
|
||||
|
||||
# Documentation fragment for ONTAP (na_cdot)
|
||||
ONTAP = r'''
|
||||
options:
|
||||
hostname:
|
||||
required: true
|
||||
description:
|
||||
- The hostname or IP address of the ONTAP instance.
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
|
||||
For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
|
||||
aliases: ['user']
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- Password for the specified user.
|
||||
aliases: ['pass']
|
||||
requirements:
|
||||
- A physical or virtual clustered Data ONTAP system. The modules were developed with Clustered Data ONTAP 8.3
|
||||
- Ansible 2.2
|
||||
- netapp-lib (2015.9.25). Install using 'pip install netapp-lib'
|
||||
|
||||
notes:
|
||||
- The modules prefixed with na\\_cdot are built to support the ONTAP storage platform.
|
||||
|
||||
'''
|
||||
|
||||
# Documentation fragment for SolidFire
|
||||
SOLIDFIRE = r'''
|
||||
options:
|
||||
hostname:
|
||||
required: true
|
||||
description:
|
||||
- The hostname or IP address of the SolidFire cluster.
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- Please ensure that the user has the adequate permissions. For more information, please read the official documentation
|
||||
U(https://mysupport.netapp.com/documentation/docweb/index.html?productID=62636&language=en-US).
|
||||
aliases: ['user']
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- Password for the specified user.
|
||||
aliases: ['pass']
|
||||
|
||||
requirements:
|
||||
- The modules were developed with SolidFire 10.1
|
||||
- solidfire-sdk-python (1.1.0.92) or greater. Install using 'pip install solidfire-sdk-python'
|
||||
|
||||
notes:
|
||||
- The modules prefixed with na\\_elementsw are built to support the SolidFire storage platform.
|
||||
|
||||
'''
|
||||
|
||||
# Documentation fragment for ONTAP (na_ontap)
|
||||
NA_ONTAP = r'''
|
||||
options:
|
||||
hostname:
|
||||
description:
|
||||
- The hostname or IP address of the ONTAP instance.
|
||||
type: str
|
||||
required: true
|
||||
username:
|
||||
description:
|
||||
- This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
|
||||
For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
|
||||
type: str
|
||||
required: true
|
||||
aliases: [ user ]
|
||||
password:
|
||||
description:
|
||||
- Password for the specified user.
|
||||
type: str
|
||||
required: true
|
||||
aliases: [ pass ]
|
||||
https:
|
||||
description:
|
||||
- Enable and disable https
|
||||
type: bool
|
||||
default: no
|
||||
validate_certs:
|
||||
description:
|
||||
- If set to C(no), the SSL certificates will not be validated.
|
||||
- This should only set to C(False) used on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: yes
|
||||
http_port:
|
||||
description:
|
||||
- Override the default port (80 or 443) with this port
|
||||
type: int
|
||||
ontapi:
|
||||
description:
|
||||
- The ontap api version to use
|
||||
type: int
|
||||
use_rest:
|
||||
description:
|
||||
- REST API if supported by the target system for all the resources and attributes the module requires. Otherwise will revert to ZAPI.
|
||||
- Always -- will always use the REST API
|
||||
- Never -- will always use the ZAPI
|
||||
- Auto -- will try to use the REST Api
|
||||
default: Auto
|
||||
choices: ['Never', 'Always', 'Auto']
|
||||
type: str
|
||||
|
||||
|
||||
requirements:
|
||||
- A physical or virtual clustered Data ONTAP system. The modules support Data ONTAP 9.1 and onward
|
||||
- Ansible 2.6
|
||||
- Python2 netapp-lib (2017.10.30) or later. Install using 'pip install netapp-lib'
|
||||
- Python3 netapp-lib (2018.11.13) or later. Install using 'pip install netapp-lib'
|
||||
- To enable http on the cluster you must run the following commands 'set -privilege advanced;' 'system services web modify -http-enabled true;'
|
||||
|
||||
notes:
|
||||
- The modules prefixed with na\\_ontap are built to support the ONTAP storage platform.
|
||||
|
||||
'''
|
||||
@@ -1,41 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Evgeniy Krysanov <evgeniy.krysanov@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
client_id:
|
||||
description:
|
||||
- The OAuth consumer key.
|
||||
- If not set the environment variable C(BITBUCKET_CLIENT_ID) will be used.
|
||||
type: str
|
||||
client_secret:
|
||||
description:
|
||||
- The OAuth consumer secret.
|
||||
- If not set the environment variable C(BITBUCKET_CLIENT_SECRET) will be used.
|
||||
type: str
|
||||
user:
|
||||
description:
|
||||
- The username.
|
||||
- If not set the environment variable C(BITBUCKET_USERNAME) will be used.
|
||||
type: str
|
||||
version_added: 4.0.0
|
||||
password:
|
||||
description:
|
||||
- The App password.
|
||||
- If not set the environment variable C(BITBUCKET_PASSWORD) will be used.
|
||||
type: str
|
||||
version_added: 4.0.0
|
||||
notes:
|
||||
- Bitbucket OAuth consumer key and secret can be obtained from Bitbucket profile -> Settings -> Access Management -> OAuth.
|
||||
- Bitbucket App password can be created from Bitbucket profile -> Personal Settings -> App passwords.
|
||||
- If both OAuth and Basic Auth credentials are passed, OAuth credentials take precedence.
|
||||
'''
|
||||
@@ -1,31 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard files documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
requirements:
|
||||
- requests (Python library U(https://pypi.org/project/requests/))
|
||||
|
||||
options:
|
||||
api_token:
|
||||
description:
|
||||
- GitLab access token with API permissions.
|
||||
type: str
|
||||
api_oauth_token:
|
||||
description:
|
||||
- GitLab OAuth token for logging in.
|
||||
type: str
|
||||
version_added: 4.2.0
|
||||
api_job_token:
|
||||
description:
|
||||
- GitLab CI job token for logging in.
|
||||
type: str
|
||||
version_added: 4.2.0
|
||||
'''
|
||||
103
plugins/doc_fragments/nios.py
Normal file
103
plugins/doc_fragments/nios.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard files documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
provider:
|
||||
description:
|
||||
- A dict object containing connection details.
|
||||
type: dict
|
||||
suboptions:
|
||||
host:
|
||||
description:
|
||||
- Specifies the DNS host name or address for connecting to the remote
|
||||
instance of NIOS WAPI over REST
|
||||
- Value can also be specified using C(INFOBLOX_HOST) environment
|
||||
variable.
|
||||
type: str
|
||||
username:
|
||||
description:
|
||||
- Configures the username to use to authenticate the connection to
|
||||
the remote instance of NIOS.
|
||||
- Value can also be specified using C(INFOBLOX_USERNAME) environment
|
||||
variable.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Specifies the password to use to authenticate the connection to
|
||||
the remote instance of NIOS.
|
||||
- Value can also be specified using C(INFOBLOX_PASSWORD) environment
|
||||
variable.
|
||||
type: str
|
||||
validate_certs:
|
||||
description:
|
||||
- Boolean value to enable or disable verifying SSL certificates
|
||||
- Value can also be specified using C(INFOBLOX_SSL_VERIFY) environment
|
||||
variable.
|
||||
type: bool
|
||||
default: no
|
||||
aliases: [ ssl_verify ]
|
||||
http_request_timeout:
|
||||
description:
|
||||
- The amount of time before to wait before receiving a response
|
||||
- Value can also be specified using C(INFOBLOX_HTTP_REQUEST_TIMEOUT) environment
|
||||
variable.
|
||||
type: int
|
||||
default: 10
|
||||
max_retries:
|
||||
description:
|
||||
- Configures the number of attempted retries before the connection
|
||||
is declared usable
|
||||
- Value can also be specified using C(INFOBLOX_MAX_RETRIES) environment
|
||||
variable.
|
||||
type: int
|
||||
default: 3
|
||||
wapi_version:
|
||||
description:
|
||||
- Specifies the version of WAPI to use
|
||||
- Value can also be specified using C(INFOBLOX_WAP_VERSION) environment
|
||||
variable.
|
||||
- Until ansible 2.8 the default WAPI was 1.4
|
||||
type: str
|
||||
default: '2.1'
|
||||
max_results:
|
||||
description:
|
||||
- Specifies the maximum number of objects to be returned,
|
||||
if set to a negative number the appliance will return an error when the
|
||||
number of returned objects would exceed the setting.
|
||||
- Value can also be specified using C(INFOBLOX_MAX_RESULTS) environment
|
||||
variable.
|
||||
type: int
|
||||
default: 1000
|
||||
http_pool_connections:
|
||||
description:
|
||||
- Number of pools to be used by the C(infoblox_client.Connector) object.
|
||||
- This is passed as-is to the underlying C(requests.adapters.HTTPAdapter) class.
|
||||
type: int
|
||||
default: 10
|
||||
http_pool_maxsize:
|
||||
description:
|
||||
- Maximum number of connections per pool to be used by the C(infoblox_client.Connector) object.
|
||||
- This is passed as-is to the underlying C(requests.adapters.HTTPAdapter) class.
|
||||
type: int
|
||||
default: 10
|
||||
silent_ssl_warnings:
|
||||
description:
|
||||
- Disable C(urllib3) SSL warnings in the C(infoblox_client.Connector) object.
|
||||
- This is passed as-is to the underlying C(requests.adapters.HTTPAdapter) class.
|
||||
type: bool
|
||||
default: true
|
||||
notes:
|
||||
- "This module must be run locally, which can be achieved by specifying C(connection: local)."
|
||||
- Please read the :ref:`nios_guide` for more detailed information on how to use Infoblox with Ansible.
|
||||
|
||||
'''
|
||||
@@ -35,23 +35,13 @@ DOCUMENTATION = '''
|
||||
type: string
|
||||
required: true
|
||||
host_filter:
|
||||
description:
|
||||
- An Icinga2 API valid host filter. Leave blank for no filtering
|
||||
description: An Icinga2 API valid host filter.
|
||||
type: string
|
||||
required: false
|
||||
validate_certs:
|
||||
description: Enables or disables SSL certificate verification.
|
||||
type: boolean
|
||||
default: true
|
||||
inventory_attr:
|
||||
description:
|
||||
- Allows the override of the inventory name based on different attributes.
|
||||
- This allows for changing the way limits are used.
|
||||
- The current default, C(address), is sometimes not unique or present. We recommend to use C(name) instead.
|
||||
type: string
|
||||
default: address
|
||||
choices: ['name', 'display_name', 'address']
|
||||
version_added: 4.2.0
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -62,7 +52,6 @@ user: ansible
|
||||
password: secure
|
||||
host_filter: \"linux-servers\" in host.groups
|
||||
validate_certs: false
|
||||
inventory_attr: name
|
||||
'''
|
||||
|
||||
import json
|
||||
@@ -70,7 +59,6 @@ import json
|
||||
from ansible.errors import AnsibleParserError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.module_utils.six.moves.urllib.error import HTTPError
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
@@ -88,7 +76,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
self.icinga2_password = None
|
||||
self.ssl_verify = None
|
||||
self.host_filter = None
|
||||
self.inventory_attr = None
|
||||
|
||||
self.cache_key = None
|
||||
self.use_cache = None
|
||||
@@ -127,21 +114,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
if data is not None:
|
||||
request_args['data'] = json.dumps(data)
|
||||
self.display.vvv("Request Args: %s" % request_args)
|
||||
try:
|
||||
response = open_url(request_url, **request_args)
|
||||
except HTTPError as e:
|
||||
try:
|
||||
error_body = json.loads(e.read().decode())
|
||||
self.display.vvv("Error returned: {0}".format(error_body))
|
||||
except Exception:
|
||||
error_body = {"status": None}
|
||||
if e.code == 404 and error_body.get('status') == "No objects found.":
|
||||
raise AnsibleParserError("Host filter returned no data. Please confirm your host_filter value is valid")
|
||||
raise AnsibleParserError("Unexpected data returned: {0} -- {1}".format(e, error_body))
|
||||
|
||||
response = open_url(request_url, **request_args)
|
||||
response_body = response.read()
|
||||
json_data = json.loads(response_body.decode('utf-8'))
|
||||
self.display.vvv("Returned Data: %s" % json.dumps(json_data, indent=4, sort_keys=True))
|
||||
if 200 <= response.status <= 299:
|
||||
return json_data
|
||||
if response.status == 404 and json_data['status'] == "No objects found.":
|
||||
@@ -180,7 +155,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
"""Query for all hosts """
|
||||
self.display.vvv("Querying Icinga2 for inventory")
|
||||
query_args = {
|
||||
"attrs": ["address", "display_name", "state_type", "state", "groups"],
|
||||
"attrs": ["address", "state_type", "state", "groups"],
|
||||
}
|
||||
if self.host_filter is not None:
|
||||
query_args['host_filter'] = self.host_filter
|
||||
@@ -202,35 +177,24 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
"""Convert Icinga2 API data to JSON format for Ansible"""
|
||||
groups_dict = {"_meta": {"hostvars": {}}}
|
||||
for entry in json_data:
|
||||
host_name = entry['name']
|
||||
host_attrs = entry['attrs']
|
||||
if self.inventory_attr == "name":
|
||||
host_name = entry.get('name')
|
||||
if self.inventory_attr == "address":
|
||||
# When looking for address for inventory, if missing fallback to object name
|
||||
if host_attrs.get('address', '') != '':
|
||||
host_name = host_attrs.get('address')
|
||||
else:
|
||||
host_name = entry.get('name')
|
||||
if self.inventory_attr == "display_name":
|
||||
host_name = host_attrs.get('display_name')
|
||||
if host_attrs['state'] == 0:
|
||||
host_attrs['state'] = 'on'
|
||||
else:
|
||||
host_attrs['state'] = 'off'
|
||||
host_groups = host_attrs.get('groups')
|
||||
self.inventory.add_host(host_name)
|
||||
host_groups = host_attrs['groups']
|
||||
host_addr = host_attrs['address']
|
||||
self.inventory.add_host(host_addr)
|
||||
for group in host_groups:
|
||||
if group not in self.inventory.groups.keys():
|
||||
self.inventory.add_group(group)
|
||||
self.inventory.add_child(group, host_name)
|
||||
# If the address attribute is populated, override ansible_host with the value
|
||||
if host_attrs.get('address') != '':
|
||||
self.inventory.set_variable(host_name, 'ansible_host', host_attrs.get('address'))
|
||||
self.inventory.set_variable(host_name, 'hostname', entry.get('name'))
|
||||
self.inventory.set_variable(host_name, 'display_name', host_attrs.get('display_name'))
|
||||
self.inventory.set_variable(host_name, 'state',
|
||||
self.inventory.add_child(group, host_addr)
|
||||
self.inventory.set_variable(host_addr, 'address', host_addr)
|
||||
self.inventory.set_variable(host_addr, 'hostname', host_name)
|
||||
self.inventory.set_variable(host_addr, 'state',
|
||||
host_attrs['state'])
|
||||
self.inventory.set_variable(host_name, 'state_type',
|
||||
self.inventory.set_variable(host_addr, 'state_type',
|
||||
host_attrs['state_type'])
|
||||
return groups_dict
|
||||
|
||||
@@ -247,7 +211,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
self.icinga2_password = self.get_option('password')
|
||||
self.ssl_verify = self.get_option('validate_certs')
|
||||
self.host_filter = self.get_option('host_filter')
|
||||
self.inventory_attr = self.get_option('inventory_attr')
|
||||
# Not currently enabled
|
||||
# self.cache_key = self.get_cache_key(path)
|
||||
# self.use_cache = cache and self.get_option('cache')
|
||||
|
||||
@@ -13,9 +13,6 @@ DOCUMENTATION = r'''
|
||||
- Uses a YAML configuration file that ends with 'lxd.(yml|yaml)'.
|
||||
version_added: "3.0.0"
|
||||
author: "Frank Dornheim (@conloos)"
|
||||
requirements:
|
||||
- ipaddress
|
||||
- lxd >= 4.0
|
||||
options:
|
||||
plugin:
|
||||
description: Token that ensures this is a source file for the 'lxd' plugin.
|
||||
@@ -50,38 +47,26 @@ DOCUMENTATION = r'''
|
||||
- If I(trust_password) is set, this module send a request for authentication before sending any requests.
|
||||
type: str
|
||||
state:
|
||||
description: Filter the instance according to the current status.
|
||||
description: Filter the container according to the current status.
|
||||
type: str
|
||||
default: none
|
||||
choices: [ 'STOPPED', 'STARTING', 'RUNNING', 'none' ]
|
||||
type_filter:
|
||||
prefered_container_network_interface:
|
||||
description:
|
||||
- Filter the instances by type C(virtual-machine), C(container) or C(both).
|
||||
- The first version of the inventory only supported containers.
|
||||
type: str
|
||||
default: container
|
||||
choices: [ 'virtual-machine', 'container', 'both' ]
|
||||
version_added: 4.2.0
|
||||
prefered_instance_network_interface:
|
||||
description:
|
||||
- If an instance has multiple network interfaces, select which one is the prefered as pattern.
|
||||
- If a container has multiple network interfaces, select which one is the prefered as pattern.
|
||||
- Combined with the first number that can be found e.g. 'eth' + 0.
|
||||
- The option has been renamed from I(prefered_container_network_interface) to I(prefered_instance_network_interface) in community.general 3.8.0.
|
||||
The old name still works as an alias.
|
||||
type: str
|
||||
default: eth
|
||||
aliases:
|
||||
- prefered_container_network_interface
|
||||
prefered_instance_network_family:
|
||||
prefered_container_network_family:
|
||||
description:
|
||||
- If an instance has multiple network interfaces, which one is the prefered by family.
|
||||
- If a container has multiple network interfaces, which one is the prefered by family.
|
||||
- Specify C(inet) for IPv4 and C(inet6) for IPv6.
|
||||
type: str
|
||||
default: inet
|
||||
choices: [ 'inet', 'inet6' ]
|
||||
groupby:
|
||||
description:
|
||||
- Create groups by the following keywords C(location), C(network_range), C(os), C(pattern), C(profile), C(release), C(type), C(vlanid).
|
||||
- Create groups by the following keywords C(location), C(pattern), C(network_range), C(os), C(release), C(profile), C(vlanid).
|
||||
- See example for syntax.
|
||||
type: dict
|
||||
'''
|
||||
@@ -96,49 +81,38 @@ plugin: community.general.lxd
|
||||
url: unix:/var/snap/lxd/common/lxd/unix.socket
|
||||
state: RUNNING
|
||||
|
||||
# simple lxd.yml including virtual machines and containers
|
||||
plugin: community.general.lxd
|
||||
url: unix:/var/snap/lxd/common/lxd/unix.socket
|
||||
type_filter: both
|
||||
|
||||
# grouping lxd.yml
|
||||
groupby:
|
||||
locationBerlin:
|
||||
type: location
|
||||
attribute: Berlin
|
||||
netRangeIPv4:
|
||||
type: network_range
|
||||
attribute: 10.98.143.0/24
|
||||
netRangeIPv6:
|
||||
type: network_range
|
||||
attribute: fd42:bd00:7b11:2167:216:3eff::/24
|
||||
osUbuntu:
|
||||
type: os
|
||||
attribute: ubuntu
|
||||
testpattern:
|
||||
type: pattern
|
||||
attribute: test
|
||||
profileDefault:
|
||||
type: profile
|
||||
attribute: default
|
||||
profileX11:
|
||||
type: profile
|
||||
attribute: x11
|
||||
vlan666:
|
||||
type: vlanid
|
||||
attribute: 666
|
||||
locationBerlin:
|
||||
type: location
|
||||
attribute: Berlin
|
||||
osUbuntu:
|
||||
type: os
|
||||
attribute: ubuntu
|
||||
releaseFocal:
|
||||
type: release
|
||||
attribute: focal
|
||||
releaseBionic:
|
||||
type: release
|
||||
attribute: bionic
|
||||
typeVM:
|
||||
type: type
|
||||
attribute: virtual-machine
|
||||
typeContainer:
|
||||
type: type
|
||||
attribute: container
|
||||
vlan666:
|
||||
type: vlanid
|
||||
attribute: 666
|
||||
profileDefault:
|
||||
type: profile
|
||||
attribute: default
|
||||
profileX11:
|
||||
type: profile
|
||||
attribute: x11
|
||||
netRangeIPv4:
|
||||
type: network_range
|
||||
attribute: 10.98.143.0/24
|
||||
netRangeIPv6:
|
||||
type: network_range
|
||||
attribute: fd42:bd00:7b11:2167:216:3eff::/24
|
||||
'''
|
||||
|
||||
import binascii
|
||||
@@ -150,17 +124,10 @@ import socket
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
from ansible.module_utils.six import raise_from
|
||||
from ansible.errors import AnsibleError, AnsibleParserError
|
||||
from ansible_collections.community.general.plugins.module_utils.compat import ipaddress
|
||||
from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException
|
||||
|
||||
try:
|
||||
import ipaddress
|
||||
except ImportError as exc:
|
||||
IPADDRESS_IMPORT_ERROR = exc
|
||||
else:
|
||||
IPADDRESS_IMPORT_ERROR = None
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin):
|
||||
DEBUG = 4
|
||||
@@ -307,10 +274,10 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
network_configs = self.socket.do('GET', '/1.0/networks')
|
||||
return [m.split('/')[3] for m in network_configs['metadata']]
|
||||
|
||||
def _get_instances(self):
|
||||
"""Get instancenames
|
||||
def _get_containers(self):
|
||||
"""Get Containernames
|
||||
|
||||
Returns all instancenames
|
||||
Returns all containernames
|
||||
|
||||
Args:
|
||||
None
|
||||
@@ -319,27 +286,25 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
list(names): names of all instances"""
|
||||
# e.g. {
|
||||
# "metadata": [
|
||||
# "/1.0/instances/foo",
|
||||
# "/1.0/instances/bar"
|
||||
# ],
|
||||
# "status": "Success",
|
||||
# "status_code": 200,
|
||||
# "type": "sync"
|
||||
# }
|
||||
instances = self.socket.do('GET', '/1.0/instances')
|
||||
return [m.split('/')[3] for m in instances['metadata']]
|
||||
list(names): names of all containers"""
|
||||
# e.g. {'type': 'sync',
|
||||
# 'status': 'Success',
|
||||
# 'status_code': 200,
|
||||
# 'operation': '',
|
||||
# 'error_code': 0,
|
||||
# 'error': '',
|
||||
# 'metadata': ['/1.0/containers/udemy-ansible-ubuntu-2004']}
|
||||
containers = self.socket.do('GET', '/1.0/containers')
|
||||
return [m.split('/')[3] for m in containers['metadata']]
|
||||
|
||||
def _get_config(self, branch, name):
|
||||
"""Get inventory of instance
|
||||
"""Get inventory of container
|
||||
|
||||
Get config of instance
|
||||
Get config of container
|
||||
|
||||
Args:
|
||||
str(branch): Name oft the API-Branch
|
||||
str(name): Name of instance
|
||||
str(name): Name of Container
|
||||
Kwargs:
|
||||
None
|
||||
Source:
|
||||
@@ -347,7 +312,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
dict(config): Config of the instance"""
|
||||
dict(config): Config of the container"""
|
||||
config = {}
|
||||
if isinstance(branch, (tuple, list)):
|
||||
config[name] = {branch[1]: self.socket.do('GET', '/1.0/{0}/{1}/{2}'.format(to_native(branch[0]), to_native(name), to_native(branch[1])))}
|
||||
@@ -355,13 +320,13 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
config[name] = {branch: self.socket.do('GET', '/1.0/{0}/{1}'.format(to_native(branch), to_native(name)))}
|
||||
return config
|
||||
|
||||
def get_instance_data(self, names):
|
||||
"""Create Inventory of the instance
|
||||
def get_container_data(self, names):
|
||||
"""Create Inventory of the container
|
||||
|
||||
Iterate through the different branches of the instances and collect Informations.
|
||||
Iterate through the different branches of the containers and collect Informations.
|
||||
|
||||
Args:
|
||||
list(names): List of instance names
|
||||
list(names): List of container names
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
@@ -370,20 +335,20 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
None"""
|
||||
# tuple(('instances','metadata/templates')) to get section in branch
|
||||
# e.g. /1.0/instances/<name>/metadata/templates
|
||||
branches = ['instances', ('instances', 'state')]
|
||||
instance_config = {}
|
||||
branches = ['containers', ('instances', 'state')]
|
||||
container_config = {}
|
||||
for branch in branches:
|
||||
for name in names:
|
||||
instance_config['instances'] = self._get_config(branch, name)
|
||||
self.data = dict_merge(instance_config, self.data)
|
||||
container_config['containers'] = self._get_config(branch, name)
|
||||
self.data = dict_merge(container_config, self.data)
|
||||
|
||||
def get_network_data(self, names):
|
||||
"""Create Inventory of the instance
|
||||
"""Create Inventory of the container
|
||||
|
||||
Iterate through the different branches of the instances and collect Informations.
|
||||
Iterate through the different branches of the containers and collect Informations.
|
||||
|
||||
Args:
|
||||
list(names): List of instance names
|
||||
list(names): List of container names
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
@@ -402,26 +367,26 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
network_config['networks'] = {name: None}
|
||||
self.data = dict_merge(network_config, self.data)
|
||||
|
||||
def extract_network_information_from_instance_config(self, instance_name):
|
||||
def extract_network_information_from_container_config(self, container_name):
|
||||
"""Returns the network interface configuration
|
||||
|
||||
Returns the network ipv4 and ipv6 config of the instance without local-link
|
||||
Returns the network ipv4 and ipv6 config of the container without local-link
|
||||
|
||||
Args:
|
||||
str(instance_name): Name oft he instance
|
||||
str(container_name): Name oft he container
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
dict(network_configuration): network config"""
|
||||
instance_network_interfaces = self._get_data_entry('instances/{0}/state/metadata/network'.format(instance_name))
|
||||
container_network_interfaces = self._get_data_entry('containers/{0}/state/metadata/network'.format(container_name))
|
||||
network_configuration = None
|
||||
if instance_network_interfaces:
|
||||
if container_network_interfaces:
|
||||
network_configuration = {}
|
||||
gen_interface_names = [interface_name for interface_name in instance_network_interfaces if interface_name != 'lo']
|
||||
gen_interface_names = [interface_name for interface_name in container_network_interfaces if interface_name != 'lo']
|
||||
for interface_name in gen_interface_names:
|
||||
gen_address = [address for address in instance_network_interfaces[interface_name]['addresses'] if address.get('scope') != 'link']
|
||||
gen_address = [address for address in container_network_interfaces[interface_name]['addresses'] if address.get('scope') != 'link']
|
||||
network_configuration[interface_name] = []
|
||||
for address in gen_address:
|
||||
address_set = {}
|
||||
@@ -432,24 +397,24 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
network_configuration[interface_name].append(address_set)
|
||||
return network_configuration
|
||||
|
||||
def get_prefered_instance_network_interface(self, instance_name):
|
||||
"""Helper to get the prefered interface of thr instance
|
||||
def get_prefered_container_network_interface(self, container_name):
|
||||
"""Helper to get the prefered interface of thr container
|
||||
|
||||
Helper to get the prefered interface provide by neme pattern from 'prefered_instance_network_interface'.
|
||||
Helper to get the prefered interface provide by neme pattern from 'prefered_container_network_interface'.
|
||||
|
||||
Args:
|
||||
str(containe_name): name of instance
|
||||
str(containe_name): name of container
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
str(prefered_interface): None or interface name"""
|
||||
instance_network_interfaces = self._get_data_entry('inventory/{0}/network_interfaces'.format(instance_name))
|
||||
container_network_interfaces = self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name))
|
||||
prefered_interface = None # init
|
||||
if instance_network_interfaces: # instance have network interfaces
|
||||
if container_network_interfaces: # container have network interfaces
|
||||
# generator if interfaces which start with the desired pattern
|
||||
net_generator = [interface for interface in instance_network_interfaces if interface.startswith(self.prefered_instance_network_interface)]
|
||||
net_generator = [interface for interface in container_network_interfaces if interface.startswith(self.prefered_container_network_interface)]
|
||||
selected_interfaces = [] # init
|
||||
for interface in net_generator:
|
||||
selected_interfaces.append(interface)
|
||||
@@ -457,13 +422,13 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
prefered_interface = sorted(selected_interfaces)[0]
|
||||
return prefered_interface
|
||||
|
||||
def get_instance_vlans(self, instance_name):
|
||||
"""Get VLAN(s) from instance
|
||||
def get_container_vlans(self, container_name):
|
||||
"""Get VLAN(s) from container
|
||||
|
||||
Helper to get the VLAN_ID from the instance
|
||||
Helper to get the VLAN_ID from the container
|
||||
|
||||
Args:
|
||||
str(containe_name): name of instance
|
||||
str(containe_name): name of container
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
@@ -476,13 +441,13 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network)):
|
||||
network_vlans[network] = self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network))
|
||||
|
||||
# get networkdevices of instance and return
|
||||
# get networkdevices of container and return
|
||||
# e.g.
|
||||
# "eth0":{ "name":"eth0",
|
||||
# "network":"lxdbr0",
|
||||
# "type":"nic"},
|
||||
vlan_ids = {}
|
||||
devices = self._get_data_entry('instances/{0}/instances/metadata/expanded_devices'.format(to_native(instance_name)))
|
||||
devices = self._get_data_entry('containers/{0}/containers/metadata/expanded_devices'.format(to_native(container_name)))
|
||||
for device in devices:
|
||||
if 'network' in devices[device]:
|
||||
if devices[device]['network'] in network_vlans:
|
||||
@@ -518,14 +483,14 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def _set_data_entry(self, instance_name, key, value, path=None):
|
||||
def _set_data_entry(self, container_name, key, value, path=None):
|
||||
"""Helper to save data
|
||||
|
||||
Helper to save the data in self.data
|
||||
Detect if data is allready in branch and use dict_merge() to prevent that branch is overwritten.
|
||||
|
||||
Args:
|
||||
str(instance_name): name of instance
|
||||
str(container_name): name of container
|
||||
str(key): same as dict
|
||||
*(value): same as dict
|
||||
Kwargs:
|
||||
@@ -536,24 +501,24 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
None"""
|
||||
if not path:
|
||||
path = self.data['inventory']
|
||||
if instance_name not in path:
|
||||
path[instance_name] = {}
|
||||
if container_name not in path:
|
||||
path[container_name] = {}
|
||||
|
||||
try:
|
||||
if isinstance(value, dict) and key in path[instance_name]:
|
||||
path[instance_name] = dict_merge(value, path[instance_name][key])
|
||||
if isinstance(value, dict) and key in path[container_name]:
|
||||
path[container_name] = dict_merge(value, path[container_name][key])
|
||||
else:
|
||||
path[instance_name][key] = value
|
||||
path[container_name][key] = value
|
||||
except KeyError as err:
|
||||
raise AnsibleParserError("Unable to store Informations: {0}".format(to_native(err)))
|
||||
|
||||
def extract_information_from_instance_configs(self):
|
||||
def extract_information_from_container_configs(self):
|
||||
"""Process configuration information
|
||||
|
||||
Preparation of the data
|
||||
|
||||
Args:
|
||||
dict(configs): instance configurations
|
||||
dict(configs): Container configurations
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
@@ -564,35 +529,33 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if 'inventory' not in self.data:
|
||||
self.data['inventory'] = {}
|
||||
|
||||
for instance_name in self.data['instances']:
|
||||
self._set_data_entry(instance_name, 'os', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/config/image.os'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'release', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/config/image.release'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'version', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/config/image.version'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'profile', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/profiles'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'location', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/location'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'state', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/config/volatile.last_state.power'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'type', self._get_data_entry(
|
||||
'instances/{0}/instances/metadata/type'.format(instance_name)))
|
||||
self._set_data_entry(instance_name, 'network_interfaces', self.extract_network_information_from_instance_config(instance_name))
|
||||
self._set_data_entry(instance_name, 'preferred_interface', self.get_prefered_instance_network_interface(instance_name))
|
||||
self._set_data_entry(instance_name, 'vlan_ids', self.get_instance_vlans(instance_name))
|
||||
for container_name in self.data['containers']:
|
||||
self._set_data_entry(container_name, 'os', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/config/image.os'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'release', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/config/image.release'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'version', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/config/image.version'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'profile', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/profiles'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'location', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/location'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'state', self._get_data_entry(
|
||||
'containers/{0}/containers/metadata/config/volatile.last_state.power'.format(container_name)))
|
||||
self._set_data_entry(container_name, 'network_interfaces', self.extract_network_information_from_container_config(container_name))
|
||||
self._set_data_entry(container_name, 'preferred_interface', self.get_prefered_container_network_interface(container_name))
|
||||
self._set_data_entry(container_name, 'vlan_ids', self.get_container_vlans(container_name))
|
||||
|
||||
def build_inventory_network(self, instance_name):
|
||||
"""Add the network interfaces of the instance to the inventory
|
||||
def build_inventory_network(self, container_name):
|
||||
"""Add the network interfaces of the container to the inventory
|
||||
|
||||
Logic:
|
||||
- if the instance have no interface -> 'ansible_connection: local'
|
||||
- get preferred_interface & prefered_instance_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
|
||||
- first Interface from: network_interfaces prefered_instance_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
|
||||
- if the container have no interface -> 'ansible_connection: local'
|
||||
- get preferred_interface & prefered_container_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
|
||||
- first Interface from: network_interfaces prefered_container_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
|
||||
|
||||
Args:
|
||||
str(instance_name): name of instance
|
||||
str(container_name): name of container
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
@@ -600,45 +563,45 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
Returns:
|
||||
None"""
|
||||
|
||||
def interface_selection(instance_name):
|
||||
"""Select instance Interface for inventory
|
||||
def interface_selection(container_name):
|
||||
"""Select container Interface for inventory
|
||||
|
||||
Logic:
|
||||
- get preferred_interface & prefered_instance_network_family -> str(IP)
|
||||
- first Interface from: network_interfaces prefered_instance_network_family -> str(IP)
|
||||
- get preferred_interface & prefered_container_network_family -> str(IP)
|
||||
- first Interface from: network_interfaces prefered_container_network_family -> str(IP)
|
||||
|
||||
Args:
|
||||
str(instance_name): name of instance
|
||||
str(container_name): name of container
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
dict(interface_name: ip)"""
|
||||
prefered_interface = self._get_data_entry('inventory/{0}/preferred_interface'.format(instance_name)) # name or None
|
||||
prefered_instance_network_family = self.prefered_instance_network_family
|
||||
prefered_interface = self._get_data_entry('inventory/{0}/preferred_interface'.format(container_name)) # name or None
|
||||
prefered_container_network_family = self.prefered_container_network_family
|
||||
|
||||
ip_address = ''
|
||||
if prefered_interface:
|
||||
interface = self._get_data_entry('inventory/{0}/network_interfaces/{1}'.format(instance_name, prefered_interface))
|
||||
interface = self._get_data_entry('inventory/{0}/network_interfaces/{1}'.format(container_name, prefered_interface))
|
||||
for config in interface:
|
||||
if config['family'] == prefered_instance_network_family:
|
||||
if config['family'] == prefered_container_network_family:
|
||||
ip_address = config['address']
|
||||
break
|
||||
else:
|
||||
interfaces = self._get_data_entry('inventory/{0}/network_interfaces'.format(instance_name))
|
||||
for interface in interfaces.values():
|
||||
for config in interface:
|
||||
if config['family'] == prefered_instance_network_family:
|
||||
ip_address = config['address']
|
||||
break
|
||||
interface = self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name))
|
||||
for config in interface:
|
||||
if config['family'] == prefered_container_network_family:
|
||||
ip_address = config['address']
|
||||
break
|
||||
return ip_address
|
||||
|
||||
if self._get_data_entry('inventory/{0}/network_interfaces'.format(instance_name)): # instance have network interfaces
|
||||
self.inventory.set_variable(instance_name, 'ansible_connection', 'ssh')
|
||||
self.inventory.set_variable(instance_name, 'ansible_host', interface_selection(instance_name))
|
||||
if self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name)): # container have network interfaces
|
||||
if self._get_data_entry('inventory/{0}/preferred_interface'.format(container_name)): # container have a preferred interface
|
||||
self.inventory.set_variable(container_name, 'ansible_connection', 'ssh')
|
||||
self.inventory.set_variable(container_name, 'ansible_host', interface_selection(container_name))
|
||||
else:
|
||||
self.inventory.set_variable(instance_name, 'ansible_connection', 'local')
|
||||
self.inventory.set_variable(container_name, 'ansible_connection', 'local')
|
||||
|
||||
def build_inventory_hosts(self):
|
||||
"""Build host-part dynamic inventory
|
||||
@@ -654,33 +617,29 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
None
|
||||
Returns:
|
||||
None"""
|
||||
for instance_name in self.data['inventory']:
|
||||
instance_state = str(self._get_data_entry('inventory/{0}/state'.format(instance_name)) or "STOPPED").lower()
|
||||
|
||||
# Only consider instances that match the "state" filter, if self.state is not None
|
||||
for container_name in self.data['inventory']:
|
||||
# Only consider containers that match the "state" filter, if self.state is not None
|
||||
if self.filter:
|
||||
if self.filter.lower() != instance_state:
|
||||
if self.filter.lower() != self._get_data_entry('inventory/{0}/state'.format(container_name)).lower():
|
||||
continue
|
||||
# add instance
|
||||
self.inventory.add_host(instance_name)
|
||||
# add container
|
||||
self.inventory.add_host(container_name)
|
||||
# add network informations
|
||||
self.build_inventory_network(instance_name)
|
||||
self.build_inventory_network(container_name)
|
||||
# add os
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_os', self._get_data_entry('inventory/{0}/os'.format(instance_name)).lower())
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_os', self._get_data_entry('inventory/{0}/os'.format(container_name)).lower())
|
||||
# add release
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_release', self._get_data_entry('inventory/{0}/release'.format(instance_name)).lower())
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_release', self._get_data_entry('inventory/{0}/release'.format(container_name)).lower())
|
||||
# add profile
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_profile', self._get_data_entry('inventory/{0}/profile'.format(instance_name)))
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_profile', self._get_data_entry('inventory/{0}/profile'.format(container_name)))
|
||||
# add state
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_state', instance_state)
|
||||
# add type
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_type', self._get_data_entry('inventory/{0}/type'.format(instance_name)))
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_state', self._get_data_entry('inventory/{0}/state'.format(container_name)).lower())
|
||||
# add location information
|
||||
if self._get_data_entry('inventory/{0}/location'.format(instance_name)) != "none": # wrong type by lxd 'none' != 'None'
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_location', self._get_data_entry('inventory/{0}/location'.format(instance_name)))
|
||||
if self._get_data_entry('inventory/{0}/location'.format(container_name)) != "none": # wrong type by lxd 'none' != 'None'
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_location', self._get_data_entry('inventory/{0}/location'.format(container_name)))
|
||||
# add VLAN_ID information
|
||||
if self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name)):
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_vlan_ids', self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name)))
|
||||
if self._get_data_entry('inventory/{0}/vlan_ids'.format(container_name)):
|
||||
self.inventory.set_variable(container_name, 'ansible_lxd_vlan_ids', self._get_data_entry('inventory/{0}/vlan_ids'.format(container_name)))
|
||||
|
||||
def build_inventory_groups_location(self, group_name):
|
||||
"""create group by attribute: location
|
||||
@@ -697,9 +656,9 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
for instance_name in self.inventory.hosts:
|
||||
if 'ansible_lxd_location' in self.inventory.get_host(instance_name).get_vars():
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
for container_name in self.inventory.hosts:
|
||||
if 'ansible_lxd_location' in self.inventory.get_host(container_name).get_vars():
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups_pattern(self, group_name):
|
||||
"""create group by name pattern
|
||||
@@ -718,10 +677,10 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
|
||||
regex_pattern = self.groupby[group_name].get('attribute')
|
||||
|
||||
for instance_name in self.inventory.hosts:
|
||||
result = re.search(regex_pattern, instance_name)
|
||||
for container_name in self.inventory.hosts:
|
||||
result = re.search(regex_pattern, container_name)
|
||||
if result:
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups_network_range(self, group_name):
|
||||
"""check if IP is in network-class
|
||||
@@ -744,14 +703,14 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
raise AnsibleParserError(
|
||||
'Error while parsing network range {0}: {1}'.format(self.groupby[group_name].get('attribute'), to_native(err)))
|
||||
|
||||
for instance_name in self.inventory.hosts:
|
||||
if self.data['inventory'][instance_name].get('network_interfaces') is not None:
|
||||
for interface in self.data['inventory'][instance_name].get('network_interfaces'):
|
||||
for interface_family in self.data['inventory'][instance_name].get('network_interfaces')[interface]:
|
||||
for container_name in self.inventory.hosts:
|
||||
if self.data['inventory'][container_name].get('network_interfaces') is not None:
|
||||
for interface in self.data['inventory'][container_name].get('network_interfaces'):
|
||||
for interface_family in self.data['inventory'][container_name].get('network_interfaces')[interface]:
|
||||
try:
|
||||
address = ipaddress.ip_address(to_text(interface_family['address']))
|
||||
if address.version == network.version and address in network:
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
except ValueError:
|
||||
# Ignore invalid IP addresses returned by lxd
|
||||
pass
|
||||
@@ -762,7 +721,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
Args:
|
||||
str(group_name): Group name
|
||||
Kwargs:
|
||||
None
|
||||
Noneself.data['inventory'][container_name][interface]
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
@@ -771,12 +730,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
gen_instances = [
|
||||
instance_name for instance_name in self.inventory.hosts
|
||||
if 'ansible_lxd_os' in self.inventory.get_host(instance_name).get_vars()]
|
||||
for instance_name in gen_instances:
|
||||
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_os'):
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
gen_containers = [
|
||||
container_name for container_name in self.inventory.hosts
|
||||
if 'ansible_lxd_os' in self.inventory.get_host(container_name).get_vars()]
|
||||
for container_name in gen_containers:
|
||||
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(container_name).get_vars().get('ansible_lxd_os'):
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups_release(self, group_name):
|
||||
"""create group by attribute: release
|
||||
@@ -793,12 +752,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
gen_instances = [
|
||||
instance_name for instance_name in self.inventory.hosts
|
||||
if 'ansible_lxd_release' in self.inventory.get_host(instance_name).get_vars()]
|
||||
for instance_name in gen_instances:
|
||||
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_release'):
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
gen_containers = [
|
||||
container_name for container_name in self.inventory.hosts
|
||||
if 'ansible_lxd_release' in self.inventory.get_host(container_name).get_vars()]
|
||||
for container_name in gen_containers:
|
||||
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(container_name).get_vars().get('ansible_lxd_release'):
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups_profile(self, group_name):
|
||||
"""create group by attribute: profile
|
||||
@@ -815,12 +774,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
gen_instances = [
|
||||
instance_name for instance_name in self.inventory.hosts.keys()
|
||||
if 'ansible_lxd_profile' in self.inventory.get_host(instance_name).get_vars().keys()]
|
||||
for instance_name in gen_instances:
|
||||
if self.groupby[group_name].get('attribute').lower() in self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_profile'):
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
gen_containers = [
|
||||
container_name for container_name in self.inventory.hosts.keys()
|
||||
if 'ansible_lxd_profile' in self.inventory.get_host(container_name).get_vars().keys()]
|
||||
for container_name in gen_containers:
|
||||
if self.groupby[group_name].get('attribute').lower() in self.inventory.get_host(container_name).get_vars().get('ansible_lxd_profile'):
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups_vlanid(self, group_name):
|
||||
"""create group by attribute: vlanid
|
||||
@@ -837,34 +796,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
gen_instances = [
|
||||
instance_name for instance_name in self.inventory.hosts.keys()
|
||||
if 'ansible_lxd_vlan_ids' in self.inventory.get_host(instance_name).get_vars().keys()]
|
||||
for instance_name in gen_instances:
|
||||
if self.groupby[group_name].get('attribute') in self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_vlan_ids').values():
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
|
||||
def build_inventory_groups_type(self, group_name):
|
||||
"""create group by attribute: type
|
||||
|
||||
Args:
|
||||
str(group_name): Group name
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
None"""
|
||||
# maybe we just want to expand one group
|
||||
if group_name not in self.inventory.groups:
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
gen_instances = [
|
||||
instance_name for instance_name in self.inventory.hosts
|
||||
if 'ansible_lxd_type' in self.inventory.get_host(instance_name).get_vars()]
|
||||
for instance_name in gen_instances:
|
||||
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_type'):
|
||||
self.inventory.add_child(group_name, instance_name)
|
||||
gen_containers = [
|
||||
container_name for container_name in self.inventory.hosts.keys()
|
||||
if 'ansible_lxd_vlan_ids' in self.inventory.get_host(container_name).get_vars().keys()]
|
||||
for container_name in gen_containers:
|
||||
if self.groupby[group_name].get('attribute') in self.inventory.get_host(container_name).get_vars().get('ansible_lxd_vlan_ids').values():
|
||||
self.inventory.add_child(group_name, container_name)
|
||||
|
||||
def build_inventory_groups(self):
|
||||
"""Build group-part dynamic inventory
|
||||
@@ -893,7 +830,6 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
* 'release'
|
||||
* 'profile'
|
||||
* 'vlanid'
|
||||
* 'type'
|
||||
|
||||
Args:
|
||||
str(group_name): Group name
|
||||
@@ -919,8 +855,6 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
self.build_inventory_groups_profile(group_name)
|
||||
elif self.groupby[group_name].get('type') == 'vlanid':
|
||||
self.build_inventory_groups_vlanid(group_name)
|
||||
elif self.groupby[group_name].get('type') == 'type':
|
||||
self.build_inventory_groups_type(group_name)
|
||||
else:
|
||||
raise AnsibleParserError('Unknown group type: {0}'.format(to_native(group_name)))
|
||||
|
||||
@@ -947,30 +881,10 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
self.build_inventory_hosts()
|
||||
self.build_inventory_groups()
|
||||
|
||||
def cleandata(self):
|
||||
"""Clean the dynamic inventory
|
||||
|
||||
The first version of the inventory only supported container.
|
||||
This will change in the future.
|
||||
The following function cleans up the data and remove the all items with the wrong type.
|
||||
|
||||
Args:
|
||||
None
|
||||
Kwargs:
|
||||
None
|
||||
Raises:
|
||||
None
|
||||
Returns:
|
||||
None"""
|
||||
iter_keys = list(self.data['instances'].keys())
|
||||
for instance_name in iter_keys:
|
||||
if self._get_data_entry('instances/{0}/instances/metadata/type'.format(instance_name)) != self.type_filter:
|
||||
del self.data['instances'][instance_name]
|
||||
|
||||
def _populate(self):
|
||||
"""Return the hosts and groups
|
||||
|
||||
Returns the processed instance configurations from the lxd import
|
||||
Returns the processed container configurations from the lxd import
|
||||
|
||||
Args:
|
||||
None
|
||||
@@ -983,16 +897,10 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
|
||||
if len(self.data) == 0: # If no data is injected by unittests open socket
|
||||
self.socket = self._connect_to_socket()
|
||||
self.get_instance_data(self._get_instances())
|
||||
self.get_container_data(self._get_containers())
|
||||
self.get_network_data(self._get_networks())
|
||||
|
||||
# The first version of the inventory only supported containers.
|
||||
# This will change in the future.
|
||||
# The following function cleans up the data.
|
||||
if self.type_filter != 'both':
|
||||
self.cleandata()
|
||||
|
||||
self.extract_information_from_instance_configs()
|
||||
self.extract_information_from_container_configs()
|
||||
|
||||
# self.display.vvv(self.save_json_data([os.path.abspath(__file__)]))
|
||||
|
||||
@@ -1016,10 +924,6 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
AnsibleParserError
|
||||
Returns:
|
||||
None"""
|
||||
if IPADDRESS_IMPORT_ERROR:
|
||||
raise_from(
|
||||
AnsibleError('another_library must be installed to use this plugin'),
|
||||
IPADDRESS_IMPORT_ERROR)
|
||||
|
||||
super(InventoryModule, self).parse(inventory, loader, path, cache=False)
|
||||
# Read the inventory YAML file
|
||||
@@ -1031,9 +935,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
self.data = {} # store for inventory-data
|
||||
self.groupby = self.get_option('groupby')
|
||||
self.plugin = self.get_option('plugin')
|
||||
self.prefered_instance_network_family = self.get_option('prefered_instance_network_family')
|
||||
self.prefered_instance_network_interface = self.get_option('prefered_instance_network_interface')
|
||||
self.type_filter = self.get_option('type_filter')
|
||||
self.prefered_container_network_family = self.get_option('prefered_container_network_family')
|
||||
self.prefered_container_network_interface = self.get_option('prefered_container_network_interface')
|
||||
if self.get_option('state').lower() == 'none': # none in config is str()
|
||||
self.filter = None
|
||||
else:
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: xen_orchestra
|
||||
short_description: Xen Orchestra inventory source
|
||||
version_added: 4.1.0
|
||||
author:
|
||||
- Dom Del Nano (@ddelnano) <ddelnano@gmail.com>
|
||||
- Samori Gorse (@shinuza) <samorigorse@gmail.com>
|
||||
requirements:
|
||||
- websocket-client >= 1.0.0
|
||||
description:
|
||||
- Get inventory hosts from a Xen Orchestra deployment.
|
||||
- 'Uses a configuration file as an inventory source, it must end in C(.xen_orchestra.yml) or C(.xen_orchestra.yaml).'
|
||||
extends_documentation_fragment:
|
||||
- constructed
|
||||
- inventory_cache
|
||||
options:
|
||||
plugin:
|
||||
description: The name of this plugin, it should always be set to C(community.general.xen_orchestra) for this plugin to recognize it as its own.
|
||||
required: yes
|
||||
choices: ['community.general.xen_orchestra']
|
||||
type: str
|
||||
api_host:
|
||||
description:
|
||||
- API host to XOA API.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(ANSIBLE_XO_HOST) will be used instead.
|
||||
type: str
|
||||
env:
|
||||
- name: ANSIBLE_XO_HOST
|
||||
user:
|
||||
description:
|
||||
- Xen Orchestra user.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(ANSIBLE_XO_USER) will be used instead.
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
- name: ANSIBLE_XO_USER
|
||||
password:
|
||||
description:
|
||||
- Xen Orchestra password.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(ANSIBLE_XO_PASSWORD) will be used instead.
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
- name: ANSIBLE_XO_PASSWORD
|
||||
validate_certs:
|
||||
description: Verify TLS certificate if using HTTPS.
|
||||
type: boolean
|
||||
default: true
|
||||
use_ssl:
|
||||
description: Use wss when connecting to the Xen Orchestra API
|
||||
type: boolean
|
||||
default: true
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = '''
|
||||
# file must be named xen_orchestra.yaml or xen_orchestra.yml
|
||||
simple_config_file:
|
||||
plugin: community.general.xen_orchestra
|
||||
api_host: 192.168.1.255
|
||||
user: xo
|
||||
password: xo_pwd
|
||||
validate_certs: true
|
||||
use_ssl: true
|
||||
groups:
|
||||
kube_nodes: "'kube_node' in tags"
|
||||
compose:
|
||||
ansible_port: 2222
|
||||
|
||||
'''
|
||||
|
||||
import json
|
||||
import ssl
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
|
||||
# 3rd party imports
|
||||
try:
|
||||
HAS_WEBSOCKET = True
|
||||
import websocket
|
||||
from websocket import create_connection
|
||||
|
||||
if LooseVersion(websocket.__version__) <= LooseVersion('1.0.0'):
|
||||
raise ImportError
|
||||
except ImportError as e:
|
||||
HAS_WEBSOCKET = False
|
||||
|
||||
|
||||
HALTED = 'Halted'
|
||||
PAUSED = 'Paused'
|
||||
RUNNING = 'Running'
|
||||
SUSPENDED = 'Suspended'
|
||||
POWER_STATES = [RUNNING, HALTED, SUSPENDED, PAUSED]
|
||||
HOST_GROUP = 'xo_hosts'
|
||||
POOL_GROUP = 'xo_pools'
|
||||
|
||||
|
||||
def clean_group_name(label):
|
||||
return label.lower().replace(' ', '-').replace('-', '_')
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
''' Host inventory parser for ansible using XenOrchestra as source. '''
|
||||
|
||||
NAME = 'community.general.xen_orchestra'
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super(InventoryModule, self).__init__()
|
||||
|
||||
# from config
|
||||
self.counter = -1
|
||||
self.session = None
|
||||
self.cache_key = None
|
||||
self.use_cache = None
|
||||
|
||||
@property
|
||||
def pointer(self):
|
||||
self.counter += 1
|
||||
return self.counter
|
||||
|
||||
def create_connection(self, xoa_api_host):
|
||||
validate_certs = self.get_option('validate_certs')
|
||||
use_ssl = self.get_option('use_ssl')
|
||||
proto = 'wss' if use_ssl else 'ws'
|
||||
|
||||
sslopt = None if validate_certs else {'cert_reqs': ssl.CERT_NONE}
|
||||
self.conn = create_connection(
|
||||
'{0}://{1}/api/'.format(proto, xoa_api_host), sslopt=sslopt)
|
||||
|
||||
def login(self, user, 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):
|
||||
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(
|
||||
'Could not request: {0}'.format(answer['error']))
|
||||
|
||||
return answer['result']
|
||||
|
||||
def _get_objects(self):
|
||||
self.create_connection(self.xoa_api_host)
|
||||
self.login(self.xoa_user, self.xoa_password)
|
||||
|
||||
return {
|
||||
'vms': self.get_object('VM'),
|
||||
'pools': self.get_object('pool'),
|
||||
'hosts': self.get_object('host'),
|
||||
}
|
||||
|
||||
def _apply_constructable(self, name, variables):
|
||||
strict = self.get_option('strict')
|
||||
self._add_host_to_composed_groups(self.get_option('groups'), variables, name, strict=strict)
|
||||
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), variables, name, strict=strict)
|
||||
self._set_composite_vars(self.get_option('compose'), variables, name, strict=strict)
|
||||
|
||||
def _add_vms(self, vms, hosts, pools):
|
||||
for uuid, vm in vms.items():
|
||||
group = 'with_ip'
|
||||
ip = vm.get('mainIpAddress')
|
||||
entry_name = uuid
|
||||
power_state = vm['power_state'].lower()
|
||||
pool_name = self._pool_group_name_for_uuid(pools, vm['$poolId'])
|
||||
host_name = self._host_group_name_for_uuid(hosts, vm['$container'])
|
||||
|
||||
self.inventory.add_host(entry_name)
|
||||
|
||||
# Grouping by power state
|
||||
self.inventory.add_child(power_state, entry_name)
|
||||
|
||||
# Grouping by host
|
||||
if host_name:
|
||||
self.inventory.add_child(host_name, entry_name)
|
||||
|
||||
# Grouping by pool
|
||||
if pool_name:
|
||||
self.inventory.add_child(pool_name, entry_name)
|
||||
|
||||
# Grouping VMs with an IP together
|
||||
if ip is None:
|
||||
group = 'without_ip'
|
||||
self.inventory.add_group(group)
|
||||
self.inventory.add_child(group, entry_name)
|
||||
|
||||
# Adding meta
|
||||
self.inventory.set_variable(entry_name, 'uuid', uuid)
|
||||
self.inventory.set_variable(entry_name, 'ip', ip)
|
||||
self.inventory.set_variable(entry_name, 'ansible_host', ip)
|
||||
self.inventory.set_variable(entry_name, 'power_state', power_state)
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'name_label', vm['name_label'])
|
||||
self.inventory.set_variable(entry_name, 'type', vm['type'])
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'cpus', vm['CPUs']['number'])
|
||||
self.inventory.set_variable(entry_name, 'tags', vm['tags'])
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'memory', vm['memory']['size'])
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'has_ip', group == 'with_ip')
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'is_managed', vm.get('managementAgentDetected', False))
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'os_version', vm['os_version'])
|
||||
|
||||
self._apply_constructable(entry_name, self.inventory.get_host(entry_name).get_vars())
|
||||
|
||||
def _add_hosts(self, hosts, pools):
|
||||
for host in hosts.values():
|
||||
entry_name = host['uuid']
|
||||
group_name = 'xo_host_{0}'.format(
|
||||
clean_group_name(host['name_label']))
|
||||
pool_name = self._pool_group_name_for_uuid(pools, host['$poolId'])
|
||||
|
||||
self.inventory.add_group(group_name)
|
||||
self.inventory.add_host(entry_name)
|
||||
self.inventory.add_child(HOST_GROUP, entry_name)
|
||||
self.inventory.add_child(pool_name, entry_name)
|
||||
|
||||
self.inventory.set_variable(entry_name, 'enabled', host['enabled'])
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'hostname', host['hostname'])
|
||||
self.inventory.set_variable(entry_name, 'memory', host['memory'])
|
||||
self.inventory.set_variable(entry_name, 'address', host['address'])
|
||||
self.inventory.set_variable(entry_name, 'cpus', host['cpus'])
|
||||
self.inventory.set_variable(entry_name, 'type', 'host')
|
||||
self.inventory.set_variable(entry_name, 'tags', host['tags'])
|
||||
self.inventory.set_variable(entry_name, 'version', host['version'])
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'power_state', host['power_state'].lower())
|
||||
self.inventory.set_variable(
|
||||
entry_name, 'product_brand', host['productBrand'])
|
||||
|
||||
for pool in pools.values():
|
||||
group_name = 'xo_pool_{0}'.format(
|
||||
clean_group_name(pool['name_label']))
|
||||
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
def _add_pools(self, pools):
|
||||
for pool in pools.values():
|
||||
group_name = 'xo_pool_{0}'.format(
|
||||
clean_group_name(pool['name_label']))
|
||||
|
||||
self.inventory.add_group(group_name)
|
||||
|
||||
# TODO: Refactor
|
||||
def _pool_group_name_for_uuid(self, pools, pool_uuid):
|
||||
for pool in pools:
|
||||
if pool == pool_uuid:
|
||||
return 'xo_pool_{0}'.format(
|
||||
clean_group_name(pools[pool_uuid]['name_label']))
|
||||
|
||||
# TODO: Refactor
|
||||
def _host_group_name_for_uuid(self, hosts, host_uuid):
|
||||
for host in hosts:
|
||||
if host == host_uuid:
|
||||
return 'xo_host_{0}'.format(
|
||||
clean_group_name(hosts[host_uuid]['name_label']
|
||||
))
|
||||
|
||||
def _populate(self, objects):
|
||||
# Prepare general groups
|
||||
self.inventory.add_group(HOST_GROUP)
|
||||
self.inventory.add_group(POOL_GROUP)
|
||||
for group in POWER_STATES:
|
||||
self.inventory.add_group(group.lower())
|
||||
|
||||
self._add_pools(objects['pools'])
|
||||
self._add_hosts(objects['hosts'], objects['pools'])
|
||||
self._add_vms(objects['vms'], objects['hosts'], objects['pools'])
|
||||
|
||||
def verify_file(self, path):
|
||||
|
||||
valid = False
|
||||
if super(InventoryModule, self).verify_file(path):
|
||||
if path.endswith(('xen_orchestra.yaml', 'xen_orchestra.yml')):
|
||||
valid = True
|
||||
else:
|
||||
self.display.vvv(
|
||||
'Skipping due to inventory source not ending in "xen_orchestra.yaml" nor "xen_orchestra.yml"')
|
||||
return valid
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
if not HAS_WEBSOCKET:
|
||||
raise AnsibleError('This plugin requires websocket-client 1.0.0 or higher: '
|
||||
'https://github.com/websocket-client/websocket-client.')
|
||||
|
||||
super(InventoryModule, self).parse(inventory, loader, path)
|
||||
|
||||
# read config from file, this sets 'options'
|
||||
self._read_config_data(path)
|
||||
self.inventory = inventory
|
||||
|
||||
self.protocol = 'wss'
|
||||
self.xoa_api_host = self.get_option('api_host')
|
||||
self.xoa_user = self.get_option('user')
|
||||
self.xoa_password = self.get_option('password')
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
|
||||
self.validate_certs = self.get_option('validate_certs')
|
||||
if not self.get_option('use_ssl'):
|
||||
self.protocol = 'ws'
|
||||
|
||||
objects = self._get_objects()
|
||||
self._populate(objects)
|
||||
@@ -1,138 +0,0 @@
|
||||
# (c) 2021, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
name: collection_version
|
||||
author: Felix Fontein (@felixfontein)
|
||||
version_added: "4.0.0"
|
||||
short_description: Retrieves the version of an installed collection
|
||||
description:
|
||||
- This lookup allows to query the version of an installed collection, and to determine whether a
|
||||
collection is installed at all.
|
||||
- By default it returns C(none) for non-existing collections and C(*) for collections without a
|
||||
version number. The latter should only happen in development environments, or when installing
|
||||
a collection from git which has no version in its C(galaxy.yml). This behavior can be adjusted
|
||||
by providing other values with I(result_not_found) and I(result_no_version).
|
||||
options:
|
||||
_terms:
|
||||
description:
|
||||
- The collections to look for.
|
||||
- For example C(community.general).
|
||||
type: list
|
||||
elements: str
|
||||
required: true
|
||||
result_not_found:
|
||||
description:
|
||||
- The value to return when the collection could not be found.
|
||||
- By default, C(none) is returned.
|
||||
type: string
|
||||
default: ~
|
||||
result_no_version:
|
||||
description:
|
||||
- The value to return when the collection has no version number.
|
||||
- This can happen for collections installed from git which do not have a version number
|
||||
in C(galaxy.yml).
|
||||
- By default, C(*) is returned.
|
||||
type: string
|
||||
default: '*'
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Check version of community.general
|
||||
ansible.builtin.debug:
|
||||
msg: "community.general version {{ lookup('community.general.collection_version', 'community.general') }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description:
|
||||
- The version number of the collections listed as input.
|
||||
- If a collection can not be found, it will return the value provided in I(result_not_found).
|
||||
By default, this is C(none).
|
||||
- If a collection can be found, but the version not identified, it will return the value provided in
|
||||
I(result_no_version). By default, this is C(*). This can happen for collections installed
|
||||
from git which do not have a version number in C(galaxy.yml).
|
||||
type: list
|
||||
elements: str
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
import yaml
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.module_utils.compat.importlib import import_module
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
FQCN_RE = re.compile(r'^[A-Za-z0-9_]+\.[A-Za-z0-9_]+$')
|
||||
|
||||
|
||||
def load_collection_meta_manifest(manifest_path):
|
||||
with open(manifest_path, 'rb') as f:
|
||||
meta = json.load(f)
|
||||
return {
|
||||
'version': meta['collection_info']['version'],
|
||||
}
|
||||
|
||||
|
||||
def load_collection_meta_galaxy(galaxy_path, no_version='*'):
|
||||
with open(galaxy_path, 'rb') as f:
|
||||
meta = yaml.safe_load(f)
|
||||
return {
|
||||
'version': meta.get('version') or no_version,
|
||||
}
|
||||
|
||||
|
||||
def load_collection_meta(collection_pkg, no_version='*'):
|
||||
path = os.path.dirname(collection_pkg.__file__)
|
||||
|
||||
# Try to load MANIFEST.json
|
||||
manifest_path = os.path.join(path, 'MANIFEST.json')
|
||||
if os.path.exists(manifest_path):
|
||||
return load_collection_meta_manifest(manifest_path)
|
||||
|
||||
# Try to load galaxy.y(a)ml
|
||||
galaxy_path = os.path.join(path, 'galaxy.yml')
|
||||
galaxy_alt_path = os.path.join(path, 'galaxy.yaml')
|
||||
# galaxy.yaml was only supported in ansible-base 2.10 and ansible-core 2.11. Support was removed
|
||||
# in https://github.com/ansible/ansible/commit/595413d11346b6f26bb3d9df2d8e05f2747508a3 for
|
||||
# ansible-core 2.12.
|
||||
for path in (galaxy_path, galaxy_alt_path):
|
||||
if os.path.exists(path):
|
||||
return load_collection_meta_galaxy(path, no_version=no_version)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
result = []
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
not_found = self.get_option('result_not_found')
|
||||
no_version = self.get_option('result_no_version')
|
||||
|
||||
for term in terms:
|
||||
if not FQCN_RE.match(term):
|
||||
raise AnsibleLookupError('"{term}" is not a FQCN'.format(term=term))
|
||||
|
||||
try:
|
||||
collection_pkg = import_module('ansible_collections.{fqcn}'.format(fqcn=term))
|
||||
except ImportError:
|
||||
# Collection not found
|
||||
result.append(not_found)
|
||||
continue
|
||||
|
||||
try:
|
||||
data = load_collection_meta(collection_pkg, no_version=no_version)
|
||||
except Exception as exc:
|
||||
raise AnsibleLookupError('Error while loading metadata for {fqcn}: {error}'.format(fqcn=term, error=exc))
|
||||
|
||||
result.append(data.get('version', no_version))
|
||||
|
||||
return result
|
||||
126
plugins/lookup/nios.py
Normal file
126
plugins/lookup/nios.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2018 Red Hat | Ansible
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: nios
|
||||
short_description: Query Infoblox NIOS objects
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it.
|
||||
alternative: infoblox.nios_modules.nios_lookup
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Uses the Infoblox WAPI API to fetch NIOS specified objects. This lookup
|
||||
supports adding additional keywords to filter the return data and specify
|
||||
the desired set of returned fields.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
_terms:
|
||||
description: The name of the object to return from NIOS
|
||||
required: True
|
||||
return_fields:
|
||||
description: The list of field names to return for the specified object.
|
||||
filter:
|
||||
description: a dict object that is used to filter the return objects
|
||||
extattrs:
|
||||
description: a dict object that is used to filter on extattrs
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: fetch all networkview objects
|
||||
ansible.builtin.set_fact:
|
||||
networkviews: "{{ lookup('community.general.nios', 'networkview',
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
- name: fetch the default dns view
|
||||
ansible.builtin.set_fact:
|
||||
dns_views: "{{ lookup('community.general.nios', 'view', filter={'name': 'default'},
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
# all of the examples below use credentials that are set using env variables
|
||||
# export INFOBLOX_HOST=nios01
|
||||
# export INFOBLOX_USERNAME=admin
|
||||
# export INFOBLOX_PASSWORD=admin
|
||||
|
||||
- name: fetch all host records and include extended attributes
|
||||
ansible.builtin.set_fact:
|
||||
host_records: "{{ lookup('community.general.nios', 'record:host', return_fields=['extattrs', 'name', 'view', 'comment']}) }}"
|
||||
|
||||
|
||||
- name: use env variables to pass credentials
|
||||
ansible.builtin.set_fact:
|
||||
networkviews: "{{ lookup('community.general.nios', 'networkview') }}"
|
||||
|
||||
- name: get a host record
|
||||
ansible.builtin.set_fact:
|
||||
host: "{{ lookup('community.general.nios', 'record:host', filter={'name': 'hostname.ansible.com'}) }}"
|
||||
|
||||
- name: get the authoritative zone from a non default dns view
|
||||
ansible.builtin.set_fact:
|
||||
host: "{{ lookup('community.general.nios', 'zone_auth', filter={'fqdn': 'ansible.com', 'view': 'ansible-dns'}) }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
obj_type:
|
||||
description:
|
||||
- The object type specified in the terms argument
|
||||
type: dictionary
|
||||
contains:
|
||||
obj_field:
|
||||
description:
|
||||
- One or more obj_type fields as specified by return_fields argument or
|
||||
the default set of fields as per the object type
|
||||
"""
|
||||
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiLookup
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_extattrs, flatten_extattrs
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
try:
|
||||
obj_type = terms[0]
|
||||
except IndexError:
|
||||
raise AnsibleError('the object_type must be specified')
|
||||
|
||||
return_fields = kwargs.pop('return_fields', None)
|
||||
filter_data = kwargs.pop('filter', {})
|
||||
extattrs = normalize_extattrs(kwargs.pop('extattrs', {}))
|
||||
provider = kwargs.pop('provider', {})
|
||||
wapi = WapiLookup(provider)
|
||||
res = wapi.get_object(obj_type, filter_data, return_fields=return_fields, extattrs=extattrs)
|
||||
if res is not None:
|
||||
for obj in res:
|
||||
if 'extattrs' in obj:
|
||||
obj['extattrs'] = flatten_extattrs(obj['extattrs'])
|
||||
else:
|
||||
res = []
|
||||
return res
|
||||
105
plugins/lookup/nios_next_ip.py
Normal file
105
plugins/lookup/nios_next_ip.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2018 Red Hat | Ansible
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: nios_next_ip
|
||||
short_description: Return the next available IP address for a network
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it.
|
||||
alternative: infoblox.nios_modules.nios_next_ip
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Uses the Infoblox WAPI API to return the next available IP addresses
|
||||
for a given network CIDR
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
_terms:
|
||||
description: The CIDR network to retrieve the next addresses from
|
||||
required: True
|
||||
num:
|
||||
description: The number of IP addresses to return
|
||||
required: false
|
||||
default: 1
|
||||
exclude:
|
||||
description: List of IP's that need to be excluded from returned IP addresses
|
||||
required: false
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: return next available IP address for network 192.168.10.0/24
|
||||
ansible.builtin.set_fact:
|
||||
ipaddr: "{{ lookup('community.general.nios_next_ip', '192.168.10.0/24', provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
- name: return the next 3 available IP addresses for network 192.168.10.0/24
|
||||
ansible.builtin.set_fact:
|
||||
ipaddr: "{{ lookup('community.general.nios_next_ip', '192.168.10.0/24', num=3, provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
- name: return the next 3 available IP addresses for network 192.168.10.0/24 excluding ip addresses - ['192.168.10.1', '192.168.10.2']
|
||||
ansible.builtin.set_fact:
|
||||
ipaddr: "{{ lookup('community.general.nios_next_ip', '192.168.10.0/24', num=3, exclude=['192.168.10.1', '192.168.10.2'],
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_list:
|
||||
description:
|
||||
- The list of next IP addresses available
|
||||
type: list
|
||||
"""
|
||||
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiLookup
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
try:
|
||||
network = terms[0]
|
||||
except IndexError:
|
||||
raise AnsibleError('missing argument in the form of A.B.C.D/E')
|
||||
|
||||
provider = kwargs.pop('provider', {})
|
||||
wapi = WapiLookup(provider)
|
||||
|
||||
network_obj = wapi.get_object('network', {'network': network})
|
||||
if network_obj is None:
|
||||
raise AnsibleError('unable to find network object %s' % network)
|
||||
|
||||
num = kwargs.get('num', 1)
|
||||
exclude_ip = kwargs.get('exclude', [])
|
||||
|
||||
try:
|
||||
ref = network_obj[0]['_ref']
|
||||
avail_ips = wapi.call_func('next_available_ip', ref, {'num': num, 'exclude': exclude_ip})
|
||||
return [avail_ips['ips']]
|
||||
except Exception as exc:
|
||||
raise AnsibleError(to_text(exc))
|
||||
118
plugins/lookup/nios_next_network.py
Normal file
118
plugins/lookup/nios_next_network.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2018 Red Hat | Ansible
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: nios_next_network
|
||||
short_description: Return the next available network range for a network-container
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it.
|
||||
alternative: infoblox.nios_modules.nios_next_network
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Uses the Infoblox WAPI API to return the next available network addresses for
|
||||
a given network CIDR
|
||||
requirements:
|
||||
- infoblox_client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
_terms:
|
||||
description: The CIDR network to retrieve the next network from next available network within the specified
|
||||
container.
|
||||
required: True
|
||||
cidr:
|
||||
description:
|
||||
- The CIDR of the network to retrieve the next network from next available network within the
|
||||
specified container. Also, Requested CIDR must be specified and greater than the parent CIDR.
|
||||
required: True
|
||||
default: 24
|
||||
num:
|
||||
description: The number of network addresses to return from network-container
|
||||
required: false
|
||||
default: 1
|
||||
exclude:
|
||||
description: Network addresses returned from network-container excluding list of user's input network range
|
||||
required: false
|
||||
default: ''
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: return next available network for network-container 192.168.10.0/24
|
||||
ansible.builtin.set_fact:
|
||||
networkaddr: "{{ lookup('community.general.nios_next_network', '192.168.10.0/24', cidr=25,
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
- name: return the next 2 available network addresses for network-container 192.168.10.0/24
|
||||
ansible.builtin.set_fact:
|
||||
networkaddr: "{{ lookup('community.general.nios_next_network', '192.168.10.0/24', cidr=25, num=2,
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
|
||||
- name: return the available network addresses for network-container 192.168.10.0/24 excluding network range '192.168.10.0/25'
|
||||
ansible.builtin.set_fact:
|
||||
networkaddr: "{{ lookup('community.general.nios_next_network', '192.168.10.0/24', cidr=25, exclude=['192.168.10.0/25'],
|
||||
provider={'host': 'nios01', 'username': 'admin', 'password': 'password'}) }}"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_list:
|
||||
description:
|
||||
- The list of next network addresses available
|
||||
type: list
|
||||
"""
|
||||
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiLookup
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
try:
|
||||
network = terms[0]
|
||||
except IndexError:
|
||||
raise AnsibleError('missing network argument in the form of A.B.C.D/E')
|
||||
try:
|
||||
cidr = kwargs.get('cidr', 24)
|
||||
except IndexError:
|
||||
raise AnsibleError('missing CIDR argument in the form of xx')
|
||||
|
||||
provider = kwargs.pop('provider', {})
|
||||
wapi = WapiLookup(provider)
|
||||
network_obj = wapi.get_object('networkcontainer', {'network': network})
|
||||
|
||||
if network_obj is None:
|
||||
raise AnsibleError('unable to find network-container object %s' % network)
|
||||
num = kwargs.get('num', 1)
|
||||
exclude_ip = kwargs.get('exclude', [])
|
||||
|
||||
try:
|
||||
ref = network_obj[0]['_ref']
|
||||
avail_nets = wapi.call_func('next_available_network', ref, {'cidr': cidr, 'num': num, 'exclude': exclude_ip})
|
||||
return [avail_nets['networks']]
|
||||
except Exception as exc:
|
||||
raise AnsibleError(to_text(exc))
|
||||
@@ -1,119 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""The community.general.random_words Ansible lookup plugin."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: random_words
|
||||
author:
|
||||
- Thomas Sjögren (@konstruktoid)
|
||||
short_description: Return a number of random words
|
||||
version_added: "4.0.0"
|
||||
requirements:
|
||||
- xkcdpass U(https://github.com/redacted/XKCD-password-generator)
|
||||
description:
|
||||
- Returns a number of random words. The output can for example be used for
|
||||
passwords.
|
||||
- See U(https://xkcd.com/936/) for background.
|
||||
options:
|
||||
numwords:
|
||||
description:
|
||||
- The number of words.
|
||||
default: 6
|
||||
type: int
|
||||
min_length:
|
||||
description:
|
||||
- Minimum length of words to make password.
|
||||
default: 5
|
||||
type: int
|
||||
max_length:
|
||||
description:
|
||||
- Maximum length of words to make password.
|
||||
default: 9
|
||||
type: int
|
||||
delimiter:
|
||||
description:
|
||||
- The delimiter character between words.
|
||||
default: " "
|
||||
type: str
|
||||
case:
|
||||
description:
|
||||
- The method for setting the case of each word in the passphrase.
|
||||
choices: ["alternating", "upper", "lower", "random", "capitalize"]
|
||||
default: "lower"
|
||||
type: str
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Generate password with default settings
|
||||
ansible.builtin.debug:
|
||||
var: lookup('community.general.random_words')
|
||||
# Example result: 'traitor gigabyte cesarean unless aspect clear'
|
||||
|
||||
- name: Generate password with six, five character, words
|
||||
ansible.builtin.debug:
|
||||
var: lookup('community.general.random_words', min_length=5, max_length=5)
|
||||
# Example result: 'brink banjo getup staff trump comfy'
|
||||
|
||||
- name: Generate password with three capitalized words and the '-' delimiter
|
||||
ansible.builtin.debug:
|
||||
var: lookup('community.general.random_words', numwords=3, delimiter='-', case='capitalize')
|
||||
# Example result: 'Overlabor-Faucet-Coastline'
|
||||
|
||||
- name: Generate password with three words without any delimiter
|
||||
ansible.builtin.debug:
|
||||
var: lookup('community.general.random_words', numwords=3, delimiter='')
|
||||
# Example result: 'deskworkmonopolystriking'
|
||||
# https://www.ncsc.gov.uk/blog-post/the-logic-behind-three-random-words
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
_raw:
|
||||
description: A single-element list containing random words.
|
||||
type: list
|
||||
elements: str
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
try:
|
||||
from xkcdpass import xkcd_password as xp
|
||||
|
||||
HAS_XKCDPASS = True
|
||||
except ImportError:
|
||||
HAS_XKCDPASS = False
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
"""The random_words Ansible lookup class."""
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
if not HAS_XKCDPASS:
|
||||
raise AnsibleLookupError(
|
||||
"Python xkcdpass library is required. "
|
||||
'Please install using "pip install xkcdpass"'
|
||||
)
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
method = self.get_option("case")
|
||||
delimiter = self.get_option("delimiter")
|
||||
max_length = self.get_option("max_length")
|
||||
min_length = self.get_option("min_length")
|
||||
numwords = self.get_option("numwords")
|
||||
|
||||
words = xp.locate_wordfile()
|
||||
wordlist = xp.generate_wordlist(
|
||||
max_length=max_length, min_length=min_length, wordfile=words
|
||||
)
|
||||
|
||||
values = xp.generate_xkcdpassword(
|
||||
wordlist, case=method, delimiter=delimiter, numwords=numwords
|
||||
)
|
||||
|
||||
return [values]
|
||||
@@ -1,107 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2021, RevBits <info@revbits.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: revbitspss
|
||||
author: RevBits (@RevBits) <info@revbits.com>
|
||||
short_description: Get secrets from RevBits PAM server
|
||||
version_added: 4.1.0
|
||||
description:
|
||||
- Uses the revbits_ansible Python SDK to get Secrets from RevBits PAM
|
||||
Server using API key authentication with the REST API.
|
||||
requirements:
|
||||
- revbits_ansible - U(https://pypi.org/project/revbits_ansible/)
|
||||
options:
|
||||
_terms:
|
||||
description:
|
||||
- This will be an array of keys for secrets which you want to fetch from RevBits PAM.
|
||||
required: true
|
||||
type: list
|
||||
elements: string
|
||||
base_url:
|
||||
description:
|
||||
- This will be the base URL of the server, for example C(https://server-url-here).
|
||||
required: true
|
||||
type: string
|
||||
api_key:
|
||||
description:
|
||||
- This will be the API key for authentication. You can get it from the RevBits PAM secret manager module.
|
||||
required: true
|
||||
type: string
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
_list:
|
||||
description:
|
||||
- The JSON responses which you can access with defined keys.
|
||||
- If you are fetching secrets named as UUID, PASSWORD it will gives you the dict of all secrets.
|
||||
type: list
|
||||
elements: dict
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- hosts: localhost
|
||||
vars:
|
||||
secret: >-
|
||||
{{
|
||||
lookup(
|
||||
'community.general.revbitspss',
|
||||
'UUIDPAM', 'DB_PASS',
|
||||
base_url='https://server-url-here',
|
||||
api_key='API_KEY_GOES_HERE'
|
||||
)
|
||||
}}
|
||||
tasks:
|
||||
- ansible.builtin.debug:
|
||||
msg: >
|
||||
UUIDPAM is {{ (secret['UUIDPAM']) }} and DB_PASS is {{ (secret['DB_PASS']) }}
|
||||
"""
|
||||
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.utils.display import Display
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.six import raise_from
|
||||
|
||||
try:
|
||||
from pam.revbits_ansible.server import SecretServer
|
||||
except ImportError as imp_exc:
|
||||
ANOTHER_LIBRARY_IMPORT_ERROR = imp_exc
|
||||
else:
|
||||
ANOTHER_LIBRARY_IMPORT_ERROR = None
|
||||
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
@staticmethod
|
||||
def Client(server_parameters):
|
||||
return SecretServer(**server_parameters)
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
if ANOTHER_LIBRARY_IMPORT_ERROR:
|
||||
raise_from(
|
||||
AnsibleError('revbits_ansible must be installed to use this plugin'),
|
||||
ANOTHER_LIBRARY_IMPORT_ERROR
|
||||
)
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
secret_server = LookupModule.Client(
|
||||
{
|
||||
"base_url": self.get_option('base_url'),
|
||||
"api_key": self.get_option('api_key'),
|
||||
}
|
||||
)
|
||||
result = []
|
||||
for term in terms:
|
||||
try:
|
||||
display.vvv(u"Secret Server lookup of Secret with ID %s" % term)
|
||||
result.append({term: secret_server.get_pam_secret(term)})
|
||||
except Exception as error:
|
||||
raise AnsibleError("Secret Server lookup failure: %s" % error.message)
|
||||
return result
|
||||
748
plugins/module_utils/_netapp.py
Normal file
748
plugins/module_utils/_netapp.py
Normal file
@@ -0,0 +1,748 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This code is part of Ansible, but is an independent component.
|
||||
# This particular file snippet, and this file snippet only, is BSD licensed.
|
||||
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Copyright (c) 2017, Sumit Kumar <sumit4@netapp.com>
|
||||
# Copyright (c) 2017, Michael Price <michael.price@netapp.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import mimetypes
|
||||
|
||||
from pprint import pformat
|
||||
from ansible.module_utils import six
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
try:
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
except ImportError:
|
||||
ansible_version = 'unknown'
|
||||
|
||||
try:
|
||||
from netapp_lib.api.zapi import zapi
|
||||
HAS_NETAPP_LIB = True
|
||||
except ImportError:
|
||||
HAS_NETAPP_LIB = False
|
||||
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
import ssl
|
||||
try:
|
||||
from urlparse import urlparse, urlunparse
|
||||
except ImportError:
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
|
||||
HAS_SF_SDK = False
|
||||
SF_BYTE_MAP = dict(
|
||||
# Management GUI displays 1024 ** 3 as 1.1 GB, thus use 1000.
|
||||
bytes=1,
|
||||
b=1,
|
||||
kb=1000,
|
||||
mb=1000 ** 2,
|
||||
gb=1000 ** 3,
|
||||
tb=1000 ** 4,
|
||||
pb=1000 ** 5,
|
||||
eb=1000 ** 6,
|
||||
zb=1000 ** 7,
|
||||
yb=1000 ** 8
|
||||
)
|
||||
|
||||
POW2_BYTE_MAP = dict(
|
||||
# Here, 1 kb = 1024
|
||||
bytes=1,
|
||||
b=1,
|
||||
kb=1024,
|
||||
mb=1024 ** 2,
|
||||
gb=1024 ** 3,
|
||||
tb=1024 ** 4,
|
||||
pb=1024 ** 5,
|
||||
eb=1024 ** 6,
|
||||
zb=1024 ** 7,
|
||||
yb=1024 ** 8
|
||||
)
|
||||
|
||||
try:
|
||||
from solidfire.factory import ElementFactory
|
||||
from solidfire.custom.models import TimeIntervalFrequency
|
||||
from solidfire.models import Schedule, ScheduleInfo
|
||||
|
||||
HAS_SF_SDK = True
|
||||
except Exception:
|
||||
HAS_SF_SDK = False
|
||||
|
||||
|
||||
def has_netapp_lib():
|
||||
return HAS_NETAPP_LIB
|
||||
|
||||
|
||||
def has_sf_sdk():
|
||||
return HAS_SF_SDK
|
||||
|
||||
|
||||
def na_ontap_host_argument_spec():
|
||||
|
||||
return dict(
|
||||
hostname=dict(required=True, type='str'),
|
||||
username=dict(required=True, type='str', aliases=['user']),
|
||||
password=dict(required=True, type='str', aliases=['pass'], no_log=True),
|
||||
https=dict(required=False, type='bool', default=False),
|
||||
validate_certs=dict(required=False, type='bool', default=True),
|
||||
http_port=dict(required=False, type='int'),
|
||||
ontapi=dict(required=False, type='int'),
|
||||
use_rest=dict(required=False, type='str', default='Auto', choices=['Never', 'Always', 'Auto'])
|
||||
)
|
||||
|
||||
|
||||
def ontap_sf_host_argument_spec():
|
||||
|
||||
return dict(
|
||||
hostname=dict(required=True, type='str'),
|
||||
username=dict(required=True, type='str', aliases=['user']),
|
||||
password=dict(required=True, type='str', aliases=['pass'], no_log=True)
|
||||
)
|
||||
|
||||
|
||||
def aws_cvs_host_argument_spec():
|
||||
|
||||
return dict(
|
||||
api_url=dict(required=True, type='str'),
|
||||
validate_certs=dict(required=False, type='bool', default=True),
|
||||
api_key=dict(required=True, type='str', no_log=True),
|
||||
secret_key=dict(required=True, type='str', no_log=True)
|
||||
)
|
||||
|
||||
|
||||
def create_sf_connection(module, port=None):
|
||||
hostname = module.params['hostname']
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
|
||||
if HAS_SF_SDK and hostname and username and password:
|
||||
try:
|
||||
return_val = ElementFactory.create(hostname, username, password, port=port)
|
||||
return return_val
|
||||
except Exception:
|
||||
raise Exception("Unable to create SF connection")
|
||||
else:
|
||||
module.fail_json(msg="the python SolidFire SDK module is required")
|
||||
|
||||
|
||||
def setup_na_ontap_zapi(module, vserver=None):
|
||||
hostname = module.params['hostname']
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
https = module.params['https']
|
||||
validate_certs = module.params['validate_certs']
|
||||
port = module.params['http_port']
|
||||
version = module.params['ontapi']
|
||||
|
||||
if HAS_NETAPP_LIB:
|
||||
# set up zapi
|
||||
server = zapi.NaServer(hostname)
|
||||
server.set_username(username)
|
||||
server.set_password(password)
|
||||
if vserver:
|
||||
server.set_vserver(vserver)
|
||||
if version:
|
||||
minor = version
|
||||
else:
|
||||
minor = 110
|
||||
server.set_api_version(major=1, minor=minor)
|
||||
# default is HTTP
|
||||
if https:
|
||||
if port is None:
|
||||
port = 443
|
||||
transport_type = 'HTTPS'
|
||||
# HACK to bypass certificate verification
|
||||
if validate_certs is False:
|
||||
if not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None):
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
else:
|
||||
if port is None:
|
||||
port = 80
|
||||
transport_type = 'HTTP'
|
||||
server.set_transport_type(transport_type)
|
||||
server.set_port(port)
|
||||
server.set_server_type('FILER')
|
||||
return server
|
||||
else:
|
||||
module.fail_json(msg="the python NetApp-Lib module is required")
|
||||
|
||||
|
||||
def setup_ontap_zapi(module, vserver=None):
|
||||
hostname = module.params['hostname']
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
|
||||
if HAS_NETAPP_LIB:
|
||||
# set up zapi
|
||||
server = zapi.NaServer(hostname)
|
||||
server.set_username(username)
|
||||
server.set_password(password)
|
||||
if vserver:
|
||||
server.set_vserver(vserver)
|
||||
# Todo : Replace hard-coded values with configurable parameters.
|
||||
server.set_api_version(major=1, minor=110)
|
||||
server.set_port(80)
|
||||
server.set_server_type('FILER')
|
||||
server.set_transport_type('HTTP')
|
||||
return server
|
||||
else:
|
||||
module.fail_json(msg="the python NetApp-Lib module is required")
|
||||
|
||||
|
||||
def eseries_host_argument_spec():
|
||||
"""Retrieve a base argument specification common to all NetApp E-Series modules"""
|
||||
argument_spec = basic_auth_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
api_username=dict(type='str', required=True),
|
||||
api_password=dict(type='str', required=True, no_log=True),
|
||||
api_url=dict(type='str', required=True),
|
||||
ssid=dict(type='str', required=False, default='1'),
|
||||
validate_certs=dict(type='bool', required=False, default=True)
|
||||
))
|
||||
return argument_spec
|
||||
|
||||
|
||||
class NetAppESeriesModule(object):
|
||||
"""Base class for all NetApp E-Series modules.
|
||||
|
||||
Provides a set of common methods for NetApp E-Series modules, including version checking, mode (proxy, embedded)
|
||||
verification, http requests, secure http redirection for embedded web services, and logging setup.
|
||||
|
||||
Be sure to add the following lines in the module's documentation section:
|
||||
extends_documentation_fragment:
|
||||
- netapp.eseries
|
||||
|
||||
:param dict(dict) ansible_options: dictionary of ansible option definitions
|
||||
:param str web_services_version: minimally required web services rest api version (default value: "02.00.0000.0000")
|
||||
:param bool supports_check_mode: whether the module will support the check_mode capabilities (default=False)
|
||||
:param list(list) mutually_exclusive: list containing list(s) of mutually exclusive options (optional)
|
||||
:param list(list) required_if: list containing list(s) containing the option, the option value, and then
|
||||
a list of required options. (optional)
|
||||
:param list(list) required_one_of: list containing list(s) of options for which at least one is required. (optional)
|
||||
:param list(list) required_together: list containing list(s) of options that are required together. (optional)
|
||||
:param bool log_requests: controls whether to log each request (default: True)
|
||||
"""
|
||||
DEFAULT_TIMEOUT = 60
|
||||
DEFAULT_SECURE_PORT = "8443"
|
||||
DEFAULT_REST_API_PATH = "devmgr/v2/"
|
||||
DEFAULT_REST_API_ABOUT_PATH = "devmgr/utils/about"
|
||||
DEFAULT_HEADERS = {"Content-Type": "application/json", "Accept": "application/json",
|
||||
"netapp-client-type": "Ansible-%s" % ansible_version}
|
||||
HTTP_AGENT = "Ansible / %s" % ansible_version
|
||||
SIZE_UNIT_MAP = dict(bytes=1, b=1, kb=1024, mb=1024**2, gb=1024**3, tb=1024**4,
|
||||
pb=1024**5, eb=1024**6, zb=1024**7, yb=1024**8)
|
||||
|
||||
def __init__(self, ansible_options, web_services_version=None, supports_check_mode=False,
|
||||
mutually_exclusive=None, required_if=None, required_one_of=None, required_together=None,
|
||||
log_requests=True):
|
||||
argument_spec = eseries_host_argument_spec()
|
||||
argument_spec.update(ansible_options)
|
||||
|
||||
self.module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=supports_check_mode,
|
||||
mutually_exclusive=mutually_exclusive, required_if=required_if,
|
||||
required_one_of=required_one_of, required_together=required_together)
|
||||
|
||||
args = self.module.params
|
||||
self.web_services_version = web_services_version if web_services_version else "02.00.0000.0000"
|
||||
self.ssid = args["ssid"]
|
||||
self.url = args["api_url"]
|
||||
self.log_requests = log_requests
|
||||
self.creds = dict(url_username=args["api_username"],
|
||||
url_password=args["api_password"],
|
||||
validate_certs=args["validate_certs"])
|
||||
|
||||
if not self.url.endswith("/"):
|
||||
self.url += "/"
|
||||
|
||||
self.is_embedded_mode = None
|
||||
self.is_web_services_valid_cache = None
|
||||
|
||||
def _check_web_services_version(self):
|
||||
"""Verify proxy or embedded web services meets minimum version required for module.
|
||||
|
||||
The minimum required web services version is evaluated against version supplied through the web services rest
|
||||
api. AnsibleFailJson exception will be raised when the minimum is not met or exceeded.
|
||||
|
||||
This helper function will update the supplied api url if secure http is not used for embedded web services
|
||||
|
||||
:raise AnsibleFailJson: raised when the contacted api service does not meet the minimum required version.
|
||||
"""
|
||||
if not self.is_web_services_valid_cache:
|
||||
|
||||
url_parts = urlparse(self.url)
|
||||
if not url_parts.scheme or not url_parts.netloc:
|
||||
self.module.fail_json(msg="Failed to provide valid API URL. Example: https://192.168.1.100:8443/devmgr/v2. URL [%s]." % self.url)
|
||||
|
||||
if url_parts.scheme not in ["http", "https"]:
|
||||
self.module.fail_json(msg="Protocol must be http or https. URL [%s]." % self.url)
|
||||
|
||||
self.url = "%s://%s/" % (url_parts.scheme, url_parts.netloc)
|
||||
about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
|
||||
rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, ignore_errors=True, **self.creds)
|
||||
|
||||
if rc != 200:
|
||||
self.module.warn("Failed to retrieve web services about information! Retrying with secure ports. Array Id [%s]." % self.ssid)
|
||||
self.url = "https://%s:8443/" % url_parts.netloc.split(":")[0]
|
||||
about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
|
||||
try:
|
||||
rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, **self.creds)
|
||||
except Exception as error:
|
||||
self.module.fail_json(msg="Failed to retrieve the webservices about information! Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(error)))
|
||||
|
||||
major, minor, other, revision = data["version"].split(".")
|
||||
minimum_major, minimum_minor, other, minimum_revision = self.web_services_version.split(".")
|
||||
|
||||
if not (major > minimum_major or
|
||||
(major == minimum_major and minor > minimum_minor) or
|
||||
(major == minimum_major and minor == minimum_minor and revision >= minimum_revision)):
|
||||
self.module.fail_json(msg="Web services version does not meet minimum version required. Current version: [%s]."
|
||||
" Version required: [%s]." % (data["version"], self.web_services_version))
|
||||
|
||||
self.module.log("Web services rest api version met the minimum required version.")
|
||||
self.is_web_services_valid_cache = True
|
||||
|
||||
def is_embedded(self):
|
||||
"""Determine whether web services server is the embedded web services.
|
||||
|
||||
If web services about endpoint fails based on an URLError then the request will be attempted again using
|
||||
secure http.
|
||||
|
||||
:raise AnsibleFailJson: raised when web services about endpoint failed to be contacted.
|
||||
:return bool: whether contacted web services is running from storage array (embedded) or from a proxy.
|
||||
"""
|
||||
self._check_web_services_version()
|
||||
|
||||
if self.is_embedded_mode is None:
|
||||
about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
|
||||
try:
|
||||
rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, **self.creds)
|
||||
self.is_embedded_mode = not data["runningAsProxy"]
|
||||
except Exception as error:
|
||||
self.module.fail_json(msg="Failed to retrieve the webservices about information! Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(error)))
|
||||
|
||||
return self.is_embedded_mode
|
||||
|
||||
def request(self, path, data=None, method='GET', headers=None, ignore_errors=False):
|
||||
"""Issue an HTTP request to a url, retrieving an optional JSON response.
|
||||
|
||||
:param str path: web services rest api endpoint path (Example: storage-systems/1/graph). Note that when the
|
||||
full url path is specified then that will be used without supplying the protocol, hostname, port and rest path.
|
||||
:param data: data required for the request (data may be json or any python structured data)
|
||||
:param str method: request method such as GET, POST, DELETE.
|
||||
:param dict headers: dictionary containing request headers.
|
||||
:param bool ignore_errors: forces the request to ignore any raised exceptions.
|
||||
"""
|
||||
self._check_web_services_version()
|
||||
|
||||
if headers is None:
|
||||
headers = self.DEFAULT_HEADERS
|
||||
|
||||
if not isinstance(data, str) and headers["Content-Type"] == "application/json":
|
||||
data = json.dumps(data)
|
||||
|
||||
if path.startswith("/"):
|
||||
path = path[1:]
|
||||
request_url = self.url + self.DEFAULT_REST_API_PATH + path
|
||||
|
||||
# if self.log_requests:
|
||||
self.module.log(pformat(dict(url=request_url, data=data, method=method)))
|
||||
|
||||
return request(url=request_url, data=data, method=method, headers=headers, use_proxy=True, force=False, last_mod_time=None,
|
||||
timeout=self.DEFAULT_TIMEOUT, http_agent=self.HTTP_AGENT, force_basic_auth=True, ignore_errors=ignore_errors, **self.creds)
|
||||
|
||||
|
||||
def create_multipart_formdata(files, fields=None, send_8kb=False):
|
||||
"""Create the data for a multipart/form request.
|
||||
|
||||
:param list(list) files: list of lists each containing (name, filename, path).
|
||||
:param list(list) fields: list of lists each containing (key, value).
|
||||
:param bool send_8kb: only sends the first 8kb of the files (default: False).
|
||||
"""
|
||||
boundary = "---------------------------" + "".join([str(random.randint(0, 9)) for x in range(27)])
|
||||
data_parts = list()
|
||||
data = None
|
||||
|
||||
if six.PY2: # Generate payload for Python 2
|
||||
newline = "\r\n"
|
||||
if fields is not None:
|
||||
for key, value in fields:
|
||||
data_parts.extend(["--%s" % boundary,
|
||||
'Content-Disposition: form-data; name="%s"' % key,
|
||||
"",
|
||||
value])
|
||||
|
||||
for name, filename, path in files:
|
||||
with open(path, "rb") as fh:
|
||||
value = fh.read(8192) if send_8kb else fh.read()
|
||||
|
||||
data_parts.extend(["--%s" % boundary,
|
||||
'Content-Disposition: form-data; name="%s"; filename="%s"' % (name, filename),
|
||||
"Content-Type: %s" % (mimetypes.guess_type(path)[0] or "application/octet-stream"),
|
||||
"",
|
||||
value])
|
||||
data_parts.extend(["--%s--" % boundary, ""])
|
||||
data = newline.join(data_parts)
|
||||
|
||||
else:
|
||||
newline = six.b("\r\n")
|
||||
if fields is not None:
|
||||
for key, value in fields:
|
||||
data_parts.extend([six.b("--%s" % boundary),
|
||||
six.b('Content-Disposition: form-data; name="%s"' % key),
|
||||
six.b(""),
|
||||
six.b(value)])
|
||||
|
||||
for name, filename, path in files:
|
||||
with open(path, "rb") as fh:
|
||||
value = fh.read(8192) if send_8kb else fh.read()
|
||||
|
||||
data_parts.extend([six.b("--%s" % boundary),
|
||||
six.b('Content-Disposition: form-data; name="%s"; filename="%s"' % (name, filename)),
|
||||
six.b("Content-Type: %s" % (mimetypes.guess_type(path)[0] or "application/octet-stream")),
|
||||
six.b(""),
|
||||
value])
|
||||
data_parts.extend([six.b("--%s--" % boundary), b""])
|
||||
data = newline.join(data_parts)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "multipart/form-data; boundary=%s" % boundary,
|
||||
"Content-Length": str(len(data))}
|
||||
|
||||
return headers, data
|
||||
|
||||
|
||||
def request(url, data=None, headers=None, method='GET', use_proxy=True,
|
||||
force=False, last_mod_time=None, timeout=10, validate_certs=True,
|
||||
url_username=None, url_password=None, http_agent=None, force_basic_auth=True, ignore_errors=False):
|
||||
"""Issue an HTTP request to a url, retrieving an optional JSON response."""
|
||||
|
||||
if headers is None:
|
||||
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
||||
headers.update({"netapp-client-type": "Ansible-%s" % ansible_version})
|
||||
|
||||
if not http_agent:
|
||||
http_agent = "Ansible / %s" % ansible_version
|
||||
|
||||
try:
|
||||
r = open_url(url=url, data=data, headers=headers, method=method, use_proxy=use_proxy,
|
||||
force=force, last_mod_time=last_mod_time, timeout=timeout, validate_certs=validate_certs,
|
||||
url_username=url_username, url_password=url_password, http_agent=http_agent,
|
||||
force_basic_auth=force_basic_auth)
|
||||
except HTTPError as err:
|
||||
r = err.fp
|
||||
|
||||
try:
|
||||
raw_data = r.read()
|
||||
if raw_data:
|
||||
data = json.loads(raw_data)
|
||||
else:
|
||||
raw_data = None
|
||||
except Exception:
|
||||
if ignore_errors:
|
||||
pass
|
||||
else:
|
||||
raise Exception(raw_data)
|
||||
|
||||
resp_code = r.getcode()
|
||||
|
||||
if resp_code >= 400 and not ignore_errors:
|
||||
raise Exception(resp_code, data)
|
||||
else:
|
||||
return resp_code, data
|
||||
|
||||
|
||||
def ems_log_event(source, server, name="Ansible", id="12345", version=ansible_version,
|
||||
category="Information", event="setup", autosupport="false"):
|
||||
ems_log = zapi.NaElement('ems-autosupport-log')
|
||||
# Host name invoking the API.
|
||||
ems_log.add_new_child("computer-name", name)
|
||||
# ID of event. A user defined event-id, range [0..2^32-2].
|
||||
ems_log.add_new_child("event-id", id)
|
||||
# Name of the application invoking the API.
|
||||
ems_log.add_new_child("event-source", source)
|
||||
# Version of application invoking the API.
|
||||
ems_log.add_new_child("app-version", version)
|
||||
# Application defined category of the event.
|
||||
ems_log.add_new_child("category", category)
|
||||
# Description of event to log. An application defined message to log.
|
||||
ems_log.add_new_child("event-description", event)
|
||||
ems_log.add_new_child("log-level", "6")
|
||||
ems_log.add_new_child("auto-support", autosupport)
|
||||
server.invoke_successfully(ems_log, True)
|
||||
|
||||
|
||||
def get_cserver_zapi(server):
|
||||
vserver_info = zapi.NaElement('vserver-get-iter')
|
||||
query_details = zapi.NaElement.create_node_with_children('vserver-info', **{'vserver-type': 'admin'})
|
||||
query = zapi.NaElement('query')
|
||||
query.add_child_elem(query_details)
|
||||
vserver_info.add_child_elem(query)
|
||||
result = server.invoke_successfully(vserver_info,
|
||||
enable_tunneling=False)
|
||||
attribute_list = result.get_child_by_name('attributes-list')
|
||||
vserver_list = attribute_list.get_child_by_name('vserver-info')
|
||||
return vserver_list.get_child_content('vserver-name')
|
||||
|
||||
|
||||
def get_cserver(connection, is_rest=False):
|
||||
if not is_rest:
|
||||
return get_cserver_zapi(connection)
|
||||
|
||||
params = {'fields': 'type'}
|
||||
api = "private/cli/vserver"
|
||||
json, error = connection.get(api, params)
|
||||
if json is None or error is not None:
|
||||
# exit if there is an error or no data
|
||||
return None
|
||||
vservers = json.get('records')
|
||||
if vservers is not None:
|
||||
for vserver in vservers:
|
||||
if vserver['type'] == 'admin': # cluster admin
|
||||
return vserver['vserver']
|
||||
if len(vservers) == 1: # assume vserver admin
|
||||
return vservers[0]['vserver']
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class OntapRestAPI(object):
|
||||
def __init__(self, module, timeout=60):
|
||||
self.module = module
|
||||
self.username = self.module.params['username']
|
||||
self.password = self.module.params['password']
|
||||
self.hostname = self.module.params['hostname']
|
||||
self.use_rest = self.module.params['use_rest']
|
||||
self.verify = self.module.params['validate_certs']
|
||||
self.timeout = timeout
|
||||
self.url = 'https://' + self.hostname + '/api/'
|
||||
self.errors = list()
|
||||
self.debug_logs = list()
|
||||
self.check_required_library()
|
||||
|
||||
def check_required_library(self):
|
||||
if not HAS_REQUESTS:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'))
|
||||
|
||||
def send_request(self, method, api, params, json=None, return_status_code=False):
|
||||
''' send http request and process reponse, including error conditions '''
|
||||
url = self.url + api
|
||||
status_code = None
|
||||
content = None
|
||||
json_dict = None
|
||||
json_error = None
|
||||
error_details = None
|
||||
|
||||
def get_json(response):
|
||||
''' extract json, and error message if present '''
|
||||
try:
|
||||
json = response.json()
|
||||
except ValueError:
|
||||
return None, None
|
||||
error = json.get('error')
|
||||
return json, error
|
||||
|
||||
try:
|
||||
response = requests.request(method, url, verify=self.verify, auth=(self.username, self.password), params=params, timeout=self.timeout, json=json)
|
||||
content = response.content # for debug purposes
|
||||
status_code = response.status_code
|
||||
# If the response was successful, no Exception will be raised
|
||||
response.raise_for_status()
|
||||
json_dict, json_error = get_json(response)
|
||||
except requests.exceptions.HTTPError as err:
|
||||
__, json_error = get_json(response)
|
||||
if json_error is None:
|
||||
self.log_error(status_code, 'HTTP error: %s' % err)
|
||||
error_details = str(err)
|
||||
# If an error was reported in the json payload, it is handled below
|
||||
except requests.exceptions.ConnectionError as err:
|
||||
self.log_error(status_code, 'Connection error: %s' % err)
|
||||
error_details = str(err)
|
||||
except Exception as err:
|
||||
self.log_error(status_code, 'Other error: %s' % err)
|
||||
error_details = str(err)
|
||||
if json_error is not None:
|
||||
self.log_error(status_code, 'Endpoint error: %d: %s' % (status_code, json_error))
|
||||
error_details = json_error
|
||||
self.log_debug(status_code, content)
|
||||
if return_status_code:
|
||||
return status_code, error_details
|
||||
return json_dict, error_details
|
||||
|
||||
def get(self, api, params):
|
||||
method = 'GET'
|
||||
return self.send_request(method, api, params)
|
||||
|
||||
def post(self, api, data, params=None):
|
||||
method = 'POST'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def patch(self, api, data, params=None):
|
||||
method = 'PATCH'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def delete(self, api, data, params=None):
|
||||
method = 'DELETE'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def _is_rest(self, used_unsupported_rest_properties=None):
|
||||
if self.use_rest == "Always":
|
||||
if used_unsupported_rest_properties:
|
||||
error = "REST API currently does not support '%s'" % \
|
||||
', '.join(used_unsupported_rest_properties)
|
||||
return True, error
|
||||
else:
|
||||
return True, None
|
||||
if self.use_rest == 'Never' or used_unsupported_rest_properties:
|
||||
# force ZAPI if requested or if some parameter requires it
|
||||
return False, None
|
||||
method = 'HEAD'
|
||||
api = 'cluster/software'
|
||||
status_code, __ = self.send_request(method, api, params=None, return_status_code=True)
|
||||
if status_code == 200:
|
||||
return True, None
|
||||
return False, None
|
||||
|
||||
def is_rest(self, used_unsupported_rest_properties=None):
|
||||
''' only return error if there is a reason to '''
|
||||
use_rest, error = self._is_rest(used_unsupported_rest_properties)
|
||||
if used_unsupported_rest_properties is None:
|
||||
return use_rest
|
||||
return use_rest, error
|
||||
|
||||
def log_error(self, status_code, message):
|
||||
self.errors.append(message)
|
||||
self.debug_logs.append((status_code, message))
|
||||
|
||||
def log_debug(self, status_code, content):
|
||||
self.debug_logs.append((status_code, content))
|
||||
|
||||
|
||||
class AwsCvsRestAPI(object):
|
||||
def __init__(self, module, timeout=60):
|
||||
self.module = module
|
||||
self.api_key = self.module.params['api_key']
|
||||
self.secret_key = self.module.params['secret_key']
|
||||
self.api_url = self.module.params['api_url']
|
||||
self.verify = self.module.params['validate_certs']
|
||||
self.timeout = timeout
|
||||
self.url = 'https://' + self.api_url + '/v1/'
|
||||
self.check_required_library()
|
||||
|
||||
def check_required_library(self):
|
||||
if not HAS_REQUESTS:
|
||||
self.module.fail_json(msg=missing_required_lib('requests'))
|
||||
|
||||
def send_request(self, method, api, params, json=None):
|
||||
''' send http request and process reponse, including error conditions '''
|
||||
url = self.url + api
|
||||
status_code = None
|
||||
content = None
|
||||
json_dict = None
|
||||
json_error = None
|
||||
error_details = None
|
||||
headers = {
|
||||
'Content-type': "application/json",
|
||||
'api-key': self.api_key,
|
||||
'secret-key': self.secret_key,
|
||||
'Cache-Control': "no-cache",
|
||||
}
|
||||
|
||||
def get_json(response):
|
||||
''' extract json, and error message if present '''
|
||||
try:
|
||||
json = response.json()
|
||||
|
||||
except ValueError:
|
||||
return None, None
|
||||
success_code = [200, 201, 202]
|
||||
if response.status_code not in success_code:
|
||||
error = json.get('message')
|
||||
else:
|
||||
error = None
|
||||
return json, error
|
||||
try:
|
||||
response = requests.request(method, url, headers=headers, timeout=self.timeout, json=json)
|
||||
status_code = response.status_code
|
||||
# If the response was successful, no Exception will be raised
|
||||
json_dict, json_error = get_json(response)
|
||||
except requests.exceptions.HTTPError as err:
|
||||
__, json_error = get_json(response)
|
||||
if json_error is None:
|
||||
error_details = str(err)
|
||||
except requests.exceptions.ConnectionError as err:
|
||||
error_details = str(err)
|
||||
except Exception as err:
|
||||
error_details = str(err)
|
||||
if json_error is not None:
|
||||
error_details = json_error
|
||||
|
||||
return json_dict, error_details
|
||||
|
||||
# If an error was reported in the json payload, it is handled below
|
||||
def get(self, api, params=None):
|
||||
method = 'GET'
|
||||
return self.send_request(method, api, params)
|
||||
|
||||
def post(self, api, data, params=None):
|
||||
method = 'POST'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def patch(self, api, data, params=None):
|
||||
method = 'PATCH'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def put(self, api, data, params=None):
|
||||
method = 'PUT'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def delete(self, api, data, params=None):
|
||||
method = 'DELETE'
|
||||
return self.send_request(method, api, params, json=data)
|
||||
|
||||
def get_state(self, jobId):
|
||||
""" Method to get the state of the job """
|
||||
method = 'GET'
|
||||
response, status_code = self.get('Jobs/%s' % jobId)
|
||||
while str(response['state']) not in 'done':
|
||||
response, status_code = self.get('Jobs/%s' % jobId)
|
||||
return 'done'
|
||||
2580
plugins/module_utils/compat/ipaddress.py
Normal file
2580
plugins/module_utils/compat/ipaddress.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,41 +7,54 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
try:
|
||||
from urllib import quote_plus # Python 2.X
|
||||
from urlparse import urljoin
|
||||
except ImportError:
|
||||
from urllib.parse import quote_plus, urljoin # Python 3+
|
||||
from urllib.parse import quote_plus # Python 3+
|
||||
|
||||
import traceback
|
||||
|
||||
GITLAB_IMP_ERR = None
|
||||
try:
|
||||
import gitlab
|
||||
import requests
|
||||
HAS_GITLAB_PACKAGE = True
|
||||
except Exception:
|
||||
GITLAB_IMP_ERR = traceback.format_exc()
|
||||
HAS_GITLAB_PACKAGE = False
|
||||
|
||||
|
||||
def auth_argument_spec(spec=None):
|
||||
arg_spec = (dict(
|
||||
api_token=dict(type='str', no_log=True),
|
||||
api_oauth_token=dict(type='str', no_log=True),
|
||||
api_job_token=dict(type='str', no_log=True),
|
||||
))
|
||||
if spec:
|
||||
arg_spec.update(spec)
|
||||
return arg_spec
|
||||
def request(module, api_url, project, path, access_token, private_token, rawdata='', method='GET'):
|
||||
url = "%s/v4/projects/%s%s" % (api_url, quote_plus(project), path)
|
||||
headers = {}
|
||||
if access_token:
|
||||
headers['Authorization'] = "Bearer %s" % access_token
|
||||
else:
|
||||
headers['Private-Token'] = private_token
|
||||
|
||||
headers['Accept'] = "application/json"
|
||||
headers['Content-Type'] = "application/json"
|
||||
|
||||
response, info = fetch_url(module=module, url=url, headers=headers, data=rawdata, method=method)
|
||||
status = info['status']
|
||||
content = ""
|
||||
if response:
|
||||
content = response.read()
|
||||
if status == 204:
|
||||
return True, content
|
||||
elif status == 200 or status == 201:
|
||||
return True, json.loads(content)
|
||||
else:
|
||||
return False, str(status) + ": " + content
|
||||
|
||||
|
||||
def find_project(gitlab_instance, identifier):
|
||||
def findProject(gitlab_instance, identifier):
|
||||
try:
|
||||
project = gitlab_instance.projects.get(identifier)
|
||||
except Exception as e:
|
||||
@@ -54,7 +67,7 @@ def find_project(gitlab_instance, identifier):
|
||||
return project
|
||||
|
||||
|
||||
def find_group(gitlab_instance, identifier):
|
||||
def findGroup(gitlab_instance, identifier):
|
||||
try:
|
||||
project = gitlab_instance.groups.get(identifier)
|
||||
except Exception as e:
|
||||
@@ -63,14 +76,12 @@ def find_group(gitlab_instance, identifier):
|
||||
return project
|
||||
|
||||
|
||||
def gitlab_authentication(module):
|
||||
def gitlabAuthentication(module):
|
||||
gitlab_url = module.params['api_url']
|
||||
validate_certs = module.params['validate_certs']
|
||||
gitlab_user = module.params['api_username']
|
||||
gitlab_password = module.params['api_password']
|
||||
gitlab_token = module.params['api_token']
|
||||
gitlab_oauth_token = module.params['api_oauth_token']
|
||||
gitlab_job_token = module.params['api_job_token']
|
||||
|
||||
if not HAS_GITLAB_PACKAGE:
|
||||
module.fail_json(msg=missing_required_lib("python-gitlab"), exception=GITLAB_IMP_ERR)
|
||||
@@ -83,16 +94,7 @@ def gitlab_authentication(module):
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, email=gitlab_user, password=gitlab_password,
|
||||
private_token=gitlab_token, api_version=4)
|
||||
else:
|
||||
# We can create an oauth_token using a username and password
|
||||
# https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
|
||||
if gitlab_user:
|
||||
data = {'grant_type': 'password', 'username': gitlab_user, 'password': gitlab_password}
|
||||
resp = requests.post(urljoin(gitlab_url, "oauth/token"), data=data, verify=validate_certs)
|
||||
resp_data = resp.json()
|
||||
gitlab_oauth_token = resp_data["access_token"]
|
||||
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, private_token=gitlab_token,
|
||||
oauth_token=gitlab_oauth_token, job_token=gitlab_job_token, api_version=4)
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, private_token=gitlab_token, api_version=4)
|
||||
|
||||
gitlab_instance.auth()
|
||||
except (gitlab.exceptions.GitlabAuthenticationError, gitlab.exceptions.GitlabGetError) as e:
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
|
||||
|
||||
|
||||
class iLORedfishUtils(RedfishUtils):
|
||||
|
||||
def get_ilo_sessions(self):
|
||||
result = {}
|
||||
# listing all users has always been slower than other operations, why?
|
||||
session_list = []
|
||||
sessions_results = []
|
||||
# Get these entries, but does not fail if not found
|
||||
properties = ['Description', 'Id', 'Name', 'UserName']
|
||||
|
||||
# Changed self.sessions_uri to Hardcoded string.
|
||||
response = self.get_request(
|
||||
self.root_uri + self.service_root + "SessionService/Sessions/")
|
||||
if not response['ret']:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
|
||||
if 'Oem' in data:
|
||||
if data["Oem"]["Hpe"]["Links"]["MySession"]["@odata.id"]:
|
||||
current_session = data["Oem"]["Hpe"]["Links"]["MySession"]["@odata.id"]
|
||||
|
||||
for sessions in data[u'Members']:
|
||||
# session_list[] are URIs
|
||||
session_list.append(sessions[u'@odata.id'])
|
||||
# for each session, get details
|
||||
for uri in session_list:
|
||||
session = {}
|
||||
if uri != current_session:
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
data = response['data']
|
||||
for property in properties:
|
||||
if property in data:
|
||||
session[property] = data[property]
|
||||
sessions_results.append(session)
|
||||
result["msg"] = sessions_results
|
||||
result["ret"] = True
|
||||
return result
|
||||
|
||||
def set_ntp_server(self, mgr_attributes):
|
||||
result = {}
|
||||
setkey = mgr_attributes['mgr_attr_name']
|
||||
|
||||
nic_info = self.get_manager_ethernet_uri()
|
||||
ethuri = nic_info["nic_addr"]
|
||||
|
||||
response = self.get_request(self.root_uri + ethuri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
payload = {"DHCPv4": {
|
||||
"UseNTPServers": ""
|
||||
}}
|
||||
|
||||
if data["DHCPv4"]["UseNTPServers"]:
|
||||
payload["DHCPv4"]["UseNTPServers"] = False
|
||||
res_dhv4 = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not res_dhv4['ret']:
|
||||
return res_dhv4
|
||||
|
||||
payload = {"DHCPv6": {
|
||||
"UseNTPServers": ""
|
||||
}}
|
||||
|
||||
if data["DHCPv6"]["UseNTPServers"]:
|
||||
payload["DHCPv6"]["UseNTPServers"] = False
|
||||
res_dhv6 = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not res_dhv6['ret']:
|
||||
return res_dhv6
|
||||
|
||||
datetime_uri = self.manager_uri + "DateTime"
|
||||
|
||||
response = self.get_request(self.root_uri + datetime_uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
data = response['data']
|
||||
|
||||
ntp_list = data[setkey]
|
||||
if(len(ntp_list) == 2):
|
||||
ntp_list.pop(0)
|
||||
|
||||
ntp_list.append(mgr_attributes['mgr_attr_value'])
|
||||
|
||||
payload = {setkey: ntp_list}
|
||||
|
||||
response1 = self.patch_request(self.root_uri + datetime_uri, payload)
|
||||
if not response1['ret']:
|
||||
return response1
|
||||
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % mgr_attributes['mgr_attr_name']}
|
||||
|
||||
def set_time_zone(self, attr):
|
||||
key = attr['mgr_attr_name']
|
||||
|
||||
uri = self.manager_uri + "DateTime/"
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
data = response["data"]
|
||||
|
||||
if key not in data:
|
||||
return {'ret': False, 'changed': False, 'msg': "Key %s not found" % key}
|
||||
|
||||
timezones = data["TimeZoneList"]
|
||||
index = ""
|
||||
for tz in timezones:
|
||||
if attr['mgr_attr_value'] in tz["Name"]:
|
||||
index = tz["Index"]
|
||||
break
|
||||
|
||||
payload = {key: {"Index": index}}
|
||||
response = self.patch_request(self.root_uri + uri, payload)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % attr['mgr_attr_name']}
|
||||
|
||||
def set_dns_server(self, attr):
|
||||
key = attr['mgr_attr_name']
|
||||
nic_info = self.get_manager_ethernet_uri()
|
||||
uri = nic_info["nic_addr"]
|
||||
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
data = response['data']
|
||||
|
||||
dns_list = data["Oem"]["Hpe"]["IPv4"][key]
|
||||
|
||||
if len(dns_list) == 3:
|
||||
dns_list.pop(0)
|
||||
|
||||
dns_list.append(attr['mgr_attr_value'])
|
||||
|
||||
payload = {
|
||||
"Oem": {
|
||||
"Hpe": {
|
||||
"IPv4": {
|
||||
key: dns_list
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = self.patch_request(self.root_uri + uri, payload)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % attr['mgr_attr_name']}
|
||||
|
||||
def set_domain_name(self, attr):
|
||||
key = attr['mgr_attr_name']
|
||||
|
||||
nic_info = self.get_manager_ethernet_uri()
|
||||
ethuri = nic_info["nic_addr"]
|
||||
|
||||
response = self.get_request(self.root_uri + ethuri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
|
||||
data = response['data']
|
||||
|
||||
payload = {"DHCPv4": {
|
||||
"UseDomainName": ""
|
||||
}}
|
||||
|
||||
if data["DHCPv4"]["UseDomainName"]:
|
||||
payload["DHCPv4"]["UseDomainName"] = False
|
||||
res_dhv4 = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not res_dhv4['ret']:
|
||||
return res_dhv4
|
||||
|
||||
payload = {"DHCPv6": {
|
||||
"UseDomainName": ""
|
||||
}}
|
||||
|
||||
if data["DHCPv6"]["UseDomainName"]:
|
||||
payload["DHCPv6"]["UseDomainName"] = False
|
||||
res_dhv6 = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not res_dhv6['ret']:
|
||||
return res_dhv6
|
||||
|
||||
domain_name = attr['mgr_attr_value']
|
||||
|
||||
payload = {"Oem": {
|
||||
"Hpe": {
|
||||
key: domain_name
|
||||
}
|
||||
}}
|
||||
|
||||
response = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not response['ret']:
|
||||
return response
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % attr['mgr_attr_name']}
|
||||
|
||||
def set_wins_registration(self, mgrattr):
|
||||
Key = mgrattr['mgr_attr_name']
|
||||
|
||||
nic_info = self.get_manager_ethernet_uri()
|
||||
ethuri = nic_info["nic_addr"]
|
||||
|
||||
payload = {
|
||||
"Oem": {
|
||||
"Hpe": {
|
||||
"IPv4": {
|
||||
Key: False
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = self.patch_request(self.root_uri + ethuri, payload)
|
||||
if not response['ret']:
|
||||
return response
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified %s" % mgrattr['mgr_attr_name']}
|
||||
@@ -179,10 +179,10 @@ class IPAClient(object):
|
||||
result.append(key)
|
||||
return result
|
||||
|
||||
def modify_if_diff(self, name, ipa_list, module_list, add_method, remove_method, item=None, append=None):
|
||||
def modify_if_diff(self, name, ipa_list, module_list, add_method, remove_method, item=None):
|
||||
changed = False
|
||||
diff = list(set(ipa_list) - set(module_list))
|
||||
if append is not True and len(diff) > 0:
|
||||
if len(diff) > 0:
|
||||
changed = True
|
||||
if not self.module.check_mode:
|
||||
if item:
|
||||
|
||||
@@ -52,36 +52,3 @@ def module_fails_on_exception(func):
|
||||
self.module.fail_json(msg=msg, exception=traceback.format_exc(),
|
||||
output=self.output, vars=self.vars.output(), **self.output)
|
||||
return wrapper
|
||||
|
||||
|
||||
def check_mode_skip(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if not self.module.check_mode:
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def check_mode_skip_returns(callable=None, value=None):
|
||||
|
||||
def deco(func):
|
||||
if callable is not None:
|
||||
@wraps(func)
|
||||
def wrapper_callable(self, *args, **kwargs):
|
||||
if self.module.check_mode:
|
||||
return callable(self, *args, **kwargs)
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper_callable
|
||||
|
||||
if value is not None:
|
||||
@wraps(func)
|
||||
def wrapper_value(self, *args, **kwargs):
|
||||
if self.module.check_mode:
|
||||
return value
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper_value
|
||||
|
||||
if callable is None and value is None:
|
||||
return check_mode_skip
|
||||
|
||||
return deco
|
||||
|
||||
@@ -141,7 +141,11 @@ class CmdMixin(object):
|
||||
fmt = find_format(param)
|
||||
value = extra_params[param]
|
||||
else:
|
||||
raise self.ModuleHelperException('Cannot determine value for parameter: {0}'.format(param))
|
||||
self.module.deprecate("Cannot determine value for parameter: {0}. "
|
||||
"From version 4.0.0 onwards this will generate an exception".format(param),
|
||||
version="4.0.0", collection_name="community.general")
|
||||
continue
|
||||
|
||||
else:
|
||||
raise self.ModuleHelperException("run_command parameter must be either a str or a dict: {0}".format(param))
|
||||
cmd_args = add_arg_formatted_param(cmd_args, fmt, value)
|
||||
@@ -158,9 +162,8 @@ class CmdMixin(object):
|
||||
publish_rc=True,
|
||||
publish_out=True,
|
||||
publish_err=True,
|
||||
publish_cmd=True,
|
||||
*args, **kwargs):
|
||||
cmd_args = self._calculate_args(extra_params, params)
|
||||
self.vars.cmd_args = self._calculate_args(extra_params, params)
|
||||
options = dict(self.run_command_fixed_options)
|
||||
options['check_rc'] = options.get('check_rc', self.check_rc)
|
||||
options.update(kwargs)
|
||||
@@ -172,15 +175,13 @@ class CmdMixin(object):
|
||||
})
|
||||
self.update_output(force_lang=self.force_lang)
|
||||
options['environ_update'] = env_update
|
||||
rc, out, err = self.module.run_command(cmd_args, *args, **options)
|
||||
rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)
|
||||
if publish_rc:
|
||||
self.update_output(rc=rc)
|
||||
if publish_out:
|
||||
self.update_output(stdout=out)
|
||||
if publish_err:
|
||||
self.update_output(stderr=err)
|
||||
if publish_cmd:
|
||||
self.update_output(cmd_args=cmd_args)
|
||||
if process_output is None:
|
||||
_process = self.process_command_output
|
||||
else:
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
class DeprecateAttrsMixin(object):
|
||||
|
||||
def _deprecate_setup(self, attr, target, module):
|
||||
if target is None:
|
||||
target = self
|
||||
if not hasattr(target, attr):
|
||||
raise ValueError("Target {0} has no attribute {1}".format(target, attr))
|
||||
if module is None:
|
||||
if isinstance(target, AnsibleModule):
|
||||
module = target
|
||||
elif hasattr(target, "module") and isinstance(target.module, AnsibleModule):
|
||||
module = target.module
|
||||
else:
|
||||
raise ValueError("Failed to automatically discover the AnsibleModule instance. Pass 'module' parameter explicitly.")
|
||||
|
||||
# setup internal state dicts
|
||||
value_attr = "__deprecated_attr_value"
|
||||
trigger_attr = "__deprecated_attr_trigger"
|
||||
if not hasattr(target, value_attr):
|
||||
setattr(target, value_attr, {})
|
||||
if not hasattr(target, trigger_attr):
|
||||
setattr(target, trigger_attr, {})
|
||||
value_dict = getattr(target, value_attr)
|
||||
trigger_dict = getattr(target, trigger_attr)
|
||||
return target, module, value_dict, trigger_dict
|
||||
|
||||
def _deprecate_attr(self, attr, msg, version=None, date=None, collection_name=None, target=None, value=None, module=None):
|
||||
target, module, value_dict, trigger_dict = self._deprecate_setup(attr, target, module)
|
||||
|
||||
value_dict[attr] = getattr(target, attr, value)
|
||||
trigger_dict[attr] = False
|
||||
|
||||
def _trigger():
|
||||
if not trigger_dict[attr]:
|
||||
module.deprecate(msg, version=version, date=date, collection_name=collection_name)
|
||||
trigger_dict[attr] = True
|
||||
|
||||
def _getter(_self):
|
||||
_trigger()
|
||||
return value_dict[attr]
|
||||
|
||||
def _setter(_self, new_value):
|
||||
_trigger()
|
||||
value_dict[attr] = new_value
|
||||
|
||||
# override attribute
|
||||
prop = property(_getter)
|
||||
setattr(target, attr, prop)
|
||||
setattr(target, "_{0}_setter".format(attr), prop.setter(_setter))
|
||||
@@ -13,10 +13,9 @@ from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd im
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyMixin
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarsMixin, VarDict as _VD
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deprecate_attrs import DeprecateAttrsMixin
|
||||
|
||||
|
||||
class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelperBase):
|
||||
class ModuleHelper(VarsMixin, DependencyMixin, ModuleHelperBase):
|
||||
_output_conflict_list = ('msg', 'exception', 'output', 'vars', 'changed')
|
||||
facts_name = None
|
||||
output_params = ()
|
||||
@@ -37,15 +36,6 @@ class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelper
|
||||
fact=name in self.facts_params,
|
||||
)
|
||||
|
||||
self._deprecate_attr(
|
||||
attr="VarDict",
|
||||
msg="ModuleHelper.VarDict attribute is deprecated, use VarDict from "
|
||||
"the ansible_collections.community.general.plugins.module_utils.mh.mixins.vars module instead",
|
||||
version="6.0.0",
|
||||
collection_name="community.general",
|
||||
target=ModuleHelper,
|
||||
module=self.module)
|
||||
|
||||
def update_output(self, **kwargs):
|
||||
self.update_vars(meta={"output": True}, **kwargs)
|
||||
|
||||
|
||||
598
plugins/module_utils/net_tools/nios/api.py
Normal file
598
plugins/module_utils/net_tools/nios/api.py
Normal file
@@ -0,0 +1,598 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This code is part of Ansible, but is an independent component.
|
||||
# This particular file snippet, and this file snippet only, is BSD licensed.
|
||||
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# (c) 2018 Red Hat Inc.
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
import os
|
||||
from functools import partial
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.basic import env_fallback
|
||||
from ansible.module_utils.common.validation import check_type_dict
|
||||
|
||||
try:
|
||||
from infoblox_client.connector import Connector
|
||||
from infoblox_client.exceptions import InfobloxException
|
||||
HAS_INFOBLOX_CLIENT = True
|
||||
except ImportError:
|
||||
HAS_INFOBLOX_CLIENT = False
|
||||
|
||||
# defining nios constants
|
||||
NIOS_DNS_VIEW = 'view'
|
||||
NIOS_NETWORK_VIEW = 'networkview'
|
||||
NIOS_HOST_RECORD = 'record:host'
|
||||
NIOS_IPV4_NETWORK = 'network'
|
||||
NIOS_IPV6_NETWORK = 'ipv6network'
|
||||
NIOS_ZONE = 'zone_auth'
|
||||
NIOS_PTR_RECORD = 'record:ptr'
|
||||
NIOS_A_RECORD = 'record:a'
|
||||
NIOS_AAAA_RECORD = 'record:aaaa'
|
||||
NIOS_CNAME_RECORD = 'record:cname'
|
||||
NIOS_MX_RECORD = 'record:mx'
|
||||
NIOS_SRV_RECORD = 'record:srv'
|
||||
NIOS_NAPTR_RECORD = 'record:naptr'
|
||||
NIOS_TXT_RECORD = 'record:txt'
|
||||
NIOS_NSGROUP = 'nsgroup'
|
||||
NIOS_IPV4_FIXED_ADDRESS = 'fixedaddress'
|
||||
NIOS_IPV6_FIXED_ADDRESS = 'ipv6fixedaddress'
|
||||
NIOS_NEXT_AVAILABLE_IP = 'func:nextavailableip'
|
||||
NIOS_IPV4_NETWORK_CONTAINER = 'networkcontainer'
|
||||
NIOS_IPV6_NETWORK_CONTAINER = 'ipv6networkcontainer'
|
||||
NIOS_MEMBER = 'member'
|
||||
|
||||
NIOS_PROVIDER_SPEC = {
|
||||
'host': dict(fallback=(env_fallback, ['INFOBLOX_HOST'])),
|
||||
'username': dict(fallback=(env_fallback, ['INFOBLOX_USERNAME'])),
|
||||
'password': dict(fallback=(env_fallback, ['INFOBLOX_PASSWORD']), no_log=True),
|
||||
'validate_certs': dict(type='bool', default=False, fallback=(env_fallback, ['INFOBLOX_SSL_VERIFY']), aliases=['ssl_verify']),
|
||||
'silent_ssl_warnings': dict(type='bool', default=True),
|
||||
'http_request_timeout': dict(type='int', default=10, fallback=(env_fallback, ['INFOBLOX_HTTP_REQUEST_TIMEOUT'])),
|
||||
'http_pool_connections': dict(type='int', default=10),
|
||||
'http_pool_maxsize': dict(type='int', default=10),
|
||||
'max_retries': dict(type='int', default=3, fallback=(env_fallback, ['INFOBLOX_MAX_RETRIES'])),
|
||||
'wapi_version': dict(default='2.1', fallback=(env_fallback, ['INFOBLOX_WAP_VERSION'])),
|
||||
'max_results': dict(type='int', default=1000, fallback=(env_fallback, ['INFOBLOX_MAX_RETRIES']))
|
||||
}
|
||||
|
||||
|
||||
def get_connector(*args, **kwargs):
|
||||
''' Returns an instance of infoblox_client.connector.Connector
|
||||
:params args: positional arguments are silently ignored
|
||||
:params kwargs: dict that is passed to Connector init
|
||||
:returns: Connector
|
||||
'''
|
||||
if not HAS_INFOBLOX_CLIENT:
|
||||
raise Exception('infoblox-client is required but does not appear '
|
||||
'to be installed. It can be installed using the '
|
||||
'command `pip install infoblox-client`')
|
||||
|
||||
if not set(kwargs.keys()).issubset(list(NIOS_PROVIDER_SPEC.keys()) + ['ssl_verify']):
|
||||
raise Exception('invalid or unsupported keyword argument for connector')
|
||||
for key, value in iteritems(NIOS_PROVIDER_SPEC):
|
||||
if key not in kwargs:
|
||||
# apply default values from NIOS_PROVIDER_SPEC since we cannot just
|
||||
# assume the provider values are coming from AnsibleModule
|
||||
if 'default' in value:
|
||||
kwargs[key] = value['default']
|
||||
|
||||
# override any values with env variables unless they were
|
||||
# explicitly set
|
||||
env = ('INFOBLOX_%s' % key).upper()
|
||||
if env in os.environ:
|
||||
kwargs[key] = os.environ.get(env)
|
||||
|
||||
if 'validate_certs' in kwargs.keys():
|
||||
kwargs['ssl_verify'] = kwargs['validate_certs']
|
||||
kwargs.pop('validate_certs', None)
|
||||
|
||||
return Connector(kwargs)
|
||||
|
||||
|
||||
def normalize_extattrs(value):
|
||||
''' Normalize extattrs field to expected format
|
||||
The module accepts extattrs as key/value pairs. This method will
|
||||
transform the key/value pairs into a structure suitable for
|
||||
sending across WAPI in the format of:
|
||||
extattrs: {
|
||||
key: {
|
||||
value: <value>
|
||||
}
|
||||
}
|
||||
'''
|
||||
return dict([(k, {'value': v}) for k, v in iteritems(value)])
|
||||
|
||||
|
||||
def flatten_extattrs(value):
|
||||
''' Flatten the key/value struct for extattrs
|
||||
WAPI returns extattrs field as a dict in form of:
|
||||
extattrs: {
|
||||
key: {
|
||||
value: <value>
|
||||
}
|
||||
}
|
||||
This method will flatten the structure to:
|
||||
extattrs: {
|
||||
key: value
|
||||
}
|
||||
'''
|
||||
return dict([(k, v['value']) for k, v in iteritems(value)])
|
||||
|
||||
|
||||
def member_normalize(member_spec):
|
||||
''' Transforms the member module arguments into a valid WAPI struct
|
||||
This function will transform the arguments into a structure that
|
||||
is a valid WAPI structure in the format of:
|
||||
{
|
||||
key: <value>,
|
||||
}
|
||||
It will remove any arguments that are set to None since WAPI will error on
|
||||
that condition.
|
||||
The remainder of the value validation is performed by WAPI
|
||||
Some parameters in ib_spec are passed as a list in order to pass the validation for elements.
|
||||
In this function, they are converted to dictionary.
|
||||
'''
|
||||
member_elements = ['vip_setting', 'ipv6_setting', 'lan2_port_setting', 'mgmt_port_setting',
|
||||
'pre_provisioning', 'network_setting', 'v6_network_setting',
|
||||
'ha_port_setting', 'lan_port_setting', 'lan2_physical_setting',
|
||||
'lan_ha_port_setting', 'mgmt_network_setting', 'v6_mgmt_network_setting']
|
||||
for key in list(member_spec.keys()):
|
||||
if key in member_elements and member_spec[key] is not None:
|
||||
member_spec[key] = member_spec[key][0]
|
||||
if isinstance(member_spec[key], dict):
|
||||
member_spec[key] = member_normalize(member_spec[key])
|
||||
elif isinstance(member_spec[key], list):
|
||||
for x in member_spec[key]:
|
||||
if isinstance(x, dict):
|
||||
x = member_normalize(x)
|
||||
elif member_spec[key] is None:
|
||||
del member_spec[key]
|
||||
return member_spec
|
||||
|
||||
|
||||
def normalize_ib_spec(ib_spec):
|
||||
result = {}
|
||||
for arg in ib_spec:
|
||||
result[arg] = dict([(k, v)
|
||||
for k, v in iteritems(ib_spec[arg])
|
||||
if k not in ('ib_req', 'transform', 'update')])
|
||||
return result
|
||||
|
||||
|
||||
class WapiBase(object):
|
||||
''' Base class for implementing Infoblox WAPI API '''
|
||||
provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)}
|
||||
|
||||
def __init__(self, provider):
|
||||
self.connector = get_connector(**provider)
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.__dict__[name]
|
||||
except KeyError:
|
||||
if name.startswith('_'):
|
||||
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
|
||||
return partial(self._invoke_method, name)
|
||||
|
||||
def _invoke_method(self, name, *args, **kwargs):
|
||||
try:
|
||||
method = getattr(self.connector, name)
|
||||
return method(*args, **kwargs)
|
||||
except InfobloxException as exc:
|
||||
if hasattr(self, 'handle_exception'):
|
||||
self.handle_exception(name, exc)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
class WapiLookup(WapiBase):
|
||||
''' Implements WapiBase for lookup plugins '''
|
||||
def handle_exception(self, method_name, exc):
|
||||
if ('text' in exc.response):
|
||||
raise Exception(exc.response['text'])
|
||||
else:
|
||||
raise Exception(exc)
|
||||
|
||||
|
||||
class WapiInventory(WapiBase):
|
||||
''' Implements WapiBase for dynamic inventory script '''
|
||||
pass
|
||||
|
||||
|
||||
class WapiModule(WapiBase):
|
||||
''' Implements WapiBase for executing a NIOS module '''
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
provider = module.params['provider']
|
||||
try:
|
||||
super(WapiModule, self).__init__(provider)
|
||||
except Exception as exc:
|
||||
self.module.fail_json(msg=to_text(exc))
|
||||
|
||||
def handle_exception(self, method_name, exc):
|
||||
''' Handles any exceptions raised
|
||||
This method will be called if an InfobloxException is raised for
|
||||
any call to the instance of Connector and also, in case of generic
|
||||
exception. This method will then gracefully fail the module.
|
||||
:args exc: instance of InfobloxException
|
||||
'''
|
||||
if ('text' in exc.response):
|
||||
self.module.fail_json(
|
||||
msg=exc.response['text'],
|
||||
type=exc.response['Error'].split(':')[0],
|
||||
code=exc.response.get('code'),
|
||||
operation=method_name
|
||||
)
|
||||
else:
|
||||
self.module.fail_json(msg=to_native(exc))
|
||||
|
||||
def run(self, ib_obj_type, ib_spec):
|
||||
''' Runs the module and performans configuration tasks
|
||||
:args ib_obj_type: the WAPI object type to operate against
|
||||
:args ib_spec: the specification for the WAPI object as a dict
|
||||
:returns: a results dict
|
||||
'''
|
||||
|
||||
update = new_name = None
|
||||
state = self.module.params['state']
|
||||
if state not in ('present', 'absent'):
|
||||
self.module.fail_json(msg='state must be one of `present`, `absent`, got `%s`' % state)
|
||||
|
||||
result = {'changed': False}
|
||||
|
||||
obj_filter = dict([(k, self.module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
|
||||
|
||||
# get object reference
|
||||
ib_obj_ref, update, new_name = self.get_object_ref(self.module, ib_obj_type, obj_filter, ib_spec)
|
||||
proposed_object = {}
|
||||
for key, value in iteritems(ib_spec):
|
||||
if self.module.params[key] is not None:
|
||||
if 'transform' in value:
|
||||
proposed_object[key] = value['transform'](self.module)
|
||||
else:
|
||||
proposed_object[key] = self.module.params[key]
|
||||
|
||||
# If configure_by_dns is set to False and view is 'default', then delete the default dns
|
||||
if not proposed_object.get('configure_for_dns') and proposed_object.get('view') == 'default'\
|
||||
and ib_obj_type == NIOS_HOST_RECORD:
|
||||
del proposed_object['view']
|
||||
|
||||
if ib_obj_ref:
|
||||
if len(ib_obj_ref) > 1:
|
||||
for each in ib_obj_ref:
|
||||
# To check for existing A_record with same name with input A_record by IP
|
||||
if each.get('ipv4addr') and each.get('ipv4addr') == proposed_object.get('ipv4addr'):
|
||||
current_object = each
|
||||
# To check for existing Host_record with same name with input Host_record by IP
|
||||
elif each.get('ipv4addrs')[0].get('ipv4addr') and each.get('ipv4addrs')[0].get('ipv4addr')\
|
||||
== proposed_object.get('ipv4addrs')[0].get('ipv4addr'):
|
||||
current_object = each
|
||||
# Else set the current_object with input value
|
||||
else:
|
||||
current_object = obj_filter
|
||||
ref = None
|
||||
else:
|
||||
current_object = ib_obj_ref[0]
|
||||
if 'extattrs' in current_object:
|
||||
current_object['extattrs'] = flatten_extattrs(current_object['extattrs'])
|
||||
if current_object.get('_ref'):
|
||||
ref = current_object.pop('_ref')
|
||||
else:
|
||||
current_object = obj_filter
|
||||
ref = None
|
||||
# checks if the object type is member to normalize the attributes being passed
|
||||
if (ib_obj_type == NIOS_MEMBER):
|
||||
proposed_object = member_normalize(proposed_object)
|
||||
|
||||
# checks if the name's field has been updated
|
||||
if update and new_name:
|
||||
proposed_object['name'] = new_name
|
||||
|
||||
check_remove = []
|
||||
if (ib_obj_type == NIOS_HOST_RECORD):
|
||||
# this check is for idempotency, as if the same ip address shall be passed
|
||||
# add param will be removed, and same exists true for remove case as well.
|
||||
if 'ipv4addrs' in [current_object and proposed_object]:
|
||||
for each in current_object['ipv4addrs']:
|
||||
if each['ipv4addr'] == proposed_object['ipv4addrs'][0]['ipv4addr']:
|
||||
if 'add' in proposed_object['ipv4addrs'][0]:
|
||||
del proposed_object['ipv4addrs'][0]['add']
|
||||
break
|
||||
check_remove += each.values()
|
||||
if proposed_object['ipv4addrs'][0]['ipv4addr'] not in check_remove:
|
||||
if 'remove' in proposed_object['ipv4addrs'][0]:
|
||||
del proposed_object['ipv4addrs'][0]['remove']
|
||||
|
||||
res = None
|
||||
modified = not self.compare_objects(current_object, proposed_object)
|
||||
if 'extattrs' in proposed_object:
|
||||
proposed_object['extattrs'] = normalize_extattrs(proposed_object['extattrs'])
|
||||
|
||||
# Checks if nios_next_ip param is passed in ipv4addrs/ipv4addr args
|
||||
proposed_object = self.check_if_nios_next_ip_exists(proposed_object)
|
||||
|
||||
if state == 'present':
|
||||
if ref is None:
|
||||
if not self.module.check_mode:
|
||||
self.create_object(ib_obj_type, proposed_object)
|
||||
result['changed'] = True
|
||||
# Check if NIOS_MEMBER and the flag to call function create_token is set
|
||||
elif (ib_obj_type == NIOS_MEMBER) and (proposed_object['create_token']):
|
||||
proposed_object = None
|
||||
# the function creates a token that can be used by a pre-provisioned member to join the grid
|
||||
result['api_results'] = self.call_func('create_token', ref, proposed_object)
|
||||
result['changed'] = True
|
||||
elif modified:
|
||||
if 'ipv4addrs' in proposed_object:
|
||||
if ('add' not in proposed_object['ipv4addrs'][0]) and ('remove' not in proposed_object['ipv4addrs'][0]):
|
||||
self.check_if_recordname_exists(obj_filter, ib_obj_ref, ib_obj_type, current_object, proposed_object)
|
||||
|
||||
if (ib_obj_type in (NIOS_HOST_RECORD, NIOS_NETWORK_VIEW, NIOS_DNS_VIEW)):
|
||||
run_update = True
|
||||
proposed_object = self.on_update(proposed_object, ib_spec)
|
||||
if 'ipv4addrs' in proposed_object:
|
||||
if ('add' or 'remove') in proposed_object['ipv4addrs'][0]:
|
||||
run_update, proposed_object = self.check_if_add_remove_ip_arg_exists(proposed_object)
|
||||
if run_update:
|
||||
res = self.update_object(ref, proposed_object)
|
||||
result['changed'] = True
|
||||
else:
|
||||
res = ref
|
||||
if (ib_obj_type in (NIOS_A_RECORD, NIOS_AAAA_RECORD, NIOS_PTR_RECORD, NIOS_SRV_RECORD)):
|
||||
# popping 'view' key as update of 'view' is not supported with respect to a:record/aaaa:record/srv:record/ptr:record
|
||||
proposed_object = self.on_update(proposed_object, ib_spec)
|
||||
del proposed_object['view']
|
||||
if not self.module.check_mode:
|
||||
res = self.update_object(ref, proposed_object)
|
||||
result['changed'] = True
|
||||
elif 'network_view' in proposed_object:
|
||||
proposed_object.pop('network_view')
|
||||
result['changed'] = True
|
||||
if not self.module.check_mode and res is None:
|
||||
proposed_object = self.on_update(proposed_object, ib_spec)
|
||||
self.update_object(ref, proposed_object)
|
||||
result['changed'] = True
|
||||
|
||||
elif state == 'absent':
|
||||
if ref is not None:
|
||||
if 'ipv4addrs' in proposed_object:
|
||||
if 'remove' in proposed_object['ipv4addrs'][0]:
|
||||
self.check_if_add_remove_ip_arg_exists(proposed_object)
|
||||
self.update_object(ref, proposed_object)
|
||||
result['changed'] = True
|
||||
elif not self.module.check_mode:
|
||||
self.delete_object(ref)
|
||||
result['changed'] = True
|
||||
|
||||
return result
|
||||
|
||||
def check_if_recordname_exists(self, obj_filter, ib_obj_ref, ib_obj_type, current_object, proposed_object):
|
||||
''' Send POST request if host record input name and retrieved ref name is same,
|
||||
but input IP and retrieved IP is different'''
|
||||
|
||||
if 'name' in (obj_filter and ib_obj_ref[0]) and ib_obj_type == NIOS_HOST_RECORD:
|
||||
obj_host_name = obj_filter['name']
|
||||
ref_host_name = ib_obj_ref[0]['name']
|
||||
if 'ipv4addrs' in (current_object and proposed_object):
|
||||
current_ip_addr = current_object['ipv4addrs'][0]['ipv4addr']
|
||||
proposed_ip_addr = proposed_object['ipv4addrs'][0]['ipv4addr']
|
||||
elif 'ipv6addrs' in (current_object and proposed_object):
|
||||
current_ip_addr = current_object['ipv6addrs'][0]['ipv6addr']
|
||||
proposed_ip_addr = proposed_object['ipv6addrs'][0]['ipv6addr']
|
||||
|
||||
if obj_host_name == ref_host_name and current_ip_addr != proposed_ip_addr:
|
||||
self.create_object(ib_obj_type, proposed_object)
|
||||
|
||||
def check_if_nios_next_ip_exists(self, proposed_object):
|
||||
''' Check if nios_next_ip argument is passed in ipaddr while creating
|
||||
host record, if yes then format proposed object ipv4addrs and pass
|
||||
func:nextavailableip and ipaddr range to create hostrecord with next
|
||||
available ip in one call to avoid any race condition '''
|
||||
|
||||
if 'ipv4addrs' in proposed_object:
|
||||
if 'nios_next_ip' in proposed_object['ipv4addrs'][0]['ipv4addr']:
|
||||
ip_range = check_type_dict(proposed_object['ipv4addrs'][0]['ipv4addr'])['nios_next_ip']
|
||||
proposed_object['ipv4addrs'][0]['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range
|
||||
elif 'ipv4addr' in proposed_object:
|
||||
if 'nios_next_ip' in proposed_object['ipv4addr']:
|
||||
ip_range = check_type_dict(proposed_object['ipv4addr'])['nios_next_ip']
|
||||
proposed_object['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range
|
||||
|
||||
return proposed_object
|
||||
|
||||
def check_if_add_remove_ip_arg_exists(self, proposed_object):
|
||||
'''
|
||||
This function shall check if add/remove param is set to true and
|
||||
is passed in the args, then we will update the proposed dictionary
|
||||
to add/remove IP to existing host_record, if the user passes false
|
||||
param with the argument nothing shall be done.
|
||||
:returns: True if param is changed based on add/remove, and also the
|
||||
changed proposed_object.
|
||||
'''
|
||||
update = False
|
||||
if 'add' in proposed_object['ipv4addrs'][0]:
|
||||
if proposed_object['ipv4addrs'][0]['add']:
|
||||
proposed_object['ipv4addrs+'] = proposed_object['ipv4addrs']
|
||||
del proposed_object['ipv4addrs']
|
||||
del proposed_object['ipv4addrs+'][0]['add']
|
||||
update = True
|
||||
else:
|
||||
del proposed_object['ipv4addrs'][0]['add']
|
||||
elif 'remove' in proposed_object['ipv4addrs'][0]:
|
||||
if proposed_object['ipv4addrs'][0]['remove']:
|
||||
proposed_object['ipv4addrs-'] = proposed_object['ipv4addrs']
|
||||
del proposed_object['ipv4addrs']
|
||||
del proposed_object['ipv4addrs-'][0]['remove']
|
||||
update = True
|
||||
else:
|
||||
del proposed_object['ipv4addrs'][0]['remove']
|
||||
return update, proposed_object
|
||||
|
||||
def issubset(self, item, objects):
|
||||
''' Checks if item is a subset of objects
|
||||
:args item: the subset item to validate
|
||||
:args objects: superset list of objects to validate against
|
||||
:returns: True if item is a subset of one entry in objects otherwise
|
||||
this method will return None
|
||||
'''
|
||||
for obj in objects:
|
||||
if isinstance(item, dict):
|
||||
if all(entry in obj.items() for entry in item.items()):
|
||||
return True
|
||||
else:
|
||||
if item in obj:
|
||||
return True
|
||||
|
||||
def compare_objects(self, current_object, proposed_object):
|
||||
for key, proposed_item in iteritems(proposed_object):
|
||||
current_item = current_object.get(key)
|
||||
|
||||
# if proposed has a key that current doesn't then the objects are
|
||||
# not equal and False will be immediately returned
|
||||
if current_item is None:
|
||||
return False
|
||||
|
||||
elif isinstance(proposed_item, list):
|
||||
if key == 'aliases':
|
||||
if set(current_item) != set(proposed_item):
|
||||
return False
|
||||
for subitem in proposed_item:
|
||||
if not self.issubset(subitem, current_item):
|
||||
return False
|
||||
|
||||
elif isinstance(proposed_item, dict):
|
||||
return self.compare_objects(current_item, proposed_item)
|
||||
|
||||
else:
|
||||
if current_item != proposed_item:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec):
|
||||
''' this function gets the reference object of pre-existing nios objects '''
|
||||
|
||||
update = False
|
||||
old_name = new_name = None
|
||||
if ('name' in obj_filter):
|
||||
# gets and returns the current object based on name/old_name passed
|
||||
try:
|
||||
name_obj = check_type_dict(obj_filter['name'])
|
||||
old_name = name_obj['old_name']
|
||||
new_name = name_obj['new_name']
|
||||
except TypeError:
|
||||
name = obj_filter['name']
|
||||
|
||||
if old_name and new_name:
|
||||
if (ib_obj_type == NIOS_HOST_RECORD):
|
||||
test_obj_filter = dict([('name', old_name), ('view', obj_filter['view'])])
|
||||
elif (ib_obj_type in (NIOS_AAAA_RECORD, NIOS_A_RECORD)):
|
||||
test_obj_filter = obj_filter
|
||||
else:
|
||||
test_obj_filter = dict([('name', old_name)])
|
||||
# get the object reference
|
||||
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=list(ib_spec.keys()))
|
||||
if ib_obj:
|
||||
obj_filter['name'] = new_name
|
||||
else:
|
||||
test_obj_filter['name'] = new_name
|
||||
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=list(ib_spec.keys()))
|
||||
update = True
|
||||
return ib_obj, update, new_name
|
||||
if (ib_obj_type == NIOS_HOST_RECORD):
|
||||
# to check only by name if dns bypassing is set
|
||||
if not obj_filter['configure_for_dns']:
|
||||
test_obj_filter = dict([('name', name)])
|
||||
else:
|
||||
test_obj_filter = dict([('name', name), ('view', obj_filter['view'])])
|
||||
elif (ib_obj_type == NIOS_IPV4_FIXED_ADDRESS or ib_obj_type == NIOS_IPV6_FIXED_ADDRESS and 'mac' in obj_filter):
|
||||
test_obj_filter = dict([['mac', obj_filter['mac']]])
|
||||
elif (ib_obj_type == NIOS_A_RECORD):
|
||||
# resolves issue where a_record with uppercase name was returning null and was failing
|
||||
test_obj_filter = obj_filter
|
||||
test_obj_filter['name'] = test_obj_filter['name'].lower()
|
||||
# resolves issue where multiple a_records with same name and different IP address
|
||||
try:
|
||||
ipaddr_obj = check_type_dict(obj_filter['ipv4addr'])
|
||||
ipaddr = ipaddr_obj['old_ipv4addr']
|
||||
except TypeError:
|
||||
ipaddr = obj_filter['ipv4addr']
|
||||
test_obj_filter['ipv4addr'] = ipaddr
|
||||
elif (ib_obj_type == NIOS_TXT_RECORD):
|
||||
# resolves issue where multiple txt_records with same name and different text
|
||||
test_obj_filter = obj_filter
|
||||
try:
|
||||
text_obj = check_type_dict(obj_filter['text'])
|
||||
txt = text_obj['old_text']
|
||||
except TypeError:
|
||||
txt = obj_filter['text']
|
||||
test_obj_filter['text'] = txt
|
||||
# check if test_obj_filter is empty copy passed obj_filter
|
||||
else:
|
||||
test_obj_filter = obj_filter
|
||||
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
elif (ib_obj_type == NIOS_A_RECORD):
|
||||
# resolves issue where multiple a_records with same name and different IP address
|
||||
test_obj_filter = obj_filter
|
||||
try:
|
||||
ipaddr_obj = check_type_dict(obj_filter['ipv4addr'])
|
||||
ipaddr = ipaddr_obj['old_ipv4addr']
|
||||
except TypeError:
|
||||
ipaddr = obj_filter['ipv4addr']
|
||||
test_obj_filter['ipv4addr'] = ipaddr
|
||||
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
elif (ib_obj_type == NIOS_TXT_RECORD):
|
||||
# resolves issue where multiple txt_records with same name and different text
|
||||
test_obj_filter = obj_filter
|
||||
try:
|
||||
text_obj = check_type_dict(obj_filter['text'])
|
||||
txt = text_obj['old_text']
|
||||
except TypeError:
|
||||
txt = obj_filter['text']
|
||||
test_obj_filter['text'] = txt
|
||||
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
elif (ib_obj_type == NIOS_ZONE):
|
||||
# del key 'restart_if_needed' as nios_zone get_object fails with the key present
|
||||
temp = ib_spec['restart_if_needed']
|
||||
del ib_spec['restart_if_needed']
|
||||
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
# reinstate restart_if_needed if ib_obj is none, meaning there's no existing nios_zone ref
|
||||
if not ib_obj:
|
||||
ib_spec['restart_if_needed'] = temp
|
||||
elif (ib_obj_type == NIOS_MEMBER):
|
||||
# del key 'create_token' as nios_member get_object fails with the key present
|
||||
temp = ib_spec['create_token']
|
||||
del ib_spec['create_token']
|
||||
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
if temp:
|
||||
# reinstate 'create_token' key
|
||||
ib_spec['create_token'] = temp
|
||||
else:
|
||||
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
|
||||
return ib_obj, update, new_name
|
||||
|
||||
def on_update(self, proposed_object, ib_spec):
|
||||
''' Event called before the update is sent to the API endpoing
|
||||
This method will allow the final proposed object to be changed
|
||||
and/or keys filtered before it is sent to the API endpoint to
|
||||
be processed.
|
||||
:args proposed_object: A dict item that will be encoded and sent
|
||||
the API endpoint with the updated data structure
|
||||
:returns: updated object to be sent to API endpoint
|
||||
'''
|
||||
keys = set()
|
||||
for key, value in iteritems(proposed_object):
|
||||
update = ib_spec[key].get('update', True)
|
||||
if not update:
|
||||
keys.add(key)
|
||||
return dict([(k, v) for k, v in iteritems(proposed_object) if k not in keys])
|
||||
@@ -1834,16 +1834,12 @@ class RedfishUtils(object):
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
|
||||
# Checking if fans are present
|
||||
if u'Fans' in data:
|
||||
for device in data[u'Fans']:
|
||||
fan = {}
|
||||
for property in properties:
|
||||
if property in device:
|
||||
fan[property] = device[property]
|
||||
fan_results.append(fan)
|
||||
else:
|
||||
return {'ret': False, 'msg': "No Fans present"}
|
||||
for device in data[u'Fans']:
|
||||
fan = {}
|
||||
for property in properties:
|
||||
if property in device:
|
||||
fan[property] = device[property]
|
||||
fan_results.append(fan)
|
||||
result["entries"] = fan_results
|
||||
return result
|
||||
|
||||
@@ -2033,28 +2029,15 @@ class RedfishUtils(object):
|
||||
def get_multi_memory_inventory(self):
|
||||
return self.aggregate_systems(self.get_memory_inventory)
|
||||
|
||||
def get_nic(self, resource_uri):
|
||||
result = {}
|
||||
properties = ['Name', 'Id', 'Description', 'FQDN', 'IPv4Addresses', 'IPv6Addresses',
|
||||
'NameServers', 'MACAddress', 'PermanentMACAddress',
|
||||
'SpeedMbps', 'MTUSize', 'AutoNeg', 'Status']
|
||||
response = self.get_request(self.root_uri + resource_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
nic = {}
|
||||
for property in properties:
|
||||
if property in data:
|
||||
nic[property] = data[property]
|
||||
result['entries'] = nic
|
||||
return(result)
|
||||
|
||||
def get_nic_inventory(self, resource_uri):
|
||||
result = {}
|
||||
nic_list = []
|
||||
nic_results = []
|
||||
key = "EthernetInterfaces"
|
||||
# Get these entries, but does not fail if not found
|
||||
properties = ['Name', 'Id', 'Description', 'FQDN', 'IPv4Addresses', 'IPv6Addresses',
|
||||
'NameServers', 'MACAddress', 'PermanentMACAddress',
|
||||
'SpeedMbps', 'MTUSize', 'AutoNeg', 'Status']
|
||||
|
||||
response = self.get_request(self.root_uri + resource_uri)
|
||||
if response['ret'] is False:
|
||||
@@ -2078,9 +2061,18 @@ class RedfishUtils(object):
|
||||
nic_list.append(nic[u'@odata.id'])
|
||||
|
||||
for n in nic_list:
|
||||
nic = self.get_nic(n)
|
||||
if nic['ret']:
|
||||
nic_results.append(nic['entries'])
|
||||
nic = {}
|
||||
uri = self.root_uri + n
|
||||
response = self.get_request(uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
|
||||
for property in properties:
|
||||
if property in data:
|
||||
nic[property] = data[property]
|
||||
|
||||
nic_results.append(nic)
|
||||
result["entries"] = nic_results
|
||||
return result
|
||||
|
||||
@@ -2705,14 +2697,39 @@ class RedfishUtils(object):
|
||||
return self.aggregate_managers(self.get_manager_health_report)
|
||||
|
||||
def set_manager_nic(self, nic_addr, nic_config):
|
||||
# Get the manager ethernet interface uri
|
||||
nic_info = self.get_manager_ethernet_uri(nic_addr)
|
||||
# Get EthernetInterface collection
|
||||
response = self.get_request(self.root_uri + self.manager_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
if 'EthernetInterfaces' not in data:
|
||||
return {'ret': False, 'msg': "EthernetInterfaces resource not found"}
|
||||
ethernetinterfaces_uri = data["EthernetInterfaces"]["@odata.id"]
|
||||
response = self.get_request(self.root_uri + ethernetinterfaces_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
uris = [a.get('@odata.id') for a in data.get('Members', []) if
|
||||
a.get('@odata.id')]
|
||||
|
||||
if nic_info.get('nic_addr') is None:
|
||||
return nic_info
|
||||
else:
|
||||
target_ethernet_uri = nic_info['nic_addr']
|
||||
target_ethernet_current_setting = nic_info['ethernet_setting']
|
||||
# Find target EthernetInterface
|
||||
target_ethernet_uri = None
|
||||
target_ethernet_current_setting = None
|
||||
if nic_addr == 'null':
|
||||
# Find root_uri matched EthernetInterface when nic_addr is not specified
|
||||
nic_addr = (self.root_uri).split('/')[-1]
|
||||
nic_addr = nic_addr.split(':')[0] # split port if existing
|
||||
for uri in uris:
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
if '"' + nic_addr.lower() + '"' in str(data).lower() or "'" + nic_addr.lower() + "'" in str(data).lower():
|
||||
target_ethernet_uri = uri
|
||||
target_ethernet_current_setting = data
|
||||
break
|
||||
if target_ethernet_uri is None:
|
||||
return {'ret': False, 'msg': "No matched EthernetInterface found under Manager"}
|
||||
|
||||
# Convert input to payload and check validity
|
||||
payload = {}
|
||||
@@ -2775,208 +2792,3 @@ class RedfishUtils(object):
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified Manager NIC"}
|
||||
|
||||
# A helper function to get the EthernetInterface URI
|
||||
def get_manager_ethernet_uri(self, nic_addr='null'):
|
||||
# Get EthernetInterface collection
|
||||
response = self.get_request(self.root_uri + self.manager_uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
data = response['data']
|
||||
if 'EthernetInterfaces' not in data:
|
||||
return {'ret': False, 'msg': "EthernetInterfaces resource not found"}
|
||||
ethernetinterfaces_uri = data["EthernetInterfaces"]["@odata.id"]
|
||||
response = self.get_request(self.root_uri + ethernetinterfaces_uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
data = response['data']
|
||||
uris = [a.get('@odata.id') for a in data.get('Members', []) if
|
||||
a.get('@odata.id')]
|
||||
|
||||
# Find target EthernetInterface
|
||||
target_ethernet_uri = None
|
||||
target_ethernet_current_setting = None
|
||||
if nic_addr == 'null':
|
||||
# Find root_uri matched EthernetInterface when nic_addr is not specified
|
||||
nic_addr = (self.root_uri).split('/')[-1]
|
||||
nic_addr = nic_addr.split(':')[0] # split port if existing
|
||||
for uri in uris:
|
||||
response = self.get_request(self.root_uri + uri)
|
||||
if not response['ret']:
|
||||
return response
|
||||
data = response['data']
|
||||
data_string = json.dumps(data)
|
||||
if nic_addr.lower() in data_string.lower():
|
||||
target_ethernet_uri = uri
|
||||
target_ethernet_current_setting = data
|
||||
break
|
||||
|
||||
nic_info = {}
|
||||
nic_info['nic_addr'] = target_ethernet_uri
|
||||
nic_info['ethernet_setting'] = target_ethernet_current_setting
|
||||
|
||||
if target_ethernet_uri is None:
|
||||
return {}
|
||||
else:
|
||||
return nic_info
|
||||
|
||||
def set_hostinterface_attributes(self, hostinterface_config, hostinterface_id=None):
|
||||
response = self.get_request(self.root_uri + self.manager_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
if 'HostInterfaces' not in data:
|
||||
return {'ret': False, 'msg': "HostInterfaces resource not found"}
|
||||
|
||||
hostinterfaces_uri = data["HostInterfaces"]["@odata.id"]
|
||||
response = self.get_request(self.root_uri + hostinterfaces_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
uris = [a.get('@odata.id') for a in data.get('Members', []) if a.get('@odata.id')]
|
||||
# Capture list of URIs that match a specified HostInterface resource ID
|
||||
if hostinterface_id:
|
||||
matching_hostinterface_uris = [uri for uri in uris if hostinterface_id in uri.split('/')[-1]]
|
||||
|
||||
if hostinterface_id and matching_hostinterface_uris:
|
||||
hostinterface_uri = list.pop(matching_hostinterface_uris)
|
||||
elif hostinterface_id and not matching_hostinterface_uris:
|
||||
return {'ret': False, 'msg': "HostInterface ID %s not present." % hostinterface_id}
|
||||
elif len(uris) == 1:
|
||||
hostinterface_uri = list.pop(uris)
|
||||
else:
|
||||
return {'ret': False, 'msg': "HostInterface ID not defined and multiple interfaces detected."}
|
||||
|
||||
response = self.get_request(self.root_uri + hostinterface_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
current_hostinterface_config = response['data']
|
||||
payload = {}
|
||||
for property in hostinterface_config.keys():
|
||||
value = hostinterface_config[property]
|
||||
if property not in current_hostinterface_config:
|
||||
return {'ret': False, 'msg': "Property %s in hostinterface_config is invalid" % property}
|
||||
if isinstance(value, dict):
|
||||
if isinstance(current_hostinterface_config[property], dict):
|
||||
payload[property] = value
|
||||
elif isinstance(current_hostinterface_config[property], list):
|
||||
payload[property] = list()
|
||||
payload[property].append(value)
|
||||
else:
|
||||
return {'ret': False, 'msg': "Value of property %s in hostinterface_config is invalid" % property}
|
||||
else:
|
||||
payload[property] = value
|
||||
|
||||
need_change = False
|
||||
for property in payload.keys():
|
||||
set_value = payload[property]
|
||||
cur_value = current_hostinterface_config[property]
|
||||
if not isinstance(set_value, dict) and not isinstance(set_value, list):
|
||||
if set_value != cur_value:
|
||||
need_change = True
|
||||
if isinstance(set_value, dict):
|
||||
for subprop in payload[property].keys():
|
||||
if subprop not in current_hostinterface_config[property]:
|
||||
need_change = True
|
||||
break
|
||||
sub_set_value = payload[property][subprop]
|
||||
sub_cur_value = current_hostinterface_config[property][subprop]
|
||||
if sub_set_value != sub_cur_value:
|
||||
need_change = True
|
||||
if isinstance(set_value, list):
|
||||
if len(set_value) != len(cur_value):
|
||||
need_change = True
|
||||
continue
|
||||
for i in range(len(set_value)):
|
||||
for subprop in payload[property][i].keys():
|
||||
if subprop not in current_hostinterface_config[property][i]:
|
||||
need_change = True
|
||||
break
|
||||
sub_set_value = payload[property][i][subprop]
|
||||
sub_cur_value = current_hostinterface_config[property][i][subprop]
|
||||
if sub_set_value != sub_cur_value:
|
||||
need_change = True
|
||||
if not need_change:
|
||||
return {'ret': True, 'changed': False, 'msg': "Host Interface already configured"}
|
||||
|
||||
response = self.patch_request(self.root_uri + hostinterface_uri, payload)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
return {'ret': True, 'changed': True, 'msg': "Modified Host Interface"}
|
||||
|
||||
def get_hostinterfaces(self):
|
||||
result = {}
|
||||
hostinterface_results = []
|
||||
properties = ['Id', 'Name', 'Description', 'HostInterfaceType', 'Status',
|
||||
'InterfaceEnabled', 'ExternallyAccessible', 'AuthenticationModes',
|
||||
'AuthNoneRoleId', 'CredentialBootstrapping']
|
||||
manager_uri_list = self.manager_uris
|
||||
for manager_uri in manager_uri_list:
|
||||
response = self.get_request(self.root_uri + manager_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
|
||||
if 'HostInterfaces' in data:
|
||||
hostinterfaces_uri = data[u'HostInterfaces'][u'@odata.id']
|
||||
else:
|
||||
continue
|
||||
|
||||
response = self.get_request(self.root_uri + hostinterfaces_uri)
|
||||
data = response['data']
|
||||
|
||||
if 'Members' in data:
|
||||
for hostinterface in data['Members']:
|
||||
hostinterface_uri = hostinterface['@odata.id']
|
||||
hostinterface_response = self.get_request(self.root_uri + hostinterface_uri)
|
||||
# dictionary for capturing individual HostInterface properties
|
||||
hostinterface_data_temp = {}
|
||||
if hostinterface_response['ret'] is False:
|
||||
return hostinterface_response
|
||||
hostinterface_data = hostinterface_response['data']
|
||||
for property in properties:
|
||||
if property in hostinterface_data:
|
||||
if hostinterface_data[property] is not None:
|
||||
hostinterface_data_temp[property] = hostinterface_data[property]
|
||||
# Check for the presence of a ManagerEthernetInterface
|
||||
# object, a link to a _single_ EthernetInterface that the
|
||||
# BMC uses to communicate with the host.
|
||||
if 'ManagerEthernetInterface' in hostinterface_data:
|
||||
interface_uri = hostinterface_data['ManagerEthernetInterface']['@odata.id']
|
||||
interface_response = self.get_nic(interface_uri)
|
||||
if interface_response['ret'] is False:
|
||||
return interface_response
|
||||
hostinterface_data_temp['ManagerEthernetInterface'] = interface_response['entries']
|
||||
|
||||
# Check for the presence of a HostEthernetInterfaces
|
||||
# object, a link to a _collection_ of EthernetInterfaces
|
||||
# that the host uses to communicate with the BMC.
|
||||
if 'HostEthernetInterfaces' in hostinterface_data:
|
||||
interfaces_uri = hostinterface_data['HostEthernetInterfaces']['@odata.id']
|
||||
interfaces_response = self.get_request(self.root_uri + interfaces_uri)
|
||||
if interfaces_response['ret'] is False:
|
||||
return interfaces_response
|
||||
interfaces_data = interfaces_response['data']
|
||||
if 'Members' in interfaces_data:
|
||||
for interface in interfaces_data['Members']:
|
||||
interface_uri = interface['@odata.id']
|
||||
interface_response = self.get_nic(interface_uri)
|
||||
if interface_response['ret'] is False:
|
||||
return interface_response
|
||||
# Check if this is the first
|
||||
# HostEthernetInterfaces item and create empty
|
||||
# list if so.
|
||||
if 'HostEthernetInterfaces' not in hostinterface_data_temp:
|
||||
hostinterface_data_temp['HostEthernetInterfaces'] = []
|
||||
|
||||
hostinterface_data_temp['HostEthernetInterfaces'].append(interface_response['entries'])
|
||||
|
||||
hostinterface_results.append(hostinterface_data_temp)
|
||||
else:
|
||||
continue
|
||||
result["entries"] = hostinterface_results
|
||||
if not result["entries"]:
|
||||
return {'ret': False, 'msg': "No HostInterface objects found"}
|
||||
return result
|
||||
|
||||
@@ -15,6 +15,13 @@ from ansible.module_utils.urls import fetch_url, basic_auth_header
|
||||
class BitbucketHelper:
|
||||
BITBUCKET_API_URL = 'https://api.bitbucket.org'
|
||||
|
||||
error_messages = {
|
||||
'required_client_id': '`client_id` must be specified as a parameter or '
|
||||
'BITBUCKET_CLIENT_ID environment variable',
|
||||
'required_client_secret': '`client_secret` must be specified as a parameter or '
|
||||
'BITBUCKET_CLIENT_SECRET environment variable',
|
||||
}
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.access_token = None
|
||||
@@ -22,40 +29,35 @@ class BitbucketHelper:
|
||||
@staticmethod
|
||||
def bitbucket_argument_spec():
|
||||
return dict(
|
||||
client_id=dict(type='str', fallback=(env_fallback, ['BITBUCKET_CLIENT_ID'])),
|
||||
client_id=dict(type='str', no_log=True, fallback=(env_fallback, ['BITBUCKET_CLIENT_ID'])),
|
||||
client_secret=dict(type='str', no_log=True, fallback=(env_fallback, ['BITBUCKET_CLIENT_SECRET'])),
|
||||
# TODO:
|
||||
# - Rename user to username once current usage of username is removed
|
||||
# - Alias user to username and deprecate it
|
||||
user=dict(type='str', fallback=(env_fallback, ['BITBUCKET_USERNAME'])),
|
||||
password=dict(type='str', no_log=True, fallback=(env_fallback, ['BITBUCKET_PASSWORD'])),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bitbucket_required_one_of():
|
||||
return [['client_id', 'client_secret', 'user', 'password']]
|
||||
def check_arguments(self):
|
||||
if self.module.params['client_id'] is None:
|
||||
self.module.fail_json(msg=self.error_messages['required_client_id'])
|
||||
|
||||
@staticmethod
|
||||
def bitbucket_required_together():
|
||||
return [['client_id', 'client_secret'], ['user', 'password']]
|
||||
if self.module.params['client_secret'] is None:
|
||||
self.module.fail_json(msg=self.error_messages['required_client_secret'])
|
||||
|
||||
def fetch_access_token(self):
|
||||
if self.module.params['client_id'] and self.module.params['client_secret']:
|
||||
headers = {
|
||||
'Authorization': basic_auth_header(self.module.params['client_id'], self.module.params['client_secret']),
|
||||
}
|
||||
self.check_arguments()
|
||||
|
||||
info, content = self.request(
|
||||
api_url='https://bitbucket.org/site/oauth2/access_token',
|
||||
method='POST',
|
||||
data='grant_type=client_credentials',
|
||||
headers=headers,
|
||||
)
|
||||
headers = {
|
||||
'Authorization': basic_auth_header(self.module.params['client_id'], self.module.params['client_secret'])
|
||||
}
|
||||
|
||||
if info['status'] == 200:
|
||||
self.access_token = content['access_token']
|
||||
else:
|
||||
self.module.fail_json(msg='Failed to retrieve access token: {0}'.format(info))
|
||||
info, content = self.request(
|
||||
api_url='https://bitbucket.org/site/oauth2/access_token',
|
||||
method='POST',
|
||||
data='grant_type=client_credentials',
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if info['status'] == 200:
|
||||
self.access_token = content['access_token']
|
||||
else:
|
||||
self.module.fail_json(msg='Failed to retrieve access token: {0}'.format(info))
|
||||
|
||||
def request(self, api_url, method, data=None, headers=None):
|
||||
headers = headers or {}
|
||||
@@ -64,10 +66,6 @@ class BitbucketHelper:
|
||||
headers.update({
|
||||
'Authorization': 'Bearer {0}'.format(self.access_token),
|
||||
})
|
||||
elif self.module.params['user'] and self.module.params['password']:
|
||||
headers.update({
|
||||
'Authorization': basic_auth_header(self.module.params['user'], self.module.params['password']),
|
||||
})
|
||||
|
||||
if isinstance(data, dict):
|
||||
data = self.module.jsonify(data)
|
||||
|
||||
@@ -422,7 +422,6 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import shlex
|
||||
|
||||
try:
|
||||
import lxc
|
||||
@@ -662,8 +661,9 @@ class LxcContainerManagement(object):
|
||||
"""
|
||||
|
||||
for key, value in variables_dict.items():
|
||||
build_command.append(str(key))
|
||||
build_command.append(str(value))
|
||||
build_command.append(
|
||||
'%s %s' % (key, value)
|
||||
)
|
||||
return build_command
|
||||
|
||||
def _get_vars(self, variables):
|
||||
@@ -686,6 +686,24 @@ class LxcContainerManagement(object):
|
||||
return_dict[v] = _var
|
||||
return return_dict
|
||||
|
||||
def _run_command(self, build_command, unsafe_shell=False):
|
||||
"""Return information from running an Ansible Command.
|
||||
|
||||
This will squash the build command list into a string and then
|
||||
execute the command via Ansible. The output is returned to the method.
|
||||
This output is returned as `return_code`, `stdout`, `stderr`.
|
||||
|
||||
:param build_command: Used for the command and all options.
|
||||
:type build_command: ``list``
|
||||
:param unsafe_shell: Enable or Disable unsafe sell commands.
|
||||
:type unsafe_shell: ``bol``
|
||||
"""
|
||||
|
||||
return self.module.run_command(
|
||||
' '.join(build_command),
|
||||
use_unsafe_shell=unsafe_shell
|
||||
)
|
||||
|
||||
def _config(self):
|
||||
"""Configure an LXC container.
|
||||
|
||||
@@ -792,7 +810,7 @@ class LxcContainerManagement(object):
|
||||
elif self.module.params.get('backing_store') == 'overlayfs':
|
||||
build_command.append('--snapshot')
|
||||
|
||||
rc, return_data, err = self.module.run_command(build_command)
|
||||
rc, return_data, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
message = "Failed executing %s." % os.path.basename(clone_cmd)
|
||||
self.failure(
|
||||
@@ -825,7 +843,7 @@ class LxcContainerManagement(object):
|
||||
|
||||
build_command = [
|
||||
self.module.get_bin_path('lxc-create', True),
|
||||
'--name', self.container_name,
|
||||
'--name %s' % self.container_name,
|
||||
'--quiet'
|
||||
]
|
||||
|
||||
@@ -851,12 +869,10 @@ class LxcContainerManagement(object):
|
||||
log_path = os.getenv('HOME')
|
||||
|
||||
build_command.extend([
|
||||
'--logfile',
|
||||
os.path.join(
|
||||
'--logfile %s' % os.path.join(
|
||||
log_path, 'lxc-%s.log' % self.container_name
|
||||
),
|
||||
'--logpriority',
|
||||
self.module.params.get(
|
||||
'--logpriority %s' % self.module.params.get(
|
||||
'container_log_level'
|
||||
).upper()
|
||||
])
|
||||
@@ -864,10 +880,9 @@ class LxcContainerManagement(object):
|
||||
# Add the template commands to the end of the command if there are any
|
||||
template_options = self.module.params.get('template_options', None)
|
||||
if template_options:
|
||||
build_command.append('--')
|
||||
build_command += shlex.split(template_options)
|
||||
build_command.append('-- %s' % template_options)
|
||||
|
||||
rc, return_data, err = self.module.run_command(build_command)
|
||||
rc, return_data, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
message = "Failed executing lxc-create."
|
||||
self.failure(
|
||||
@@ -1171,7 +1186,7 @@ class LxcContainerManagement(object):
|
||||
self.module.get_bin_path('lxc-config', True),
|
||||
"lxc.bdev.lvm.vg"
|
||||
]
|
||||
rc, vg, err = self.module.run_command(build_command)
|
||||
rc, vg, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1189,7 +1204,7 @@ class LxcContainerManagement(object):
|
||||
build_command = [
|
||||
self.module.get_bin_path('lvs', True)
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1216,7 +1231,7 @@ class LxcContainerManagement(object):
|
||||
'--units',
|
||||
'g'
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1247,7 +1262,7 @@ class LxcContainerManagement(object):
|
||||
'--units',
|
||||
'g'
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1296,7 +1311,7 @@ class LxcContainerManagement(object):
|
||||
os.path.join(vg, source_lv),
|
||||
"-L%sg" % snapshot_size_gb
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1321,7 +1336,7 @@ class LxcContainerManagement(object):
|
||||
"/dev/%s/%s" % (vg, lv_name),
|
||||
mount_point,
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1365,8 +1380,9 @@ class LxcContainerManagement(object):
|
||||
'.'
|
||||
]
|
||||
|
||||
rc, stdout, err = self.module.run_command(
|
||||
build_command
|
||||
rc, stdout, err = self._run_command(
|
||||
build_command=build_command,
|
||||
unsafe_shell=True
|
||||
)
|
||||
|
||||
os.umask(old_umask)
|
||||
@@ -1394,7 +1410,7 @@ class LxcContainerManagement(object):
|
||||
"-f",
|
||||
"%s/%s" % (vg, lv_name),
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1426,10 +1442,11 @@ class LxcContainerManagement(object):
|
||||
self.module.get_bin_path('rsync', True),
|
||||
'-aHAX',
|
||||
fs_path,
|
||||
temp_dir,
|
||||
temp_dir
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(
|
||||
rc, stdout, err = self._run_command(
|
||||
build_command,
|
||||
unsafe_shell=True
|
||||
)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
@@ -1450,7 +1467,7 @@ class LxcContainerManagement(object):
|
||||
self.module.get_bin_path('umount', True),
|
||||
mount_point,
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
@@ -1472,12 +1489,12 @@ class LxcContainerManagement(object):
|
||||
|
||||
build_command = [
|
||||
self.module.get_bin_path('mount', True),
|
||||
'-t', 'overlayfs',
|
||||
'-o', 'lowerdir=%s,upperdir=%s' % (lowerdir, upperdir),
|
||||
'-t overlayfs',
|
||||
'-o lowerdir=%s,upperdir=%s' % (lowerdir, upperdir),
|
||||
'overlayfs',
|
||||
mount_point,
|
||||
]
|
||||
rc, stdout, err = self.module.run_command(build_command)
|
||||
rc, stdout, err = self._run_command(build_command)
|
||||
if rc != 0:
|
||||
self.failure(
|
||||
err=err,
|
||||
|
||||
@@ -11,28 +11,29 @@ __metaclass__ = type
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: lxd_container
|
||||
short_description: Manage LXD instances
|
||||
short_description: Manage LXD Containers
|
||||
description:
|
||||
- Management of LXD containers and virtual machines.
|
||||
- Management of LXD containers
|
||||
author: "Hiroaki Nakamura (@hnakamur)"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of an instance.
|
||||
- Name of a container.
|
||||
type: str
|
||||
required: true
|
||||
architecture:
|
||||
description:
|
||||
- 'The architecture for the instance (for example C(x86_64) or C(i686)).
|
||||
- 'The architecture for the container (for example C(x86_64) or C(i686)).
|
||||
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
|
||||
type: str
|
||||
required: false
|
||||
config:
|
||||
description:
|
||||
- 'The config for the instance (for example C({"limits.cpu": "2"})).
|
||||
- 'The config for the container (for example C({"limits.cpu": "2"})).
|
||||
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
|
||||
- If the instance already exists and its "config" values in metadata
|
||||
obtained from the LXD API U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#instances-containers-and-virtual-machines)
|
||||
- If the container already exists and its "config" values in metadata
|
||||
obtained from GET /1.0/containers/<name>
|
||||
U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#10containersname)
|
||||
are different, this module tries to apply the configurations.
|
||||
- The keys starting with C(volatile.) are ignored for this comparison when I(ignore_volatile_options=true).
|
||||
type: dict
|
||||
@@ -42,32 +43,33 @@ options:
|
||||
- If set to C(true), options starting with C(volatile.) are ignored. As a result,
|
||||
they are reapplied for each execution.
|
||||
- This default behavior can be changed by setting this option to C(false).
|
||||
- The current default value C(true) is deprecated since community.general 4.0.0,
|
||||
and will change to C(false) in community.general 6.0.0.
|
||||
- The default value C(true) will be deprecated in community.general 4.0.0,
|
||||
and will change to C(false) in community.general 5.0.0.
|
||||
type: bool
|
||||
default: true
|
||||
required: false
|
||||
version_added: 3.7.0
|
||||
profiles:
|
||||
description:
|
||||
- Profile to be used by the instance.
|
||||
- Profile to be used by the container.
|
||||
type: list
|
||||
elements: str
|
||||
devices:
|
||||
description:
|
||||
- 'The devices for the instance
|
||||
- 'The devices for the container
|
||||
(for example C({ "rootfs": { "path": "/dev/kvm", "type": "unix-char" }})).
|
||||
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
|
||||
type: dict
|
||||
required: false
|
||||
ephemeral:
|
||||
description:
|
||||
- Whether or not the instance is ephemeral (for example C(true) or C(false)).
|
||||
- Whether or not the container is ephemeral (for example C(true) or C(false)).
|
||||
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).
|
||||
required: false
|
||||
type: bool
|
||||
source:
|
||||
description:
|
||||
- 'The source for the instance
|
||||
- 'The source for the container
|
||||
(e.g. { "type": "image",
|
||||
"mode": "pull",
|
||||
"server": "https://images.linuxcontainers.org",
|
||||
@@ -85,49 +87,39 @@ options:
|
||||
- absent
|
||||
- frozen
|
||||
description:
|
||||
- Define the state of an instance.
|
||||
- Define the state of a container.
|
||||
required: false
|
||||
default: started
|
||||
type: str
|
||||
target:
|
||||
description:
|
||||
- For cluster deployments. Will attempt to create an instance on a target node.
|
||||
If the instance exists elsewhere in a cluster, then it will not be replaced or moved.
|
||||
- For cluster deployments. Will attempt to create a container on a target node.
|
||||
If container exists elsewhere in a cluster, then container will not be replaced or moved.
|
||||
The name should respond to same name of the node you see in C(lxc cluster list).
|
||||
type: str
|
||||
required: false
|
||||
version_added: 1.0.0
|
||||
timeout:
|
||||
description:
|
||||
- A timeout for changing the state of the instance.
|
||||
- A timeout for changing the state of the container.
|
||||
- This is also used as a timeout for waiting until IPv4 addresses
|
||||
are set to the all network interfaces in the instance after
|
||||
are set to the all network interfaces in the container after
|
||||
starting or restarting.
|
||||
required: false
|
||||
default: 30
|
||||
type: int
|
||||
type:
|
||||
description:
|
||||
- Instance type can be either C(virtual-machine) or C(container).
|
||||
required: false
|
||||
default: container
|
||||
choices:
|
||||
- container
|
||||
- virtual-machine
|
||||
type: str
|
||||
version_added: 4.1.0
|
||||
wait_for_ipv4_addresses:
|
||||
description:
|
||||
- If this is true, the C(lxd_container) waits until IPv4 addresses
|
||||
are set to the all network interfaces in the instance after
|
||||
are set to the all network interfaces in the container after
|
||||
starting or restarting.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
force_stop:
|
||||
description:
|
||||
- If this is true, the C(lxd_container) forces to stop the instance
|
||||
when it stops or restarts the instance.
|
||||
- If this is true, the C(lxd_container) forces to stop the container
|
||||
when it stops or restarts the container.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
@@ -169,18 +161,18 @@ options:
|
||||
required: false
|
||||
type: str
|
||||
notes:
|
||||
- Instances can be a container or a virtual machine, both of them must have unique name. If you attempt to create an instance
|
||||
- Containers must have a unique name. If you attempt to create a container
|
||||
with a name that already existed in the users namespace the module will
|
||||
simply return as "unchanged".
|
||||
- There are two ways to run commands inside a container or virtual machine, using the command
|
||||
- There are two ways to run commands in containers, using the command
|
||||
module or using the ansible lxd connection plugin bundled in Ansible >=
|
||||
2.1, the later requires python to be installed in the instance which can
|
||||
2.1, the later requires python to be installed in the container which can
|
||||
be done with the command module.
|
||||
- You can copy a file from the host to the instance
|
||||
- You can copy a file from the host to the container
|
||||
with the Ansible M(ansible.builtin.copy) and M(ansible.builtin.template) module and the `lxd` connection plugin.
|
||||
See the example below.
|
||||
- You can copy a file in the created instance to the localhost
|
||||
with `command=lxc file pull instance_name/dir/filename filename`.
|
||||
- You can copy a file in the created container to the localhost
|
||||
with `command=lxc file pull container_name/dir/filename filename`.
|
||||
See the first example below.
|
||||
'''
|
||||
|
||||
@@ -249,7 +241,6 @@ EXAMPLES = '''
|
||||
community.general.lxd_container:
|
||||
name: mycontainer
|
||||
state: absent
|
||||
type: container
|
||||
|
||||
# An example for restarting a container
|
||||
- hosts: localhost
|
||||
@@ -259,7 +250,6 @@ EXAMPLES = '''
|
||||
community.general.lxd_container:
|
||||
name: mycontainer
|
||||
state: restarted
|
||||
type: container
|
||||
|
||||
# An example for restarting a container using https to connect to the LXD server
|
||||
- hosts: localhost
|
||||
@@ -317,36 +307,16 @@ EXAMPLES = '''
|
||||
mode: pull
|
||||
alias: ubuntu/xenial/amd64
|
||||
target: node02
|
||||
|
||||
# An example for creating a virtual machine
|
||||
- hosts: localhost
|
||||
connection: local
|
||||
tasks:
|
||||
- name: Create container on another node
|
||||
community.general.lxd_container:
|
||||
name: new-vm-1
|
||||
type: virtual-machine
|
||||
state: started
|
||||
ignore_volatile_options: true
|
||||
wait_for_ipv4_addresses: true
|
||||
profiles: ["default"]
|
||||
source:
|
||||
protocol: simplestreams
|
||||
type: image
|
||||
mode: pull
|
||||
server: https://images.linuxcontainers.org
|
||||
alias: debian/11
|
||||
timeout: 600
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
addresses:
|
||||
description: Mapping from the network device name to a list of IPv4 addresses in the instance.
|
||||
description: Mapping from the network device name to a list of IPv4 addresses in the container
|
||||
returned: when state is started or restarted
|
||||
type: dict
|
||||
sample: {"eth0": ["10.155.92.191"]}
|
||||
old_state:
|
||||
description: The old state of the instance.
|
||||
description: The old state of the container
|
||||
returned: when state is started or restarted
|
||||
type: str
|
||||
sample: "stopped"
|
||||
@@ -356,7 +326,7 @@ logs:
|
||||
type: list
|
||||
sample: "(too long to be placed here)"
|
||||
actions:
|
||||
description: List of actions performed for the instance.
|
||||
description: List of actions performed for the container.
|
||||
returned: success
|
||||
type: list
|
||||
sample: '["create", "start"]'
|
||||
@@ -415,15 +385,6 @@ class LXDContainerManagement(object):
|
||||
self.addresses = None
|
||||
self.target = self.module.params['target']
|
||||
|
||||
self.type = self.module.params['type']
|
||||
|
||||
# LXD Rest API provides additional endpoints for creating containers and virtual-machines.
|
||||
self.api_endpoint = None
|
||||
if self.type == 'container':
|
||||
self.api_endpoint = '/1.0/containers'
|
||||
elif self.type == 'virtual-machine':
|
||||
self.api_endpoint = '/1.0/virtual-machines'
|
||||
|
||||
self.key_file = self.module.params.get('client_key')
|
||||
if self.key_file is None:
|
||||
self.key_file = '{0}/.config/lxc/client.key'.format(os.environ['HOME'])
|
||||
@@ -459,20 +420,20 @@ class LXDContainerManagement(object):
|
||||
if param_val is not None:
|
||||
self.config[attr] = param_val
|
||||
|
||||
def _get_instance_json(self):
|
||||
def _get_container_json(self):
|
||||
return self.client.do(
|
||||
'GET', '{0}/{1}'.format(self.api_endpoint, self.name),
|
||||
'GET', '/1.0/containers/{0}'.format(self.name),
|
||||
ok_error_codes=[404]
|
||||
)
|
||||
|
||||
def _get_instance_state_json(self):
|
||||
def _get_container_state_json(self):
|
||||
return self.client.do(
|
||||
'GET', '{0}/{1}/state'.format(self.api_endpoint, self.name),
|
||||
'GET', '/1.0/containers/{0}/state'.format(self.name),
|
||||
ok_error_codes=[404]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _instance_json_to_module_state(resp_json):
|
||||
def _container_json_to_module_state(resp_json):
|
||||
if resp_json['type'] == 'error':
|
||||
return 'absent'
|
||||
return ANSIBLE_LXD_STATES[resp_json['metadata']['status']]
|
||||
@@ -481,45 +442,45 @@ class LXDContainerManagement(object):
|
||||
body_json = {'action': action, 'timeout': self.timeout}
|
||||
if force_stop:
|
||||
body_json['force'] = True
|
||||
return self.client.do('PUT', '{0}/{1}/state'.format(self.api_endpoint, self.name), body_json=body_json)
|
||||
return self.client.do('PUT', '/1.0/containers/{0}/state'.format(self.name), body_json=body_json)
|
||||
|
||||
def _create_instance(self):
|
||||
def _create_container(self):
|
||||
config = self.config.copy()
|
||||
config['name'] = self.name
|
||||
if self.target:
|
||||
self.client.do('POST', '{0}?{1}'.format(self.api_endpoint, urlencode(dict(target=self.target))), config)
|
||||
self.client.do('POST', '/1.0/containers?' + urlencode(dict(target=self.target)), config)
|
||||
else:
|
||||
self.client.do('POST', self.api_endpoint, config)
|
||||
self.client.do('POST', '/1.0/containers', config)
|
||||
self.actions.append('create')
|
||||
|
||||
def _start_instance(self):
|
||||
def _start_container(self):
|
||||
self._change_state('start')
|
||||
self.actions.append('start')
|
||||
|
||||
def _stop_instance(self):
|
||||
def _stop_container(self):
|
||||
self._change_state('stop', self.force_stop)
|
||||
self.actions.append('stop')
|
||||
|
||||
def _restart_instance(self):
|
||||
def _restart_container(self):
|
||||
self._change_state('restart', self.force_stop)
|
||||
self.actions.append('restart')
|
||||
|
||||
def _delete_instance(self):
|
||||
self.client.do('DELETE', '{0}/{1}'.format(self.api_endpoint, self.name))
|
||||
def _delete_container(self):
|
||||
self.client.do('DELETE', '/1.0/containers/{0}'.format(self.name))
|
||||
self.actions.append('delete')
|
||||
|
||||
def _freeze_instance(self):
|
||||
def _freeze_container(self):
|
||||
self._change_state('freeze')
|
||||
self.actions.append('freeze')
|
||||
|
||||
def _unfreeze_instance(self):
|
||||
def _unfreeze_container(self):
|
||||
self._change_state('unfreeze')
|
||||
self.actions.append('unfreez')
|
||||
|
||||
def _instance_ipv4_addresses(self, ignore_devices=None):
|
||||
def _container_ipv4_addresses(self, ignore_devices=None):
|
||||
ignore_devices = ['lo'] if ignore_devices is None else ignore_devices
|
||||
|
||||
resp_json = self._get_instance_state_json()
|
||||
resp_json = self._get_container_state_json()
|
||||
network = resp_json['metadata']['network'] or {}
|
||||
network = dict((k, v) for k, v in network.items() if k not in ignore_devices) or {}
|
||||
addresses = dict((k, [a['address'] for a in v['addresses'] if a['family'] == 'inet']) for k, v in network.items()) or {}
|
||||
@@ -534,7 +495,7 @@ class LXDContainerManagement(object):
|
||||
due = datetime.datetime.now() + datetime.timedelta(seconds=self.timeout)
|
||||
while datetime.datetime.now() < due:
|
||||
time.sleep(1)
|
||||
addresses = self._instance_ipv4_addresses()
|
||||
addresses = self._container_ipv4_addresses()
|
||||
if self._has_all_ipv4_addresses(addresses):
|
||||
self.addresses = addresses
|
||||
return
|
||||
@@ -544,72 +505,72 @@ class LXDContainerManagement(object):
|
||||
|
||||
def _started(self):
|
||||
if self.old_state == 'absent':
|
||||
self._create_instance()
|
||||
self._start_instance()
|
||||
self._create_container()
|
||||
self._start_container()
|
||||
else:
|
||||
if self.old_state == 'frozen':
|
||||
self._unfreeze_instance()
|
||||
self._unfreeze_container()
|
||||
elif self.old_state == 'stopped':
|
||||
self._start_instance()
|
||||
if self._needs_to_apply_instance_configs():
|
||||
self._apply_instance_configs()
|
||||
self._start_container()
|
||||
if self._needs_to_apply_container_configs():
|
||||
self._apply_container_configs()
|
||||
if self.wait_for_ipv4_addresses:
|
||||
self._get_addresses()
|
||||
|
||||
def _stopped(self):
|
||||
if self.old_state == 'absent':
|
||||
self._create_instance()
|
||||
self._create_container()
|
||||
else:
|
||||
if self.old_state == 'stopped':
|
||||
if self._needs_to_apply_instance_configs():
|
||||
self._start_instance()
|
||||
self._apply_instance_configs()
|
||||
self._stop_instance()
|
||||
if self._needs_to_apply_container_configs():
|
||||
self._start_container()
|
||||
self._apply_container_configs()
|
||||
self._stop_container()
|
||||
else:
|
||||
if self.old_state == 'frozen':
|
||||
self._unfreeze_instance()
|
||||
if self._needs_to_apply_instance_configs():
|
||||
self._apply_instance_configs()
|
||||
self._stop_instance()
|
||||
self._unfreeze_container()
|
||||
if self._needs_to_apply_container_configs():
|
||||
self._apply_container_configs()
|
||||
self._stop_container()
|
||||
|
||||
def _restarted(self):
|
||||
if self.old_state == 'absent':
|
||||
self._create_instance()
|
||||
self._start_instance()
|
||||
self._create_container()
|
||||
self._start_container()
|
||||
else:
|
||||
if self.old_state == 'frozen':
|
||||
self._unfreeze_instance()
|
||||
if self._needs_to_apply_instance_configs():
|
||||
self._apply_instance_configs()
|
||||
self._restart_instance()
|
||||
self._unfreeze_container()
|
||||
if self._needs_to_apply_container_configs():
|
||||
self._apply_container_configs()
|
||||
self._restart_container()
|
||||
if self.wait_for_ipv4_addresses:
|
||||
self._get_addresses()
|
||||
|
||||
def _destroyed(self):
|
||||
if self.old_state != 'absent':
|
||||
if self.old_state == 'frozen':
|
||||
self._unfreeze_instance()
|
||||
self._unfreeze_container()
|
||||
if self.old_state != 'stopped':
|
||||
self._stop_instance()
|
||||
self._delete_instance()
|
||||
self._stop_container()
|
||||
self._delete_container()
|
||||
|
||||
def _frozen(self):
|
||||
if self.old_state == 'absent':
|
||||
self._create_instance()
|
||||
self._start_instance()
|
||||
self._freeze_instance()
|
||||
self._create_container()
|
||||
self._start_container()
|
||||
self._freeze_container()
|
||||
else:
|
||||
if self.old_state == 'stopped':
|
||||
self._start_instance()
|
||||
if self._needs_to_apply_instance_configs():
|
||||
self._apply_instance_configs()
|
||||
self._freeze_instance()
|
||||
self._start_container()
|
||||
if self._needs_to_apply_container_configs():
|
||||
self._apply_container_configs()
|
||||
self._freeze_container()
|
||||
|
||||
def _needs_to_change_instance_config(self, key):
|
||||
def _needs_to_change_container_config(self, key):
|
||||
if key not in self.config:
|
||||
return False
|
||||
if key == 'config' and self.ignore_volatile_options: # the old behavior is to ignore configurations by keyword "volatile"
|
||||
old_configs = dict((k, v) for k, v in self.old_instance_json['metadata'][key].items() if not k.startswith('volatile.'))
|
||||
old_configs = dict((k, v) for k, v in self.old_container_json['metadata'][key].items() if not k.startswith('volatile.'))
|
||||
for k, v in self.config['config'].items():
|
||||
if k not in old_configs:
|
||||
return True
|
||||
@@ -617,7 +578,7 @@ class LXDContainerManagement(object):
|
||||
return True
|
||||
return False
|
||||
elif key == 'config': # next default behavior
|
||||
old_configs = dict((k, v) for k, v in self.old_instance_json['metadata'][key].items())
|
||||
old_configs = dict((k, v) for k, v in self.old_container_json['metadata'][key].items())
|
||||
for k, v in self.config['config'].items():
|
||||
if k not in old_configs:
|
||||
return True
|
||||
@@ -625,41 +586,39 @@ class LXDContainerManagement(object):
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
old_configs = self.old_instance_json['metadata'][key]
|
||||
old_configs = self.old_container_json['metadata'][key]
|
||||
return self.config[key] != old_configs
|
||||
|
||||
def _needs_to_apply_instance_configs(self):
|
||||
def _needs_to_apply_container_configs(self):
|
||||
return (
|
||||
self._needs_to_change_instance_config('architecture') or
|
||||
self._needs_to_change_instance_config('config') or
|
||||
self._needs_to_change_instance_config('ephemeral') or
|
||||
self._needs_to_change_instance_config('devices') or
|
||||
self._needs_to_change_instance_config('profiles')
|
||||
self._needs_to_change_container_config('architecture') or
|
||||
self._needs_to_change_container_config('config') or
|
||||
self._needs_to_change_container_config('ephemeral') or
|
||||
self._needs_to_change_container_config('devices') or
|
||||
self._needs_to_change_container_config('profiles')
|
||||
)
|
||||
|
||||
def _apply_instance_configs(self):
|
||||
old_metadata = self.old_instance_json['metadata']
|
||||
def _apply_container_configs(self):
|
||||
old_metadata = self.old_container_json['metadata']
|
||||
body_json = {
|
||||
'architecture': old_metadata['architecture'],
|
||||
'config': old_metadata['config'],
|
||||
'devices': old_metadata['devices'],
|
||||
'profiles': old_metadata['profiles']
|
||||
}
|
||||
|
||||
if self._needs_to_change_instance_config('architecture'):
|
||||
if self._needs_to_change_container_config('architecture'):
|
||||
body_json['architecture'] = self.config['architecture']
|
||||
if self._needs_to_change_instance_config('config'):
|
||||
if self._needs_to_change_container_config('config'):
|
||||
for k, v in self.config['config'].items():
|
||||
body_json['config'][k] = v
|
||||
if self._needs_to_change_instance_config('ephemeral'):
|
||||
if self._needs_to_change_container_config('ephemeral'):
|
||||
body_json['ephemeral'] = self.config['ephemeral']
|
||||
if self._needs_to_change_instance_config('devices'):
|
||||
if self._needs_to_change_container_config('devices'):
|
||||
body_json['devices'] = self.config['devices']
|
||||
if self._needs_to_change_instance_config('profiles'):
|
||||
if self._needs_to_change_container_config('profiles'):
|
||||
body_json['profiles'] = self.config['profiles']
|
||||
|
||||
self.client.do('PUT', '{0}/{1}'.format(self.api_endpoint, self.name), body_json=body_json)
|
||||
self.actions.append('apply_instance_configs')
|
||||
self.client.do('PUT', '/1.0/containers/{0}'.format(self.name), body_json=body_json)
|
||||
self.actions.append('apply_container_configs')
|
||||
|
||||
def run(self):
|
||||
"""Run the main method."""
|
||||
@@ -669,8 +628,8 @@ class LXDContainerManagement(object):
|
||||
self.client.authenticate(self.trust_password)
|
||||
self.ignore_volatile_options = self.module.params.get('ignore_volatile_options')
|
||||
|
||||
self.old_instance_json = self._get_instance_json()
|
||||
self.old_state = self._instance_json_to_module_state(self.old_instance_json)
|
||||
self.old_container_json = self._get_container_json()
|
||||
self.old_state = self._container_json_to_module_state(self.old_container_json)
|
||||
action = getattr(self, LXD_ANSIBLE_STATES[self.state])
|
||||
action()
|
||||
|
||||
@@ -715,6 +674,7 @@ def main():
|
||||
),
|
||||
ignore_volatile_options=dict(
|
||||
type='bool',
|
||||
default=True
|
||||
),
|
||||
devices=dict(
|
||||
type='dict',
|
||||
@@ -740,11 +700,6 @@ def main():
|
||||
type='int',
|
||||
default=30
|
||||
),
|
||||
type=dict(
|
||||
type='str',
|
||||
default='container',
|
||||
choices=['container', 'virtual-machine'],
|
||||
),
|
||||
wait_for_ipv4_addresses=dict(
|
||||
type='bool',
|
||||
default=False
|
||||
@@ -773,17 +728,13 @@ def main():
|
||||
),
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
if module.params['ignore_volatile_options'] is None:
|
||||
module.params['ignore_volatile_options'] = True
|
||||
module.deprecate(
|
||||
'If the keyword "volatile" is used in a playbook in the config'
|
||||
'section, a "changed" message will appear with every run, even without a change'
|
||||
'to the playbook.'
|
||||
'This will change in the future. Please test your scripts'
|
||||
'by "ignore_volatile_options: false". To keep the old behavior, set that option explicitly to "true"',
|
||||
version='6.0.0', collection_name='community.general')
|
||||
|
||||
# if module.params['ignore_volatile_options'] is None:
|
||||
# module.params['ignore_volatile_options'] = True
|
||||
# module.deprecate(
|
||||
# 'If the keyword "volatile" is used in a playbook in the config section, a
|
||||
# "changed" message will appear with every run, even without a change to the playbook.
|
||||
# This will change in the future.
|
||||
# Please test your scripts by "ignore_volatile_options: false"', version='5.0.0', collection_name='community.general')
|
||||
lxd_manage = LXDContainerManagement(module=module)
|
||||
lxd_manage.run()
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ short_description: management of instances in Proxmox VE cluster
|
||||
description:
|
||||
- allows you to create/delete/stop instances in Proxmox VE cluster
|
||||
- Starting in Ansible 2.1, it automatically detects containerization type (lxc for PVE 4, openvz for older)
|
||||
- Since community.general 4.0.0 on, there are no more default values, see I(proxmox_default_behavior).
|
||||
- From community.general 4.0.0 on, there will be no default values, see I(proxmox_default_behavior).
|
||||
options:
|
||||
password:
|
||||
description:
|
||||
@@ -40,27 +40,37 @@ options:
|
||||
comma-delimited list C([volume=]<volume> [,acl=<1|0>] [,mountoptions=<opt[;opt...]>] [,quota=<1|0>]
|
||||
[,replicate=<1|0>] [,ro=<1|0>] [,shared=<1|0>] [,size=<DiskSize>])."
|
||||
- See U(https://pve.proxmox.com/wiki/Linux_Container) for a full description.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(3).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(3). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: str
|
||||
cores:
|
||||
description:
|
||||
- Specify number of cores per socket.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
cpus:
|
||||
description:
|
||||
- numbers of allocated cpus for instance
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
memory:
|
||||
description:
|
||||
- memory size in MB for instance
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(512).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(512). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
swap:
|
||||
description:
|
||||
- swap memory size in MB for instance
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(0).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(0). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
netif:
|
||||
description:
|
||||
@@ -84,7 +94,9 @@ options:
|
||||
onboot:
|
||||
description:
|
||||
- specifies whether a VM will be started during system bootup
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(no).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
storage:
|
||||
description:
|
||||
@@ -94,7 +106,9 @@ options:
|
||||
cpuunits:
|
||||
description:
|
||||
- CPU weight for a VM
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1000).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1000). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
nameserver:
|
||||
description:
|
||||
@@ -154,15 +168,16 @@ options:
|
||||
version_added: '0.2.0'
|
||||
proxmox_default_behavior:
|
||||
description:
|
||||
- As of community.general 4.0.0, various options no longer have default values.
|
||||
These default values caused problems when users expected different behavior from Proxmox
|
||||
by default or filled options which caused problems when set.
|
||||
- The value C(compatibility) (default before community.general 4.0.0) will ensure that the default values
|
||||
are used when the values are not explicitly specified by the user. The new default is C(no_defaults),
|
||||
which makes sure these options have no defaults.
|
||||
- Various module options used to have default values. This cause problems when
|
||||
user expects different behavior from proxmox by default or fill options which cause
|
||||
problems when they have been set.
|
||||
- The default value is C(compatibility), which will ensure that the default values
|
||||
are used when the values are not explicitly specified by the user.
|
||||
- From community.general 4.0.0 on, the default value will switch to C(no_defaults). To avoid
|
||||
deprecation warnings, please set I(proxmox_default_behavior) to an explicit
|
||||
value.
|
||||
- This affects the I(disk), I(cores), I(cpus), I(memory), I(onboot), I(swap), I(cpuunits) options.
|
||||
type: str
|
||||
default: no_defaults
|
||||
choices:
|
||||
- compatibility
|
||||
- no_defaults
|
||||
@@ -518,7 +533,7 @@ def main():
|
||||
unprivileged=dict(type='bool', default=False),
|
||||
description=dict(type='str'),
|
||||
hookscript=dict(type='str'),
|
||||
proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']),
|
||||
proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
|
||||
),
|
||||
required_if=[('state', 'present', ['node', 'hostname', 'ostemplate'])],
|
||||
required_together=[('api_token_id', 'api_token_secret')],
|
||||
@@ -547,6 +562,13 @@ def main():
|
||||
template_store = module.params['ostemplate'].split(":")[0]
|
||||
timeout = module.params['timeout']
|
||||
|
||||
if module.params['proxmox_default_behavior'] is None:
|
||||
module.params['proxmox_default_behavior'] = 'compatibility'
|
||||
module.deprecate(
|
||||
'The proxmox_default_behavior option will change its default value from "compatibility" to '
|
||||
'"no_defaults" in community.general 4.0.0. To remove this warning, please specify an explicit value for it now',
|
||||
version='4.0.0', collection_name='community.general'
|
||||
)
|
||||
if module.params['proxmox_default_behavior'] == 'compatibility':
|
||||
old_default_values = dict(
|
||||
disk="3",
|
||||
|
||||
@@ -13,13 +13,15 @@ module: proxmox_kvm
|
||||
short_description: Management of Qemu(KVM) Virtual Machines in Proxmox VE cluster.
|
||||
description:
|
||||
- Allows you to create/delete/stop Qemu(KVM) Virtual Machines in Proxmox VE cluster.
|
||||
- Since community.general 4.0.0 on, there are no more default values, see I(proxmox_default_behavior).
|
||||
- From community.general 4.0.0 on, there will be no default values, see I(proxmox_default_behavior).
|
||||
author: "Abdoul Bah (@helldorado) <bahabdoul at gmail.com>"
|
||||
options:
|
||||
acpi:
|
||||
description:
|
||||
- Specify if ACPI should be enabled/disabled.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(yes).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
agent:
|
||||
description:
|
||||
@@ -29,19 +31,24 @@ options:
|
||||
description:
|
||||
- Pass arbitrary arguments to kvm.
|
||||
- This option is for experts only!
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity), this option has a default of
|
||||
C(-serial unix:/var/run/qemu-server/<vmid>.serial,server,nowait).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(-serial unix:/var/run/qemu-server/<vmid>.serial,server,nowait).
|
||||
Note that the default value of I(proxmox_default_behavior) changes in community.general 4.0.0.
|
||||
type: str
|
||||
autostart:
|
||||
description:
|
||||
- Specify if the VM should be automatically restarted after crash (currently ignored in PVE API).
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(no).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
balloon:
|
||||
description:
|
||||
- Specify the amount of RAM for the VM in MB.
|
||||
- Using zero disables the balloon driver.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(0).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(0). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
bios:
|
||||
description:
|
||||
@@ -52,7 +59,9 @@ options:
|
||||
description:
|
||||
- Specify the boot order -> boot on floppy C(a), hard disk C(c), CD-ROM C(d), or network C(n).
|
||||
- You can combine to set order.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(cnd).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(cnd). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: str
|
||||
bootdisk:
|
||||
description:
|
||||
@@ -88,12 +97,16 @@ options:
|
||||
cores:
|
||||
description:
|
||||
- Specify number of cores per socket.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
cpu:
|
||||
description:
|
||||
- Specify emulated CPU type.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(kvm64).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(kvm64). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: str
|
||||
cpulimit:
|
||||
description:
|
||||
@@ -104,7 +117,9 @@ options:
|
||||
description:
|
||||
- Specify CPU weight for a VM.
|
||||
- You can disable fair-scheduler configuration by setting this to 0
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1000).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1000). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
delete:
|
||||
description:
|
||||
@@ -124,15 +139,19 @@ options:
|
||||
description:
|
||||
- Allow to force stop VM.
|
||||
- Can be used with states C(stopped), C(restarted) and C(absent).
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(no).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
format:
|
||||
description:
|
||||
- Target drive's backing file's data format.
|
||||
- Used only with clone
|
||||
- Use I(format=unspecified) and I(full=false) for a linked clone.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(qcow2).
|
||||
If I(proxmox_default_behavior) is set to C(no_defaults), not specifying this option is equivalent to setting it to C(unspecified).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(qcow2). If I(proxmox_default_behavior) is set to C(no_defaults),
|
||||
not specifying this option is equivalent to setting it to C(unspecified).
|
||||
Note that the default value of I(proxmox_default_behavior) changes in community.general 4.0.0.
|
||||
type: str
|
||||
choices: [ "cloop", "cow", "qcow", "qcow2", "qed", "raw", "vmdk", "unspecified" ]
|
||||
freeze:
|
||||
@@ -197,7 +216,9 @@ options:
|
||||
kvm:
|
||||
description:
|
||||
- Enable/disable KVM hardware virtualization.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(yes).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
localtime:
|
||||
description:
|
||||
@@ -217,7 +238,9 @@ options:
|
||||
memory:
|
||||
description:
|
||||
- Memory size in MB for instance.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(512).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(512). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
migrate_downtime:
|
||||
description:
|
||||
@@ -273,13 +296,17 @@ options:
|
||||
onboot:
|
||||
description:
|
||||
- Specifies whether a VM will be started during system bootup.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(yes).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
ostype:
|
||||
description:
|
||||
- Specifies guest operating system. This is used to enable special optimization/features for specific operating systems.
|
||||
- The l26 is Linux 2.6/3.X Kernel.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(l26).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(l26). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: str
|
||||
choices: ['other', 'wxp', 'w2k', 'w2k3', 'w2k8', 'wvista', 'win7', 'win8', 'win10', 'l24', 'l26', 'solaris']
|
||||
parallel:
|
||||
@@ -360,7 +387,9 @@ options:
|
||||
sockets:
|
||||
description:
|
||||
- Sets the number of CPU sockets. (1 - N).
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: int
|
||||
sshkeys:
|
||||
description:
|
||||
@@ -392,7 +421,9 @@ options:
|
||||
tablet:
|
||||
description:
|
||||
- Enables/disables the USB tablet device.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(no).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
tags:
|
||||
description:
|
||||
@@ -414,7 +445,9 @@ options:
|
||||
template:
|
||||
description:
|
||||
- Enables/disables the template.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(no).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: bool
|
||||
timeout:
|
||||
description:
|
||||
@@ -436,7 +469,9 @@ options:
|
||||
vga:
|
||||
description:
|
||||
- Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'.
|
||||
- This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(std).
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(std). Note that the default value of I(proxmox_default_behavior)
|
||||
changes in community.general 4.0.0.
|
||||
type: str
|
||||
choices: ['std', 'cirrus', 'vmware', 'qxl', 'serial0', 'serial1', 'serial2', 'serial3', 'qxl2', 'qxl3', 'qxl4']
|
||||
virtio:
|
||||
@@ -454,17 +489,18 @@ options:
|
||||
type: str
|
||||
proxmox_default_behavior:
|
||||
description:
|
||||
- As of community.general 4.0.0, various options no longer have default values.
|
||||
These default values caused problems when users expected different behavior from Proxmox
|
||||
by default or filled options which caused problems when set.
|
||||
- The value C(compatibility) (default before community.general 4.0.0) will ensure that the default values
|
||||
are used when the values are not explicitly specified by the user. The new default is C(no_defaults),
|
||||
which makes sure these options have no defaults.
|
||||
- Various module options used to have default values. This cause problems when
|
||||
user expects different behavior from proxmox by default or fill options which cause
|
||||
problems when they have been set.
|
||||
- The default value is C(compatibility), which will ensure that the default values
|
||||
are used when the values are not explicitly specified by the user.
|
||||
- From community.general 4.0.0 on, the default value will switch to C(no_defaults). To avoid
|
||||
deprecation warnings, please set I(proxmox_default_behavior) to an explicit
|
||||
value.
|
||||
- This affects the I(acpi), I(autostart), I(balloon), I(boot), I(cores), I(cpu),
|
||||
I(cpuunits), I(force), I(format), I(kvm), I(memory), I(onboot), I(ostype), I(sockets),
|
||||
I(tablet), I(template), I(vga), options.
|
||||
type: str
|
||||
default: no_defaults
|
||||
choices:
|
||||
- compatibility
|
||||
- no_defaults
|
||||
@@ -1055,7 +1091,7 @@ def main():
|
||||
virtio=dict(type='dict'),
|
||||
vmid=dict(type='int'),
|
||||
watchdog=dict(),
|
||||
proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']),
|
||||
proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
|
||||
),
|
||||
mutually_exclusive=[('delete', 'revert'), ('delete', 'update'), ('revert', 'update'), ('clone', 'update'), ('clone', 'delete'), ('clone', 'revert')],
|
||||
required_together=[('api_token_id', 'api_token_secret')],
|
||||
@@ -1086,6 +1122,13 @@ def main():
|
||||
vmid = module.params['vmid']
|
||||
validate_certs = module.params['validate_certs']
|
||||
|
||||
if module.params['proxmox_default_behavior'] is None:
|
||||
module.params['proxmox_default_behavior'] = 'compatibility'
|
||||
module.deprecate(
|
||||
'The proxmox_default_behavior option will change its default value from "compatibility" to '
|
||||
'"no_defaults" in community.general 4.0.0. To remove this warning, please specify an explicit value for it now',
|
||||
version='4.0.0', collection_name='community.general'
|
||||
)
|
||||
if module.params['proxmox_default_behavior'] == 'compatibility':
|
||||
old_default_values = dict(
|
||||
acpi=True,
|
||||
|
||||
@@ -18,12 +18,11 @@ module: scaleway_security_group_rule
|
||||
short_description: Scaleway Security Group Rule management module
|
||||
author: Antoine Barbare (@abarbare)
|
||||
description:
|
||||
- This module manages Security Group Rule on Scaleway account
|
||||
U(https://developer.scaleway.com)
|
||||
- This module manages Security Group Rule on Scaleway account
|
||||
U(https://developer.scaleway.com)
|
||||
extends_documentation_fragment:
|
||||
- community.general.scaleway
|
||||
requirements:
|
||||
- ipaddress
|
||||
- community.general.scaleway
|
||||
|
||||
|
||||
options:
|
||||
state:
|
||||
@@ -131,19 +130,10 @@ data:
|
||||
}
|
||||
'''
|
||||
|
||||
import traceback
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway, payload_from_object
|
||||
from ansible_collections.community.general.plugins.module_utils.compat.ipaddress import ip_network
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
try:
|
||||
from ipaddress import ip_network
|
||||
except ImportError:
|
||||
IPADDRESS_IMP_ERR = traceback.format_exc()
|
||||
HAS_IPADDRESS = False
|
||||
else:
|
||||
HAS_IPADDRESS = True
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def get_sgr_from_api(security_group_rules, security_group_rule):
|
||||
@@ -266,8 +256,6 @@ def main():
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
if not HAS_IPADDRESS:
|
||||
module.fail_json(msg=missing_required_lib('ipaddress'), exception=IPADDRESS_IMP_ERR)
|
||||
|
||||
core(module)
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ extends_documentation_fragment:
|
||||
- community.general.redis.documentation
|
||||
|
||||
seealso:
|
||||
- module: community.general.redis_data_incr
|
||||
- module: community.general.redis_data_info
|
||||
- module: community.general.redis
|
||||
'''
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2021, Andreas Botzner <andreas at botzner dot com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: redis_data_incr
|
||||
short_description: Increment keys in Redis
|
||||
version_added: 4.0.0
|
||||
description:
|
||||
- Increment integers or float keys in Redis database and get new value.
|
||||
- Default increment for all keys is 1. For specific increments use the
|
||||
I(increment_int) and I(increment_float) options.
|
||||
- When using I(check_mode) the module will try to calculate the value that
|
||||
Redis would return. If the key is not present, 0.0 is used as value.
|
||||
author: "Andreas Botzner (@paginabianca)"
|
||||
options:
|
||||
key:
|
||||
description:
|
||||
- Database key.
|
||||
type: str
|
||||
required: true
|
||||
increment_int:
|
||||
description:
|
||||
- Integer amount to increment the key by.
|
||||
required: false
|
||||
type: int
|
||||
increment_float:
|
||||
description:
|
||||
- Float amount to increment the key by.
|
||||
- This only works with keys that contain float values
|
||||
in their string representation.
|
||||
type: float
|
||||
required: false
|
||||
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.redis.documentation
|
||||
|
||||
notes:
|
||||
- For C(check_mode) to work, the specified I(redis_user) needs permission to
|
||||
run the C(GET) command on the key, otherwise the module will fail.
|
||||
|
||||
seealso:
|
||||
- module: community.general.redis_data
|
||||
- module: community.general.redis_data_info
|
||||
- module: community.general.redis
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Increment integer key foo on localhost with no username and print new value
|
||||
community.general.redis_data_incr:
|
||||
login_host: localhost
|
||||
login_password: supersecret
|
||||
key: foo
|
||||
increment_int: 1
|
||||
register: result
|
||||
- name: Print new value
|
||||
debug:
|
||||
var: result.value
|
||||
|
||||
- name: Increment float key foo by 20.4
|
||||
community.general.redis_data_incr:
|
||||
login_host: redishost
|
||||
login_user: redisuser
|
||||
login_password: somepass
|
||||
key: foo
|
||||
increment_float: '20.4'
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
value:
|
||||
description: Incremented value of key
|
||||
returned: on success
|
||||
type: float
|
||||
sample: '4039.4'
|
||||
msg:
|
||||
description: A short message.
|
||||
returned: always
|
||||
type: str
|
||||
sample: 'Incremented key: foo by 20.4 to 65.9'
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.redis import (
|
||||
fail_imports, redis_auth_argument_spec, RedisAnsible)
|
||||
|
||||
|
||||
def main():
|
||||
redis_auth_args = redis_auth_argument_spec()
|
||||
module_args = dict(
|
||||
key=dict(type='str', required=True, no_log=False),
|
||||
increment_int=dict(type='int', required=False),
|
||||
increment_float=dict(type='float', required=False),
|
||||
)
|
||||
module_args.update(redis_auth_args)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[['increment_int', 'increment_float']],
|
||||
)
|
||||
fail_imports(module)
|
||||
|
||||
redis = RedisAnsible(module)
|
||||
key = module.params['key']
|
||||
increment_float = module.params['increment_float']
|
||||
increment_int = module.params['increment_int']
|
||||
increment = 1
|
||||
if increment_float is not None:
|
||||
increment = increment_float
|
||||
elif increment_int is not None:
|
||||
increment = increment_int
|
||||
|
||||
result = {'changed': False}
|
||||
if module.check_mode:
|
||||
value = 0.0
|
||||
try:
|
||||
res = redis.connection.get(key)
|
||||
if res is not None:
|
||||
value = float(res)
|
||||
except ValueError as e:
|
||||
msg = 'Value: {0} of key: {1} is not incrementable(int or float)'.format(
|
||||
res, key)
|
||||
result['msg'] = msg
|
||||
module.fail_json(**result)
|
||||
except Exception as e:
|
||||
msg = 'Failed to get value of key: {0} with exception: {1}'.format(
|
||||
key, str(e))
|
||||
result['msg'] = msg
|
||||
module.fail_json(**result)
|
||||
msg = 'Incremented key: {0} by {1} to {2}'.format(
|
||||
key, increment, value + increment)
|
||||
result['msg'] = msg
|
||||
result['value'] = float(value + increment)
|
||||
module.exit_json(**result)
|
||||
|
||||
if increment_float is not None:
|
||||
try:
|
||||
value = redis.connection.incrbyfloat(key, increment)
|
||||
msg = 'Incremented key: {0} by {1} to {2}'.format(
|
||||
key, increment, value)
|
||||
result['msg'] = msg
|
||||
result['value'] = float(value)
|
||||
result['changed'] = True
|
||||
module.exit_json(**result)
|
||||
except Exception as e:
|
||||
msg = 'Failed to increment key: {0} by {1} with exception: {2}'.format(
|
||||
key, increment, str(e))
|
||||
result['msg'] = msg
|
||||
module.fail_json(**result)
|
||||
elif increment_int is not None:
|
||||
try:
|
||||
value = redis.connection.incrby(key, increment)
|
||||
msg = 'Incremented key: {0} by {1} to {2}'.format(
|
||||
key, increment, value)
|
||||
result['msg'] = msg
|
||||
result['value'] = float(value)
|
||||
result['changed'] = True
|
||||
module.exit_json(**result)
|
||||
except Exception as e:
|
||||
msg = 'Failed to increment key: {0} by {1} with exception: {2}'.format(
|
||||
key, increment, str(e))
|
||||
result['msg'] = msg
|
||||
module.fail_json(**result)
|
||||
else:
|
||||
try:
|
||||
value = redis.connection.incr(key)
|
||||
msg = 'Incremented key: {0} to {1}'.format(key, value)
|
||||
result['msg'] = msg
|
||||
result['value'] = float(value)
|
||||
result['changed'] = True
|
||||
module.exit_json(**result)
|
||||
except Exception as e:
|
||||
msg = 'Failed to increment key: {0} with exception: {1}'.format(
|
||||
key, str(e))
|
||||
result['msg'] = msg
|
||||
module.fail_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -27,7 +27,6 @@ extends_documentation_fragment:
|
||||
|
||||
seealso:
|
||||
- module: community.general.redis_data
|
||||
- module: community.general.redis_data_incr
|
||||
- module: community.general.redis_info
|
||||
- module: community.general.redis
|
||||
'''
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright: (c) 2021, Kris Budde <kris@budd.ee
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mssql_script
|
||||
|
||||
short_description: Execute SQL scripts on a MSSQL database
|
||||
|
||||
version_added: "4.0.0"
|
||||
|
||||
description:
|
||||
- Execute SQL scripts on a MSSQL database.
|
||||
|
||||
options:
|
||||
name:
|
||||
description: Database to run script against.
|
||||
aliases: [ db ]
|
||||
default: ''
|
||||
type: str
|
||||
login_user:
|
||||
description: The username used to authenticate with.
|
||||
type: str
|
||||
login_password:
|
||||
description: The password used to authenticate with.
|
||||
type: str
|
||||
login_host:
|
||||
description: Host running the database.
|
||||
type: str
|
||||
required: true
|
||||
login_port:
|
||||
description: Port of the MSSQL server. Requires I(login_host) be defined as well.
|
||||
default: 1433
|
||||
type: int
|
||||
script:
|
||||
description:
|
||||
- The SQL script to be executed.
|
||||
- Script can contain multiple SQL statements. Multiple Batches can be separated by C(GO) command.
|
||||
- Each batch must return at least one result set.
|
||||
required: true
|
||||
type: str
|
||||
output:
|
||||
description:
|
||||
- With C(default) each row will be returned as a list of values. See C(query_results).
|
||||
- Output format C(dict) will return dictionary with the column names as keys. See C(query_results_dict).
|
||||
- C(dict) requires named columns to be returned by each query otherwise an error is thrown.
|
||||
choices: [ "dict", "default" ]
|
||||
default: 'default'
|
||||
type: str
|
||||
params:
|
||||
description: |
|
||||
Parameters passed to the script as SQL parameters. ('SELECT %(name)s"' with C(example: '{"name": "John Doe"}).)'
|
||||
type: dict
|
||||
notes:
|
||||
- Requires the pymssql Python package on the remote host. For Ubuntu, this
|
||||
is as easy as C(pip install pymssql) (See M(ansible.builtin.pip).)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- pymssql
|
||||
|
||||
author:
|
||||
- Kris Budde (@kbudde)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Check DB connection
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
db: master
|
||||
script: "SELECT 1"
|
||||
|
||||
- name: Query with parameter
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
script: |
|
||||
SELECT name, state_desc FROM sys.databases WHERE name = %(dbname)s
|
||||
params:
|
||||
dbname: msdb
|
||||
register: result_params
|
||||
- assert:
|
||||
that:
|
||||
- result_params.query_results[0][0][0][0] == 'msdb'
|
||||
- result_params.query_results[0][0][0][1] == 'ONLINE'
|
||||
|
||||
- name: two batches with default output
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
script: |
|
||||
SELECT 'Batch 0 - Select 0'
|
||||
SELECT 'Batch 0 - Select 1'
|
||||
GO
|
||||
SELECT 'Batch 1 - Select 0'
|
||||
register: result_batches
|
||||
- assert:
|
||||
that:
|
||||
- result_batches.query_results | length == 2 # two batch results
|
||||
- result_batches.query_results[0] | length == 2 # two selects in first batch
|
||||
- result_batches.query_results[0][0] | length == 1 # one row in first select
|
||||
- result_batches.query_results[0][0][0] | length == 1 # one column in first row
|
||||
- result_batches.query_results[0][0][0][0] == 'Batch 0 - Select 0' # each row contains a list of values.
|
||||
|
||||
- name: two batches with dict output
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
output: dict
|
||||
script: |
|
||||
SELECT 'Batch 0 - Select 0' as b0s0
|
||||
SELECT 'Batch 0 - Select 1' as b0s1
|
||||
GO
|
||||
SELECT 'Batch 1 - Select 0' as b1s0
|
||||
register: result_batches_dict
|
||||
- assert:
|
||||
that:
|
||||
- result_batches_dict.query_results_dict | length == 2 # two batch results
|
||||
- result_batches_dict.query_results_dict[0] | length == 2 # two selects in first batch
|
||||
- result_batches_dict.query_results_dict[0][0] | length == 1 # one row in first select
|
||||
- result_batches_dict.query_results_dict[0][0][0]['b0s0'] == 'Batch 0 - Select 0' # column 'b0s0' of first row
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
query_results:
|
||||
description: List of batches (queries separated by C(GO) keyword).
|
||||
type: list
|
||||
elements: list
|
||||
returned: success and I(output=default)
|
||||
sample: [[[["Batch 0 - Select 0"]], [["Batch 0 - Select 1"]]], [[["Batch 1 - Select 0"]]]]
|
||||
contains:
|
||||
queries:
|
||||
description:
|
||||
- List of result sets of each query.
|
||||
- If a query returns no results, the results of this and all the following queries will not be included in the output.
|
||||
- Use the C(GO) keyword in I(script) to separate queries.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
rows:
|
||||
description: List of rows returned by query.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
column_value:
|
||||
description:
|
||||
- List of column values.
|
||||
- Any non-standard JSON type is converted to string.
|
||||
type: list
|
||||
example: ["Batch 0 - Select 0"]
|
||||
returned: success, if output is default
|
||||
query_results_dict:
|
||||
description: List of batches (queries separated by C(GO) keyword).
|
||||
type: list
|
||||
elements: list
|
||||
returned: success and I(output=dict)
|
||||
sample: [[[["Batch 0 - Select 0"]], [["Batch 0 - Select 1"]]], [[["Batch 1 - Select 0"]]]]
|
||||
contains:
|
||||
queries:
|
||||
description:
|
||||
- List of result sets of each query.
|
||||
- If a query returns no results, the results of this and all the following queries will not be included in the output.
|
||||
Use 'GO' keyword to separate queries.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
rows:
|
||||
description: List of rows returned by query.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
column_dict:
|
||||
description:
|
||||
- Dictionary of column names and values.
|
||||
- Any non-standard JSON type is converted to string.
|
||||
type: dict
|
||||
example: {"col_name": "Batch 0 - Select 0"}
|
||||
returned: success, if output is dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
import traceback
|
||||
import json
|
||||
PYMSSQL_IMP_ERR = None
|
||||
try:
|
||||
import pymssql
|
||||
except ImportError:
|
||||
PYMSSQL_IMP_ERR = traceback.format_exc()
|
||||
MSSQL_FOUND = False
|
||||
else:
|
||||
MSSQL_FOUND = True
|
||||
|
||||
|
||||
def clean_output(o):
|
||||
return str(o)
|
||||
|
||||
|
||||
def run_module():
|
||||
module_args = dict(
|
||||
name=dict(required=False, aliases=['db'], default=''),
|
||||
login_user=dict(),
|
||||
login_password=dict(no_log=True),
|
||||
login_host=dict(required=True),
|
||||
login_port=dict(type='int', default=1433),
|
||||
script=dict(required=True),
|
||||
output=dict(default='default', choices=['dict', 'default']),
|
||||
params=dict(type='dict'),
|
||||
)
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True
|
||||
)
|
||||
if not MSSQL_FOUND:
|
||||
module.fail_json(msg=missing_required_lib(
|
||||
'pymssql'), exception=PYMSSQL_IMP_ERR)
|
||||
|
||||
db = module.params['name']
|
||||
login_user = module.params['login_user']
|
||||
login_password = module.params['login_password']
|
||||
login_host = module.params['login_host']
|
||||
login_port = module.params['login_port']
|
||||
script = module.params['script']
|
||||
output = module.params['output']
|
||||
sql_params = module.params['params']
|
||||
|
||||
login_querystring = login_host
|
||||
if login_port != 1433:
|
||||
login_querystring = "%s:%s" % (login_host, login_port)
|
||||
|
||||
if login_user is not None and login_password is None:
|
||||
module.fail_json(
|
||||
msg="when supplying login_user argument, login_password must also be provided")
|
||||
|
||||
try:
|
||||
conn = pymssql.connect(
|
||||
user=login_user, password=login_password, host=login_querystring, database=db)
|
||||
cursor = conn.cursor()
|
||||
except Exception as e:
|
||||
if "Unknown database" in str(e):
|
||||
errno, errstr = e.args
|
||||
module.fail_json(msg="ERROR: %s %s" % (errno, errstr))
|
||||
else:
|
||||
module.fail_json(msg="unable to connect, check login_user and login_password are correct, or alternatively check your "
|
||||
"@sysconfdir@/freetds.conf / ${HOME}/.freetds.conf")
|
||||
|
||||
conn.autocommit(True)
|
||||
|
||||
query_results_key = 'query_results'
|
||||
if output == 'dict':
|
||||
cursor = conn.cursor(as_dict=True)
|
||||
query_results_key = 'query_results_dict'
|
||||
|
||||
queries = script.split('\nGO\n')
|
||||
result['changed'] = True
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
query_results = []
|
||||
try:
|
||||
for query in queries:
|
||||
cursor.execute(query, sql_params)
|
||||
qry_result = []
|
||||
rows = cursor.fetchall()
|
||||
while rows:
|
||||
qry_result.append(rows)
|
||||
rows = cursor.fetchall()
|
||||
query_results.append(qry_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))
|
||||
|
||||
result[query_results_key] = qry_results
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1 +0,0 @@
|
||||
./packaging/os/dnf_versionlock.py
|
||||
@@ -1 +0,0 @@
|
||||
./net_tools/dnsimple_info.py
|
||||
@@ -182,7 +182,6 @@ import zipfile
|
||||
from fnmatch import fnmatch
|
||||
from sys import version_info
|
||||
from traceback import format_exc
|
||||
from zlib import crc32
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native
|
||||
@@ -235,6 +234,10 @@ def expand_paths(paths):
|
||||
return expanded_path, is_globby
|
||||
|
||||
|
||||
def legacy_filter(path, exclusion_patterns):
|
||||
return matches_exclusion_patterns(path, exclusion_patterns)
|
||||
|
||||
|
||||
def matches_exclusion_patterns(path, exclusion_patterns):
|
||||
return any(fnmatch(path, p) for p in exclusion_patterns)
|
||||
|
||||
@@ -310,7 +313,6 @@ class Archive(object):
|
||||
if self.remove:
|
||||
self._check_removal_safety()
|
||||
|
||||
self.original_checksums = self.destination_checksums()
|
||||
self.original_size = self.destination_size()
|
||||
|
||||
def add(self, path, archive_name):
|
||||
@@ -375,16 +377,8 @@ class Archive(object):
|
||||
msg='Errors when writing archive at %s: %s' % (_to_native(self.destination), '; '.join(self.errors))
|
||||
)
|
||||
|
||||
def is_different_from_original(self):
|
||||
if self.original_checksums is None:
|
||||
return self.original_size != self.destination_size()
|
||||
else:
|
||||
return self.original_checksums != self.destination_checksums()
|
||||
|
||||
def destination_checksums(self):
|
||||
if self.destination_exists() and self.destination_readable():
|
||||
return self._get_checksums(self.destination)
|
||||
return None
|
||||
def compare_with_original(self):
|
||||
self.changed |= self.original_size != self.destination_size()
|
||||
|
||||
def destination_exists(self):
|
||||
return self.destination and os.path.exists(self.destination)
|
||||
@@ -500,10 +494,6 @@ class Archive(object):
|
||||
def _add(self, path, archive_name):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_checksums(self, path):
|
||||
pass
|
||||
|
||||
|
||||
class ZipArchive(Archive):
|
||||
def __init__(self, module):
|
||||
@@ -523,18 +513,9 @@ class ZipArchive(Archive):
|
||||
self.file = zipfile.ZipFile(_to_native_ascii(self.destination), 'w', zipfile.ZIP_DEFLATED, True)
|
||||
|
||||
def _add(self, path, archive_name):
|
||||
if not matches_exclusion_patterns(path, self.exclusion_patterns):
|
||||
if not legacy_filter(path, self.exclusion_patterns):
|
||||
self.file.write(path, archive_name)
|
||||
|
||||
def _get_checksums(self, path):
|
||||
try:
|
||||
archive = zipfile.ZipFile(_to_native_ascii(path), 'r')
|
||||
checksums = set((info.filename, info.CRC) for info in archive.infolist())
|
||||
archive.close()
|
||||
except zipfile.BadZipfile:
|
||||
checksums = set()
|
||||
return checksums
|
||||
|
||||
|
||||
class TarArchive(Archive):
|
||||
def __init__(self, module):
|
||||
@@ -573,35 +554,13 @@ class TarArchive(Archive):
|
||||
return None if matches_exclusion_patterns(tarinfo.name, self.exclusion_patterns) else tarinfo
|
||||
|
||||
def py26_filter(path):
|
||||
return matches_exclusion_patterns(path, self.exclusion_patterns)
|
||||
return legacy_filter(path, self.exclusion_patterns)
|
||||
|
||||
if PY27:
|
||||
self.file.add(path, archive_name, recursive=False, filter=py27_filter)
|
||||
else:
|
||||
self.file.add(path, archive_name, recursive=False, exclude=py26_filter)
|
||||
|
||||
def _get_checksums(self, path):
|
||||
try:
|
||||
if self.format == 'xz':
|
||||
with lzma.open(_to_native_ascii(path), 'r') as f:
|
||||
archive = tarfile.open(fileobj=f)
|
||||
checksums = set((info.name, info.chksum) for info in archive.getmembers())
|
||||
archive.close()
|
||||
else:
|
||||
archive = tarfile.open(_to_native_ascii(path), 'r|' + self.format)
|
||||
checksums = set((info.name, info.chksum) for info in archive.getmembers())
|
||||
archive.close()
|
||||
except (lzma.LZMAError, tarfile.ReadError, tarfile.CompressionError):
|
||||
try:
|
||||
# The python implementations of gzip, bz2, and lzma do not support restoring compressed files
|
||||
# to their original names so only file checksum is returned
|
||||
f = self._open_compressed_file(_to_native_ascii(path), 'r')
|
||||
checksums = set([(b'', crc32(f.read()))])
|
||||
f.close()
|
||||
except Exception:
|
||||
checksums = set()
|
||||
return checksums
|
||||
|
||||
|
||||
def get_archive(module):
|
||||
if module.params['format'] == 'zip':
|
||||
@@ -644,7 +603,7 @@ def main():
|
||||
else:
|
||||
archive.add_targets()
|
||||
archive.destination_state = STATE_INCOMPLETE if archive.has_unfound_targets() else STATE_ARCHIVED
|
||||
archive.changed |= archive.is_different_from_original()
|
||||
archive.compare_with_original()
|
||||
if archive.remove:
|
||||
archive.remove_targets()
|
||||
else:
|
||||
@@ -654,7 +613,7 @@ def main():
|
||||
else:
|
||||
path = archive.paths[0]
|
||||
archive.add_single_target(path)
|
||||
archive.changed |= archive.is_different_from_original()
|
||||
archive.compare_with_original()
|
||||
if archive.remove:
|
||||
archive.remove_single_target(path)
|
||||
|
||||
|
||||
@@ -85,6 +85,11 @@ import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
try: # python 3.3+
|
||||
from shlex import quote
|
||||
except ImportError: # older python
|
||||
from pipes import quote
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
@@ -149,9 +154,9 @@ def main():
|
||||
|
||||
# Use 7zip when we have a binary, otherwise try to mount
|
||||
if binary:
|
||||
cmd = [binary, 'x', image, '-o%s' % tmp_dir] + extract_files
|
||||
cmd = '%s x "%s" -o"%s" %s' % (binary, image, tmp_dir, ' '.join([quote(f) for f in extract_files]))
|
||||
else:
|
||||
cmd = [module.get_bin_path('mount'), '-o', 'loop,ro', image, tmp_dir]
|
||||
cmd = 'mount -o loop,ro "%s" "%s"' % (image, tmp_dir)
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
if rc != 0:
|
||||
@@ -196,7 +201,7 @@ def main():
|
||||
result['changed'] = True
|
||||
finally:
|
||||
if not binary:
|
||||
module.run_command([module.get_bin_path('umount'), tmp_dir])
|
||||
module.run_command('umount "%s"' % tmp_dir)
|
||||
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ DOCUMENTATION = '''
|
||||
module: xattr
|
||||
short_description: Manage user defined extended attributes
|
||||
description:
|
||||
- Manages filesystem user defined extended attributes.
|
||||
- Requires that extended attributes are enabled on the target filesystem
|
||||
and that the setfattr/getfattr utilities are present.
|
||||
- Manages filesystem user defined extended attributes.
|
||||
- Requires that extended attributes are enabled on the target filesystem
|
||||
and that the setfattr/getfattr utilities are present.
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
@@ -34,13 +34,13 @@ options:
|
||||
type: str
|
||||
value:
|
||||
description:
|
||||
- The value to set the named name/key to, it automatically sets the I(state) to C(present).
|
||||
- The value to set the named name/key to, it automatically sets the C(state) to 'set'.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- defines which state you want to do.
|
||||
C(read) retrieves the current value for a I(key) (default)
|
||||
C(present) sets I(path) to C(value), default if value is set
|
||||
C(read) retrieves the current value for a C(key) (default)
|
||||
C(present) sets C(name) to C(value), default if value is set
|
||||
C(all) dumps all data
|
||||
C(keys) retrieves all keys
|
||||
C(absent) deletes the key
|
||||
@@ -49,14 +49,14 @@ options:
|
||||
default: read
|
||||
follow:
|
||||
description:
|
||||
- If C(true), dereferences symlinks and sets/gets attributes on symlink target,
|
||||
- If C(yes), dereferences symlinks and sets/gets attributes on symlink target,
|
||||
otherwise acts on symlink itself.
|
||||
type: bool
|
||||
default: true
|
||||
default: yes
|
||||
notes:
|
||||
- As of Ansible 2.3, the I(name) option has been changed to I(path) as default, but I(name) still works as well.
|
||||
author:
|
||||
- Brian Coca (@bcoca)
|
||||
- Brian Coca (@bcoca)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -116,8 +116,7 @@ def get_xattr(module, path, key, follow):
|
||||
if key is None:
|
||||
cmd.append('-d')
|
||||
else:
|
||||
cmd.append('-n')
|
||||
cmd.append(key)
|
||||
cmd.append('-n %s' % key)
|
||||
cmd.append(path)
|
||||
|
||||
return _run_xattr(module, cmd, False)
|
||||
@@ -128,10 +127,8 @@ def set_xattr(module, path, key, value, follow):
|
||||
cmd = [module.get_bin_path('setfattr', True)]
|
||||
if not follow:
|
||||
cmd.append('-h')
|
||||
cmd.append('-n')
|
||||
cmd.append(key)
|
||||
cmd.append('-v')
|
||||
cmd.append(value)
|
||||
cmd.append('-n %s' % key)
|
||||
cmd.append('-v %s' % value)
|
||||
cmd.append(path)
|
||||
|
||||
return _run_xattr(module, cmd)
|
||||
@@ -142,8 +139,7 @@ def rm_xattr(module, path, key, follow):
|
||||
cmd = [module.get_bin_path('setfattr', True)]
|
||||
if not follow:
|
||||
cmd.append('-h')
|
||||
cmd.append('-x')
|
||||
cmd.append(key)
|
||||
cmd.append('-x %s' % key)
|
||||
cmd.append(path)
|
||||
|
||||
return _run_xattr(module, cmd, False)
|
||||
@@ -152,7 +148,7 @@ def rm_xattr(module, path, key, follow):
|
||||
def _run_xattr(module, cmd, check_rc=True):
|
||||
|
||||
try:
|
||||
(rc, out, err) = module.run_command(cmd, check_rc=check_rc)
|
||||
(rc, out, err) = module.run_command(' '.join(cmd), check_rc=check_rc)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="%s!" % to_native(e))
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
source_control/gitlab/gitlab_branch.py
|
||||
@@ -14,13 +14,6 @@ short_description: Manage FreeIPA group
|
||||
description:
|
||||
- Add, modify and delete group within IPA server
|
||||
options:
|
||||
append:
|
||||
description:
|
||||
- If C(yes), add the listed I(user) and I(group) to the group members.
|
||||
- If C(no), only the listed I(user) and I(group) will be group members, removing any other members.
|
||||
default: no
|
||||
type: bool
|
||||
version_added: 4.0.0
|
||||
cn:
|
||||
description:
|
||||
- Canonical name.
|
||||
@@ -44,10 +37,9 @@ options:
|
||||
group:
|
||||
description:
|
||||
- List of group names assigned to this group.
|
||||
- If I(append=no) and an empty list is passed all groups will be removed from this group.
|
||||
- Groups that are already assigned but not passed will be removed.
|
||||
- If I(append=yes) the listed groups will be assigned without removing other groups.
|
||||
- If an empty list is passed all groups will be removed from this group.
|
||||
- If option is omitted assigned groups will not be checked or changed.
|
||||
- Groups that are already assigned but not passed will be removed.
|
||||
type: list
|
||||
elements: str
|
||||
nonposix:
|
||||
@@ -57,10 +49,9 @@ options:
|
||||
user:
|
||||
description:
|
||||
- List of user names assigned to this group.
|
||||
- If I(append=no) and an empty list is passed all users will be removed from this group.
|
||||
- Users that are already assigned but not passed will be removed.
|
||||
- If I(append=yes) the listed users will be assigned without removing other users.
|
||||
- If an empty list is passed all users will be removed from this group.
|
||||
- If option is omitted assigned users will not be checked or changed.
|
||||
- Users that are already assigned but not passed will be removed.
|
||||
type: list
|
||||
elements: str
|
||||
state:
|
||||
@@ -104,17 +95,6 @@ EXAMPLES = r'''
|
||||
ipa_user: admin
|
||||
ipa_pass: topsecret
|
||||
|
||||
- name: Ensure that new starter named john is member of the group, without removing other members
|
||||
community.general.ipa_group:
|
||||
name: developers
|
||||
user:
|
||||
- john
|
||||
append: yes
|
||||
state: present
|
||||
ipa_host: ipa.example.com
|
||||
ipa_user: admin
|
||||
ipa_pass: topsecret
|
||||
|
||||
- name: Ensure group is absent
|
||||
community.general.ipa_group:
|
||||
name: sysops
|
||||
@@ -207,7 +187,6 @@ def ensure(module, client):
|
||||
name = module.params['cn']
|
||||
group = module.params['group']
|
||||
user = module.params['user']
|
||||
append = module.params['append']
|
||||
|
||||
module_group = get_group_dict(description=module.params['description'], external=module.params['external'],
|
||||
gid=module.params['gidnumber'], nonposix=module.params['nonposix'])
|
||||
@@ -232,14 +211,12 @@ def ensure(module, client):
|
||||
if group is not None:
|
||||
changed = client.modify_if_diff(name, ipa_group.get('member_group', []), group,
|
||||
client.group_add_member_group,
|
||||
client.group_remove_member_group,
|
||||
append=append) or changed
|
||||
client.group_remove_member_group) or changed
|
||||
|
||||
if user is not None:
|
||||
changed = client.modify_if_diff(name, ipa_group.get('member_user', []), user,
|
||||
client.group_add_member_user,
|
||||
client.group_remove_member_user,
|
||||
append=append) or changed
|
||||
client.group_remove_member_user) or changed
|
||||
|
||||
else:
|
||||
if ipa_group:
|
||||
@@ -259,8 +236,7 @@ def main():
|
||||
group=dict(type='list', elements='str'),
|
||||
nonposix=dict(type='bool'),
|
||||
state=dict(type='str', default='present', choices=['present', 'absent']),
|
||||
user=dict(type='list', elements='str'),
|
||||
append=dict(type='bool', default=False))
|
||||
user=dict(type='list', elements='str'))
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
|
||||
@@ -9,15 +9,11 @@ __metaclass__ = type
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: keycloak_authentication
|
||||
|
||||
short_description: Configure authentication in Keycloak
|
||||
|
||||
description:
|
||||
- This module actually can only make a copy of an existing authentication flow, add an execution to it and configure it.
|
||||
- It can also delete the flow.
|
||||
|
||||
version_added: "3.3.0"
|
||||
|
||||
options:
|
||||
realm:
|
||||
description:
|
||||
@@ -83,7 +79,6 @@ options:
|
||||
default: false
|
||||
description:
|
||||
- If C(true), allows to remove the authentication flow and recreate it.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
|
||||
@@ -167,74 +162,10 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
|
||||
flow:
|
||||
description:
|
||||
- JSON representation for the authentication.
|
||||
- Deprecated return value, it will be removed in community.general 6.0.0. Please use the return value I(end_state) instead.
|
||||
returned: on success
|
||||
type: dict
|
||||
sample: {
|
||||
"alias": "Copy of first broker login",
|
||||
"authenticationExecutions": [
|
||||
{
|
||||
"alias": "review profile config",
|
||||
"authenticationConfig": {
|
||||
"alias": "review profile config",
|
||||
"config": { "update.profile.on.first.login": "missing" },
|
||||
"id": "6f09e4fb-aad4-496a-b873-7fa9779df6d7"
|
||||
},
|
||||
"configurable": true,
|
||||
"displayName": "Review Profile",
|
||||
"id": "8f77dab8-2008-416f-989e-88b09ccf0b4c",
|
||||
"index": 0,
|
||||
"level": 0,
|
||||
"providerId": "idp-review-profile",
|
||||
"requirement": "REQUIRED",
|
||||
"requirementChoices": [ "REQUIRED", "ALTERNATIVE", "DISABLED" ]
|
||||
}
|
||||
],
|
||||
"builtIn": false,
|
||||
"description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
|
||||
"id": "bc228863-5887-4297-b898-4d988f8eaa5c",
|
||||
"providerId": "basic-flow",
|
||||
"topLevel": true
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of the authentication after module execution.
|
||||
returned: on success
|
||||
type: dict
|
||||
sample: {
|
||||
"alias": "Copy of first broker login",
|
||||
"authenticationExecutions": [
|
||||
{
|
||||
"alias": "review profile config",
|
||||
"authenticationConfig": {
|
||||
"alias": "review profile config",
|
||||
"config": { "update.profile.on.first.login": "missing" },
|
||||
"id": "6f09e4fb-aad4-496a-b873-7fa9779df6d7"
|
||||
},
|
||||
"configurable": true,
|
||||
"displayName": "Review Profile",
|
||||
"id": "8f77dab8-2008-416f-989e-88b09ccf0b4c",
|
||||
"index": 0,
|
||||
"level": 0,
|
||||
"providerId": "idp-review-profile",
|
||||
"requirement": "REQUIRED",
|
||||
"requirementChoices": [ "REQUIRED", "ALTERNATIVE", "DISABLED" ]
|
||||
}
|
||||
],
|
||||
"builtIn": false,
|
||||
"description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
|
||||
"id": "bc228863-5887-4297-b898-4d988f8eaa5c",
|
||||
"providerId": "basic-flow",
|
||||
"topLevel": true
|
||||
}
|
||||
description: JSON representation for the authentication.
|
||||
returned: on success
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak \
|
||||
@@ -340,11 +271,9 @@ def create_or_update_executions(kc, config, realm='master'):
|
||||
def main():
|
||||
"""
|
||||
Module execution
|
||||
|
||||
:return:
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
realm=dict(type='str', required=True),
|
||||
alias=dict(type='str', required=True),
|
||||
@@ -363,7 +292,6 @@ def main():
|
||||
state=dict(choices=["absent", "present"], default='present'),
|
||||
force=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
@@ -373,7 +301,6 @@ def main():
|
||||
)
|
||||
|
||||
result = dict(changed=False, msg='', flow={})
|
||||
|
||||
# Obtain access token, initialize API
|
||||
try:
|
||||
connection_header = get_token(module.params)
|
||||
@@ -381,7 +308,6 @@ def main():
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
kc = KeycloakAPI(module, connection_header)
|
||||
|
||||
realm = module.params.get('realm')
|
||||
state = module.params.get('state')
|
||||
force = module.params.get('force')
|
||||
@@ -397,54 +323,35 @@ def main():
|
||||
}
|
||||
|
||||
auth_repr = kc.get_authentication_flow_by_alias(alias=new_auth_repr["alias"], realm=realm)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not auth_repr:
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['flow'] = result['end_state']
|
||||
result['msg'] = new_auth_repr["alias"] + ' absent'
|
||||
module.exit_json(**result)
|
||||
|
||||
elif state == 'present':
|
||||
# Process a creation
|
||||
if auth_repr == {}: # Authentication flow does not exist
|
||||
if state == 'present': # If desired state is present
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=new_auth_repr)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# If copyFrom is defined, create authentication flow from a copy
|
||||
if "copyFrom" in new_auth_repr and new_auth_repr["copyFrom"] is not None:
|
||||
auth_repr = kc.copy_auth_flow(config=new_auth_repr, realm=realm)
|
||||
else: # Create an empty authentication flow
|
||||
auth_repr = kc.create_empty_auth_flow(config=new_auth_repr, realm=realm)
|
||||
|
||||
# If the authentication still not exist on the server, raise an exception.
|
||||
if auth_repr is None:
|
||||
result['msg'] = "Authentication just created not found: " + str(new_auth_repr)
|
||||
module.fail_json(**result)
|
||||
|
||||
# Configure the executions for the flow
|
||||
create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm)
|
||||
|
||||
# Get executions created
|
||||
exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm)
|
||||
if exec_repr is not None:
|
||||
auth_repr["authenticationExecutions"] = exec_repr
|
||||
result['end_state'] = auth_repr
|
||||
result['flow'] = result['end_state']
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
result['flow'] = auth_repr
|
||||
elif state == 'absent': # If desired state is absent.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['msg'] = new_auth_repr["alias"] + ' absent'
|
||||
else: # The authentication flow already exist
|
||||
if state == 'present': # if desired state is present
|
||||
if force: # If force option is true
|
||||
# Delete the actual authentication flow
|
||||
result['changed'] = True
|
||||
@@ -463,35 +370,25 @@ def main():
|
||||
result['msg'] = "Authentication just created not found: " + str(new_auth_repr)
|
||||
module.fail_json(**result)
|
||||
# Configure the executions for the flow
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
changed, diff = create_or_update_executions(kc=kc, config=new_auth_repr, realm=realm)
|
||||
result['changed'] |= changed
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = diff
|
||||
|
||||
# Get executions created
|
||||
exec_repr = kc.get_executions_representation(config=new_auth_repr, realm=realm)
|
||||
if exec_repr is not None:
|
||||
auth_repr["authenticationExecutions"] = exec_repr
|
||||
result['end_state'] = auth_repr
|
||||
result['flow'] = result['end_state']
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
result['flow'] = auth_repr
|
||||
elif state == 'absent': # If desired state is absent
|
||||
result['changed'] = True
|
||||
|
||||
# Delete the authentication flow alias.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=auth_repr, after='')
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
kc.delete_authentication_flow_by_id(id=auth_repr["id"], realm=realm)
|
||||
|
||||
result['msg'] = 'Authentication flow: {alias} id: {id} is deleted'.format(alias=new_auth_repr['alias'],
|
||||
id=auth_repr["id"])
|
||||
|
||||
|
||||
@@ -62,17 +62,17 @@ options:
|
||||
|
||||
name:
|
||||
description:
|
||||
- Name of the client (this is not the same as I(client_id)).
|
||||
- Name of the client (this is not the same as I(client_id))
|
||||
type: str
|
||||
|
||||
description:
|
||||
description:
|
||||
- Description of the client in Keycloak.
|
||||
- Description of the client in Keycloak
|
||||
type: str
|
||||
|
||||
root_url:
|
||||
description:
|
||||
- Root URL appended to relative URLs for this client.
|
||||
- Root URL appended to relative URLs for this client
|
||||
This is 'rootUrl' in the Keycloak REST API.
|
||||
aliases:
|
||||
- rootUrl
|
||||
@@ -80,7 +80,7 @@ options:
|
||||
|
||||
admin_url:
|
||||
description:
|
||||
- URL to the admin interface of the client.
|
||||
- URL to the admin interface of the client
|
||||
This is 'adminUrl' in the Keycloak REST API.
|
||||
aliases:
|
||||
- adminUrl
|
||||
@@ -357,7 +357,7 @@ options:
|
||||
|
||||
protocol:
|
||||
description:
|
||||
- This is either C(openid-connect) or C(saml), this specifies for which protocol this protocol mapper.
|
||||
- This is either C(openid-connect) or C(saml), this specifies for which protocol this protocol mapper
|
||||
is active.
|
||||
choices: ['openid-connect', 'saml']
|
||||
type: str
|
||||
@@ -513,6 +513,7 @@ options:
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
|
||||
|
||||
author:
|
||||
- Eike Frost (@eikef)
|
||||
'''
|
||||
@@ -644,21 +645,20 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client testclient has been updated"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client testclient has been updated"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed client.
|
||||
description: client representation of proposed changes to client
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
clientId: "test"
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing client (sample is truncated).
|
||||
description: client representation of existing client (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -667,10 +667,9 @@ existing:
|
||||
"request.object.signature.alg": "RS256",
|
||||
}
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of client after module execution (sample is truncated).
|
||||
returned: on success
|
||||
description: client representation of client after module execution (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"adminUrl": "http://www.example.com/admin_url",
|
||||
@@ -685,38 +684,8 @@ from ansible_collections.community.general.plugins.module_utils.identity.keycloa
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def normalise_cr(clientrep, remove_ids=False):
|
||||
""" Re-sorts any properties where the order so that diff's is minimised, and adds default values where appropriate so that the
|
||||
the change detection is more effective.
|
||||
|
||||
:param clientrep: the clientrep dict to be sanitized
|
||||
:param remove_ids: If set to true, then the unique ID's of objects is removed to make the diff and checks for changed
|
||||
not alert when the ID's of objects are not usually known, (e.g. for protocol_mappers)
|
||||
:return: normalised clientrep dict
|
||||
"""
|
||||
# Avoid the dict passed in to be modified
|
||||
clientrep = clientrep.copy()
|
||||
|
||||
if 'attributes' in clientrep:
|
||||
clientrep['attributes'] = list(sorted(clientrep['attributes']))
|
||||
|
||||
if 'redirectUris' in clientrep:
|
||||
clientrep['redirectUris'] = list(sorted(clientrep['redirectUris']))
|
||||
|
||||
if 'protocolMappers' in clientrep:
|
||||
clientrep['protocolMappers'] = sorted(clientrep['protocolMappers'], key=lambda x: (x.get('name'), x.get('protocol'), x.get('protocolMapper')))
|
||||
for mapper in clientrep['protocolMappers']:
|
||||
if remove_ids:
|
||||
mapper.pop('id', None)
|
||||
|
||||
# Set to a default value.
|
||||
mapper['consentRequired'] = mapper.get('consentRequired', False)
|
||||
|
||||
return clientrep
|
||||
|
||||
|
||||
def sanitize_cr(clientrep):
|
||||
""" Removes probably sensitive details from a client representation.
|
||||
""" Removes probably sensitive details from a client representation
|
||||
|
||||
:param clientrep: the clientrep dict to be sanitized
|
||||
:return: sanitized clientrep dict
|
||||
@@ -727,7 +696,7 @@ def sanitize_cr(clientrep):
|
||||
if 'attributes' in result:
|
||||
if 'saml.signing.private.key' in result['attributes']:
|
||||
result['attributes']['saml.signing.private.key'] = 'no_log'
|
||||
return normalise_cr(result)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
@@ -790,7 +759,6 @@ def main():
|
||||
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']),
|
||||
authorization_settings=dict(type='dict', aliases=['authorizationSettings']),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
@@ -813,12 +781,12 @@ def main():
|
||||
cid = module.params.get('id')
|
||||
state = module.params.get('state')
|
||||
|
||||
# Filter and map the parameters names that apply to the client
|
||||
# convert module parameters to client representation parameters (if they belong in there)
|
||||
client_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
keycloak_argument_spec().keys()
|
||||
# See whether the client already exists in Keycloak
|
||||
if cid is None:
|
||||
before_client = kc.get_client_by_clientid(module.params.get('client_id'), realm=realm)
|
||||
if before_client is not None:
|
||||
@@ -827,10 +795,10 @@ def main():
|
||||
before_client = kc.get_client_by_id(cid, realm=realm)
|
||||
|
||||
if before_client is None:
|
||||
before_client = {}
|
||||
before_client = dict()
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
changeset = dict()
|
||||
|
||||
for client_param in client_params:
|
||||
new_param_value = module.params.get(client_param)
|
||||
@@ -849,63 +817,54 @@ def main():
|
||||
|
||||
changeset[camel(client_param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_client = before_client.copy()
|
||||
desired_client.update(changeset)
|
||||
# Whether creating or updating a client, take the before-state and merge the changeset into it
|
||||
updated_client = before_client.copy()
|
||||
updated_client.update(changeset)
|
||||
|
||||
result['proposed'] = sanitize_cr(changeset)
|
||||
result['existing'] = sanitize_cr(before_client)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_client:
|
||||
# If the client does not exist yet, before_client is still empty
|
||||
if before_client == dict():
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# do nothing and exit
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['msg'] = 'Client does not exist; doing nothing.'
|
||||
result['msg'] = 'Client does not exist, doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# create new client
|
||||
result['changed'] = True
|
||||
|
||||
if 'clientId' not in desired_client:
|
||||
if 'clientId' not in updated_client:
|
||||
module.fail_json(msg='client_id needs to be specified when creating a new client')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize_cr(desired_client))
|
||||
result['diff'] = dict(before='', after=sanitize_cr(updated_client))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
kc.create_client(desired_client, realm=realm)
|
||||
after_client = kc.get_client_by_clientid(desired_client['clientId'], realm=realm)
|
||||
kc.create_client(updated_client, realm=realm)
|
||||
after_client = kc.get_client_by_clientid(updated_client['clientId'], realm=realm)
|
||||
|
||||
result['end_state'] = sanitize_cr(after_client)
|
||||
|
||||
result['msg'] = 'Client %s has been created.' % desired_client['clientId']
|
||||
result['msg'] = 'Client %s has been created.' % updated_client['clientId']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
# update existing client
|
||||
result['changed'] = True
|
||||
|
||||
if module.check_mode:
|
||||
# We can only compare the current client with the proposed updates we have
|
||||
before_norm = normalise_cr(before_client, remove_ids=True)
|
||||
desired_norm = normalise_cr(desired_client, remove_ids=True)
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_norm),
|
||||
after=sanitize_cr(desired_norm))
|
||||
result['changed'] = (before_norm != desired_norm)
|
||||
result['diff'] = dict(before=sanitize_cr(before_client),
|
||||
after=sanitize_cr(updated_client))
|
||||
result['changed'] = (before_client != updated_client)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
kc.update_client(cid, desired_client, realm=realm)
|
||||
kc.update_client(cid, updated_client, realm=realm)
|
||||
|
||||
after_client = kc.get_client_by_id(cid, realm=realm)
|
||||
if before_client == after_client:
|
||||
@@ -913,29 +872,25 @@ def main():
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_client),
|
||||
after=sanitize_cr(after_client))
|
||||
|
||||
result['end_state'] = sanitize_cr(after_client)
|
||||
|
||||
result['msg'] = 'Client %s has been updated.' % desired_client['clientId']
|
||||
result['msg'] = 'Client %s has been updated.' % updated_client['clientId']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
# Delete existing client
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_client), after='')
|
||||
result['diff']['before'] = sanitize_cr(before_client)
|
||||
result['diff']['after'] = ''
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
kc.delete_client(cid, realm=realm)
|
||||
result['proposed'] = {}
|
||||
|
||||
result['end_state'] = {}
|
||||
|
||||
result['proposed'] = dict()
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'Client %s has been deleted.' % before_client['clientId']
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ DOCUMENTATION = '''
|
||||
module: keycloak_client_rolemapping
|
||||
|
||||
short_description: Allows administration of Keycloak client_rolemapping with the Keycloak API
|
||||
|
||||
version_added: 3.5.0
|
||||
|
||||
description:
|
||||
@@ -159,22 +158,21 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Role role1 assigned to group group1."
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Role role1 assigned to group group1."
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed client role mapping.
|
||||
description: role_representation representation of proposed changes to client_rolemapping.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
clientId: "test"
|
||||
}
|
||||
|
||||
existing:
|
||||
description:
|
||||
- Representation of existing client role mapping.
|
||||
- role_representation representation of existing role_representation.
|
||||
- The sample is truncated.
|
||||
returned: always
|
||||
type: dict
|
||||
@@ -184,12 +182,11 @@ existing:
|
||||
"request.object.signature.alg": "RS256",
|
||||
}
|
||||
}
|
||||
|
||||
end_state:
|
||||
description:
|
||||
- Representation of client role mapping after module execution.
|
||||
- role_representation representation of role_representation after module execution.
|
||||
- The sample is truncated.
|
||||
returned: on success
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"adminUrl": "http://www.example.com/admin_url",
|
||||
|
||||
@@ -86,7 +86,7 @@ options:
|
||||
suboptions:
|
||||
protocol:
|
||||
description:
|
||||
- This specifies for which protocol this protocol mapper.
|
||||
- This specifies for which protocol this protocol mapper
|
||||
- is active.
|
||||
choices: ['openid-connect', 'saml', 'wsfed']
|
||||
type: str
|
||||
@@ -256,21 +256,20 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client_scope testclientscope has been updated"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client_scope testclientscope has been updated"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed client scope.
|
||||
description: client_scope representation of proposed changes to client_scope
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
clientId: "test"
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing client scope (sample is truncated).
|
||||
description: client_scope representation of existing client_scope (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -279,10 +278,9 @@ existing:
|
||||
"request.object.signature.alg": "RS256",
|
||||
}
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of client scope after module execution (sample is truncated).
|
||||
returned: on success
|
||||
description: client_scope representation of client_scope after module execution (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"adminUrl": "http://www.example.com/admin_url",
|
||||
@@ -298,7 +296,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def sanitize_cr(clientscoperep):
|
||||
""" Removes probably sensitive details from a clientscoperep representation.
|
||||
""" Removes probably sensitive details from a clientscoperep representation
|
||||
|
||||
:param clientscoperep: the clientscoperep dict to be sanitized
|
||||
:return: sanitized clientrep dict
|
||||
@@ -363,22 +361,22 @@ def main():
|
||||
name = module.params.get('name')
|
||||
protocol_mappers = module.params.get('protocol_mappers')
|
||||
|
||||
# Filter and map the parameters names that apply to the client scope
|
||||
clientscope_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and
|
||||
module.params.get(x) is not None]
|
||||
before_clientscope = None # current state of the clientscope, for merging.
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
# does the clientscope already exist?
|
||||
if cid is None:
|
||||
before_clientscope = kc.get_clientscope_by_name(name, realm=realm)
|
||||
else:
|
||||
before_clientscope = kc.get_clientscope_by_clientscopeid(cid, realm=realm)
|
||||
|
||||
if before_clientscope is None:
|
||||
before_clientscope = {}
|
||||
before_clientscope = {} if before_clientscope is None else before_clientscope
|
||||
|
||||
clientscope_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
changeset = dict()
|
||||
|
||||
for clientscope_param in clientscope_params:
|
||||
new_param_value = module.params.get(clientscope_param)
|
||||
@@ -396,87 +394,81 @@ def main():
|
||||
new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value]
|
||||
changeset[camel(clientscope_param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_clientscope = before_clientscope.copy()
|
||||
desired_clientscope.update(changeset)
|
||||
# prepare the new clientscope
|
||||
updated_clientscope = before_clientscope.copy()
|
||||
updated_clientscope.update(changeset)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_clientscope:
|
||||
# if before_clientscope is none, the clientscope doesn't exist.
|
||||
if before_clientscope == {}:
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# nothing to do.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['msg'] = 'Clientscope does not exist; doing nothing.'
|
||||
result['end_state'] = dict()
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# for 'present', create a new clientscope.
|
||||
result['changed'] = True
|
||||
|
||||
if name is None:
|
||||
module.fail_json(msg='name must be specified when creating a new clientscope')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize_cr(desired_clientscope))
|
||||
result['diff'] = dict(before='', after=sanitize_cr(updated_clientscope))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
kc.create_clientscope(desired_clientscope, realm=realm)
|
||||
# do it for real!
|
||||
kc.create_clientscope(updated_clientscope, realm=realm)
|
||||
after_clientscope = kc.get_clientscope_by_name(name, realm)
|
||||
|
||||
result['end_state'] = sanitize_cr(after_clientscope)
|
||||
|
||||
result['msg'] = 'Clientscope {name} has been created with ID {id}'.format(name=after_clientscope['name'],
|
||||
id=after_clientscope['id'])
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# no changes
|
||||
if desired_clientscope == before_clientscope:
|
||||
if updated_clientscope == before_clientscope:
|
||||
result['changed'] = False
|
||||
result['end_state'] = sanitize_cr(desired_clientscope)
|
||||
result['end_state'] = sanitize_cr(updated_clientscope)
|
||||
result['msg'] = "No changes required to clientscope {name}.".format(name=before_clientscope['name'])
|
||||
module.exit_json(**result)
|
||||
|
||||
# doing an update
|
||||
# update the existing clientscope
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_clientscope), after=sanitize_cr(desired_clientscope))
|
||||
result['diff'] = dict(before=sanitize_cr(before_clientscope), after=sanitize_cr(updated_clientscope))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
kc.update_clientscope(desired_clientscope, realm=realm)
|
||||
# do the clientscope update
|
||||
kc.update_clientscope(updated_clientscope, realm=realm)
|
||||
|
||||
# do the protocolmappers update
|
||||
if protocol_mappers is not None:
|
||||
for protocol_mapper in protocol_mappers:
|
||||
# update if protocolmapper exist
|
||||
current_protocolmapper = kc.get_clientscope_protocolmapper_by_name(desired_clientscope['id'], protocol_mapper['name'], realm=realm)
|
||||
current_protocolmapper = kc.get_clientscope_protocolmapper_by_name(updated_clientscope['id'], protocol_mapper['name'], realm=realm)
|
||||
if current_protocolmapper is not None:
|
||||
protocol_mapper['id'] = current_protocolmapper['id']
|
||||
kc.update_clientscope_protocolmappers(desired_clientscope['id'], protocol_mapper, realm=realm)
|
||||
kc.update_clientscope_protocolmappers(updated_clientscope['id'], protocol_mapper, realm=realm)
|
||||
# create otherwise
|
||||
else:
|
||||
kc.create_clientscope_protocolmapper(desired_clientscope['id'], protocol_mapper, realm=realm)
|
||||
kc.create_clientscope_protocolmapper(updated_clientscope['id'], protocol_mapper, realm=realm)
|
||||
|
||||
after_clientscope = kc.get_clientscope_by_clientscopeid(desired_clientscope['id'], realm=realm)
|
||||
after_clientscope = kc.get_clientscope_by_clientscopeid(updated_clientscope['id'], realm=realm)
|
||||
|
||||
result['end_state'] = after_clientscope
|
||||
|
||||
result['msg'] = "Clientscope {id} has been updated".format(id=after_clientscope['id'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
result['changed'] = True
|
||||
elif state == 'absent':
|
||||
result['end_state'] = dict()
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize_cr(before_clientscope), after='')
|
||||
@@ -484,14 +476,15 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
# delete for real
|
||||
cid = before_clientscope['id']
|
||||
kc.delete_clientscope(cid=cid, realm=realm)
|
||||
|
||||
result['end_state'] = {}
|
||||
|
||||
result['changed'] = True
|
||||
result['msg'] = "Clientscope {name} has been deleted".format(name=before_clientscope['name'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ description:
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- State of the client template.
|
||||
- State of the client template
|
||||
- On C(present), the client template will be created (or updated if it exists already).
|
||||
- On C(absent), the client template will be removed if it exists
|
||||
choices: ['present', 'absent']
|
||||
@@ -51,12 +51,12 @@ options:
|
||||
|
||||
name:
|
||||
description:
|
||||
- Name of the client template.
|
||||
- Name of the client template
|
||||
type: str
|
||||
|
||||
description:
|
||||
description:
|
||||
- Description of the client template in Keycloak.
|
||||
- Description of the client template in Keycloak
|
||||
type: str
|
||||
|
||||
protocol:
|
||||
@@ -100,7 +100,7 @@ options:
|
||||
|
||||
protocol:
|
||||
description:
|
||||
- This is either C(openid-connect) or C(saml), this specifies for which protocol this protocol mapper.
|
||||
- is either 'openid-connect' or 'saml', this specifies for which protocol this protocol mapper
|
||||
is active.
|
||||
choices: ['openid-connect', 'saml']
|
||||
type: str
|
||||
@@ -143,7 +143,7 @@ options:
|
||||
contents differ depending on the value of I(protocolMapper) and are not documented
|
||||
other than by the source of the mappers and its parent class(es). An example is given
|
||||
below. It is easiest to obtain valid config values by dumping an already-existing
|
||||
protocol mapper configuration through check-mode in the I(existing) field.
|
||||
protocol mapper configuration through check-mode in the "existing" field.
|
||||
type: dict
|
||||
|
||||
attributes:
|
||||
@@ -163,6 +163,7 @@ notes:
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
|
||||
|
||||
author:
|
||||
- Eike Frost (@eikef)
|
||||
'''
|
||||
@@ -230,21 +231,20 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client template testclient has been updated"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Client template testclient has been updated"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed client template.
|
||||
description: client template representation of proposed changes to client template
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
name: "test01"
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing client template (sample is truncated).
|
||||
description: client template representation of existing client template (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -254,10 +254,9 @@ existing:
|
||||
"name": "test01",
|
||||
"protocol": "saml"
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of client template after module execution (sample is truncated).
|
||||
returned: on success
|
||||
description: client template representation of client template after module execution (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"description": "test01",
|
||||
@@ -303,7 +302,6 @@ def main():
|
||||
full_scope_allowed=dict(type='bool'),
|
||||
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
@@ -319,20 +317,19 @@ def main():
|
||||
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')
|
||||
state = module.params.get('state')
|
||||
cid = module.params.get('id')
|
||||
|
||||
# Filter and map the parameters names that apply to the client template
|
||||
# convert module parameters to client representation parameters (if they belong in there)
|
||||
clientt_params = [x for x in module.params
|
||||
if x not in ['state', 'auth_keycloak_url', 'auth_client_id', 'auth_realm',
|
||||
'auth_client_secret', 'auth_username', 'auth_password',
|
||||
'validate_certs', 'realm'] and module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
# See whether the client template already exists in Keycloak
|
||||
if cid is None:
|
||||
before_clientt = kc.get_client_template_by_name(module.params.get('name'), realm=realm)
|
||||
if before_clientt is not None:
|
||||
@@ -341,12 +338,12 @@ def main():
|
||||
before_clientt = kc.get_client_template_by_id(cid, realm=realm)
|
||||
|
||||
if before_clientt is None:
|
||||
before_clientt = {}
|
||||
before_clientt = dict()
|
||||
|
||||
result['existing'] = before_clientt
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
changeset = dict()
|
||||
|
||||
for clientt_param in clientt_params:
|
||||
# lists in the Keycloak API are sorted
|
||||
@@ -358,89 +355,78 @@ def main():
|
||||
pass
|
||||
changeset[camel(clientt_param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_clientt = before_clientt.copy()
|
||||
desired_clientt.update(changeset)
|
||||
# Whether creating or updating a client, take the before-state and merge the changeset into it
|
||||
updated_clientt = before_clientt.copy()
|
||||
updated_clientt.update(changeset)
|
||||
|
||||
result['proposed'] = changeset
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_clientt:
|
||||
# If the client template does not exist yet, before_client is still empty
|
||||
if before_clientt == dict():
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# do nothing and exit
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['msg'] = 'Client template does not exist, doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# create new client template
|
||||
result['changed'] = True
|
||||
|
||||
if 'name' not in desired_clientt:
|
||||
if 'name' not in updated_clientt:
|
||||
module.fail_json(msg='name needs to be specified when creating a new client')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=desired_clientt)
|
||||
result['diff'] = dict(before='', after=updated_clientt)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
kc.create_client_template(desired_clientt, realm=realm)
|
||||
after_clientt = kc.get_client_template_by_name(desired_clientt['name'], realm=realm)
|
||||
kc.create_client_template(updated_clientt, realm=realm)
|
||||
after_clientt = kc.get_client_template_by_name(updated_clientt['name'], realm=realm)
|
||||
|
||||
result['end_state'] = after_clientt
|
||||
|
||||
result['msg'] = 'Client template %s has been created.' % desired_clientt['name']
|
||||
result['msg'] = 'Client template %s has been created.' % updated_clientt['name']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# update existing client template
|
||||
result['changed'] = True
|
||||
if module.check_mode:
|
||||
# We can only compare the current client template with the proposed updates we have
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_clientt,
|
||||
after=desired_clientt)
|
||||
after=updated_clientt)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
kc.update_client_template(cid, desired_clientt, realm=realm)
|
||||
kc.update_client_template(cid, updated_clientt, realm=realm)
|
||||
|
||||
after_clientt = kc.get_client_template_by_id(cid, realm=realm)
|
||||
if before_clientt == after_clientt:
|
||||
result['changed'] = False
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_clientt,
|
||||
after=after_clientt)
|
||||
result['end_state'] = after_clientt
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_clientt, after=after_clientt)
|
||||
|
||||
result['msg'] = 'Client template %s has been updated.' % desired_clientt['name']
|
||||
result['msg'] = 'Client template %s has been updated.' % updated_clientt['name']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
# Delete existing client
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_clientt, after='')
|
||||
result['diff']['before'] = before_clientt
|
||||
result['diff']['after'] = ''
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
kc.delete_client_template(cid, realm=realm)
|
||||
result['proposed'] = {}
|
||||
|
||||
result['end_state'] = {}
|
||||
|
||||
result['proposed'] = dict()
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'Client template %s has been deleted.' % before_clientt['name']
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -159,91 +159,34 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
|
||||
end_state:
|
||||
description: Representation of the group after module execution (sample is truncated).
|
||||
returned: on success
|
||||
type: complex
|
||||
contains:
|
||||
id:
|
||||
description: GUID that identifies the group.
|
||||
type: str
|
||||
returned: always
|
||||
sample: 23f38145-3195-462c-97e7-97041ccea73e
|
||||
name:
|
||||
description: Name of the group.
|
||||
type: str
|
||||
returned: always
|
||||
sample: grp-test-123
|
||||
attributes:
|
||||
description: Attributes applied to this group.
|
||||
type: dict
|
||||
returned: always
|
||||
sample:
|
||||
attr1: ["val1", "val2", "val3"]
|
||||
path:
|
||||
description: URI path to the group.
|
||||
type: str
|
||||
returned: always
|
||||
sample: /grp-test-123
|
||||
realmRoles:
|
||||
description: An array of the realm-level roles granted to this group.
|
||||
type: list
|
||||
returned: always
|
||||
sample: []
|
||||
subGroups:
|
||||
description: A list of groups that are children of this group. These groups will have the same parameters as
|
||||
documented here.
|
||||
type: list
|
||||
returned: always
|
||||
clientRoles:
|
||||
description: A list of client-level roles granted to this group.
|
||||
type: list
|
||||
returned: always
|
||||
sample: []
|
||||
access:
|
||||
description: A dict describing the accesses you have to this group based on the credentials used.
|
||||
type: dict
|
||||
returned: always
|
||||
sample:
|
||||
manage: true
|
||||
manageMembership: true
|
||||
view: true
|
||||
|
||||
group:
|
||||
description:
|
||||
- Representation of the group after module execution.
|
||||
- Deprecated return value, it will be removed in community.general 6.0.0. Please use the return value I(end_state) instead.
|
||||
description: Group representation of the group after module execution (sample is truncated).
|
||||
returned: always
|
||||
type: complex
|
||||
contains:
|
||||
id:
|
||||
description: GUID that identifies the group.
|
||||
description: GUID that identifies the group
|
||||
type: str
|
||||
returned: always
|
||||
sample: 23f38145-3195-462c-97e7-97041ccea73e
|
||||
name:
|
||||
description: Name of the group.
|
||||
description: Name of the group
|
||||
type: str
|
||||
returned: always
|
||||
sample: grp-test-123
|
||||
attributes:
|
||||
description: Attributes applied to this group.
|
||||
description: Attributes applied to this group
|
||||
type: dict
|
||||
returned: always
|
||||
sample:
|
||||
attr1: ["val1", "val2", "val3"]
|
||||
path:
|
||||
description: URI path to the group.
|
||||
description: URI path to the group
|
||||
type: str
|
||||
returned: always
|
||||
sample: /grp-test-123
|
||||
realmRoles:
|
||||
description: An array of the realm-level roles granted to this group.
|
||||
description: An array of the realm-level roles granted to this group
|
||||
type: list
|
||||
returned: always
|
||||
sample: []
|
||||
@@ -253,7 +196,7 @@ group:
|
||||
type: list
|
||||
returned: always
|
||||
clientRoles:
|
||||
description: A list of client-level roles granted to this group.
|
||||
description: A list of client-level roles granted to this group
|
||||
type: list
|
||||
returned: always
|
||||
sample: []
|
||||
@@ -265,7 +208,6 @@ group:
|
||||
manage: true
|
||||
manageMembership: true
|
||||
view: true
|
||||
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
|
||||
@@ -280,7 +222,6 @@ def main():
|
||||
:return:
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
realm=dict(default='master'),
|
||||
@@ -313,6 +254,16 @@ def main():
|
||||
name = module.params.get('name')
|
||||
attributes = module.params.get('attributes')
|
||||
|
||||
before_group = None # current state of the group, for merging.
|
||||
|
||||
# does the group already exist?
|
||||
if gid is None:
|
||||
before_group = kc.get_group_by_name(name, realm=realm)
|
||||
else:
|
||||
before_group = kc.get_group_by_groupid(gid, realm=realm)
|
||||
|
||||
before_group = {} if before_group is None else before_group
|
||||
|
||||
# attributes in Keycloak have their values returned as lists
|
||||
# via the API. attributes is a dict, so we'll transparently convert
|
||||
# the values to lists.
|
||||
@@ -320,103 +271,81 @@ def main():
|
||||
for key, val in module.params['attributes'].items():
|
||||
module.params['attributes'][key] = [val] if not isinstance(val, list) else val
|
||||
|
||||
# Filter and map the parameters names that apply to the group
|
||||
group_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
if gid is None:
|
||||
before_group = kc.get_group_by_name(name, realm=realm)
|
||||
else:
|
||||
before_group = kc.get_group_by_groupid(gid, realm=realm)
|
||||
|
||||
if before_group is None:
|
||||
before_group = {}
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
# build a changeset
|
||||
changeset = {}
|
||||
|
||||
for param in group_params:
|
||||
new_param_value = module.params.get(param)
|
||||
old_value = before_group[param] if param in before_group else None
|
||||
if new_param_value != old_value:
|
||||
changeset[camel(param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_group = before_group.copy()
|
||||
desired_group.update(changeset)
|
||||
# prepare the new group
|
||||
updated_group = before_group.copy()
|
||||
updated_group.update(changeset)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_group:
|
||||
# if before_group is none, the group doesn't exist.
|
||||
if before_group == {}:
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# nothing to do.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['group'] = result['end_state']
|
||||
result['msg'] = 'Group does not exist; doing nothing.'
|
||||
result['group'] = dict()
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# for 'present', create a new group.
|
||||
result['changed'] = True
|
||||
|
||||
if name is None:
|
||||
module.fail_json(msg='name must be specified when creating a new group')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=desired_group)
|
||||
result['diff'] = dict(before='', after=updated_group)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
kc.create_group(desired_group, realm=realm)
|
||||
# do it for real!
|
||||
kc.create_group(updated_group, realm=realm)
|
||||
after_group = kc.get_group_by_name(name, realm)
|
||||
|
||||
result['end_state'] = after_group
|
||||
result['group'] = result['end_state']
|
||||
|
||||
result['group'] = after_group
|
||||
result['msg'] = 'Group {name} has been created with ID {id}'.format(name=after_group['name'],
|
||||
id=after_group['id'])
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# no changes
|
||||
if desired_group == before_group:
|
||||
if updated_group == before_group:
|
||||
result['changed'] = False
|
||||
result['end_state'] = desired_group
|
||||
result['group'] = result['end_state']
|
||||
result['group'] = updated_group
|
||||
result['msg'] = "No changes required to group {name}.".format(name=before_group['name'])
|
||||
module.exit_json(**result)
|
||||
|
||||
# doing an update
|
||||
# update the existing group
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_group, after=desired_group)
|
||||
result['diff'] = dict(before=before_group, after=updated_group)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
kc.update_group(desired_group, realm=realm)
|
||||
kc.update_group(updated_group, realm=realm)
|
||||
|
||||
after_group = kc.get_group_by_groupid(desired_group['id'], realm=realm)
|
||||
|
||||
result['end_state'] = after_group
|
||||
result['group'] = result['end_state']
|
||||
after_group = kc.get_group_by_groupid(updated_group['id'], realm=realm)
|
||||
|
||||
result['group'] = after_group
|
||||
result['msg'] = "Group {id} has been updated".format(id=after_group['id'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
result['changed'] = True
|
||||
elif state == 'absent':
|
||||
result['group'] = dict()
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_group, after='')
|
||||
@@ -424,15 +353,15 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
# delete for real
|
||||
gid = before_group['id']
|
||||
kc.delete_group(groupid=gid, realm=realm)
|
||||
|
||||
result['end_state'] = {}
|
||||
result['group'] = result['end_state']
|
||||
|
||||
result['changed'] = True
|
||||
result['msg'] = "Group {name} has been deleted".format(name=before_group['name'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
|
||||
@@ -339,13 +339,13 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Identity provider my-idp has been created"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Identity provider my-idp has been created"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed identity provider.
|
||||
description: Representation of proposed changes to identity provider
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -363,7 +363,7 @@ proposed:
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing identity provider.
|
||||
description: Representation of existing identity provider
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -391,8 +391,8 @@ existing:
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of identity provider after module execution.
|
||||
returned: on success
|
||||
description: Representation of identity provider after module execution
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"addReadTokenRoleOnCreate": false,
|
||||
@@ -416,6 +416,7 @@ end_state:
|
||||
"storeToken": false,
|
||||
"trustEmail": false,
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
|
||||
@@ -437,7 +438,7 @@ def get_identity_provider_with_mappers(kc, alias, realm):
|
||||
if idp is not None:
|
||||
idp['mappers'] = sorted(kc.get_identity_provider_mappers(alias, realm), key=lambda x: x.get('name'))
|
||||
if idp is None:
|
||||
idp = {}
|
||||
idp = dict()
|
||||
return idp
|
||||
|
||||
|
||||
@@ -496,16 +497,16 @@ def main():
|
||||
alias = module.params.get('alias')
|
||||
state = module.params.get('state')
|
||||
|
||||
# Filter and map the parameters names that apply to the identity provider.
|
||||
# convert module parameters to client representation parameters (if they belong in there)
|
||||
idp_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
# does the identity provider already exist?
|
||||
before_idp = get_identity_provider_with_mappers(kc, alias, realm)
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
# build a changeset
|
||||
changeset = dict()
|
||||
|
||||
for param in idp_params:
|
||||
new_param_value = module.params.get(param)
|
||||
@@ -538,37 +539,37 @@ def main():
|
||||
changeset['mappers'] = list()
|
||||
changeset['mappers'].append(new_mapper)
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_idp = before_idp.copy()
|
||||
desired_idp.update(changeset)
|
||||
# prepare the new representation
|
||||
updated_idp = before_idp.copy()
|
||||
updated_idp.update(changeset)
|
||||
|
||||
result['proposed'] = sanitize(changeset)
|
||||
result['existing'] = sanitize(before_idp)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_idp:
|
||||
# if before_idp is none, the identity provider doesn't exist.
|
||||
if before_idp == dict():
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# nothing to do.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'Identity provider does not exist; doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# for 'present', create a new identity provider.
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize(desired_idp))
|
||||
result['diff'] = dict(before='', after=sanitize(updated_idp))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
desired_idp = desired_idp.copy()
|
||||
mappers = desired_idp.pop('mappers', [])
|
||||
kc.create_identity_provider(desired_idp, realm)
|
||||
# do it for real!
|
||||
updated_idp = updated_idp.copy()
|
||||
mappers = updated_idp.pop('mappers', [])
|
||||
kc.create_identity_provider(updated_idp, realm)
|
||||
for mapper in mappers:
|
||||
if mapper.get('identityProviderAlias') is None:
|
||||
mapper['identityProviderAlias'] = alias
|
||||
@@ -582,28 +583,26 @@ def main():
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# no changes
|
||||
if desired_idp == before_idp:
|
||||
if updated_idp == before_idp:
|
||||
result['changed'] = False
|
||||
result['end_state'] = sanitize(desired_idp)
|
||||
result['end_state'] = sanitize(updated_idp)
|
||||
result['msg'] = "No changes required to identity provider {alias}.".format(alias=alias)
|
||||
module.exit_json(**result)
|
||||
|
||||
# doing an update
|
||||
# update the existing role
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize(before_idp), after=sanitize(desired_idp))
|
||||
result['diff'] = dict(before=sanitize(before_idp), after=sanitize(updated_idp))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
desired_idp = desired_idp.copy()
|
||||
updated_mappers = desired_idp.pop('mappers', [])
|
||||
kc.update_identity_provider(desired_idp, realm)
|
||||
updated_idp = updated_idp.copy()
|
||||
updated_mappers = updated_idp.pop('mappers', [])
|
||||
kc.update_identity_provider(updated_idp, realm)
|
||||
for mapper in updated_mappers:
|
||||
if mapper.get('id') is not None:
|
||||
kc.update_identity_provider_mapper(mapper, alias, realm)
|
||||
@@ -623,7 +622,6 @@ def main():
|
||||
module.exit_json(**result)
|
||||
|
||||
elif state == 'absent':
|
||||
# Process a deletion
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
@@ -632,12 +630,13 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
# delete for real
|
||||
kc.delete_identity_provider(alias, realm)
|
||||
|
||||
result['end_state'] = {}
|
||||
result['end_state'] = dict()
|
||||
|
||||
result['msg'] = "Identity provider {alias} has been deleted".format(alias=alias)
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ DOCUMENTATION = '''
|
||||
module: keycloak_realm
|
||||
|
||||
short_description: Allows administration of Keycloak realm via Keycloak API
|
||||
|
||||
version_added: 3.0.0
|
||||
|
||||
|
||||
@@ -534,21 +533,20 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Realm testrealm has been updated"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Realm testrealm has been updated"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed realm.
|
||||
description: realm representation of proposed changes to realm
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
id: "test"
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing realm (sample is truncated).
|
||||
description: realm representation of existing realm (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -557,10 +555,9 @@ existing:
|
||||
"request.object.signature.alg": "RS256",
|
||||
}
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of realm after module execution (sample is truncated).
|
||||
returned: on success
|
||||
description: realm representation of realm after module execution (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"adminUrl": "http://www.example.com/admin_url",
|
||||
@@ -576,7 +573,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def sanitize_cr(realmrep):
|
||||
""" Removes probably sensitive details from a realm representation.
|
||||
""" Removes probably sensitive details from a realm representation
|
||||
|
||||
:param realmrep: the realmrep dict to be sanitized
|
||||
:return: sanitized realmrep dict
|
||||
@@ -679,7 +676,6 @@ def main():
|
||||
verify_email=dict(type='bool', aliases=['verifyEmail']),
|
||||
wait_increment_seconds=dict(type='int', aliases=['waitIncrementSeconds']),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
@@ -703,114 +699,95 @@ def main():
|
||||
|
||||
# convert module parameters to realm representation parameters (if they belong in there)
|
||||
params_to_ignore = list(keycloak_argument_spec().keys()) + ['state']
|
||||
|
||||
# Filter and map the parameters names that apply to the role
|
||||
realm_params = [x for x in module.params
|
||||
if x not in params_to_ignore and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See whether the realm already exists in Keycloak
|
||||
before_realm = kc.get_realm_by_id(realm=realm)
|
||||
|
||||
if before_realm is None:
|
||||
before_realm = {}
|
||||
before_realm = kc.get_realm_by_id(realm=realm) or {}
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
changeset = dict()
|
||||
|
||||
for realm_param in realm_params:
|
||||
new_param_value = module.params.get(realm_param)
|
||||
changeset[camel(realm_param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_realm = before_realm.copy()
|
||||
desired_realm.update(changeset)
|
||||
# Whether creating or updating a realm, take the before-state and merge the changeset into it
|
||||
updated_realm = before_realm.copy()
|
||||
updated_realm.update(changeset)
|
||||
|
||||
result['proposed'] = sanitize_cr(changeset)
|
||||
before_realm_sanitized = sanitize_cr(before_realm)
|
||||
result['existing'] = before_realm_sanitized
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
# If the realm does not exist yet, before_realm is still empty
|
||||
if not before_realm:
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# do nothing and exit
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['msg'] = 'Realm does not exist, doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# create new realm
|
||||
result['changed'] = True
|
||||
|
||||
if 'id' not in desired_realm:
|
||||
if 'id' not in updated_realm:
|
||||
module.fail_json(msg='id needs to be specified when creating a new realm')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize_cr(desired_realm))
|
||||
result['diff'] = dict(before='', after=sanitize_cr(updated_realm))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
kc.create_realm(desired_realm)
|
||||
after_realm = kc.get_realm_by_id(desired_realm['id'])
|
||||
kc.create_realm(updated_realm)
|
||||
after_realm = kc.get_realm_by_id(updated_realm['id'])
|
||||
|
||||
result['end_state'] = sanitize_cr(after_realm)
|
||||
|
||||
result['msg'] = 'Realm %s has been created.' % desired_realm['id']
|
||||
result['msg'] = 'Realm %s has been created.' % updated_realm['id']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# doing an update
|
||||
# update existing realm
|
||||
result['changed'] = True
|
||||
if module.check_mode:
|
||||
# We can only compare the current realm with the proposed updates we have
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_realm_sanitized,
|
||||
after=sanitize_cr(desired_realm))
|
||||
result['changed'] = (before_realm != desired_realm)
|
||||
after=sanitize_cr(updated_realm))
|
||||
result['changed'] = (before_realm != updated_realm)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
kc.update_realm(desired_realm, realm=realm)
|
||||
kc.update_realm(updated_realm, realm=realm)
|
||||
|
||||
after_realm = kc.get_realm_by_id(realm=realm)
|
||||
|
||||
if before_realm == after_realm:
|
||||
result['changed'] = False
|
||||
|
||||
result['end_state'] = sanitize_cr(after_realm)
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_realm_sanitized,
|
||||
after=sanitize_cr(after_realm))
|
||||
result['end_state'] = sanitize_cr(after_realm)
|
||||
|
||||
result['msg'] = 'Realm %s has been updated.' % desired_realm['id']
|
||||
result['msg'] = 'Realm %s has been updated.' % updated_realm['id']
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
# Delete existing realm
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_realm_sanitized, after='')
|
||||
result['diff']['before'] = before_realm_sanitized
|
||||
result['diff']['after'] = ''
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
kc.delete_realm(realm=realm)
|
||||
|
||||
result['proposed'] = {}
|
||||
result['end_state'] = {}
|
||||
|
||||
result['proposed'] = dict()
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'Realm %s has been deleted.' % before_realm['id']
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -149,21 +149,20 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Role myrole has been updated"
|
||||
description: Message as to what action was taken
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Role myrole has been updated"
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed role.
|
||||
description: Role representation of proposed changes to role
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"description": "My updated test description"
|
||||
}
|
||||
|
||||
existing:
|
||||
description: Representation of existing role.
|
||||
description: Role representation of existing role
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -175,10 +174,9 @@ existing:
|
||||
"id": "561703dd-0f38-45ff-9a5a-0c978f794547",
|
||||
"name": "myrole"
|
||||
}
|
||||
|
||||
end_state:
|
||||
description: Representation of role after module execution (sample is truncated).
|
||||
returned: on success
|
||||
description: Role representation of role after module execution (sample is truncated)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"attributes": {},
|
||||
@@ -203,7 +201,6 @@ def main():
|
||||
:return:
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
state=dict(type='str', default='present', choices=['present', 'absent']),
|
||||
name=dict(type='str', required=True),
|
||||
@@ -242,22 +239,22 @@ def main():
|
||||
for key, val in module.params['attributes'].items():
|
||||
module.params['attributes'][key] = [val] if not isinstance(val, list) else val
|
||||
|
||||
# Filter and map the parameters names that apply to the role
|
||||
# convert module parameters to client representation parameters (if they belong in there)
|
||||
role_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id', 'composites'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
# does the role already exist?
|
||||
if clientid is None:
|
||||
before_role = kc.get_realm_role(name, realm)
|
||||
else:
|
||||
before_role = kc.get_client_role(name, clientid, realm)
|
||||
|
||||
if before_role is None:
|
||||
before_role = {}
|
||||
before_role = dict()
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
# build a changeset
|
||||
changeset = dict()
|
||||
|
||||
for param in role_params:
|
||||
new_param_value = module.params.get(param)
|
||||
@@ -265,42 +262,42 @@ def main():
|
||||
if new_param_value != old_value:
|
||||
changeset[camel(param)] = new_param_value
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_role = before_role.copy()
|
||||
desired_role.update(changeset)
|
||||
# prepare the new role
|
||||
updated_role = before_role.copy()
|
||||
updated_role.update(changeset)
|
||||
|
||||
result['proposed'] = changeset
|
||||
result['existing'] = before_role
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_role:
|
||||
# if before_role is none, the role doesn't exist.
|
||||
if before_role == dict():
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# nothing to do.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['msg'] = 'Role does not exist, doing nothing.'
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'Role does not exist; doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# for 'present', create a new role.
|
||||
result['changed'] = True
|
||||
|
||||
if name is None:
|
||||
module.fail_json(msg='name must be specified when creating a new role')
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=desired_role)
|
||||
result['diff'] = dict(before='', after=updated_role)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
# do it for real!
|
||||
if clientid is None:
|
||||
kc.create_realm_role(desired_role, realm)
|
||||
kc.create_realm_role(updated_role, realm)
|
||||
after_role = kc.get_realm_role(name, realm)
|
||||
else:
|
||||
kc.create_client_role(desired_role, clientid, realm)
|
||||
kc.create_client_role(updated_role, clientid, realm)
|
||||
after_role = kc.get_client_role(name, clientid, realm)
|
||||
|
||||
result['end_state'] = after_role
|
||||
@@ -310,30 +307,28 @@ def main():
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# no changes
|
||||
if desired_role == before_role:
|
||||
if updated_role == before_role:
|
||||
result['changed'] = False
|
||||
result['end_state'] = desired_role
|
||||
result['end_state'] = updated_role
|
||||
result['msg'] = "No changes required to role {name}.".format(name=name)
|
||||
module.exit_json(**result)
|
||||
|
||||
# doing an update
|
||||
# update the existing role
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=before_role, after=desired_role)
|
||||
result['diff'] = dict(before=before_role, after=updated_role)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
if clientid is None:
|
||||
kc.update_realm_role(desired_role, realm)
|
||||
kc.update_realm_role(updated_role, realm)
|
||||
after_role = kc.get_realm_role(name, realm)
|
||||
else:
|
||||
kc.update_client_role(desired_role, clientid, realm)
|
||||
kc.update_client_role(updated_role, clientid, realm)
|
||||
after_role = kc.get_client_role(name, clientid, realm)
|
||||
|
||||
result['end_state'] = after_role
|
||||
@@ -341,8 +336,7 @@ def main():
|
||||
result['msg'] = "Role {name} has been updated".format(name=name)
|
||||
module.exit_json(**result)
|
||||
|
||||
else:
|
||||
# Process a deletion (because state was not 'present')
|
||||
elif state == 'absent':
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
@@ -351,15 +345,16 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
# delete for real
|
||||
if clientid is None:
|
||||
kc.delete_realm_role(name, realm)
|
||||
else:
|
||||
kc.delete_client_role(name, clientid, realm)
|
||||
|
||||
result['end_state'] = {}
|
||||
result['end_state'] = dict()
|
||||
|
||||
result['msg'] = "Role {name} has been deleted".format(name=name)
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ options:
|
||||
choices:
|
||||
- ldap
|
||||
- kerberos
|
||||
- sssd
|
||||
|
||||
provider_type:
|
||||
description:
|
||||
@@ -84,10 +83,9 @@ options:
|
||||
config:
|
||||
description:
|
||||
- Dict specifying the configuration options for the provider; the contents differ depending on
|
||||
the value of I(provider_id). Examples are given below for C(ldap), C(kerberos) and C(sssd).
|
||||
It is easiest to obtain valid config values by dumping an already-existing user federation
|
||||
configuration through check-mode in the I(existing) field.
|
||||
- The value C(sssd) has been supported since community.general 4.2.0.
|
||||
the value of I(provider_id). Examples are given below for C(ldap) and C(kerberos). It is easiest
|
||||
to obtain valid config values by dumping an already-existing user federation configuration
|
||||
through check-mode in the I(existing) field.
|
||||
type: dict
|
||||
suboptions:
|
||||
enabled:
|
||||
@@ -184,7 +182,7 @@ options:
|
||||
description:
|
||||
- For one level, the search applies only for users in the DNs specified by User DNs.
|
||||
For subtree, the search applies to the whole subtree. See LDAP documentation for
|
||||
more details.
|
||||
more details
|
||||
default: '1'
|
||||
type: str
|
||||
choices:
|
||||
@@ -533,22 +531,6 @@ EXAMPLES = '''
|
||||
allowPasswordAuthentication: false
|
||||
updateProfileFirstLogin: false
|
||||
|
||||
- name: Create sssd user federation
|
||||
community.general.keycloak_user_federation:
|
||||
auth_keycloak_url: https://keycloak.example.com/auth
|
||||
auth_realm: master
|
||||
auth_username: admin
|
||||
auth_password: password
|
||||
realm: my-realm
|
||||
name: my-sssd
|
||||
state: present
|
||||
provider_id: sssd
|
||||
provider_type: org.keycloak.storage.UserStorageProvider
|
||||
config:
|
||||
priority: 0
|
||||
enabled: true
|
||||
cachePolicy: DEFAULT
|
||||
|
||||
- name: Delete user federation
|
||||
community.general.keycloak_user_federation:
|
||||
auth_keycloak_url: https://keycloak.example.com/auth
|
||||
@@ -569,7 +551,7 @@ msg:
|
||||
sample: "No changes required to user federation 164bb483-c613-482e-80fe-7f1431308799."
|
||||
|
||||
proposed:
|
||||
description: Representation of proposed user federation.
|
||||
description: Representation of proposed changes to user federation.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
@@ -666,7 +648,7 @@ existing:
|
||||
|
||||
end_state:
|
||||
description: Representation of user federation after module execution.
|
||||
returned: on success
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"config": {
|
||||
@@ -686,6 +668,7 @@ end_state:
|
||||
"providerId": "kerberos",
|
||||
"providerType": "org.keycloak.storage.UserStorageProvider"
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
|
||||
@@ -783,7 +766,7 @@ def main():
|
||||
realm=dict(type='str', default='master'),
|
||||
id=dict(type='str'),
|
||||
name=dict(type='str'),
|
||||
provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos', 'sssd']),
|
||||
provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos']),
|
||||
provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'),
|
||||
parent_id=dict(type='str', aliases=['parentId']),
|
||||
mappers=dict(type='list', elements='dict', options=mapper_spec),
|
||||
@@ -825,12 +808,12 @@ def main():
|
||||
mapper['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v])
|
||||
for k, v in mapper['config'].items() if mapper['config'][k] is not None)
|
||||
|
||||
# Filter and map the parameters names that apply
|
||||
# convert module parameters to client representation parameters (if they belong in there)
|
||||
comp_params = [x for x in module.params
|
||||
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# See if it already exists in Keycloak
|
||||
# does the user federation already exist?
|
||||
if cid is None:
|
||||
found = kc.get_components(urlencode(dict(type='org.keycloak.storage.UserStorageProvider', parent=realm, name=name)), realm)
|
||||
if len(found) > 1:
|
||||
@@ -842,14 +825,14 @@ def main():
|
||||
before_comp = kc.get_component(cid, realm)
|
||||
|
||||
if before_comp is None:
|
||||
before_comp = {}
|
||||
before_comp = dict()
|
||||
|
||||
# if user federation exists, get associated mappers
|
||||
if cid is not None:
|
||||
before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name'))
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
# build a changeset
|
||||
changeset = dict()
|
||||
|
||||
for param in comp_params:
|
||||
new_param_value = module.params.get(param)
|
||||
@@ -861,18 +844,18 @@ def main():
|
||||
|
||||
# special handling of mappers list to allow change detection
|
||||
if module.params.get('mappers') is not None:
|
||||
if module.params['provider_id'] in ['kerberos', 'sssd']:
|
||||
module.fail_json(msg='Cannot configure mappers for {type} provider.'.format(type=module.params['provider_id']))
|
||||
if module.params['provider_id'] == 'kerberos':
|
||||
module.fail_json(msg='Cannot configure mappers for Kerberos federations.')
|
||||
for change in module.params['mappers']:
|
||||
change = dict((k, v) for k, v in change.items() if change[k] is not None)
|
||||
if change.get('id') is None and change.get('name') is None:
|
||||
module.fail_json(msg='Either `name` or `id` has to be specified on each mapper.')
|
||||
if cid is None:
|
||||
old_mapper = {}
|
||||
old_mapper = dict()
|
||||
elif change.get('id') is not None:
|
||||
old_mapper = kc.get_component(change['id'], realm)
|
||||
if old_mapper is None:
|
||||
old_mapper = {}
|
||||
old_mapper = dict()
|
||||
else:
|
||||
found = kc.get_components(urlencode(dict(parent=cid, name=change['name'])), realm)
|
||||
if len(found) > 1:
|
||||
@@ -880,7 +863,7 @@ def main():
|
||||
if len(found) == 1:
|
||||
old_mapper = found[0]
|
||||
else:
|
||||
old_mapper = {}
|
||||
old_mapper = dict()
|
||||
new_mapper = old_mapper.copy()
|
||||
new_mapper.update(change)
|
||||
if new_mapper != old_mapper:
|
||||
@@ -888,37 +871,37 @@ def main():
|
||||
changeset['mappers'] = list()
|
||||
changeset['mappers'].append(new_mapper)
|
||||
|
||||
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
|
||||
desired_comp = before_comp.copy()
|
||||
desired_comp.update(changeset)
|
||||
# prepare the new representation
|
||||
updated_comp = before_comp.copy()
|
||||
updated_comp.update(changeset)
|
||||
|
||||
result['proposed'] = sanitize(changeset)
|
||||
result['existing'] = sanitize(before_comp)
|
||||
|
||||
# Cater for when it doesn't exist (an empty dict)
|
||||
if not before_comp:
|
||||
# if before_comp is none, the user federation doesn't exist.
|
||||
if before_comp == dict():
|
||||
if state == 'absent':
|
||||
# Do nothing and exit
|
||||
# nothing to do.
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after='')
|
||||
result['changed'] = False
|
||||
result['end_state'] = {}
|
||||
result['end_state'] = dict()
|
||||
result['msg'] = 'User federation does not exist; doing nothing.'
|
||||
module.exit_json(**result)
|
||||
|
||||
# Process a creation
|
||||
# for 'present', create a new user federation.
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before='', after=sanitize(desired_comp))
|
||||
result['diff'] = dict(before='', after=sanitize(updated_comp))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# create it
|
||||
desired_comp = desired_comp.copy()
|
||||
updated_mappers = desired_comp.pop('mappers', [])
|
||||
after_comp = kc.create_component(desired_comp, realm)
|
||||
# do it for real!
|
||||
updated_comp = updated_comp.copy()
|
||||
updated_mappers = updated_comp.pop('mappers', [])
|
||||
after_comp = kc.create_component(updated_comp, realm)
|
||||
|
||||
for mapper in updated_mappers:
|
||||
if mapper.get('id') is not None:
|
||||
@@ -936,28 +919,26 @@ def main():
|
||||
|
||||
else:
|
||||
if state == 'present':
|
||||
# Process an update
|
||||
|
||||
# no changes
|
||||
if desired_comp == before_comp:
|
||||
if updated_comp == before_comp:
|
||||
result['changed'] = False
|
||||
result['end_state'] = sanitize(desired_comp)
|
||||
result['end_state'] = sanitize(updated_comp)
|
||||
result['msg'] = "No changes required to user federation {id}.".format(id=cid)
|
||||
module.exit_json(**result)
|
||||
|
||||
# doing an update
|
||||
# update the existing role
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = dict(before=sanitize(before_comp), after=sanitize(desired_comp))
|
||||
result['diff'] = dict(before=sanitize(before_comp), after=sanitize(updated_comp))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# do the update
|
||||
desired_comp = desired_comp.copy()
|
||||
updated_mappers = desired_comp.pop('mappers', [])
|
||||
kc.update_component(desired_comp, realm)
|
||||
updated_comp = updated_comp.copy()
|
||||
updated_mappers = updated_comp.pop('mappers', [])
|
||||
kc.update_component(updated_comp, realm)
|
||||
after_comp = kc.get_component(cid, realm)
|
||||
|
||||
for mapper in updated_mappers:
|
||||
@@ -965,7 +946,7 @@ def main():
|
||||
kc.update_component(mapper, realm)
|
||||
else:
|
||||
if mapper.get('parentId') is None:
|
||||
mapper['parentId'] = desired_comp['id']
|
||||
mapper['parentId'] = updated_comp['id']
|
||||
mapper = kc.create_component(mapper, realm)
|
||||
|
||||
after_comp['mappers'] = updated_mappers
|
||||
@@ -975,7 +956,6 @@ def main():
|
||||
module.exit_json(**result)
|
||||
|
||||
elif state == 'absent':
|
||||
# Process a deletion
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
@@ -984,12 +964,13 @@ def main():
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
# delete it
|
||||
# delete for real
|
||||
kc.delete_component(cid, realm)
|
||||
|
||||
result['end_state'] = {}
|
||||
result['end_state'] = dict()
|
||||
|
||||
result['msg'] = "User federation {id} has been deleted".format(id=cid)
|
||||
module.exit_json(**result)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
remote_management/redfish/ilo_redfish_config.py
|
||||
@@ -1 +0,0 @@
|
||||
remote_management/redfish/ilo_redfish_info.py
|
||||
@@ -63,7 +63,7 @@ def query_log_status(module, le_path, path, state="present"):
|
||||
""" Returns whether a log is followed or not. """
|
||||
|
||||
if state == "present":
|
||||
rc, out, err = module.run_command([le_path, "followed", path])
|
||||
rc, out, err = module.run_command("%s followed %s" % (le_path, path))
|
||||
if rc == 0:
|
||||
return True
|
||||
|
||||
@@ -87,7 +87,7 @@ def follow_log(module, le_path, logs, name=None, logtype=None):
|
||||
cmd.extend(['--name', name])
|
||||
if logtype:
|
||||
cmd.extend(['--type', logtype])
|
||||
rc, out, err = module.run_command(cmd)
|
||||
rc, out, err = module.run_command(' '.join(cmd))
|
||||
|
||||
if not query_log_status(module, le_path, log):
|
||||
module.fail_json(msg="failed to follow '%s': %s" % (log, err.strip()))
|
||||
|
||||
@@ -82,7 +82,7 @@ PACKAGE_STATE_MAP = dict(
|
||||
|
||||
def is_plugin_present(module, plugin_bin, plugin_name):
|
||||
cmd_args = [plugin_bin, "list", plugin_name]
|
||||
rc, out, err = module.run_command(cmd_args)
|
||||
rc, out, err = module.run_command(" ".join(cmd_args))
|
||||
return rc == 0
|
||||
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ class Monit(object):
|
||||
return self._monit_version
|
||||
|
||||
def _get_monit_version(self):
|
||||
rc, out, err = self.module.run_command([self.monit_bin_path, '-V'], check_rc=True)
|
||||
rc, out, err = self.module.run_command('%s -V' % self.monit_bin_path, check_rc=True)
|
||||
version_line = out.split('\n')[0]
|
||||
raw_version = re.search(r"([0-9]+\.){1,2}([0-9]+)?", version_line).group()
|
||||
return raw_version, tuple(map(int, raw_version.split('.')))
|
||||
@@ -140,7 +140,7 @@ class Monit(object):
|
||||
|
||||
@property
|
||||
def command_args(self):
|
||||
return ["-B"] if self.monit_version() > (5, 18) else []
|
||||
return "-B" if self.monit_version() > (5, 18) else ""
|
||||
|
||||
def get_status(self, validate=False):
|
||||
"""Return the status of the process in monit.
|
||||
@@ -149,7 +149,7 @@ class Monit(object):
|
||||
"""
|
||||
monit_command = "validate" if validate else "status"
|
||||
check_rc = False if validate else True # 'validate' always has rc = 1
|
||||
command = [self.monit_bin_path, monit_command] + self.command_args + [self.process_name]
|
||||
command = ' '.join([self.monit_bin_path, monit_command, self.command_args, self.process_name])
|
||||
rc, out, err = self.module.run_command(command, check_rc=check_rc)
|
||||
return self._parse_status(out, err)
|
||||
|
||||
@@ -182,8 +182,7 @@ class Monit(object):
|
||||
return status
|
||||
|
||||
def is_process_present(self):
|
||||
command = [self.monit_bin_path, 'summary'] + self.command_args
|
||||
rc, out, err = self.module.run_command(command, check_rc=True)
|
||||
rc, out, err = self.module.run_command('%s summary %s' % (self.monit_bin_path, self.command_args), check_rc=True)
|
||||
return bool(re.findall(r'\b%s\b' % self.process_name, out))
|
||||
|
||||
def is_process_running(self):
|
||||
@@ -191,7 +190,7 @@ class Monit(object):
|
||||
|
||||
def run_command(self, command):
|
||||
"""Runs a monit command, and returns the new status."""
|
||||
return self.module.run_command([self.monit_bin_path, command, self.process_name], check_rc=True)
|
||||
return self.module.run_command('%s %s %s' % (self.monit_bin_path, command, self.process_name), check_rc=True)
|
||||
|
||||
def wait_for_status_change(self, current_status):
|
||||
running_status = self.get_status()
|
||||
@@ -229,7 +228,7 @@ class Monit(object):
|
||||
return current_status
|
||||
|
||||
def reload(self):
|
||||
rc, out, err = self.module.run_command([self.monit_bin_path, 'reload'])
|
||||
rc, out, err = self.module.run_command('%s reload' % self.monit_bin_path)
|
||||
if rc != 0:
|
||||
self.exit_fail('monit reload failed', stdout=out, stderr=err)
|
||||
self.exit_success(state='reloaded')
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
./database/mssql/mssql_script.py
|
||||
@@ -82,8 +82,6 @@ options:
|
||||
version_added: 3.5.0
|
||||
requirements:
|
||||
- "dnsimple >= 1.0.0"
|
||||
notes:
|
||||
- "Support for C(dnsimple < 2) is deprecated and will be removed in community.general 5.0.0."
|
||||
author: "Alex Coomans (@drcapulet)"
|
||||
'''
|
||||
|
||||
@@ -397,11 +395,6 @@ def main():
|
||||
if DNSIMPLE_MAJOR_VERSION > 1:
|
||||
ds = DNSimpleV2(account_email, account_api_token, sandbox, module)
|
||||
else:
|
||||
module.deprecate(
|
||||
'Support for python-dnsimple < 2 is deprecated. '
|
||||
'Update python-dnsimple to version >= 2.0.0',
|
||||
version='5.0.0', collection_name='community.general'
|
||||
)
|
||||
ds = DNSimpleV1(account_email, account_api_token, sandbox, module)
|
||||
# Let's figure out what operation we want to do
|
||||
# No domain, return a list
|
||||
|
||||
@@ -1,335 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: Edward Hilgendorf, <edward@hilgendorf.me>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: dnsimple_info
|
||||
|
||||
short_description: Pull basic info from DNSimple API
|
||||
|
||||
version_added: "4.2.0"
|
||||
|
||||
description: Retrieve existing records and domains from DNSimple API.
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The domain name to retrieve info from.
|
||||
- Will return all associated records for this domain if specified.
|
||||
- If not specified, will return all domains associated with the account ID.
|
||||
type: str
|
||||
|
||||
account_id:
|
||||
description: The account ID to query.
|
||||
required: true
|
||||
type: str
|
||||
|
||||
api_key:
|
||||
description: The API key to use.
|
||||
required: true
|
||||
type: str
|
||||
|
||||
record:
|
||||
description:
|
||||
- The record to find.
|
||||
- If specified, only this record will be returned instead of all records.
|
||||
required: false
|
||||
type: str
|
||||
|
||||
sandbox:
|
||||
description: Whether or not to use sandbox environment.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
|
||||
author:
|
||||
- Edward Hilgendorf (@edhilgendorf)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Get all domains from an account
|
||||
community.general.dnsimple_info:
|
||||
account_id: "1234"
|
||||
api_key: "1234"
|
||||
|
||||
- name: Get all records from a domain
|
||||
community.general.dnsimple_info:
|
||||
name: "example.com"
|
||||
account_id: "1234"
|
||||
api_key: "1234"
|
||||
|
||||
- name: Get all info from a matching record
|
||||
community.general.dnsimple_info:
|
||||
name: "example.com"
|
||||
record: "subdomain"
|
||||
account_id: "1234"
|
||||
api_key: "1234"
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
dnsimple_domain_info:
|
||||
description: Returns a list of dictionaries of all domains associated with the supplied account ID.
|
||||
type: list
|
||||
elements: dict
|
||||
returned: success when I(name) is not specified
|
||||
sample:
|
||||
- account_id: 1234
|
||||
created_at: '2021-10-16T21:25:42Z'
|
||||
id: 123456
|
||||
last_transferred_at:
|
||||
name: example.com
|
||||
reverse: false
|
||||
secondary: false
|
||||
updated_at: '2021-11-10T20:22:50Z'
|
||||
contains:
|
||||
account_id:
|
||||
description: The account ID.
|
||||
type: int
|
||||
created_at:
|
||||
description: When the domain entry was created.
|
||||
type: str
|
||||
id:
|
||||
description: ID of the entry.
|
||||
type: int
|
||||
last_transferred_at:
|
||||
description: Date the domain was transferred, or empty if not.
|
||||
type: str
|
||||
name:
|
||||
description: Name of the record.
|
||||
type: str
|
||||
reverse:
|
||||
description: Whether or not it is a reverse zone record.
|
||||
type: bool
|
||||
updated_at:
|
||||
description: When the domain entry was updated.
|
||||
type: str
|
||||
|
||||
dnsimple_records_info:
|
||||
description: Returns a list of dictionaries with all records for the domain supplied.
|
||||
type: list
|
||||
elements: dict
|
||||
returned: success when I(name) is specified, but I(record) is not
|
||||
sample:
|
||||
- content: ns1.dnsimple.com admin.dnsimple.com
|
||||
created_at: '2021-10-16T19:07:34Z'
|
||||
id: 12345
|
||||
name: 'catheadbiscuit'
|
||||
parent_id: null
|
||||
priority: null
|
||||
regions:
|
||||
- global
|
||||
system_record: true
|
||||
ttl: 3600
|
||||
type: SOA
|
||||
updated_at: '2021-11-15T23:55:51Z'
|
||||
zone_id: example.com
|
||||
contains:
|
||||
content:
|
||||
description: Content of the returned record.
|
||||
type: str
|
||||
created_at:
|
||||
description: When the domain entry was created.
|
||||
type: str
|
||||
id:
|
||||
description: ID of the entry.
|
||||
type: int
|
||||
name:
|
||||
description: Name of the record.
|
||||
type: str
|
||||
parent_id:
|
||||
description: Parent record or null.
|
||||
type: int
|
||||
priority:
|
||||
description: Priority setting of the record.
|
||||
type: str
|
||||
regions:
|
||||
description: List of regions where the record is available.
|
||||
type: list
|
||||
system_record:
|
||||
description: Whether or not it is a system record.
|
||||
type: bool
|
||||
ttl:
|
||||
description: Record TTL.
|
||||
type: int
|
||||
type:
|
||||
description: Record type.
|
||||
type: str
|
||||
updated_at:
|
||||
description: When the domain entry was updated.
|
||||
type: str
|
||||
zone_id:
|
||||
description: ID of the zone that the record is associated with.
|
||||
type: str
|
||||
dnsimple_record_info:
|
||||
description: Returns a list of dictionaries that match the record supplied.
|
||||
returned: success when I(name) and I(record) are specified
|
||||
type: list
|
||||
elements: dict
|
||||
sample:
|
||||
- content: 1.2.3.4
|
||||
created_at: '2021-11-15T23:55:51Z'
|
||||
id: 123456
|
||||
name: catheadbiscuit
|
||||
parent_id: null
|
||||
priority: null
|
||||
regions:
|
||||
- global
|
||||
system_record: false
|
||||
ttl: 3600
|
||||
type: A
|
||||
updated_at: '2021-11-15T23:55:51Z'
|
||||
zone_id: example.com
|
||||
contains:
|
||||
content:
|
||||
description: Content of the returned record.
|
||||
type: str
|
||||
created_at:
|
||||
description: When the domain entry was created.
|
||||
type: str
|
||||
id:
|
||||
description: ID of the entry.
|
||||
type: int
|
||||
name:
|
||||
description: Name of the record.
|
||||
type: str
|
||||
parent_id:
|
||||
description: Parent record or null.
|
||||
type: int
|
||||
priority:
|
||||
description: Priority setting of the record.
|
||||
type: str
|
||||
regions:
|
||||
description: List of regions where the record is available.
|
||||
type: list
|
||||
system_record:
|
||||
description: Whether or not it is a system record.
|
||||
type: bool
|
||||
ttl:
|
||||
description: Record TTL.
|
||||
type: int
|
||||
type:
|
||||
description: Record type.
|
||||
type: str
|
||||
updated_at:
|
||||
description: When the domain entry was updated.
|
||||
type: str
|
||||
zone_id:
|
||||
description: ID of the zone that the record is associated with.
|
||||
type: str
|
||||
'''
|
||||
|
||||
import traceback
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
import json
|
||||
|
||||
try:
|
||||
from requests import Request, Session
|
||||
except ImportError:
|
||||
HAS_ANOTHER_LIBRARY = False
|
||||
ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_ANOTHER_LIBRARY = True
|
||||
|
||||
|
||||
def build_url(account, key, is_sandbox):
|
||||
headers = {'Accept': 'application/json',
|
||||
'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
|
||||
|
||||
|
||||
def iterate_data(module, request_object):
|
||||
base_url = request_object.url
|
||||
response = Session().send(request_object)
|
||||
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')
|
||||
|
||||
|
||||
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'
|
||||
return iterate_data(dnsimple_mod, req_obj)
|
||||
|
||||
|
||||
def domain_info(dnsimple_mod, req_obj):
|
||||
req_obj.url, req_obj.method = req_obj.url + '/zones/' + dnsimple_mod.params["name"] + '/records?per_page=100', 'GET'
|
||||
return iterate_data(dnsimple_mod, req_obj)
|
||||
|
||||
|
||||
def account_info(dnsimple_mod, req_obj):
|
||||
req_obj.url, req_obj.method = req_obj.url + '/zones/?per_page=100', 'GET'
|
||||
return iterate_data(dnsimple_mod, req_obj)
|
||||
|
||||
|
||||
def main():
|
||||
# define available arguments/parameters a user can pass to the module
|
||||
fields = {
|
||||
"account_id": {"required": True, "type": "str"},
|
||||
"api_key": {"required": True, "type": "str", "no_log": True},
|
||||
"name": {"required": False, "type": "str"},
|
||||
"record": {"required": False, "type": "str"},
|
||||
"sandbox": {"required": False, "type": "bool", "default": False}
|
||||
}
|
||||
|
||||
result = {
|
||||
'changed': False
|
||||
}
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=fields,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
params = module.params
|
||||
req = build_url(params['account_id'],
|
||||
params['api_key'],
|
||||
params['sandbox'])
|
||||
|
||||
if not HAS_ANOTHER_LIBRARY:
|
||||
# Needs: from ansible.module_utils.basic import missing_required_lib
|
||||
module.exit_json(
|
||||
msg=missing_required_lib('another_library'),
|
||||
exception=ANOTHER_LIBRARY_IMPORT_ERROR)
|
||||
|
||||
# At minimum we need account and key
|
||||
if params['account_id'] and params['api_key']:
|
||||
# If we have a record return info on that record
|
||||
if params['name'] and params['record']:
|
||||
result['dnsimple_record_info'] = record_info(module, req)
|
||||
module.exit_json(**result)
|
||||
|
||||
# If we have the account only and domain, return records for the domain
|
||||
elif params['name']:
|
||||
result['dnsimple_records_info'] = domain_info(module, req)
|
||||
module.exit_json(**result)
|
||||
|
||||
# If we have the account only, return domains
|
||||
else:
|
||||
result['dnsimple_domain_info'] = account_info(module, req)
|
||||
module.exit_json(**result)
|
||||
else:
|
||||
module.fail_json(msg="Need at least account_id and api_key")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -76,7 +76,7 @@ class Namespace(object):
|
||||
|
||||
def exists(self):
|
||||
'''Check if the namespace already exists'''
|
||||
rc, out, err = self.module.run_command(['ip', 'netns', 'list'])
|
||||
rc, out, err = self.module.run_command('ip netns list')
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg=to_text(err))
|
||||
return self.name in out
|
||||
|
||||
179
plugins/modules/net_tools/nios/nios_a_record.py
Normal file
179
plugins/modules/net_tools/nios/nios_a_record.py
Normal file
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_a_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS A records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_a_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of A record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:a) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this A record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
ipv4addr:
|
||||
description:
|
||||
- Configures the IPv4 address for this A record. Users can dynamically
|
||||
allocate ipv4 address to A record by passing dictionary containing,
|
||||
I(nios_next_ip) and I(CIDR network range). See example
|
||||
aliases:
|
||||
- ipv4
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this A record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure an A record
|
||||
community.general.nios_a_record:
|
||||
name: a.ansible.com
|
||||
ipv4: 192.168.10.1
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing A record
|
||||
community.general.nios_a_record:
|
||||
name: a.ansible.com
|
||||
ipv4: 192.168.10.1
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove an A record from the system
|
||||
community.general.nios_a_record:
|
||||
name: a.ansible.com
|
||||
ipv4: 192.168.10.1
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Update an A record name
|
||||
community.general.nios_a_record:
|
||||
name: {new_name: a_new.ansible.com, old_name: a.ansible.com}
|
||||
ipv4: 192.168.10.1
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Dynamically add a record to next available ip
|
||||
community.general.nios_a_record:
|
||||
name: a.ansible.com
|
||||
ipv4: {nios_next_ip: 192.168.10.0/24}
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_A_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
ipv4addr=dict(aliases=['ipv4'], ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_A_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
166
plugins/modules/net_tools/nios/nios_aaaa_record.py
Normal file
166
plugins/modules/net_tools/nios/nios_aaaa_record.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_aaaa_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS AAAA records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_aaaa_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of AAAA record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:aaaa) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this AAAA record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
ipv6addr:
|
||||
description:
|
||||
- Configures the IPv6 address for this AAAA record.
|
||||
aliases:
|
||||
- ipv6
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this AAAA record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure an AAAA record
|
||||
community.general.nios_aaaa_record:
|
||||
name: aaaa.ansible.com
|
||||
ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing AAAA record
|
||||
community.general.nios_aaaa_record:
|
||||
name: aaaa.ansible.com
|
||||
ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove an AAAA record from the system
|
||||
community.general.nios_aaaa_record:
|
||||
name: aaaa.ansible.com
|
||||
ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Update an AAAA record name
|
||||
community.general.nios_aaaa_record:
|
||||
name: {new_name: aaaa_new.ansible.com, old_name: aaaa.ansible.com}
|
||||
ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_AAAA_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
ipv6addr=dict(aliases=['ipv6'], ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_AAAA_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
155
plugins/modules/net_tools/nios/nios_cname_record.py
Normal file
155
plugins/modules/net_tools/nios/nios_cname_record.py
Normal file
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_cname_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS CNAME records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_cname_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of CNAME record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:cname) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this CNAME record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
canonical:
|
||||
description:
|
||||
- Configures the canonical name for this CNAME record.
|
||||
aliases:
|
||||
- cname
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this CNAME record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a CNAME record
|
||||
community.general.nios_cname_record:
|
||||
name: cname.ansible.com
|
||||
canonical: realhost.ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing CNAME record
|
||||
community.general.nios_cname_record:
|
||||
name: cname.ansible.com
|
||||
canonical: realhost.ansible.com
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove a CNAME record from the system
|
||||
community.general.nios_cname_record:
|
||||
name: cname.ansible.com
|
||||
canonical: realhost.ansible.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_CNAME_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
canonical=dict(aliases=['cname'], ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_CNAME_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
150
plugins/modules/net_tools/nios/nios_dns_view.py
Normal file
150
plugins/modules/net_tools/nios/nios_dns_view.py
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_dns_view
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configure Infoblox NIOS DNS views
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_dns_view
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of DNS view objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(view) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
- Updates instances of DNS view object from Infoblox NIOS servers.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system. User can also update the hostname as it is possible
|
||||
to pass a dict containing I(new_name), I(old_name). See examples.
|
||||
required: true
|
||||
aliases:
|
||||
- view
|
||||
type: str
|
||||
network_view:
|
||||
description:
|
||||
- Specifies the name of the network view to assign the configured
|
||||
DNS view to. The network view must already be configured on the
|
||||
target system.
|
||||
default: default
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
required: false
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
required: false
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
required: false
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a new dns view instance
|
||||
community.general.nios_dns_view:
|
||||
name: ansible-dns
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update the comment for dns view
|
||||
community.general.nios_dns_view:
|
||||
name: ansible-dns
|
||||
comment: this is an example comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove the dns view instance
|
||||
community.general.nios_dns_view:
|
||||
name: ansible-dns
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update the dns view instance
|
||||
community.general.nios_dns_view:
|
||||
name: {new_name: ansible-dns-new, old_name: ansible-dns}
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_DNS_VIEW
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, aliases=['view'], ib_req=True),
|
||||
network_view=dict(default='default', ib_req=True),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict()
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_DNS_VIEW, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
301
plugins/modules/net_tools/nios/nios_fixed_address.py
Normal file
301
plugins/modules/net_tools/nios/nios_fixed_address.py
Normal file
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_fixed_address
|
||||
author: "Sumit Jaiswal (@sjaiswal)"
|
||||
short_description: Configure Infoblox NIOS DHCP Fixed Address
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_fixed_address
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- A fixed address is a specific IP address that a DHCP server
|
||||
always assigns when a lease request comes from a particular
|
||||
MAC address of the client.
|
||||
- Supports both IPV4 and IPV6 internet protocols
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the hostname with which fixed DHCP ip-address is stored
|
||||
for respective mac.
|
||||
required: true
|
||||
type: str
|
||||
ipaddr:
|
||||
description:
|
||||
- IPV4/V6 address of the fixed address.
|
||||
required: true
|
||||
type: str
|
||||
mac:
|
||||
description:
|
||||
- The MAC address of the interface.
|
||||
required: true
|
||||
type: str
|
||||
network:
|
||||
description:
|
||||
- Specifies the network range in which ipaddr exists.
|
||||
required: true
|
||||
type: str
|
||||
network_view:
|
||||
description:
|
||||
- Configures the name of the network view to associate with this
|
||||
configured instance.
|
||||
required: false
|
||||
default: default
|
||||
type: str
|
||||
options:
|
||||
description:
|
||||
- Configures the set of DHCP options to be included as part of
|
||||
the configured network instance. This argument accepts a list
|
||||
of values (see suboptions). When configuring suboptions at
|
||||
least one of C(name) or C(num) must be specified.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- The name of the DHCP option to configure
|
||||
type: str
|
||||
num:
|
||||
description:
|
||||
- The number of the DHCP option to configure
|
||||
type: int
|
||||
value:
|
||||
description:
|
||||
- The value of the DHCP option specified by C(name)
|
||||
required: true
|
||||
type: str
|
||||
use_option:
|
||||
description:
|
||||
- Only applies to a subset of options (see NIOS API documentation)
|
||||
type: bool
|
||||
default: 'yes'
|
||||
vendor_class:
|
||||
description:
|
||||
- The name of the space this DHCP option is associated to
|
||||
default: DHCP
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure ipv4 dhcp fixed address
|
||||
community.general.nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a ipv6 dhcp fixed address
|
||||
community.general.nios_fixed_address:
|
||||
name: ipv6_fixed
|
||||
ipaddr: fe80::1/10
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: fe80::/64
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Set dhcp options for a ipv4 fixed address
|
||||
community.general.nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
options:
|
||||
- name: domain-name
|
||||
value: ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove a ipv4 dhcp fixed address
|
||||
community.general.nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
import socket
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_IPV4_FIXED_ADDRESS, NIOS_IPV6_FIXED_ADDRESS
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def validate_ip_address(address):
|
||||
try:
|
||||
socket.inet_aton(address)
|
||||
except socket.error:
|
||||
return False
|
||||
return address.count(".") == 3
|
||||
|
||||
|
||||
def validate_ip_v6_address(address):
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, address)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def options(module):
|
||||
''' Transforms the module argument into a valid WAPI struct
|
||||
This function will transform the options argument into a structure that
|
||||
is a valid WAPI structure in the format of:
|
||||
{
|
||||
name: <value>,
|
||||
num: <value>,
|
||||
value: <value>,
|
||||
use_option: <value>,
|
||||
vendor_class: <value>
|
||||
}
|
||||
It will remove any options that are set to None since WAPI will error on
|
||||
that condition. The use_option field only applies
|
||||
to special options that are displayed separately from other options and
|
||||
have a use flag. This function removes the use_option flag from all
|
||||
other options. It will also verify that either `name` or `num` is
|
||||
set in the structure but does not validate the values are equal.
|
||||
The remainder of the value validation is performed by WAPI
|
||||
'''
|
||||
special_options = ['routers', 'router-templates', 'domain-name-servers',
|
||||
'domain-name', 'broadcast-address', 'broadcast-address-offset',
|
||||
'dhcp-lease-time', 'dhcp6.name-servers']
|
||||
options = list()
|
||||
for item in module.params['options']:
|
||||
opt = dict([(k, v) for k, v in iteritems(item) if v is not None])
|
||||
if 'name' not in opt and 'num' not in opt:
|
||||
module.fail_json(msg='one of `name` or `num` is required for option value')
|
||||
if opt['name'] not in special_options:
|
||||
del opt['use_option']
|
||||
options.append(opt)
|
||||
return options
|
||||
|
||||
|
||||
def validate_ip_addr_type(ip, arg_spec, module):
|
||||
'''This function will check if the argument ip is type v4/v6 and return appropriate infoblox network type
|
||||
'''
|
||||
check_ip = ip.split('/')
|
||||
|
||||
if validate_ip_address(check_ip[0]) and 'ipaddr' in arg_spec:
|
||||
arg_spec['ipv4addr'] = arg_spec.pop('ipaddr')
|
||||
module.params['ipv4addr'] = module.params.pop('ipaddr')
|
||||
return NIOS_IPV4_FIXED_ADDRESS, arg_spec, module
|
||||
elif validate_ip_v6_address(check_ip[0]) and 'ipaddr' in arg_spec:
|
||||
arg_spec['ipv6addr'] = arg_spec.pop('ipaddr')
|
||||
module.params['ipv6addr'] = module.params.pop('ipaddr')
|
||||
return NIOS_IPV6_FIXED_ADDRESS, arg_spec, module
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
option_spec = dict(
|
||||
# one of name or num is required; enforced by the function options()
|
||||
name=dict(),
|
||||
num=dict(type='int'),
|
||||
|
||||
value=dict(required=True),
|
||||
|
||||
use_option=dict(type='bool', default=True),
|
||||
vendor_class=dict(default='DHCP')
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True),
|
||||
ipaddr=dict(required=True, ib_req=True),
|
||||
mac=dict(required=True, ib_req=True),
|
||||
network=dict(required=True),
|
||||
network_view=dict(default='default'),
|
||||
|
||||
options=dict(type='list', elements='dict', options=option_spec, transform=options),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict()
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
# to get the argument ipaddr
|
||||
obj_filter = dict([(k, module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
|
||||
# to modify argument based on ipaddr type i.e. IPV4/IPV6
|
||||
fixed_address_ip_type, ib_spec, module = validate_ip_addr_type(obj_filter['ipaddr'], ib_spec, module)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
|
||||
result = wapi.run(fixed_address_ip_type, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
361
plugins/modules/net_tools/nios/nios_host_record.py
Normal file
361
plugins/modules/net_tools/nios/nios_host_record.py
Normal file
@@ -0,0 +1,361 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_host_record
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configure Infoblox NIOS host records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_host_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of host record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:host) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
- Updates instances of host record object from Infoblox NIOS servers.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system. User can also update the hostname as it is possible
|
||||
to pass a dict containing I(new_name), I(old_name). See examples.
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this host record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
configure_for_dns:
|
||||
description:
|
||||
- Sets the DNS to particular parent. If user needs to bypass DNS
|
||||
user can make the value to false.
|
||||
type: bool
|
||||
required: false
|
||||
default: true
|
||||
aliases:
|
||||
- dns
|
||||
ipv4addrs:
|
||||
description:
|
||||
- Configures the IPv4 addresses for this host record. This argument
|
||||
accepts a list of values (see suboptions)
|
||||
aliases:
|
||||
- ipv4
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
ipv4addr:
|
||||
description:
|
||||
- Configures the IPv4 address for the host record. Users can dynamically
|
||||
allocate ipv4 address to host record by passing dictionary containing,
|
||||
I(nios_next_ip) and I(CIDR network range). If user wants to add or
|
||||
remove the ipv4 address from existing record, I(add/remove)
|
||||
params need to be used. See examples
|
||||
required: true
|
||||
aliases:
|
||||
- address
|
||||
type: str
|
||||
configure_for_dhcp:
|
||||
description:
|
||||
- Configure the host_record over DHCP instead of DNS, if user
|
||||
changes it to true, user need to mention MAC address to configure
|
||||
required: false
|
||||
aliases:
|
||||
- dhcp
|
||||
type: bool
|
||||
mac:
|
||||
description:
|
||||
- Configures the hardware MAC address for the host record. If user makes
|
||||
DHCP to true, user need to mention MAC address.
|
||||
required: false
|
||||
type: str
|
||||
add:
|
||||
description:
|
||||
- If user wants to add the ipv4 address to an existing host record.
|
||||
Note that with I(add) user will have to keep the I(state) as I(present),
|
||||
as new IP address is allocated to existing host record. See examples.
|
||||
type: bool
|
||||
required: false
|
||||
version_added: '0.2.0'
|
||||
remove:
|
||||
description:
|
||||
- If user wants to remove the ipv4 address from an existing host record.
|
||||
Note that with I(remove) user will have to change the I(state) to I(absent),
|
||||
as IP address is de-allocated from an existing host record. See examples.
|
||||
type: bool
|
||||
required: false
|
||||
version_added: '0.2.0'
|
||||
ipv6addrs:
|
||||
description:
|
||||
- Configures the IPv6 addresses for the host record. This argument
|
||||
accepts a list of values (see options)
|
||||
aliases:
|
||||
- ipv6
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
ipv6addr:
|
||||
description:
|
||||
- Configures the IPv6 address for the host record
|
||||
required: true
|
||||
aliases:
|
||||
- address
|
||||
type: str
|
||||
configure_for_dhcp:
|
||||
description:
|
||||
- Configure the host_record over DHCP instead of DNS, if user
|
||||
changes it to true, user need to mention MAC address to configure
|
||||
required: false
|
||||
type: bool
|
||||
mac:
|
||||
description:
|
||||
- Configures the hardware MAC address for the host record. If user makes
|
||||
DHCP to true, user need to mention MAC address.
|
||||
required: false
|
||||
type: str
|
||||
aliases:
|
||||
description:
|
||||
- Configures an optional list of additional aliases to add to the host
|
||||
record. These are equivalent to CNAMEs but held within a host
|
||||
record. Must be in list format.
|
||||
type: list
|
||||
elements: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this host record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure an ipv4 host record
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
aliases:
|
||||
- cname.ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Add a comment to an existing host record
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove a host record from the system
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update an ipv4 host record
|
||||
community.general.nios_host_record:
|
||||
name: {new_name: host-new.ansible.com, old_name: host.ansible.com}
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Create an ipv4 host record bypassing DNS
|
||||
community.general.nios_host_record:
|
||||
name: new_host
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
dns: false
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Create an ipv4 host record over DHCP
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
dhcp: true
|
||||
mac: 00-80-C8-E3-4C-BD
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Dynamically add host record to next available ip
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: {nios_next_ip: 192.168.10.0/24}
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Add ip to host record
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: 192.168.10.2
|
||||
add: true
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove ip to host record
|
||||
community.general.nios_host_record:
|
||||
name: host.ansible.com
|
||||
ipv4:
|
||||
- address: 192.168.10.1
|
||||
remove: true
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_HOST_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def ipaddr(module, key, filtered_keys=None):
|
||||
''' Transforms the input value into a struct supported by WAPI
|
||||
This function will transform the input from the playbook into a struct
|
||||
that is valid for WAPI in the form of:
|
||||
{
|
||||
ipv4addr: <value>,
|
||||
mac: <value>
|
||||
}
|
||||
This function does not validate the values are properly formatted or in
|
||||
the acceptable range, that is left to WAPI.
|
||||
'''
|
||||
filtered_keys = filtered_keys or list()
|
||||
objects = list()
|
||||
for item in module.params[key]:
|
||||
objects.append(dict([(k, v) for k, v in iteritems(item) if v is not None and k not in filtered_keys]))
|
||||
return objects
|
||||
|
||||
|
||||
def ipv4addrs(module):
|
||||
return ipaddr(module, 'ipv4addrs', filtered_keys=['address', 'dhcp'])
|
||||
|
||||
|
||||
def ipv6addrs(module):
|
||||
return ipaddr(module, 'ipv6addrs', filtered_keys=['address', 'dhcp'])
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
ipv4addr_spec = dict(
|
||||
ipv4addr=dict(required=True, aliases=['address']),
|
||||
configure_for_dhcp=dict(type='bool', required=False, aliases=['dhcp']),
|
||||
mac=dict(required=False),
|
||||
add=dict(type='bool', required=False),
|
||||
remove=dict(type='bool', required=False)
|
||||
)
|
||||
|
||||
ipv6addr_spec = dict(
|
||||
ipv6addr=dict(required=True, aliases=['address']),
|
||||
configure_for_dhcp=dict(type='bool', required=False),
|
||||
mac=dict(required=False)
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
ipv4addrs=dict(type='list', aliases=['ipv4'], elements='dict', options=ipv4addr_spec, transform=ipv4addrs),
|
||||
ipv6addrs=dict(type='list', aliases=['ipv6'], elements='dict', options=ipv6addr_spec, transform=ipv6addrs),
|
||||
configure_for_dns=dict(type='bool', default=True, required=False, aliases=['dns'], ib_req=True),
|
||||
aliases=dict(type='list', elements='str'),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_HOST_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
574
plugins/modules/net_tools/nios/nios_member.py
Normal file
574
plugins/modules/net_tools/nios/nios_member.py
Normal file
@@ -0,0 +1,574 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_member
|
||||
author: "Krishna Vasudevan (@krisvasudevan)"
|
||||
short_description: Configure Infoblox NIOS members
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_member
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes Infoblox NIOS servers. This module manages NIOS C(member) objects using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
host_name:
|
||||
description:
|
||||
- Specifies the host name of the member to either add or remove from
|
||||
the NIOS instance.
|
||||
required: true
|
||||
aliases:
|
||||
- name
|
||||
type: str
|
||||
vip_setting:
|
||||
description:
|
||||
- Configures the network settings for the grid member.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- The IPv4 Address of the Grid Member
|
||||
type: str
|
||||
subnet_mask:
|
||||
description:
|
||||
- The subnet mask for the Grid Member
|
||||
type: str
|
||||
gateway:
|
||||
description:
|
||||
- The default gateway for the Grid Member
|
||||
type: str
|
||||
ipv6_setting:
|
||||
description:
|
||||
- Configures the IPv6 settings for the grid member.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
virtual_ip:
|
||||
description:
|
||||
- The IPv6 Address of the Grid Member
|
||||
type: str
|
||||
cidr_prefix:
|
||||
description:
|
||||
- The IPv6 CIDR prefix for the Grid Member
|
||||
type: int
|
||||
gateway:
|
||||
description:
|
||||
- The gateway address for the Grid Member
|
||||
type: str
|
||||
config_addr_type:
|
||||
description:
|
||||
- Address configuration type (IPV4/IPV6/BOTH)
|
||||
default: IPV4
|
||||
type: str
|
||||
comment:
|
||||
description:
|
||||
- A descriptive comment of the Grid member.
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Extensible attributes associated with the object.
|
||||
type: dict
|
||||
enable_ha:
|
||||
description:
|
||||
- If set to True, the member has two physical nodes (HA pair).
|
||||
type: bool
|
||||
default: false
|
||||
router_id:
|
||||
description:
|
||||
- Virtual router identifier. Provide this ID if "ha_enabled" is set to "true". This is a unique VRID number (from 1 to 255) for the local subnet.
|
||||
type: int
|
||||
lan2_enabled:
|
||||
description:
|
||||
- When set to "true", the LAN2 port is enabled as an independent port or as a port for failover purposes.
|
||||
type: bool
|
||||
default: false
|
||||
lan2_port_setting:
|
||||
description:
|
||||
- Settings for the Grid member LAN2 port if 'lan2_enabled' is set to "true".
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
enabled:
|
||||
description:
|
||||
- If set to True, then it has its own IP settings.
|
||||
type: bool
|
||||
network_setting:
|
||||
description:
|
||||
- If the 'enable' field is set to True, this defines IPv4 network settings for LAN2.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- The IPv4 Address of LAN2
|
||||
type: str
|
||||
subnet_mask:
|
||||
description:
|
||||
- The subnet mask of LAN2
|
||||
type: str
|
||||
gateway:
|
||||
description:
|
||||
- The default gateway of LAN2
|
||||
type: str
|
||||
v6_network_setting:
|
||||
description:
|
||||
- If the 'enable' field is set to True, this defines IPv6 network settings for LAN2.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
virtual_ip:
|
||||
description:
|
||||
- The IPv6 Address of LAN2
|
||||
type: str
|
||||
cidr_prefix:
|
||||
description:
|
||||
- The IPv6 CIDR prefix of LAN2
|
||||
type: int
|
||||
gateway:
|
||||
description:
|
||||
- The gateway address of LAN2
|
||||
type: str
|
||||
platform:
|
||||
description:
|
||||
- Configures the Hardware Platform.
|
||||
default: INFOBLOX
|
||||
type: str
|
||||
node_info:
|
||||
description:
|
||||
- Configures the node information list with detailed status report on the operations of the Grid Member.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
lan2_physical_setting:
|
||||
description:
|
||||
- Physical port settings for the LAN2 interface.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
auto_port_setting_enabled:
|
||||
description:
|
||||
- Enable or disalbe the auto port setting.
|
||||
type: bool
|
||||
duplex:
|
||||
description:
|
||||
- The port duplex; if speed is 1000, duplex must be FULL.
|
||||
type: str
|
||||
speed:
|
||||
description:
|
||||
- The port speed; if speed is 1000, duplex is FULL.
|
||||
type: str
|
||||
lan_ha_port_setting:
|
||||
description:
|
||||
- LAN/HA port settings for the node.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
ha_ip_address:
|
||||
description:
|
||||
- HA IP address.
|
||||
type: str
|
||||
ha_port_setting:
|
||||
description:
|
||||
- Physical port settings for the HA interface.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
auto_port_setting_enabled:
|
||||
description:
|
||||
- Enable or disalbe the auto port setting.
|
||||
type: bool
|
||||
duplex:
|
||||
description:
|
||||
- The port duplex; if speed is 1000, duplex must be FULL.
|
||||
type: str
|
||||
speed:
|
||||
description:
|
||||
- The port speed; if speed is 1000, duplex is FULL.
|
||||
type: str
|
||||
lan_port_setting:
|
||||
description:
|
||||
- Physical port settings for the LAN interface.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
auto_port_setting_enabled:
|
||||
description:
|
||||
- Enable or disalbe the auto port setting.
|
||||
type: bool
|
||||
duplex:
|
||||
description:
|
||||
- The port duplex; if speed is 1000, duplex must be FULL.
|
||||
type: str
|
||||
speed:
|
||||
description:
|
||||
- The port speed; if speed is 1000, duplex is FULL.
|
||||
type: str
|
||||
mgmt_ipv6addr:
|
||||
description:
|
||||
- Public IPv6 address for the LAN1 interface.
|
||||
type: str
|
||||
mgmt_lan:
|
||||
description:
|
||||
- Public IPv4 address for the LAN1 interface.
|
||||
type: str
|
||||
mgmt_network_setting:
|
||||
description:
|
||||
- Network settings for the MGMT port of the node.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- The IPv4 Address of MGMT
|
||||
type: str
|
||||
subnet_mask:
|
||||
description:
|
||||
- The subnet mask of MGMT
|
||||
type: str
|
||||
gateway:
|
||||
description:
|
||||
- The default gateway of MGMT
|
||||
type: str
|
||||
v6_mgmt_network_setting:
|
||||
description:
|
||||
- The network settings for the IPv6 MGMT port of the node.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
virtual_ip:
|
||||
description:
|
||||
- The IPv6 Address of MGMT
|
||||
type: str
|
||||
cidr_prefix:
|
||||
description:
|
||||
- The IPv6 CIDR prefix of MGMT
|
||||
type: int
|
||||
gateway:
|
||||
description:
|
||||
- The gateway address of MGMT
|
||||
type: str
|
||||
mgmt_port_setting:
|
||||
description:
|
||||
- Settings for the member MGMT port.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
enabled:
|
||||
description:
|
||||
- Determines if MGMT port settings should be enabled.
|
||||
type: bool
|
||||
security_access_enabled:
|
||||
description:
|
||||
- Determines if security access on the MGMT port is enabled or not.
|
||||
type: bool
|
||||
vpn_enabled:
|
||||
description:
|
||||
- Determines if VPN on the MGMT port is enabled or not.
|
||||
type: bool
|
||||
upgrade_group:
|
||||
description:
|
||||
- The name of the upgrade group to which this Grid member belongs.
|
||||
default: Default
|
||||
type: str
|
||||
use_syslog_proxy_setting:
|
||||
description:
|
||||
- Use flag for external_syslog_server_enable , syslog_servers, syslog_proxy_setting, syslog_size
|
||||
type: bool
|
||||
external_syslog_server_enable:
|
||||
description:
|
||||
- Determines if external syslog servers should be enabled
|
||||
type: bool
|
||||
syslog_servers:
|
||||
description:
|
||||
- The list of external syslog servers.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- The server address.
|
||||
type: str
|
||||
category_list:
|
||||
description:
|
||||
- The list of all syslog logging categories.
|
||||
type: list
|
||||
elements: str
|
||||
connection_type:
|
||||
description:
|
||||
- The connection type for communicating with this server.(STCP/TCP?UDP)
|
||||
default: UDP
|
||||
type: str
|
||||
local_interface:
|
||||
description:
|
||||
- The local interface through which the appliance sends syslog messages to the syslog server.(ANY/LAN/MGMT)
|
||||
default: ANY
|
||||
type: str
|
||||
message_node_id:
|
||||
description:
|
||||
- Identify the node in the syslog message. (HOSTNAME/IP_HOSTNAME/LAN/MGMT)
|
||||
default: LAN
|
||||
type: str
|
||||
message_source:
|
||||
description:
|
||||
- The source of syslog messages to be sent to the external syslog server.
|
||||
default: ANY
|
||||
type: str
|
||||
only_category_list:
|
||||
description:
|
||||
- The list of selected syslog logging categories. The appliance forwards syslog messages that belong to the selected categories.
|
||||
type: bool
|
||||
port:
|
||||
description:
|
||||
- The port this server listens on.
|
||||
default: 514
|
||||
type: int
|
||||
severity:
|
||||
description:
|
||||
- The severity filter. The appliance sends log messages of the specified severity and above to the external syslog server.
|
||||
default: DEBUG
|
||||
type: str
|
||||
pre_provisioning:
|
||||
description:
|
||||
- Pre-provisioning information.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
hardware_info:
|
||||
description:
|
||||
- An array of structures that describe the hardware being pre-provisioned.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
hwmodel:
|
||||
description:
|
||||
- Hardware model
|
||||
type: str
|
||||
hwtype:
|
||||
description:
|
||||
- Hardware type.
|
||||
type: str
|
||||
licenses:
|
||||
description:
|
||||
- An array of license types.
|
||||
type: list
|
||||
elements: str
|
||||
create_token:
|
||||
description:
|
||||
- Flag for initiating a create token request for pre-provisioned members.
|
||||
type: bool
|
||||
default: False
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Add a member to the grid with IPv4 address
|
||||
community.general.nios_member:
|
||||
host_name: member01.localdomain
|
||||
vip_setting:
|
||||
- address: 192.168.1.100
|
||||
subnet_mask: 255.255.255.0
|
||||
gateway: 192.168.1.1
|
||||
config_addr_type: IPV4
|
||||
platform: VNIOS
|
||||
comment: "Created by Ansible"
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Add a HA member to the grid
|
||||
community.general.nios_member:
|
||||
host_name: memberha.localdomain
|
||||
vip_setting:
|
||||
- address: 192.168.1.100
|
||||
subnet_mask: 255.255.255.0
|
||||
gateway: 192.168.1.1
|
||||
config_addr_type: IPV4
|
||||
platform: VNIOS
|
||||
enable_ha: true
|
||||
router_id: 150
|
||||
node_info:
|
||||
- lan_ha_port_setting:
|
||||
- ha_ip_address: 192.168.1.70
|
||||
mgmt_lan: 192.168.1.80
|
||||
- lan_ha_port_setting:
|
||||
- ha_ip_address: 192.168.1.71
|
||||
mgmt_lan: 192.168.1.81
|
||||
comment: "Created by Ansible"
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update the member with pre-provisioning details specified
|
||||
community.general.nios_member:
|
||||
name: member01.localdomain
|
||||
pre_provisioning:
|
||||
- hardware_info:
|
||||
- hwmodel: IB-VM-820
|
||||
hwtype: IB-VNIOS
|
||||
licenses:
|
||||
- dns
|
||||
- dhcp
|
||||
- enterprise
|
||||
- vnios
|
||||
comment: "Updated by Ansible"
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove the member
|
||||
community.general.nios_member:
|
||||
name: member01.localdomain
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_MEMBER
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
ipv4_spec = dict(
|
||||
address=dict(),
|
||||
subnet_mask=dict(),
|
||||
gateway=dict(),
|
||||
)
|
||||
|
||||
ipv6_spec = dict(
|
||||
virtual_ip=dict(),
|
||||
cidr_prefix=dict(type='int'),
|
||||
gateway=dict(),
|
||||
)
|
||||
|
||||
port_spec = dict(
|
||||
auto_port_setting_enabled=dict(type='bool'),
|
||||
duplex=dict(),
|
||||
speed=dict(),
|
||||
)
|
||||
|
||||
lan2_port_spec = dict(
|
||||
enabled=dict(type='bool'),
|
||||
network_setting=dict(type='list', elements='dict', options=ipv4_spec),
|
||||
v6_network_setting=dict(type='list', elements='dict', options=ipv6_spec),
|
||||
)
|
||||
|
||||
ha_port_spec = dict(
|
||||
ha_ip_address=dict(),
|
||||
ha_port_setting=dict(type='list', elements='dict', options=port_spec),
|
||||
lan_port_setting=dict(type='list', elements='dict', options=port_spec),
|
||||
mgmt_lan=dict(),
|
||||
mgmt_ipv6addr=dict(),
|
||||
)
|
||||
|
||||
node_spec = dict(
|
||||
lan2_physical_setting=dict(type='list', elements='dict', options=port_spec),
|
||||
lan_ha_port_setting=dict(type='list', elements='dict', options=ha_port_spec),
|
||||
mgmt_network_setting=dict(type='list', elements='dict', options=ipv4_spec),
|
||||
v6_mgmt_network_setting=dict(type='list', elements='dict', options=ipv6_spec),
|
||||
)
|
||||
|
||||
mgmt_port_spec = dict(
|
||||
enabled=dict(type='bool'),
|
||||
security_access_enabled=dict(type='bool'),
|
||||
vpn_enabled=dict(type='bool'),
|
||||
)
|
||||
|
||||
syslog_spec = dict(
|
||||
address=dict(),
|
||||
category_list=dict(type='list', elements='str'),
|
||||
connection_type=dict(default='UDP'),
|
||||
local_interface=dict(default='ANY'),
|
||||
message_node_id=dict(default='LAN'),
|
||||
message_source=dict(default='ANY'),
|
||||
only_category_list=dict(type='bool'),
|
||||
port=dict(type='int', default=514),
|
||||
severity=dict(default='DEBUG'),
|
||||
)
|
||||
|
||||
hw_spec = dict(
|
||||
hwmodel=dict(),
|
||||
hwtype=dict(),
|
||||
)
|
||||
|
||||
pre_prov_spec = dict(
|
||||
hardware_info=dict(type='list', elements='dict', options=hw_spec),
|
||||
licenses=dict(type='list', elements='str'),
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
host_name=dict(required=True, aliases=['name'], ib_req=True),
|
||||
vip_setting=dict(type='list', elements='dict', options=ipv4_spec),
|
||||
ipv6_setting=dict(type='list', elements='dict', options=ipv6_spec),
|
||||
config_addr_type=dict(default='IPV4'),
|
||||
comment=dict(),
|
||||
enable_ha=dict(type='bool', default=False),
|
||||
router_id=dict(type='int'),
|
||||
lan2_enabled=dict(type='bool', default=False),
|
||||
lan2_port_setting=dict(type='list', elements='dict', options=lan2_port_spec),
|
||||
platform=dict(default='INFOBLOX'),
|
||||
node_info=dict(type='list', elements='dict', options=node_spec),
|
||||
mgmt_port_setting=dict(type='list', elements='dict', options=mgmt_port_spec),
|
||||
upgrade_group=dict(default='Default'),
|
||||
use_syslog_proxy_setting=dict(type='bool'),
|
||||
external_syslog_server_enable=dict(type='bool'),
|
||||
syslog_servers=dict(type='list', elements='dict', options=syslog_spec),
|
||||
pre_provisioning=dict(type='list', elements='dict', options=pre_prov_spec),
|
||||
extattrs=dict(type='dict'),
|
||||
create_token=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_MEMBER, ib_spec)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
163
plugins/modules/net_tools/nios/nios_mx_record.py
Normal file
163
plugins/modules/net_tools/nios/nios_mx_record.py
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_mx_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS MX records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_mx_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of MX record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:mx) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this a record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
mail_exchanger:
|
||||
description:
|
||||
- Configures the mail exchanger FQDN for this MX record.
|
||||
aliases:
|
||||
- mx
|
||||
type: str
|
||||
preference:
|
||||
description:
|
||||
- Configures the preference (0-65535) for this MX record.
|
||||
type: int
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this host record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure an MX record
|
||||
community.general.nios_mx_record:
|
||||
name: ansible.com
|
||||
mx: mailhost.ansible.com
|
||||
preference: 0
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing MX record
|
||||
community.general.nios_mx_record:
|
||||
name: ansible.com
|
||||
mx: mailhost.ansible.com
|
||||
preference: 0
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove an MX record from the system
|
||||
community.general.nios_mx_record:
|
||||
name: ansible.com
|
||||
mx: mailhost.ansible.com
|
||||
preference: 0
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_MX_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
mail_exchanger=dict(aliases=['mx'], ib_req=True),
|
||||
preference=dict(type='int', ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_MX_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
200
plugins/modules/net_tools/nios/nios_naptr_record.py
Normal file
200
plugins/modules/net_tools/nios/nios_naptr_record.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_naptr_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS NAPTR records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_naptr_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of NAPTR record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:naptr) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox_client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this a record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
order:
|
||||
description:
|
||||
- Configures the order (0-65535) for this NAPTR record. This parameter
|
||||
specifies the order in which the NAPTR rules are applied when
|
||||
multiple rules are present.
|
||||
type: int
|
||||
preference:
|
||||
description:
|
||||
- Configures the preference (0-65535) for this NAPTR record. The
|
||||
preference field determines the order NAPTR records are processed
|
||||
when multiple records with the same order parameter are present.
|
||||
type: int
|
||||
replacement:
|
||||
description:
|
||||
- Configures the replacement field for this NAPTR record.
|
||||
For nonterminal NAPTR records, this field specifies the
|
||||
next domain name to look up.
|
||||
type: str
|
||||
services:
|
||||
description:
|
||||
- Configures the services field (128 characters maximum) for this
|
||||
NAPTR record. The services field contains protocol and service
|
||||
identifiers, such as "http+E2U" or "SIPS+D2T".
|
||||
required: false
|
||||
type: str
|
||||
flags:
|
||||
description:
|
||||
- Configures the flags field for this NAPTR record. These control the
|
||||
interpretation of the fields for an NAPTR record object. Supported
|
||||
values for the flags field are "U", "S", "P" and "A".
|
||||
required: false
|
||||
type: str
|
||||
regexp:
|
||||
description:
|
||||
- Configures the regexp field for this NAPTR record. This is the
|
||||
regular expression-based rewriting rule of the NAPTR record. This
|
||||
should be a POSIX compliant regular expression, including the
|
||||
substitution rule and flags. Refer to RFC 2915 for the field syntax
|
||||
details.
|
||||
required: false
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this NAPTR record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a NAPTR record
|
||||
community.general.nios_naptr_record:
|
||||
name: '*.subscriber-100.ansiblezone.com'
|
||||
order: 1000
|
||||
preference: 10
|
||||
replacement: replacement1.network.ansiblezone.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing NAPTR record
|
||||
community.general.nios_naptr_record:
|
||||
name: '*.subscriber-100.ansiblezone.com'
|
||||
order: 1000
|
||||
preference: 10
|
||||
replacement: replacement1.network.ansiblezone.com
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove a NAPTR record from the system
|
||||
community.general.nios_naptr_record:
|
||||
name: '*.subscriber-100.ansiblezone.com'
|
||||
order: 1000
|
||||
preference: 10
|
||||
replacement: replacement1.network.ansiblezone.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
order=dict(type='int', ib_req=True),
|
||||
preference=dict(type='int', ib_req=True),
|
||||
replacement=dict(ib_req=True),
|
||||
services=dict(),
|
||||
flags=dict(),
|
||||
regexp=dict(),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run('record:naptr', ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
334
plugins/modules/net_tools/nios/nios_network.py
Normal file
334
plugins/modules/net_tools/nios/nios_network.py
Normal file
@@ -0,0 +1,334 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_network
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configure Infoblox NIOS network object
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_network
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of network objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(network) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
- Supports both IPV4 and IPV6 internet protocols
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
network:
|
||||
description:
|
||||
- Specifies the network to add or remove from the system. The value
|
||||
should use CIDR notation.
|
||||
required: true
|
||||
aliases:
|
||||
- name
|
||||
- cidr
|
||||
type: str
|
||||
network_view:
|
||||
description:
|
||||
- Configures the name of the network view to associate with this
|
||||
configured instance.
|
||||
default: default
|
||||
type: str
|
||||
options:
|
||||
description:
|
||||
- Configures the set of DHCP options to be included as part of
|
||||
the configured network instance. This argument accepts a list
|
||||
of values (see suboptions). When configuring suboptions at
|
||||
least one of C(name) or C(num) must be specified.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- The name of the DHCP option to configure. The standard options are
|
||||
C(router), C(router-templates), C(domain-name-servers), C(domain-name),
|
||||
C(broadcast-address), C(broadcast-address-offset), C(dhcp-lease-time),
|
||||
and C(dhcp6.name-servers).
|
||||
type: str
|
||||
num:
|
||||
description:
|
||||
- The number of the DHCP option to configure
|
||||
type: int
|
||||
value:
|
||||
description:
|
||||
- The value of the DHCP option specified by C(name)
|
||||
required: true
|
||||
type: str
|
||||
use_option:
|
||||
description:
|
||||
- Only applies to a subset of options (see NIOS API documentation)
|
||||
type: bool
|
||||
default: 'yes'
|
||||
vendor_class:
|
||||
description:
|
||||
- The name of the space this DHCP option is associated to
|
||||
default: DHCP
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
container:
|
||||
description:
|
||||
- If set to true it'll create the network container to be added or removed
|
||||
from the system.
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a network ipv4
|
||||
community.general.nios_network:
|
||||
network: 192.168.10.0/24
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a network ipv6
|
||||
community.general.nios_network:
|
||||
network: fe80::/64
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Set dhcp options for a network ipv4
|
||||
community.general.nios_network:
|
||||
network: 192.168.10.0/24
|
||||
comment: this is a test comment
|
||||
options:
|
||||
- name: domain-name
|
||||
value: ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove a network ipv4
|
||||
community.general.nios_network:
|
||||
network: 192.168.10.0/24
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a ipv4 network container
|
||||
community.general.nios_network:
|
||||
network: 192.168.10.0/24
|
||||
container: true
|
||||
comment: test network container
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a ipv6 network container
|
||||
community.general.nios_network:
|
||||
network: fe80::/64
|
||||
container: true
|
||||
comment: test network container
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove a ipv4 network container
|
||||
community.general.nios_network:
|
||||
networkr: 192.168.10.0/24
|
||||
container: true
|
||||
comment: test network container
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
import socket
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_IPV4_NETWORK, NIOS_IPV6_NETWORK
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_IPV4_NETWORK_CONTAINER, NIOS_IPV6_NETWORK_CONTAINER
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
# The following function validate_ip_address has been taken from
|
||||
# https://github.com/ansible-collections/ansible.netcommon/blob/20124ecbb420daa0f5bb9cdaa865a952657aa0e7/plugins/module_utils/network/common/utils.py#L496
|
||||
# The code there is licensed under BSD 2-clause.
|
||||
# Copyright (c) 2016 Red Hat Inc.
|
||||
def validate_ip_address(address):
|
||||
try:
|
||||
socket.inet_aton(address)
|
||||
except socket.error:
|
||||
return False
|
||||
return address.count(".") == 3
|
||||
|
||||
|
||||
# The following function validate_ip_v6_address has been taken from
|
||||
# https://github.com/ansible-collections/ansible.netcommon/blob/20124ecbb420daa0f5bb9cdaa865a952657aa0e7/plugins/module_utils/network/common/utils.py#L504
|
||||
# The code there is licensed under BSD 2-clause.
|
||||
# Copyright (c) 2016 Red Hat Inc.
|
||||
def validate_ip_v6_address(address):
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, address)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def options(module):
|
||||
''' Transforms the module argument into a valid WAPI struct
|
||||
This function will transform the options argument into a structure that
|
||||
is a valid WAPI structure in the format of:
|
||||
{
|
||||
name: <value>,
|
||||
num: <value>,
|
||||
value: <value>,
|
||||
use_option: <value>,
|
||||
vendor_class: <value>
|
||||
}
|
||||
It will remove any options that are set to None since WAPI will error on
|
||||
that condition. It will also verify that either `name` or `num` is
|
||||
set in the structure but does not validate the values are equal.
|
||||
The remainder of the value validation is performed by WAPI
|
||||
'''
|
||||
options = list()
|
||||
for item in module.params['options']:
|
||||
opt = dict([(k, v) for k, v in iteritems(item) if v is not None])
|
||||
if 'name' not in opt and 'num' not in opt:
|
||||
module.fail_json(msg='one of `name` or `num` is required for option value')
|
||||
options.append(opt)
|
||||
return options
|
||||
|
||||
|
||||
def check_ip_addr_type(obj_filter, ib_spec):
|
||||
'''This function will check if the argument ip is type v4/v6 and return appropriate infoblox
|
||||
network/networkcontainer type
|
||||
'''
|
||||
|
||||
ip = obj_filter['network']
|
||||
if 'container' in obj_filter and obj_filter['container']:
|
||||
check_ip = ip.split('/')
|
||||
del ib_spec['container'] # removing the container key from post arguments
|
||||
del ib_spec['options'] # removing option argument as for network container it's not supported
|
||||
if validate_ip_address(check_ip[0]):
|
||||
return NIOS_IPV4_NETWORK_CONTAINER, ib_spec
|
||||
elif validate_ip_v6_address(check_ip[0]):
|
||||
return NIOS_IPV6_NETWORK_CONTAINER, ib_spec
|
||||
else:
|
||||
check_ip = ip.split('/')
|
||||
del ib_spec['container'] # removing the container key from post arguments
|
||||
if validate_ip_address(check_ip[0]):
|
||||
return NIOS_IPV4_NETWORK, ib_spec
|
||||
elif validate_ip_v6_address(check_ip[0]):
|
||||
return NIOS_IPV6_NETWORK, ib_spec
|
||||
|
||||
|
||||
def check_vendor_specific_dhcp_option(module, ib_spec):
|
||||
'''This function will check if the argument dhcp option belongs to vendor-specific and if yes then will remove
|
||||
use_options flag which is not supported with vendor-specific dhcp options.
|
||||
'''
|
||||
for key, value in iteritems(ib_spec):
|
||||
if isinstance(module.params[key], list):
|
||||
temp_dict = module.params[key][0]
|
||||
if 'num' in temp_dict:
|
||||
if temp_dict['num'] in (43, 124, 125):
|
||||
del module.params[key][0]['use_option']
|
||||
return ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
option_spec = dict(
|
||||
# one of name or num is required; enforced by the function options()
|
||||
name=dict(),
|
||||
num=dict(type='int'),
|
||||
|
||||
value=dict(required=True),
|
||||
|
||||
use_option=dict(type='bool', default=True),
|
||||
vendor_class=dict(default='DHCP')
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
network=dict(required=True, aliases=['name', 'cidr'], ib_req=True),
|
||||
network_view=dict(default='default', ib_req=True),
|
||||
|
||||
options=dict(type='list', elements='dict', options=option_spec, transform=options),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
container=dict(type='bool', ib_req=True)
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
# to get the argument ipaddr
|
||||
obj_filter = dict([(k, module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
|
||||
network_type, ib_spec = check_ip_addr_type(obj_filter, ib_spec)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
# to check for vendor specific dhcp option
|
||||
ib_spec = check_vendor_specific_dhcp_option(module, ib_spec)
|
||||
|
||||
result = wapi.run(network_type, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
138
plugins/modules/net_tools/nios/nios_network_view.py
Normal file
138
plugins/modules/net_tools/nios/nios_network_view.py
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_network_view
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configure Infoblox NIOS network views
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_network_view
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of network view objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(networkview) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
- Updates instances of network view object from Infoblox NIOS servers.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system. User can also update the hostname as it is possible
|
||||
to pass a dict containing I(new_name), I(old_name). See examples.
|
||||
required: true
|
||||
aliases:
|
||||
- network_view
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a new network view
|
||||
community.general.nios_network_view:
|
||||
name: ansible
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update the comment for network view
|
||||
community.general.nios_network_view:
|
||||
name: ansible
|
||||
comment: this is an example comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove the network view
|
||||
community.general.nios_network_view:
|
||||
name: ansible
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update a existing network view
|
||||
community.general.nios_network_view:
|
||||
name: {new_name: ansible-new, old_name: ansible}
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_NETWORK_VIEW
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, aliases=['network_view'], ib_req=True),
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_NETWORK_VIEW, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
446
plugins/modules/net_tools/nios/nios_nsgroup.py
Normal file
446
plugins/modules/net_tools/nios/nios_nsgroup.py
Normal file
@@ -0,0 +1,446 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_nsgroup
|
||||
short_description: Configure InfoBlox DNS Nameserver Groups
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_nsgroup
|
||||
removed_in: 5.0.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
author:
|
||||
- Erich Birngruber (@ebirn)
|
||||
- Sumit Jaiswal (@sjaiswal)
|
||||
description:
|
||||
- Adds and/or removes nameserver groups form Infoblox NIOS servers.
|
||||
This module manages NIOS C(nsgroup) objects using the Infoblox. WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox_client
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the name of the NIOS nameserver group to be managed.
|
||||
required: true
|
||||
type: str
|
||||
grid_primary:
|
||||
description:
|
||||
- This host is to be used as primary server in this nameserver group. It must be a grid member.
|
||||
This option is required when setting I(use_external_primaries) to C(false).
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Provide the name of the grid member to identify the host.
|
||||
required: true
|
||||
type: str
|
||||
enable_preferred_primaries:
|
||||
description:
|
||||
- This flag represents whether the preferred_primaries field values of this member are used (see Infoblox WAPI docs).
|
||||
default: false
|
||||
type: bool
|
||||
grid_replicate:
|
||||
description:
|
||||
- Use DNS zone transfers if set to C(True) or ID Grid Replication if set to C(False).
|
||||
type: bool
|
||||
default: false
|
||||
lead:
|
||||
description:
|
||||
- This flag controls if the grid lead secondary nameserver performs zone transfers to non lead secondaries.
|
||||
type: bool
|
||||
default: false
|
||||
stealth:
|
||||
description:
|
||||
- Configure the external nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
preferred_primaries:
|
||||
description:
|
||||
- Provide a list of elements like in I(external_primaries) to set the precedence of preferred primary nameservers.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Configures the IP address of the preferred primary nameserver.
|
||||
required: true
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Set a label for the preferred primary nameserver.
|
||||
required: true
|
||||
type: str
|
||||
stealth:
|
||||
description:
|
||||
- Configure the preferred primary nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
tsig_key_name:
|
||||
description:
|
||||
- Sets a label for the I(tsig_key) value.
|
||||
required: true
|
||||
type: str
|
||||
tsig_key_alg:
|
||||
description:
|
||||
- Provides the algorithm used for the I(tsig_key) in use.
|
||||
choices: ['HMAC-MD5', 'HMAC-SHA256']
|
||||
default: 'HMAC-MD5'
|
||||
type: str
|
||||
tsig_key:
|
||||
description:
|
||||
- Set a DNS TSIG key for the nameserver to secure zone transfers (AFXRs).
|
||||
type: str
|
||||
grid_secondaries:
|
||||
description:
|
||||
- Configures the list of grid member hosts that act as secondary nameservers.
|
||||
This option is required when setting I(use_external_primaries) to C(true).
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Provide the name of the grid member to identify the host.
|
||||
required: true
|
||||
type: str
|
||||
enable_preferred_primaries:
|
||||
description:
|
||||
- This flag represents whether the preferred_primaries field values of this member are used (see Infoblox WAPI docs).
|
||||
default: false
|
||||
type: bool
|
||||
grid_replicate:
|
||||
description:
|
||||
- Use DNS zone transfers if set to C(True) or ID Grid Replication if set to C(False)
|
||||
type: bool
|
||||
default: false
|
||||
lead:
|
||||
description:
|
||||
- This flag controls if the grid lead secondary nameserver performs zone transfers to non lead secondaries.
|
||||
type: bool
|
||||
default: false
|
||||
stealth:
|
||||
description:
|
||||
- Configure the external nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
preferred_primaries:
|
||||
description:
|
||||
- Provide a list of elements like in I(external_primaries) to set the precedence of preferred primary nameservers.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Configures the IP address of the preferred primary nameserver.
|
||||
required: true
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Set a label for the preferred primary nameserver.
|
||||
required: true
|
||||
type: str
|
||||
stealth:
|
||||
description:
|
||||
- Configure the preferred primary nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
tsig_key_name:
|
||||
description:
|
||||
- Sets a label for the I(tsig_key) value.
|
||||
type: str
|
||||
required: true
|
||||
tsig_key_alg:
|
||||
description:
|
||||
- Provides the algorithm used for the I(tsig_key) in use.
|
||||
choices: ['HMAC-MD5', 'HMAC-SHA256']
|
||||
default: 'HMAC-MD5'
|
||||
type: str
|
||||
tsig_key:
|
||||
description:
|
||||
- Set a DNS TSIG key for the nameserver to secure zone transfers (AFXRs).
|
||||
type: str
|
||||
is_grid_default:
|
||||
description:
|
||||
- If set to C(True) this nsgroup will become the default nameserver group for new zones.
|
||||
type: bool
|
||||
required: false
|
||||
default: false
|
||||
use_external_primary:
|
||||
description:
|
||||
- This flag controls whether the group is using an external primary nameserver.
|
||||
Note that modification of this field requires passing values for I(grid_secondaries) and I(external_primaries).
|
||||
type: bool
|
||||
required: false
|
||||
default: false
|
||||
external_primaries:
|
||||
description:
|
||||
- Configures a list of external nameservers (non-members of the grid).
|
||||
This option is required when setting I(use_external_primaries) to C(true).
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Configures the IP address of the external nameserver
|
||||
required: true
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Set a label for the external nameserver
|
||||
required: true
|
||||
type: str
|
||||
stealth:
|
||||
description:
|
||||
- Configure the external nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
tsig_key_name:
|
||||
description:
|
||||
- Sets a label for the I(tsig_key) value
|
||||
type: str
|
||||
required: true
|
||||
tsig_key_alg:
|
||||
description:
|
||||
- Provides the algorithm used for the I(tsig_key) in use.
|
||||
choices: ['HMAC-MD5', 'HMAC-SHA256']
|
||||
default: 'HMAC-MD5'
|
||||
type: str
|
||||
tsig_key:
|
||||
description:
|
||||
- Set a DNS TSIG key for the nameserver to secure zone transfers (AFXRs).
|
||||
type: str
|
||||
required: false
|
||||
external_secondaries:
|
||||
description:
|
||||
- Allows to provide a list of external secondary nameservers, that are not members of the grid.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- Configures the IP address of the external nameserver
|
||||
required: true
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Set a label for the external nameserver
|
||||
required: true
|
||||
type: str
|
||||
stealth:
|
||||
description:
|
||||
- Configure the external nameserver as stealth server (without NS record) in the zones.
|
||||
type: bool
|
||||
default: false
|
||||
tsig_key_name:
|
||||
description:
|
||||
- Sets a label for the I(tsig_key) value
|
||||
type: str
|
||||
required: true
|
||||
tsig_key_alg:
|
||||
description:
|
||||
- Provides the algorithm used for the I(tsig_key) in use.
|
||||
choices: ['HMAC-MD5', 'HMAC-SHA256']
|
||||
default: 'HMAC-MD5'
|
||||
type: str
|
||||
tsig_key:
|
||||
description:
|
||||
- Set a DNS TSIG key for the nameserver to secure zone transfers (AFXRs).
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
required: false
|
||||
type: str
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
required: false
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create simple infoblox nameserver group
|
||||
community.general.nios_nsgroup:
|
||||
name: my-simple-group
|
||||
comment: "this is a simple nameserver group"
|
||||
grid_primary:
|
||||
- name: infoblox-test.example.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Create infoblox nameserver group with external primaries
|
||||
community.general.nios_nsgroup:
|
||||
name: my-example-group
|
||||
use_external_primary: true
|
||||
comment: "this is my example nameserver group"
|
||||
external_primaries: "{{ ext_nameservers }}"
|
||||
grid_secondaries:
|
||||
- name: infoblox-test.example.com
|
||||
lead: True
|
||||
preferred_primaries: "{{ ext_nameservers }}"
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Delete infoblox nameserver group
|
||||
community.general.nios_nsgroup:
|
||||
name: my-simple-group
|
||||
comment: "this is a simple nameserver group"
|
||||
grid_primary:
|
||||
- name: infoblox-test.example.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_NSGROUP
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
# from infoblox documentation
|
||||
# Fields List
|
||||
# Field Type Req R/O Base Search
|
||||
# comment String N N Y : = ~
|
||||
# extattrs Extattr N N N ext
|
||||
# external_primaries [struct] N N N N/A
|
||||
# external_secondaries [struct] N N N N/A
|
||||
# grid_primary [struct] N N N N/A
|
||||
# grid_secondaries [struct] N N N N/A
|
||||
# is_grid_default Bool N N N N/A
|
||||
# is_multimaster Bool N Y N N/A
|
||||
# name String Y N Y : = ~
|
||||
# use_external_primary Bool N N N N/A
|
||||
|
||||
|
||||
def main():
|
||||
'''entrypoint for module execution.'''
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
)
|
||||
|
||||
# cleanup tsig fields
|
||||
def clean_tsig(ext):
|
||||
if 'tsig_key' in ext and not ext['tsig_key']:
|
||||
del ext['tsig_key']
|
||||
if 'tsig_key' not in ext and 'tsig_key_name' in ext and not ext['tsig_key_name']:
|
||||
del ext['tsig_key_name']
|
||||
if 'tsig_key' not in ext and 'tsig_key_alg' in ext:
|
||||
del ext['tsig_key_alg']
|
||||
|
||||
def clean_grid_member(member):
|
||||
if member['preferred_primaries']:
|
||||
for ext in member['preferred_primaries']:
|
||||
clean_tsig(ext)
|
||||
if member['enable_preferred_primaries'] is False:
|
||||
del member['enable_preferred_primaries']
|
||||
del member['preferred_primaries']
|
||||
if member['lead'] is False:
|
||||
del member['lead']
|
||||
if member['grid_replicate'] is False:
|
||||
del member['grid_replicate']
|
||||
|
||||
def ext_primaries_transform(module):
|
||||
if module.params['external_primaries']:
|
||||
for ext in module.params['external_primaries']:
|
||||
clean_tsig(ext)
|
||||
return module.params['external_primaries']
|
||||
|
||||
def ext_secondaries_transform(module):
|
||||
if module.params['external_secondaries']:
|
||||
for ext in module.params['external_secondaries']:
|
||||
clean_tsig(ext)
|
||||
return module.params['external_secondaries']
|
||||
|
||||
def grid_primary_preferred_transform(module):
|
||||
for member in module.params['grid_primary']:
|
||||
clean_grid_member(member)
|
||||
return module.params['grid_primary']
|
||||
|
||||
def grid_secondaries_preferred_primaries_transform(module):
|
||||
for member in module.params['grid_secondaries']:
|
||||
clean_grid_member(member)
|
||||
return module.params['grid_secondaries']
|
||||
|
||||
extserver_spec = dict(
|
||||
address=dict(required=True),
|
||||
name=dict(required=True),
|
||||
stealth=dict(type='bool', default=False),
|
||||
tsig_key=dict(no_log=True),
|
||||
tsig_key_alg=dict(choices=['HMAC-MD5', 'HMAC-SHA256'], default='HMAC-MD5'),
|
||||
tsig_key_name=dict(required=True)
|
||||
)
|
||||
|
||||
memberserver_spec = dict(
|
||||
name=dict(required=True, ),
|
||||
enable_preferred_primaries=dict(type='bool', default=False),
|
||||
grid_replicate=dict(type='bool', default=False),
|
||||
lead=dict(type='bool', default=False),
|
||||
preferred_primaries=dict(type='list', elements='dict', options=extserver_spec, default=[]),
|
||||
stealth=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
grid_primary=dict(type='list', elements='dict', options=memberserver_spec,
|
||||
transform=grid_primary_preferred_transform),
|
||||
grid_secondaries=dict(type='list', elements='dict', options=memberserver_spec,
|
||||
transform=grid_secondaries_preferred_primaries_transform),
|
||||
external_primaries=dict(type='list', elements='dict', options=extserver_spec, transform=ext_primaries_transform),
|
||||
external_secondaries=dict(type='list', elements='dict', options=extserver_spec,
|
||||
transform=ext_secondaries_transform),
|
||||
is_grid_default=dict(type='bool', default=False),
|
||||
use_external_primary=dict(type='bool', default=False),
|
||||
extattrs=dict(),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_NSGROUP, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
168
plugins/modules/net_tools/nios/nios_ptr_record.py
Normal file
168
plugins/modules/net_tools/nios/nios_ptr_record.py
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_ptr_record
|
||||
author: "Trebuchet Clement (@clementtrebuchet)"
|
||||
short_description: Configure Infoblox NIOS PTR records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_ptr_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of PTR record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:ptr) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox_client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the DNS PTR record in FQDN format to add or remove from
|
||||
the system.
|
||||
The field is required only for an PTR object in Forward Mapping Zone.
|
||||
required: false
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this a record with. The DNS
|
||||
view must already be configured on the system
|
||||
required: false
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
ipv4addr:
|
||||
description:
|
||||
- The IPv4 Address of the record. Mutually exclusive with the ipv6addr.
|
||||
aliases:
|
||||
- ipv4
|
||||
type: str
|
||||
ipv6addr:
|
||||
description:
|
||||
- The IPv6 Address of the record. Mutually exclusive with the ipv4addr.
|
||||
aliases:
|
||||
- ipv6
|
||||
type: str
|
||||
ptrdname:
|
||||
description:
|
||||
- The domain name of the DNS PTR record in FQDN format.
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Time To Live (TTL) value for the record.
|
||||
A 32-bit unsigned integer that represents the duration, in seconds, that the record is valid (cached).
|
||||
Zero indicates that the record should not be cached.
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance. Maximum 256 characters.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a PTR Record
|
||||
community.general.nios_ptr_record:
|
||||
ipv4: 192.168.10.1
|
||||
ptrdname: host.ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Delete a PTR Record
|
||||
community.general.nios_ptr_record:
|
||||
ipv4: 192.168.10.1
|
||||
ptrdname: host.ansible.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_PTR_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
# Module entry point
|
||||
ib_spec = dict(
|
||||
name=dict(required=False),
|
||||
view=dict(aliases=['dns_view'], ib_req=True),
|
||||
ipv4addr=dict(aliases=['ipv4'], ib_req=True),
|
||||
ipv6addr=dict(aliases=['ipv6'], ib_req=True),
|
||||
ptrdname=dict(ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
mutually_exclusive = [('ipv4addr', 'ipv6addr')]
|
||||
required_one_of = [
|
||||
['ipv4addr', 'ipv6addr']
|
||||
]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True,
|
||||
required_one_of=required_one_of)
|
||||
|
||||
if module.params['ipv4addr']:
|
||||
del ib_spec['ipv6addr']
|
||||
elif module.params['ipv6addr']:
|
||||
del ib_spec['ipv4addr']
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_PTR_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
177
plugins/modules/net_tools/nios/nios_srv_record.py
Normal file
177
plugins/modules/net_tools/nios/nios_srv_record.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_srv_record
|
||||
author: "Blair Rampling (@brampling)"
|
||||
short_description: Configure Infoblox NIOS SRV records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_srv_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of SRV record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:srv) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this a record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
port:
|
||||
description:
|
||||
- Configures the port (0-65535) of this SRV record.
|
||||
type: int
|
||||
priority:
|
||||
description:
|
||||
- Configures the priority (0-65535) for this SRV record.
|
||||
type: int
|
||||
target:
|
||||
description:
|
||||
- Configures the target FQDN for this SRV record.
|
||||
type: str
|
||||
weight:
|
||||
description:
|
||||
- Configures the weight (0-65535) for this SRV record.
|
||||
type: int
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this host record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure an SRV record
|
||||
community.general.nios_srv_record:
|
||||
name: _sip._tcp.service.ansible.com
|
||||
port: 5080
|
||||
priority: 10
|
||||
target: service1.ansible.com
|
||||
weight: 10
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Add a comment to an existing SRV record
|
||||
community.general.nios_srv_record:
|
||||
name: _sip._tcp.service.ansible.com
|
||||
port: 5080
|
||||
priority: 10
|
||||
target: service1.ansible.com
|
||||
weight: 10
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
|
||||
- name: Remove an SRV record from the system
|
||||
community.general.nios_srv_record:
|
||||
name: _sip._tcp.service.ansible.com
|
||||
port: 5080
|
||||
priority: 10
|
||||
target: service1.ansible.com
|
||||
weight: 10
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_SRV_RECORD
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
port=dict(type='int', ib_req=True),
|
||||
priority=dict(type='int', ib_req=True),
|
||||
target=dict(ib_req=True),
|
||||
weight=dict(type='int', ib_req=True),
|
||||
|
||||
ttl=dict(type='int'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_SRV_RECORD, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
140
plugins/modules/net_tools/nios/nios_txt_record.py
Normal file
140
plugins/modules/net_tools/nios/nios_txt_record.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_txt_record
|
||||
author: "Corey Wanless (@coreywan)"
|
||||
short_description: Configure Infoblox NIOS txt records
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_txt_record
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of txt record objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(record:txt) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox_client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the fully qualified hostname to add or remove from
|
||||
the system
|
||||
required: true
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Sets the DNS view to associate this tst record with. The DNS
|
||||
view must already be configured on the system
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
text:
|
||||
description:
|
||||
- Text associated with the record. It can contain up to 255 bytes
|
||||
per substring, up to a total of 512 bytes. To enter leading,
|
||||
trailing, or embedded spaces in the text, add quotes around the
|
||||
text to preserve the spaces.
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- Configures the TTL to be associated with this tst record
|
||||
type: int
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Ensure a text Record Exists
|
||||
community.general.nios_txt_record:
|
||||
name: fqdn.txt.record.com
|
||||
text: mytext
|
||||
state: present
|
||||
view: External
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
|
||||
- name: Ensure a text Record does not exist
|
||||
community.general.nios_txt_record:
|
||||
name: fqdn.txt.record.com
|
||||
text: mytext
|
||||
state: absent
|
||||
view: External
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True, ib_req=True),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
text=dict(ib_req=True),
|
||||
ttl=dict(type='int'),
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict(),
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run('record:txt', ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
241
plugins/modules/net_tools/nios/nios_zone.py
Normal file
241
plugins/modules/net_tools/nios/nios_zone.py
Normal file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_zone
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configure Infoblox NIOS DNS zones
|
||||
deprecated:
|
||||
why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
|
||||
alternative: infoblox.nios_modules.nios_zone
|
||||
removed_in: 5.0.0
|
||||
description:
|
||||
- Adds and/or removes instances of DNS zone objects from
|
||||
Infoblox NIOS servers. This module manages NIOS C(zone_auth) objects
|
||||
using the Infoblox WAPI interface over REST.
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment:
|
||||
- community.general.nios
|
||||
|
||||
options:
|
||||
fqdn:
|
||||
description:
|
||||
- Specifies the qualified domain name to either add or remove from
|
||||
the NIOS instance based on the configured C(state) value.
|
||||
required: true
|
||||
aliases:
|
||||
- name
|
||||
type: str
|
||||
view:
|
||||
description:
|
||||
- Configures the DNS view name for the configured resource. The
|
||||
specified DNS zone must already exist on the running NIOS instance
|
||||
prior to configuring zones.
|
||||
default: default
|
||||
aliases:
|
||||
- dns_view
|
||||
type: str
|
||||
grid_primary:
|
||||
description:
|
||||
- Configures the grid primary servers for this zone.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- The name of the grid primary server
|
||||
required: true
|
||||
type: str
|
||||
grid_secondaries:
|
||||
description:
|
||||
- Configures the grid secondary servers for this zone.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- The name of the grid secondary server
|
||||
required: true
|
||||
type: str
|
||||
ns_group:
|
||||
description:
|
||||
- Configures the name server group for this zone. Name server group is
|
||||
mutually exclusive with grid primary and grid secondaries.
|
||||
type: str
|
||||
restart_if_needed:
|
||||
description:
|
||||
- If set to true, causes the NIOS DNS service to restart and load the
|
||||
new zone configuration
|
||||
type: bool
|
||||
zone_format:
|
||||
description:
|
||||
- Create an authorative Reverse-Mapping Zone which is an area of network
|
||||
space for which one or more name servers-primary and secondary-have the
|
||||
responsibility to respond to address-to-name queries. It supports
|
||||
reverse-mapping zones for both IPv4 and IPv6 addresses.
|
||||
default: FORWARD
|
||||
type: str
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
type: dict
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Configure a zone on the system using grid primary and secondaries
|
||||
community.general.nios_zone:
|
||||
name: ansible.com
|
||||
grid_primary:
|
||||
- name: gridprimary.grid.com
|
||||
grid_secondaries:
|
||||
- name: gridsecondary1.grid.com
|
||||
- name: gridsecondary2.grid.com
|
||||
restart_if_needed: true
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a zone on the system using a name server group
|
||||
community.general.nios_zone:
|
||||
name: ansible.com
|
||||
ns_group: examplensg
|
||||
restart_if_needed: true
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a reverse mapping zone on the system using IPV4 zone format
|
||||
community.general.nios_zone:
|
||||
name: 10.10.10.0/24
|
||||
zone_format: IPV4
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Configure a reverse mapping zone on the system using IPV6 zone format
|
||||
community.general.nios_zone:
|
||||
name: 100::1/128
|
||||
zone_format: IPV6
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Update the comment and ext attributes for an existing zone
|
||||
community.general.nios_zone:
|
||||
name: ansible.com
|
||||
comment: this is an example comment
|
||||
extattrs:
|
||||
Site: west-dc
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove the dns zone
|
||||
community.general.nios_zone:
|
||||
name: ansible.com
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: Remove the reverse mapping dns zone from the system with IPV4 zone format
|
||||
community.general.nios_zone:
|
||||
name: 10.10.10.0/24
|
||||
zone_format: IPV4
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_ZONE
|
||||
from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
grid_spec = dict(
|
||||
name=dict(required=True),
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
fqdn=dict(required=True, aliases=['name'], ib_req=True, update=False),
|
||||
zone_format=dict(default='FORWARD', ib_req=False),
|
||||
view=dict(default='default', aliases=['dns_view'], ib_req=True),
|
||||
|
||||
grid_primary=dict(type='list', elements='dict', options=grid_spec),
|
||||
grid_secondaries=dict(type='list', elements='dict', options=grid_spec),
|
||||
ns_group=dict(),
|
||||
restart_if_needed=dict(type='bool'),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict()
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(normalize_ib_spec(ib_spec))
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
['ns_group', 'grid_primary'],
|
||||
['ns_group', 'grid_secondaries']
|
||||
])
|
||||
|
||||
wapi = WapiModule(module)
|
||||
result = wapi.run(NIOS_ZONE, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -69,11 +69,10 @@ options:
|
||||
type: str
|
||||
ip4:
|
||||
description:
|
||||
- List of IPv4 addresses to this interface.
|
||||
- The IPv4 address to this interface.
|
||||
- Use the format C(192.0.2.24/24) or C(192.0.2.24).
|
||||
- If defined and I(method4) is not specified, automatically set C(ipv4.method) to C(manual).
|
||||
type: list
|
||||
elements: str
|
||||
type: str
|
||||
gw4:
|
||||
description:
|
||||
- The IPv4 gateway for this interface.
|
||||
@@ -143,11 +142,10 @@ options:
|
||||
version_added: 3.3.0
|
||||
ip6:
|
||||
description:
|
||||
- List of IPv6 addresses to this interface.
|
||||
- Use the format C(abbe::cafe/128) or C(abbe::cafe).
|
||||
- The IPv6 address to this interface.
|
||||
- Use the format C(abbe::cafe/128 or abbe::cafe).
|
||||
- If defined and I(method6) is not specified, automatically set C(ipv6.method) to C(manual).
|
||||
type: list
|
||||
elements: str
|
||||
type: str
|
||||
gw6:
|
||||
description:
|
||||
- The IPv6 gateway for this interface.
|
||||
@@ -184,18 +182,6 @@ options:
|
||||
type: str
|
||||
choices: [ignore, auto, dhcp, link-local, manual, shared, disabled]
|
||||
version_added: 2.2.0
|
||||
ip_privacy6:
|
||||
description:
|
||||
- If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one.
|
||||
type: str
|
||||
choices: [disabled, prefer-public-addr, prefer-temp-addr, unknown]
|
||||
version_added: 4.2.0
|
||||
addr_gen_mode6:
|
||||
description:
|
||||
- Configure method for creating the address for use with IPv6 Stateless Address Autoconfiguration.
|
||||
type: str
|
||||
choices: [eui64, stable-privacy]
|
||||
version_added: 4.2.0
|
||||
mtu:
|
||||
description:
|
||||
- The connection MTU, e.g. 9000. This can't be applied when creating the interface and is done once the interface has been created.
|
||||
@@ -836,9 +822,7 @@ EXAMPLES = r'''
|
||||
# nmcli_ethernet:
|
||||
# - conn_name: em1
|
||||
# ifname: em1
|
||||
# ip4:
|
||||
# - '{{ tenant_ip }}'
|
||||
# - '{{ second_tenant_ip }}'
|
||||
# ip4: '{{ tenant_ip }}'
|
||||
# gw4: '{{ tenant_gw }}'
|
||||
# - conn_name: em2
|
||||
# ifname: em2
|
||||
@@ -860,7 +844,6 @@ EXAMPLES = r'''
|
||||
# storage_ip: "192.0.2.91/23"
|
||||
# external_ip: "198.51.100.23/21"
|
||||
# tenant_ip: "203.0.113.77/23"
|
||||
# second_tenant_ip: "204.0.113.77/23"
|
||||
# ```
|
||||
|
||||
|
||||
@@ -1014,26 +997,6 @@ EXAMPLES = r'''
|
||||
type: ethernet
|
||||
state: present
|
||||
|
||||
- name: Add second ip4 address
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
ifname: eth1
|
||||
type: ethernet
|
||||
ip4:
|
||||
- 192.0.2.100/24
|
||||
- 192.0.3.100/24
|
||||
state: present
|
||||
|
||||
- name: Add second ip6 address
|
||||
community.general.nmcli:
|
||||
conn_name: my-eth1
|
||||
ifname: eth1
|
||||
type: ethernet
|
||||
ip6:
|
||||
- 2001:db8::cafe
|
||||
- 2002:db8::cafe
|
||||
state: present
|
||||
|
||||
- name: Add VxLan
|
||||
community.general.nmcli:
|
||||
type: vxlan
|
||||
@@ -1194,8 +1157,6 @@ class Nmcli(object):
|
||||
self.dns6_search = module.params['dns6_search']
|
||||
self.dns6_ignore_auto = module.params['dns6_ignore_auto']
|
||||
self.method6 = module.params['method6']
|
||||
self.ip_privacy6 = module.params['ip_privacy6']
|
||||
self.addr_gen_mode6 = module.params['addr_gen_mode6']
|
||||
self.mtu = module.params['mtu']
|
||||
self.stp = module.params['stp']
|
||||
self.priority = module.params['priority']
|
||||
@@ -1300,8 +1261,6 @@ class Nmcli(object):
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.ignore-auto-routes': self.gw6_ignore_auto,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.ip6-privacy': self.ip_privacy6,
|
||||
'ipv6.addr-gen-mode': self.addr_gen_mode6
|
||||
})
|
||||
|
||||
# Layer 2 options.
|
||||
@@ -1418,8 +1377,6 @@ class Nmcli(object):
|
||||
elif setting == self.mtu_setting:
|
||||
# MTU is 'auto' by default when detecting changes.
|
||||
convert_func = self.mtu_to_string
|
||||
elif setting == 'ipv6.ip6-privacy':
|
||||
convert_func = self.ip6_privacy_to_num
|
||||
elif setting_type is list:
|
||||
# Convert lists to strings for nmcli create/modify commands.
|
||||
convert_func = self.list_to_string
|
||||
@@ -1473,23 +1430,6 @@ class Nmcli(object):
|
||||
else:
|
||||
return to_text(mtu)
|
||||
|
||||
@staticmethod
|
||||
def ip6_privacy_to_num(privacy):
|
||||
ip6_privacy_values = {
|
||||
'disabled': '0',
|
||||
'prefer-public-addr': '1 (enabled, prefer public IP)',
|
||||
'prefer-temp-addr': '2 (enabled, prefer temporary IP)',
|
||||
'unknown': '-1',
|
||||
}
|
||||
|
||||
if privacy is None:
|
||||
return None
|
||||
|
||||
if privacy not in ip6_privacy_values:
|
||||
raise AssertionError('{privacy} is invalid ip_privacy6 option'.format(privacy=privacy))
|
||||
|
||||
return ip6_privacy_values[privacy]
|
||||
|
||||
@property
|
||||
def slave_conn_type(self):
|
||||
return self.type in (
|
||||
@@ -1508,16 +1448,20 @@ class Nmcli(object):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def enforce_ipv4_cidr_notation(ip4_addresses):
|
||||
if ip4_addresses is None:
|
||||
return None
|
||||
return [address if '/' in address else address + '/32' for address in ip4_addresses]
|
||||
def enforce_ipv4_cidr_notation(ip4_address):
|
||||
if ip4_address is None or '/' in ip4_address:
|
||||
return ip4_address
|
||||
|
||||
return ip4_address + '/32'
|
||||
|
||||
@staticmethod
|
||||
def enforce_ipv6_cidr_notation(ip6_addresses):
|
||||
if ip6_addresses is None:
|
||||
def enforce_ipv6_cidr_notation(ip6_address):
|
||||
if ip6_address is None:
|
||||
return None
|
||||
return [address if '/' in address else address + '/128' for address in ip6_addresses]
|
||||
elif '/' in ip6_address:
|
||||
return ip6_address
|
||||
else:
|
||||
return ip6_address + '/128'
|
||||
|
||||
@staticmethod
|
||||
def bool_to_string(boolean):
|
||||
@@ -1543,9 +1487,7 @@ class Nmcli(object):
|
||||
'ipv6.ignore-auto-routes',
|
||||
'802-11-wireless.hidden'):
|
||||
return bool
|
||||
elif setting in ('ipv4.addresses',
|
||||
'ipv6.addresses',
|
||||
'ipv4.dns',
|
||||
elif setting in ('ipv4.dns',
|
||||
'ipv4.dns-search',
|
||||
'ipv4.routes',
|
||||
'ipv4.routing-rules',
|
||||
@@ -1835,7 +1777,7 @@ def main():
|
||||
'wifi',
|
||||
'gsm',
|
||||
]),
|
||||
ip4=dict(type='list', elements='str'),
|
||||
ip4=dict(type='str'),
|
||||
gw4=dict(type='str'),
|
||||
gw4_ignore_auto=dict(type='bool', default=False),
|
||||
routes4=dict(type='list', elements='str'),
|
||||
@@ -1848,15 +1790,13 @@ def main():
|
||||
method4=dict(type='str', choices=['auto', 'link-local', 'manual', 'shared', 'disabled']),
|
||||
may_fail4=dict(type='bool', default=True),
|
||||
dhcp_client_id=dict(type='str'),
|
||||
ip6=dict(type='list', elements='str'),
|
||||
ip6=dict(type='str'),
|
||||
gw6=dict(type='str'),
|
||||
gw6_ignore_auto=dict(type='bool', default=False),
|
||||
dns6=dict(type='list', elements='str'),
|
||||
dns6_search=dict(type='list', elements='str'),
|
||||
dns6_ignore_auto=dict(type='bool', default=False),
|
||||
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=['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']),
|
||||
|
||||
1
plugins/modules/nios_a_record.py
Symbolic link
1
plugins/modules/nios_a_record.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_a_record.py
|
||||
1
plugins/modules/nios_aaaa_record.py
Symbolic link
1
plugins/modules/nios_aaaa_record.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_aaaa_record.py
|
||||
1
plugins/modules/nios_cname_record.py
Symbolic link
1
plugins/modules/nios_cname_record.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_cname_record.py
|
||||
1
plugins/modules/nios_dns_view.py
Symbolic link
1
plugins/modules/nios_dns_view.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_dns_view.py
|
||||
1
plugins/modules/nios_fixed_address.py
Symbolic link
1
plugins/modules/nios_fixed_address.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_fixed_address.py
|
||||
1
plugins/modules/nios_host_record.py
Symbolic link
1
plugins/modules/nios_host_record.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_host_record.py
|
||||
1
plugins/modules/nios_member.py
Symbolic link
1
plugins/modules/nios_member.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./net_tools/nios/nios_member.py
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user