mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-01 10:53:20 +00:00
Compare commits
233 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c45c38f04b | ||
|
|
f7efb2e394 | ||
|
|
093b83c34f | ||
|
|
579fdbbc1c | ||
|
|
c970c14c71 | ||
|
|
24f6493cd4 | ||
|
|
68364df409 | ||
|
|
fb61da5246 | ||
|
|
cf9b01ec6b | ||
|
|
89663a0688 | ||
|
|
7fcb21e044 | ||
|
|
1bf9caa90f | ||
|
|
c6ecc0f3f8 | ||
|
|
4d74aa05a8 | ||
|
|
7fb44b0643 | ||
|
|
7ddb2eb438 | ||
|
|
3158495572 | ||
|
|
58f110ae9c | ||
|
|
5695c919f1 | ||
|
|
6e1a1c028e | ||
|
|
d02b8507d1 | ||
|
|
14d43b10c1 | ||
|
|
92c41a5f55 | ||
|
|
012f684133 | ||
|
|
77b7a65002 | ||
|
|
7f4cd86fe5 | ||
|
|
06980d8239 | ||
|
|
d4740ff387 | ||
|
|
a0b22e4402 | ||
|
|
a56879c1b0 | ||
|
|
d7b31655c4 | ||
|
|
70a7f66d4c | ||
|
|
391c3aa850 | ||
|
|
deb95ea6bf | ||
|
|
806ca0a9e0 | ||
|
|
a171d9bb90 | ||
|
|
dd70c8b031 | ||
|
|
30e707aa79 | ||
|
|
7be95c8bbe | ||
|
|
8e9a348e92 | ||
|
|
2622513d65 | ||
|
|
37d37b20cb | ||
|
|
631d555f8a | ||
|
|
c4a53243d5 | ||
|
|
c0008e976f | ||
|
|
f60c90873f | ||
|
|
c08a57a7c1 | ||
|
|
3d2caf3933 | ||
|
|
df6a00dc89 | ||
|
|
bdddc50358 | ||
|
|
8a01ad200d | ||
|
|
b6ccac372c | ||
|
|
3b1b7966ca | ||
|
|
1f522c414e | ||
|
|
cf60761cf9 | ||
|
|
4b28b036c9 | ||
|
|
ec7c39351d | ||
|
|
b3963fd3c7 | ||
|
|
271bafb637 | ||
|
|
6f5152d053 | ||
|
|
f8842e39be | ||
|
|
b1459b13fe | ||
|
|
57fa900f40 | ||
|
|
f0a232d7a7 | ||
|
|
64f91aafa8 | ||
|
|
7600fec752 | ||
|
|
5af1ac26ac | ||
|
|
5c85b2d891 | ||
|
|
0a8aa03425 | ||
|
|
fa689ffadc | ||
|
|
7d2332626e | ||
|
|
bdc7e48779 | ||
|
|
815638f2ec | ||
|
|
a678029bd2 | ||
|
|
fab30c5e55 | ||
|
|
3e25c692d7 | ||
|
|
e1a4b50074 | ||
|
|
3a270cea95 | ||
|
|
41672c20d3 | ||
|
|
57f5ceece8 | ||
|
|
945bb91e04 | ||
|
|
b48a5c264f | ||
|
|
5bae017de9 | ||
|
|
e568a760ac | ||
|
|
8132568d2f | ||
|
|
0e320641b8 | ||
|
|
8679d59376 | ||
|
|
2554b4b0f4 | ||
|
|
379b6d3523 | ||
|
|
fe4f4198af | ||
|
|
db84ea4ab6 | ||
|
|
de5970d17a | ||
|
|
433d0571b4 | ||
|
|
53b95fd182 | ||
|
|
ad1f25e576 | ||
|
|
49eda7270e | ||
|
|
9c4799c903 | ||
|
|
88bf99b272 | ||
|
|
3ca6e8525e | ||
|
|
0169cb8358 | ||
|
|
499f4b4066 | ||
|
|
ff08c20f12 | ||
|
|
d27c06faeb | ||
|
|
0f98b63944 | ||
|
|
55c70dfb72 | ||
|
|
f78993ba12 | ||
|
|
b97ce10156 | ||
|
|
9250430d7d | ||
|
|
d61305d267 | ||
|
|
198b813b55 | ||
|
|
9e6df4f1c9 | ||
|
|
a477044fb7 | ||
|
|
2a97812856 | ||
|
|
c85bb8713e | ||
|
|
5cdc8f4b07 | ||
|
|
50131f5dfa | ||
|
|
c734e7c2e5 | ||
|
|
7e6e8f7749 | ||
|
|
687acdc961 | ||
|
|
16092feaab | ||
|
|
6676fb8fb4 | ||
|
|
a860f537dd | ||
|
|
f1a9c2f00a | ||
|
|
f8de068e32 | ||
|
|
70b4bacf0f | ||
|
|
41f5d1741c | ||
|
|
54ede7dd7f | ||
|
|
7f0702b786 | ||
|
|
89a3abe64a | ||
|
|
59eff2e3e0 | ||
|
|
1115b463fe | ||
|
|
77bf1fedf5 | ||
|
|
89560ea2e7 | ||
|
|
f9919d28d4 | ||
|
|
7b4660d28a | ||
|
|
29496be80e | ||
|
|
991c96615c | ||
|
|
fe5ad997c1 | ||
|
|
468b28bbb8 | ||
|
|
9b57221d9a | ||
|
|
cd1a92d417 | ||
|
|
7486e3a074 | ||
|
|
6661917370 | ||
|
|
ec0bd3143a | ||
|
|
cce68def8b | ||
|
|
6f5ad22d28 | ||
|
|
6c53a09eef | ||
|
|
9b6e75f7f4 | ||
|
|
e09650140d | ||
|
|
67388be1a9 | ||
|
|
130d07948a | ||
|
|
5d6fcaef53 | ||
|
|
f044a83c49 | ||
|
|
e3f7e8dadf | ||
|
|
8d1a028dbd | ||
|
|
8823e5c061 | ||
|
|
102456d033 | ||
|
|
aad4c55d3d | ||
|
|
e31c98f17f | ||
|
|
6a5dfc5579 | ||
|
|
ab7efef9df | ||
|
|
ca9c763b57 | ||
|
|
cfeb40ed23 | ||
|
|
c495d136fa | ||
|
|
d9e2d6682b | ||
|
|
d7fe288ffd | ||
|
|
7de89699f7 | ||
|
|
b0a9cceeb5 | ||
|
|
b08f0b2f82 | ||
|
|
f23f409bd6 | ||
|
|
cfea62793f | ||
|
|
62bda91466 | ||
|
|
473d5fa2af | ||
|
|
cc76d684d5 | ||
|
|
7a6770c731 | ||
|
|
d2214af6e8 | ||
|
|
fad1220869 | ||
|
|
fe09516235 | ||
|
|
78cd8886f4 | ||
|
|
6b99d48f06 | ||
|
|
6e0e17a7e3 | ||
|
|
90de95c7b2 | ||
|
|
07c6b8b24e | ||
|
|
d106de6d51 | ||
|
|
e96101fb3f | ||
|
|
a60d55f03c | ||
|
|
d6a09ada98 | ||
|
|
9ddb75a3a2 | ||
|
|
b85ff2a997 | ||
|
|
3d1ca5638b | ||
|
|
35fd4700bf | ||
|
|
9add9df7d6 | ||
|
|
cdb747b41d | ||
|
|
7a70fda784 | ||
|
|
13d1b9569e | ||
|
|
c8c5021773 | ||
|
|
206ac72bd8 | ||
|
|
1ee123bb1e | ||
|
|
aa0bcad9df | ||
|
|
6e51690a95 | ||
|
|
3eab4faf0b | ||
|
|
c6589a772b | ||
|
|
cb06c4ff77 | ||
|
|
78316fbb75 | ||
|
|
c7df82652f | ||
|
|
342a1a7faa | ||
|
|
17431bd42c | ||
|
|
eacf663999 | ||
|
|
84a806be08 | ||
|
|
7db5c86dc8 | ||
|
|
1e82b5c580 | ||
|
|
7f6be665f9 | ||
|
|
174b00cd29 | ||
|
|
a7c92f491d | ||
|
|
592cd3747b | ||
|
|
4295ee0bb4 | ||
|
|
7e1ff300f8 | ||
|
|
b3ddec2b29 | ||
|
|
227f6e333e | ||
|
|
94711ca506 | ||
|
|
d53052f27a | ||
|
|
95d57c338a | ||
|
|
4fd7a65a52 | ||
|
|
0e3c1f867d | ||
|
|
fa24edf89c | ||
|
|
ac3e803a36 | ||
|
|
8168ddca4f | ||
|
|
cc264be644 | ||
|
|
9bd2d1ec90 | ||
|
|
01b2c48161 | ||
|
|
a26792418e | ||
|
|
485834526f | ||
|
|
5e68ea41e9 |
@@ -24,14 +24,15 @@ schedules:
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-5
|
||||
- stable-3
|
||||
- stable-4
|
||||
- cron: 0 11 * * 0
|
||||
displayName: Weekly (old stable branches)
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-3
|
||||
- stable-1
|
||||
- stable-2
|
||||
|
||||
variables:
|
||||
- name: checkoutPath
|
||||
@@ -48,7 +49,7 @@ variables:
|
||||
resources:
|
||||
containers:
|
||||
- container: default
|
||||
image: quay.io/ansible/azure-pipelines-test-container:3.0.0
|
||||
image: quay.io/ansible/azure-pipelines-test-container:1.9.0
|
||||
|
||||
pool: Standard
|
||||
|
||||
@@ -68,19 +69,6 @@ stages:
|
||||
- test: 3
|
||||
- test: 4
|
||||
- test: extra
|
||||
- stage: Sanity_2_13
|
||||
displayName: Sanity 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.13/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_12
|
||||
displayName: Sanity 2.12
|
||||
dependsOn: []
|
||||
@@ -107,6 +95,32 @@ stages:
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_10
|
||||
displayName: Sanity 2.10
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.10/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_9
|
||||
displayName: Sanity 2.9
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.9/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
### Units
|
||||
- stage: Units_devel
|
||||
displayName: Units devel
|
||||
@@ -124,19 +138,6 @@ stages:
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- test: '3.10'
|
||||
- stage: Units_2_13
|
||||
displayName: Units 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.13/units/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- stage: Units_2_12
|
||||
displayName: Units 2.12
|
||||
dependsOn: []
|
||||
@@ -147,8 +148,12 @@ stages:
|
||||
testFormat: 2.12/units/{0}/1
|
||||
targets:
|
||||
- test: 2.6
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
- test: '3.10'
|
||||
- stage: Units_2_11
|
||||
displayName: Units 2.11
|
||||
dependsOn: []
|
||||
@@ -162,7 +167,35 @@ stages:
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- stage: Units_2_10
|
||||
displayName: Units 2.10
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.10/units/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- stage: Units_2_9
|
||||
displayName: Units 2.9
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.9/units/{0}/1
|
||||
targets:
|
||||
- test: 2.6
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
|
||||
## Remote
|
||||
- stage: Remote_devel
|
||||
@@ -177,28 +210,12 @@ stages:
|
||||
test: macos/12.0
|
||||
- name: RHEL 7.9
|
||||
test: rhel/7.9
|
||||
- name: RHEL 9.0
|
||||
test: rhel/9.0
|
||||
- name: FreeBSD 12.3
|
||||
test: freebsd/12.3
|
||||
- name: FreeBSD 13.1
|
||||
test: freebsd/13.1
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_13
|
||||
displayName: Remote 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.13/{0}
|
||||
targets:
|
||||
- name: macOS 12.0
|
||||
test: macos/12.0
|
||||
- name: RHEL 8.5
|
||||
test: rhel/8.5
|
||||
- name: FreeBSD 12.3
|
||||
test: freebsd/12.3
|
||||
- name: FreeBSD 13.0
|
||||
test: freebsd/13.0
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -220,7 +237,6 @@ stages:
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_11
|
||||
displayName: Remote 2.11
|
||||
dependsOn: []
|
||||
@@ -233,12 +249,43 @@ stages:
|
||||
test: rhel/7.9
|
||||
- name: RHEL 8.3
|
||||
test: rhel/8.3
|
||||
#- name: FreeBSD 12.2
|
||||
# test: freebsd/12.2
|
||||
- name: FreeBSD 12.2
|
||||
test: freebsd/12.2
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- stage: Remote_2_10
|
||||
displayName: Remote 2.10
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.10/{0}
|
||||
targets:
|
||||
- name: OS X 10.11
|
||||
test: osx/10.11
|
||||
- name: macOS 10.15
|
||||
test: macos/10.15
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- stage: Remote_2_9
|
||||
displayName: Remote 2.9
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.9/{0}
|
||||
targets:
|
||||
- name: RHEL 8.2
|
||||
test: rhel/8.2
|
||||
- name: RHEL 7.8
|
||||
test: rhel/7.8
|
||||
- name: FreeBSD 12.0
|
||||
test: freebsd/12.0
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
|
||||
### Docker
|
||||
- stage: Docker_devel
|
||||
@@ -251,34 +298,18 @@ stages:
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 35
|
||||
test: fedora35
|
||||
- name: Fedora 36
|
||||
test: fedora36
|
||||
- name: openSUSE 15
|
||||
test: opensuse15
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_13
|
||||
displayName: Docker 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.13/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 34
|
||||
test: fedora34
|
||||
- name: Fedora 35
|
||||
test: fedora35
|
||||
- name: openSUSE 15 py2
|
||||
test: opensuse15py2
|
||||
- name: openSUSE 15 py3
|
||||
test: opensuse15
|
||||
- name: Ubuntu 18.04
|
||||
test: ubuntu1804
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
@@ -297,8 +328,10 @@ stages:
|
||||
test: centos6
|
||||
- name: Fedora 34
|
||||
test: fedora34
|
||||
- name: Ubuntu 18.04
|
||||
test: ubuntu1804
|
||||
- name: openSUSE 15 py3
|
||||
test: opensuse15
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -311,14 +344,45 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.11/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 32
|
||||
test: fedora32
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 33
|
||||
test: fedora33
|
||||
- name: openSUSE 15 py2
|
||||
test: opensuse15py2
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_10
|
||||
displayName: Docker 2.10
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.10/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 32
|
||||
test: fedora32
|
||||
- name: Ubuntu 16.04
|
||||
test: ubuntu1604
|
||||
groups:
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_9
|
||||
displayName: Docker 2.9
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.9/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 31
|
||||
test: fedora31
|
||||
- name: openSUSE 15 py3
|
||||
test: opensuse15
|
||||
groups:
|
||||
- 2
|
||||
- 3
|
||||
|
||||
@@ -353,16 +417,6 @@ stages:
|
||||
testFormat: devel/cloud/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: '3.10'
|
||||
- stage: Cloud_2_13
|
||||
displayName: Cloud 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.13/cloud/{0}/1
|
||||
targets:
|
||||
- test: 3.9
|
||||
- stage: Cloud_2_12
|
||||
displayName: Cloud 2.12
|
||||
@@ -383,32 +437,55 @@ stages:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.11/cloud/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- stage: Cloud_2_10
|
||||
displayName: Cloud 2.10
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.10/cloud/{0}/1
|
||||
targets:
|
||||
- test: 3.5
|
||||
|
||||
- stage: Cloud_2_9
|
||||
displayName: Cloud 2.9
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.9/cloud/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- stage: Summary
|
||||
condition: succeededOrFailed()
|
||||
dependsOn:
|
||||
- Sanity_devel
|
||||
- Sanity_2_9
|
||||
- Sanity_2_10
|
||||
- Sanity_2_11
|
||||
- Sanity_2_12
|
||||
- Sanity_2_13
|
||||
- Units_devel
|
||||
- Units_2_9
|
||||
- Units_2_10
|
||||
- Units_2_11
|
||||
- Units_2_12
|
||||
- Units_2_13
|
||||
- Remote_devel
|
||||
- Remote_2_9
|
||||
- Remote_2_10
|
||||
- Remote_2_11
|
||||
- Remote_2_12
|
||||
- Remote_2_13
|
||||
- Docker_devel
|
||||
- Docker_2_9
|
||||
- Docker_2_10
|
||||
- Docker_2_11
|
||||
- Docker_2_12
|
||||
- Docker_2_13
|
||||
- Docker_community_devel
|
||||
- Cloud_devel
|
||||
- Cloud_2_9
|
||||
- Cloud_2_10
|
||||
- Cloud_2_11
|
||||
- Cloud_2_12
|
||||
- Cloud_2_13
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
||||
75
.github/BOTMETA.yml
vendored
75
.github/BOTMETA.yml
vendored
@@ -126,42 +126,22 @@ files:
|
||||
maintainers: giner
|
||||
$filters/from_csv.py:
|
||||
maintainers: Ajpantuso
|
||||
$filters/groupby_as_dict.py:
|
||||
$filters/groupby:
|
||||
maintainers: felixfontein
|
||||
$filters/hashids.py:
|
||||
maintainers: Ajpantuso
|
||||
$filters/hashids_decode.yml:
|
||||
maintainers: Ajpantuso
|
||||
$filters/hashids_encode.yml:
|
||||
$filters/hashids:
|
||||
maintainers: Ajpantuso
|
||||
$filters/jc.py:
|
||||
maintainers: kellyjonbrazil
|
||||
$filters/json_query.py: {}
|
||||
$filters/lists_mergeby.py:
|
||||
$filters/list.py:
|
||||
maintainers: vbotka
|
||||
$filters/path_join_shim.py:
|
||||
maintainers: felixfontein
|
||||
$filters/random_mac.py: {}
|
||||
$filters/time.py:
|
||||
maintainers: resmo
|
||||
$filters/unicode_normalize.py:
|
||||
maintainers: Ajpantuso
|
||||
$filters/to_days.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_hours.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_milliseconds.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_minutes.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_months.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_seconds.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_time_unit.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_weeks.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_years.yml:
|
||||
maintainers: resmo
|
||||
$filters/version_sort.py:
|
||||
maintainers: ericzolf
|
||||
$inventories/:
|
||||
@@ -197,8 +177,6 @@ files:
|
||||
$inventories/virtualbox.py: {}
|
||||
$lookups/:
|
||||
labels: lookups
|
||||
$lookups/bitwarden.py:
|
||||
maintainers: lungj
|
||||
$lookups/cartesian.py: {}
|
||||
$lookups/chef_databag.py: {}
|
||||
$lookups/collection_version.py:
|
||||
@@ -216,7 +194,7 @@ files:
|
||||
$lookups/dnstxt.py:
|
||||
maintainers: jpmens
|
||||
$lookups/dsv.py:
|
||||
maintainers: amigus endlesstrax delineaKrehl tylerezimmerman
|
||||
maintainers: amigus endlesstrax
|
||||
$lookups/etcd3.py:
|
||||
maintainers: eric-belhomme
|
||||
$lookups/etcd.py:
|
||||
@@ -253,12 +231,9 @@ files:
|
||||
maintainers: RevBits
|
||||
$lookups/shelvefile.py: {}
|
||||
$lookups/tss.py:
|
||||
maintainers: amigus endlesstrax delineaKrehl tylerezimmerman
|
||||
maintainers: amigus endlesstrax
|
||||
$module_utils/:
|
||||
labels: module_utils
|
||||
$module_utils/gconftool2.py:
|
||||
maintainers: russoz
|
||||
labels: gconftool2
|
||||
$module_utils/gitlab.py:
|
||||
notify: jlozadad
|
||||
maintainers: $team_gitlab
|
||||
@@ -285,8 +260,6 @@ files:
|
||||
$module_utils/module_helper.py:
|
||||
maintainers: russoz
|
||||
labels: module_helper
|
||||
$module_utils/net_tools/pritunl/:
|
||||
maintainers: Lowess
|
||||
$module_utils/oracle/oci_utils.py:
|
||||
maintainers: $team_oracle
|
||||
labels: cloud
|
||||
@@ -306,15 +279,9 @@ files:
|
||||
$module_utils/utm_utils.py:
|
||||
maintainers: $team_e_spirit
|
||||
labels: utm_utils
|
||||
$module_utils/wdc_redfish_utils.py:
|
||||
maintainers: $team_wdc
|
||||
labels: wdc_redfish_utils
|
||||
$module_utils/xenserver.py:
|
||||
maintainers: bvitnik
|
||||
labels: xenserver
|
||||
$module_utils/xfconf.py:
|
||||
maintainers: russoz
|
||||
labels: xfconf
|
||||
$modules/cloud/alicloud/:
|
||||
maintainers: xiaozhu36
|
||||
$modules/cloud/atomic/atomic_container.py:
|
||||
@@ -343,8 +310,6 @@ files:
|
||||
ignore: hnakamur
|
||||
$modules/cloud/lxd/lxd_profile.py:
|
||||
maintainers: conloos
|
||||
$modules/cloud/lxd/lxd_project.py:
|
||||
maintainers: we10710aa
|
||||
$modules/cloud/memset/:
|
||||
maintainers: glitchcrab
|
||||
$modules/cloud/misc/cloud_init_data_facts.py:
|
||||
@@ -445,8 +410,6 @@ files:
|
||||
maintainers: claco
|
||||
$modules/cloud/scaleway/:
|
||||
maintainers: $team_scaleway
|
||||
$modules/cloud/scaleway/scaleway_compute_private_network.py:
|
||||
maintainers: pastral
|
||||
$modules/cloud/scaleway/scaleway_database_backup.py:
|
||||
maintainers: guillaume_ro_fr
|
||||
$modules/cloud/scaleway/scaleway_image_info.py:
|
||||
@@ -595,8 +558,6 @@ files:
|
||||
maintainers: phumpal
|
||||
labels: airbrake_deployment
|
||||
ignore: bpennypacker
|
||||
$modules/monitoring/alerta_customer.py:
|
||||
maintainers: cwollinger
|
||||
$modules/monitoring/bigpanda.py:
|
||||
maintainers: hkariti
|
||||
$modules/monitoring/circonus_annotation.py:
|
||||
@@ -628,7 +589,7 @@ files:
|
||||
$modules/monitoring/nagios.py:
|
||||
maintainers: tbielawa tgoetheyn
|
||||
$modules/monitoring/newrelic_deployment.py:
|
||||
ignore: mcodd
|
||||
maintainers: mcodd
|
||||
$modules/monitoring/pagerduty.py:
|
||||
maintainers: suprememoocow thaumos
|
||||
labels: pagerduty
|
||||
@@ -720,7 +681,7 @@ files:
|
||||
$modules/notification/discord.py:
|
||||
maintainers: cwollinger
|
||||
$modules/notification/flowdock.py:
|
||||
ignore: mcodd
|
||||
maintainers: mcodd
|
||||
$modules/notification/grove.py:
|
||||
maintainers: zimbatm
|
||||
$modules/notification/hipchat.py:
|
||||
@@ -928,7 +889,7 @@ files:
|
||||
$modules/packaging/os/xbps.py:
|
||||
maintainers: dinoocch the-maldridge
|
||||
$modules/packaging/os/yum_versionlock.py:
|
||||
maintainers: gyptazy aminvakil
|
||||
maintainers: florianpaulhoberg aminvakil
|
||||
$modules/packaging/os/zypper.py:
|
||||
maintainers: $team_suse
|
||||
labels: zypper
|
||||
@@ -973,10 +934,6 @@ files:
|
||||
$modules/remote_management/redfish/:
|
||||
maintainers: $team_redfish
|
||||
ignore: jose-delarosa
|
||||
$modules/remote_management/redfish/wdc_redfish_command.py:
|
||||
maintainers: $team_wdc
|
||||
$modules/remote_management/redfish/wdc_redfish_info.py:
|
||||
maintainers: $team_wdc
|
||||
$modules/remote_management/stacki/stacki_host.py:
|
||||
maintainers: bsanders bbyhuy
|
||||
labels: stacki_host
|
||||
@@ -1041,7 +998,7 @@ files:
|
||||
$modules/system/alternatives.py:
|
||||
maintainers: mulby
|
||||
labels: alternatives
|
||||
ignore: DavidWittman jiuka
|
||||
ignore: DavidWittman
|
||||
$modules/system/aix_lvol.py:
|
||||
maintainers: adejoux
|
||||
$modules/system/awall.py:
|
||||
@@ -1069,9 +1026,6 @@ files:
|
||||
$modules/system/gconftool2.py:
|
||||
maintainers: Akasurde kevensen
|
||||
labels: gconftool2
|
||||
$modules/system/gconftool2_info.py:
|
||||
maintainers: russoz
|
||||
labels: gconftool2
|
||||
$modules/system/homectl.py:
|
||||
maintainers: jameslivulpi
|
||||
$modules/system/interfaces_file.py:
|
||||
@@ -1079,10 +1033,6 @@ files:
|
||||
labels: interfaces_file
|
||||
$modules/system/iptables_state.py:
|
||||
maintainers: quidame
|
||||
$modules/system/keyring.py:
|
||||
maintainers: ahussey-redhat
|
||||
$modules/system/keyring_info.py:
|
||||
maintainers: ahussey-redhat
|
||||
$modules/system/shutdown.py:
|
||||
maintainers: nitzmahone samdoran aminvakil
|
||||
$modules/system/java_cert.py:
|
||||
@@ -1305,6 +1255,5 @@ macros:
|
||||
team_rhn: FlossWare alikins barnabycourt vritant
|
||||
team_scaleway: remyleone abarbare
|
||||
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
|
||||
team_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
|
||||
team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
|
||||
team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso
|
||||
team_wdc: mikemoerk
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -105,7 +105,7 @@ body:
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: |
|
||||
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also passed any playbooks, configs and commands you used.
|
||||
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also pased any playbooks, configs and commands you used.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
|
||||
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -1,7 +1,6 @@
|
||||
---
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval:
|
||||
schedule: "weekly"
|
||||
|
||||
15
.github/workflows/codeql-analysis.yml
vendored
15
.github/workflows/codeql-analysis.yml
vendored
@@ -4,21 +4,14 @@ on:
|
||||
schedule:
|
||||
- cron: '26 19 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
permissions:
|
||||
actions: read # for github/codeql-action/init to get workflow details
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -31,7 +24,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -39,7 +32,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -53,4 +46,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
104
.gitignore
vendored
104
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/vim,git,macos,linux,pydev,emacs,dotenv,python,windows,webstorm,pycharm+all,jupyternotebooks
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,git,macos,linux,pydev,emacs,dotenv,python,windows,webstorm,pycharm+all,jupyternotebooks
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
|
||||
|
||||
### dotenv ###
|
||||
.env
|
||||
@@ -71,19 +71,7 @@ flycheck_*.el
|
||||
*_LOCAL_*.txt
|
||||
*_REMOTE_*.txt
|
||||
|
||||
### JupyterNotebooks ###
|
||||
# gitignore template for Jupyter Notebooks
|
||||
# website: http://jupyter.org/
|
||||
|
||||
.ipynb_checkpoints
|
||||
*/.ipynb_checkpoints/*
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# Remove previous ipynb_checkpoints
|
||||
# git rm -r .ipynb_checkpoints/
|
||||
#!! ERROR: jupyternotebook is undefined. Use list command to see defined gitignore types !!#
|
||||
|
||||
### Linux ###
|
||||
|
||||
@@ -99,39 +87,6 @@ ipython_config.py
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### PyCharm+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
@@ -196,9 +151,6 @@ atlassian-ide-plugin.xml
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
@@ -212,13 +164,20 @@ fabric.properties
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### PyCharm+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/*
|
||||
.idea/
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### pydev ###
|
||||
.pydevproject
|
||||
@@ -301,13 +260,16 @@ docs/_build/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
@@ -316,22 +278,7 @@ target/
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
@@ -373,13 +320,6 @@ dmypy.json
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
@@ -441,8 +381,6 @@ tags
|
||||
|
||||
# Cursive Clojure plugin
|
||||
|
||||
# SonarLint plugin
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
|
||||
# Editor-based Rest Client
|
||||
@@ -505,4 +443,4 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/vim,git,macos,linux,pydev,emacs,dotenv,python,windows,webstorm,pycharm+all,jupyternotebooks
|
||||
# End of https://www.toptal.com/developers/gitignore/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=lf]
|
||||
- id: fix-encoding-pragma
|
||||
- id: check-ast
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.9.0
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
types: [file]
|
||||
files: changelogs/fragments/.*\.(yml|yaml)$
|
||||
1232
CHANGELOG.rst
1232
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -106,15 +106,9 @@ Creating new modules and plugins requires a bit more work than other Pull Reques
|
||||
- Make sure that new plugins and modules have tests (unit tests, integration tests, or both); it is preferable to have some tests
|
||||
which run in CI.
|
||||
|
||||
4. For modules and action plugins, make sure to create your module/plugin in the correct subdirectory, and add a redirect entry
|
||||
in `meta/runtime.yml`. For example, for the `aerospike_migrations` module located in
|
||||
`plugins/modules/database/aerospike/aerospike_migrations.py`, you need to create the following entry:
|
||||
```.yaml
|
||||
aerospike_migrations:
|
||||
redirect: community.general.database.aerospike.aerospike_migrations
|
||||
```
|
||||
Here, the relative path `database/aerospike/` is inserted into the module's FQCN (Fully Qualified Collection Name) after the
|
||||
collection's name and before the module's name. This must not be done for other plugin types but modules and action plugins!
|
||||
4. For modules and action plugins, make sure to create your module/plugin in the correct subdirectory, and create a symbolic link
|
||||
from `plugins/modules/` respectively `plugins/action/` to the actual module/plugin code. (Other plugin types should not use
|
||||
subdirectories.)
|
||||
|
||||
- Action plugins need to be accompanied by a module, even if the module file only contains documentation
|
||||
(`DOCUMENTATION`, `EXAMPLES` and `RETURN`). The module must have the same name and directory path in `plugins/modules/`
|
||||
@@ -125,19 +119,3 @@ Creating new modules and plugins requires a bit more work than other Pull Reques
|
||||
listed as `maintainers` will be pinged for new issues and PRs that modify the module/plugin or its tests.
|
||||
|
||||
When you add a new plugin/module, we expect that you perform maintainer duty for at least some time after contributing it.
|
||||
|
||||
## pre-commit
|
||||
|
||||
To help ensure high-quality contributions this repository includes a [pre-commit](https://pre-commit.com) configuration which
|
||||
corrects and tests against common issues that would otherwise cause CI to fail. To begin using these pre-commit hooks see
|
||||
the [Installation](#installation) section below.
|
||||
|
||||
This is optional and not required to contribute to this repository.
|
||||
|
||||
### Installation
|
||||
|
||||
Follow the [instructions](https://pre-commit.com/#install) provided with pre-commit and run `pre-commit install` under the repository base. If for any reason you would like to disable the pre-commit hooks run `pre-commit uninstall`.
|
||||
|
||||
This is optional to run it locally.
|
||||
|
||||
You can trigger it locally with `pre-commit run --all-files` or even to run only for a given file `pre-commit run --files YOUR_FILE`.
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
16
README.md
16
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.
|
||||
@@ -17,9 +17,7 @@ If you encounter abusive behavior violating the [Ansible Code of Conduct](https:
|
||||
|
||||
## Tested with Ansible
|
||||
|
||||
Tested with the current ansible-core 2.11, ansible-core 2.12, ansible-core 2.13 releases and the current development version of ansible-core. Ansible-core versions before 2.11.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
|
||||
Parts of this collection will not work with ansible-core 2.11 on Python 3.12+.
|
||||
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11, ansible-core 2.12 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
|
||||
|
||||
## External requirements
|
||||
|
||||
@@ -66,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/main/CONTRIBUTING.md)!
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/stable-4/CONTRIBUTING.md)!
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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/main/CONTRIBUTING.md).
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/stable-4/CONTRIBUTING.md).
|
||||
|
||||
### Running tests
|
||||
|
||||
@@ -82,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/main/commit-rights.md).
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/stable-4/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:
|
||||
@@ -110,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-5/CHANGELOG.rst).
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-4/CHANGELOG.rst).
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
---
|
||||
edit_on_github:
|
||||
repository: ansible-collections/community.general
|
||||
branch: main
|
||||
path_prefix: ''
|
||||
|
||||
extra_links:
|
||||
- description: Submit a bug report
|
||||
url: https://github.com/ansible-collections/community.general/issues/new?assignees=&labels=&template=bug_report.yml
|
||||
- description: Request a feature
|
||||
url: https://github.com/ansible-collections/community.general/issues/new?assignees=&labels=&template=feature_request.yml
|
||||
|
||||
communication:
|
||||
matrix_rooms:
|
||||
- topic: General usage and support questions
|
||||
room: '#users:ansible.im'
|
||||
irc_channels:
|
||||
- topic: General usage and support questions
|
||||
network: Libera
|
||||
channel: '#ansible'
|
||||
mailing_lists:
|
||||
- topic: Ansible Project List
|
||||
url: https://groups.google.com/g/ansible-project
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: community
|
||||
name: general
|
||||
version: 5.4.0
|
||||
version: 4.6.1
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
1120
meta/runtime.yml
1120
meta/runtime.yml
File diff suppressed because it is too large
Load Diff
1
plugins/action/iptables_state.py
Symbolic link
1
plugins/action/iptables_state.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./system/iptables_state.py
|
||||
1
plugins/action/shutdown.py
Symbolic link
1
plugins/action/shutdown.py
Symbolic link
@@ -0,0 +1 @@
|
||||
./system/shutdown.py
|
||||
@@ -66,46 +66,15 @@ DOCUMENTATION = '''
|
||||
ini:
|
||||
- section: machinectl_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- When not using this plugin with user C(root), it only works correctly with a polkit rule which will alter
|
||||
the behaviour of machinectl. This rule must alter the prompt behaviour to ask directly for the user credentials,
|
||||
if the user is allowed to perform the action (take a look at the examples section).
|
||||
If such a rule is not present the plugin only work if it is used in context with the root user,
|
||||
because then no further prompt will be shown by machinectl.
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
# A polkit rule needed to use the module with a non-root user.
|
||||
# See the Notes section for details.
|
||||
60-machinectl-fast-user-auth.rules: |
|
||||
polkit.addRule(function(action, subject) {
|
||||
if(action.id == "org.freedesktop.machine1.host-shell" && subject.isInGroup("wheel")) {
|
||||
return polkit.Result.AUTH_SELF_KEEP;
|
||||
}
|
||||
});
|
||||
'''
|
||||
|
||||
from re import compile as re_compile
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
from ansible.module_utils._text import to_bytes
|
||||
|
||||
|
||||
ansi_color_codes = re_compile(to_bytes(r'\x1B\[[0-9;]+m'))
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'community.general.machinectl'
|
||||
|
||||
prompt = 'Password: '
|
||||
fail = ('==== AUTHENTICATION FAILED ====',)
|
||||
success = ('==== AUTHENTICATION COMPLETE ====',)
|
||||
|
||||
@staticmethod
|
||||
def remove_ansi_codes(line):
|
||||
return ansi_color_codes.sub(b"", line)
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
@@ -117,15 +86,3 @@ class BecomeModule(BecomeBase):
|
||||
flags = self.get_option('become_flags')
|
||||
user = self.get_option('become_user')
|
||||
return '%s -q shell %s %s@ %s' % (become, flags, user, cmd)
|
||||
|
||||
def check_success(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_success(b_output)
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_incorrect_password(b_output)
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_missing_password(b_output)
|
||||
|
||||
@@ -8,9 +8,9 @@ DOCUMENTATION = """
|
||||
name: sudosu
|
||||
short_description: Run tasks using sudo su -
|
||||
description:
|
||||
- This become plugin allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
- Dag Wieers (@dagwieers)
|
||||
version_added: 2.4.0
|
||||
options:
|
||||
become_user:
|
||||
|
||||
19
plugins/cache/memcached.py
vendored
19
plugins/cache/memcached.py
vendored
@@ -176,11 +176,20 @@ class CacheModule(BaseCacheModule):
|
||||
def __init__(self, *args, **kwargs):
|
||||
connection = ['127.0.0.1:11211']
|
||||
|
||||
super(CacheModule, self).__init__(*args, **kwargs)
|
||||
if self.get_option('_uri'):
|
||||
connection = self.get_option('_uri')
|
||||
self._timeout = self.get_option('_timeout')
|
||||
self._prefix = self.get_option('_prefix')
|
||||
try:
|
||||
super(CacheModule, self).__init__(*args, **kwargs)
|
||||
if self.get_option('_uri'):
|
||||
connection = self.get_option('_uri')
|
||||
self._timeout = self.get_option('_timeout')
|
||||
self._prefix = self.get_option('_prefix')
|
||||
except KeyError:
|
||||
# TODO: remove once we no longer support Ansible 2.9
|
||||
if not ansible_base_version.startswith('2.9.'):
|
||||
raise AnsibleError("Do not import CacheModules directly. Use ansible.plugins.loader.cache_loader instead.")
|
||||
if C.CACHE_PLUGIN_CONNECTION:
|
||||
connection = C.CACHE_PLUGIN_CONNECTION.split(',')
|
||||
self._timeout = C.CACHE_PLUGIN_TIMEOUT
|
||||
self._prefix = C.CACHE_PLUGIN_PREFIX
|
||||
|
||||
if not HAS_MEMCACHE:
|
||||
raise AnsibleError("python-memcached is required for the memcached fact cache")
|
||||
|
||||
24
plugins/cache/redis.py
vendored
24
plugins/cache/redis.py
vendored
@@ -99,13 +99,23 @@ class CacheModule(BaseCacheModule):
|
||||
def __init__(self, *args, **kwargs):
|
||||
uri = ''
|
||||
|
||||
super(CacheModule, self).__init__(*args, **kwargs)
|
||||
if self.get_option('_uri'):
|
||||
uri = self.get_option('_uri')
|
||||
self._timeout = float(self.get_option('_timeout'))
|
||||
self._prefix = self.get_option('_prefix')
|
||||
self._keys_set = self.get_option('_keyset_name')
|
||||
self._sentinel_service_name = self.get_option('_sentinel_service_name')
|
||||
try:
|
||||
super(CacheModule, self).__init__(*args, **kwargs)
|
||||
if self.get_option('_uri'):
|
||||
uri = self.get_option('_uri')
|
||||
self._timeout = float(self.get_option('_timeout'))
|
||||
self._prefix = self.get_option('_prefix')
|
||||
self._keys_set = self.get_option('_keyset_name')
|
||||
self._sentinel_service_name = self.get_option('_sentinel_service_name')
|
||||
except KeyError:
|
||||
# TODO: remove once we no longer support Ansible 2.9
|
||||
if not ansible_base_version.startswith('2.9.'):
|
||||
raise AnsibleError("Do not import CacheModules directly. Use ansible.plugins.loader.cache_loader instead.")
|
||||
if C.CACHE_PLUGIN_CONNECTION:
|
||||
uri = C.CACHE_PLUGIN_CONNECTION
|
||||
self._timeout = float(C.CACHE_PLUGIN_TIMEOUT)
|
||||
self._prefix = C.CACHE_PLUGIN_PREFIX
|
||||
self._keys_set = 'ansible_cache_keys'
|
||||
|
||||
if not HAS_REDIS:
|
||||
raise AnsibleError("The 'redis' python module (version 2.4.5 or newer) is required for the redis fact cache, 'pip install redis'")
|
||||
|
||||
@@ -12,7 +12,7 @@ DOCUMENTATION = '''
|
||||
type: notification
|
||||
short_description: write playbook output to log file
|
||||
description:
|
||||
- This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory
|
||||
- This callback writes playbook output to a file per host in the `/var/log/ansible/hosts` directory
|
||||
requirements:
|
||||
- Whitelist in configuration
|
||||
- A writeable /var/log/ansible/hosts directory by the user executing Ansible on the controller
|
||||
|
||||
@@ -45,7 +45,7 @@ DOCUMENTATION = r'''
|
||||
version_added: 1.0.0
|
||||
default: ansible
|
||||
pre_command:
|
||||
description: Executes command before run and its result is added to the C(ansible_pre_command_output) logstash field.
|
||||
description: Executes command before run and result put to ansible_pre_command_output field.
|
||||
version_added: 2.0.0
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
|
||||
@@ -24,10 +24,6 @@ DOCUMENTATION = '''
|
||||
- Hide the arguments for a task.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: hide_task_arguments
|
||||
version_added: 5.3.0
|
||||
enable_from_environment:
|
||||
type: str
|
||||
description:
|
||||
@@ -38,10 +34,6 @@ DOCUMENTATION = '''
|
||||
and if set to true this plugin will be enabled.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_ENABLE_FROM_ENVIRONMENT
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: enable_from_environment
|
||||
version_added: 5.3.0
|
||||
version_added: 3.8.0
|
||||
otel_service_name:
|
||||
default: ansible
|
||||
@@ -50,10 +42,6 @@ DOCUMENTATION = '''
|
||||
- The service name resource attribute.
|
||||
env:
|
||||
- name: OTEL_SERVICE_NAME
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: otel_service_name
|
||||
version_added: 5.3.0
|
||||
traceparent:
|
||||
default: None
|
||||
type: str
|
||||
@@ -73,14 +61,11 @@ examples: |
|
||||
Enable the plugin in ansible.cfg:
|
||||
[defaults]
|
||||
callbacks_enabled = community.general.opentelemetry
|
||||
[callback_opentelemetry]
|
||||
enable_from_environment = ANSIBLE_OPENTELEMETRY_ENABLED
|
||||
|
||||
Set the environment variable:
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=<your endpoint (OTLP/HTTP)>
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer your_otel_token"
|
||||
export OTEL_SERVICE_NAME=your_service_name
|
||||
export ANSIBLE_OPENTELEMETRY_ENABLED=true
|
||||
'''
|
||||
|
||||
import getpass
|
||||
@@ -212,7 +197,7 @@ class OpenTelemetrySource(object):
|
||||
|
||||
task = tasks_data[task_uuid]
|
||||
|
||||
if self.ansible_version is None and hasattr(result, '_task_fields') and result._task_fields['args'].get('_ansible_version'):
|
||||
if self.ansible_version is None and result._task_fields['args'].get('_ansible_version'):
|
||||
self.ansible_version = result._task_fields['args'].get('_ansible_version')
|
||||
|
||||
task.add_host(HostData(host_uuid, host_name, status, result))
|
||||
@@ -273,9 +258,8 @@ class OpenTelemetrySource(object):
|
||||
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)
|
||||
message = self.get_error_message(res)
|
||||
enriched_error_message = self.enrich_error_message(res)
|
||||
|
||||
if host_data.status == 'failed':
|
||||
status = Status(status_code=StatusCode.ERROR, description=message)
|
||||
|
||||
1
plugins/callback/osx_say.py
Symbolic link
1
plugins/callback/osx_say.py
Symbolic link
@@ -0,0 +1 @@
|
||||
say.py
|
||||
@@ -14,9 +14,9 @@ DOCUMENTATION = '''
|
||||
- set as main display callback
|
||||
short_description: only print certain tasks
|
||||
description:
|
||||
- This callback only prints tasks that have been tagged with C(print_action) or that have failed.
|
||||
- This callback only prints tasks that have been tagged with `print_action` or that have failed.
|
||||
This allows operators to focus on the tasks that provide value only.
|
||||
- Tasks that are not printed are placed with a C(.).
|
||||
- Tasks that are not printed are placed with a '.'.
|
||||
- If you increase verbosity all tasks are printed.
|
||||
options:
|
||||
nocolor:
|
||||
|
||||
@@ -15,7 +15,7 @@ DOCUMENTATION = '''
|
||||
short_description: sends JSON events to syslog
|
||||
description:
|
||||
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format
|
||||
- Before Ansible 2.9 only environment variables were available for configuration
|
||||
- Before 2.9 only environment variables were available for configuration
|
||||
options:
|
||||
server:
|
||||
description: syslog server that will receive the event
|
||||
|
||||
@@ -18,7 +18,6 @@ DOCUMENTATION = '''
|
||||
- Container identifier.
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: inventory_hostname
|
||||
- name: ansible_host
|
||||
- name: ansible_lxd_host
|
||||
executable:
|
||||
@@ -62,6 +61,7 @@ class Connection(ConnectionBase):
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
||||
self._host = self._play_context.remote_addr
|
||||
try:
|
||||
self._lxc_cmd = get_bin_path("lxc")
|
||||
except ValueError:
|
||||
@@ -75,14 +75,14 @@ class Connection(ConnectionBase):
|
||||
super(Connection, self)._connect()
|
||||
|
||||
if not self._connected:
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self._host)
|
||||
self._connected = True
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
""" execute a command on the lxd host """
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self._host)
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
@@ -104,10 +104,10 @@ class Connection(ConnectionBase):
|
||||
stderr = to_text(stderr)
|
||||
|
||||
if stderr == "error: Container is not running.\n":
|
||||
raise AnsibleConnectionFailure("container not running: %s" % self.get_option('remote_addr'))
|
||||
raise AnsibleConnectionFailure("container not running: %s" % self._host)
|
||||
|
||||
if stderr == "error: not found\n":
|
||||
raise AnsibleConnectionFailure("container not found: %s" % self.get_option('remote_addr'))
|
||||
raise AnsibleConnectionFailure("container not found: %s" % self._host)
|
||||
|
||||
return process.returncode, stdout, stderr
|
||||
|
||||
@@ -115,7 +115,7 @@ class Connection(ConnectionBase):
|
||||
""" put a file from local to lxd """
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._host)
|
||||
|
||||
if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')):
|
||||
raise AnsibleFileNotFound("input path is not a file: %s" % in_path)
|
||||
@@ -138,7 +138,7 @@ class Connection(ConnectionBase):
|
||||
""" fetch a file from lxd to local """
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self._host)
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
|
||||
@@ -68,10 +68,4 @@ options:
|
||||
type: int
|
||||
default: 10
|
||||
version_added: 4.5.0
|
||||
http_agent:
|
||||
description:
|
||||
- Configures the HTTP User-Agent header.
|
||||
type: str
|
||||
default: Ansible
|
||||
version_added: 5.4.0
|
||||
'''
|
||||
|
||||
@@ -38,10 +38,8 @@ options:
|
||||
version_added: 2.0.0
|
||||
server_uri:
|
||||
description:
|
||||
- The I(server_uri) parameter may be a comma- or whitespace-separated list of URIs containing only the schema, the host, and the port fields.
|
||||
- A URI to the LDAP server.
|
||||
- The default value lets the underlying LDAP client library look for a UNIX domain socket in its default location.
|
||||
- Note that when using multiple URIs you cannot determine to which URI your client gets connected.
|
||||
- For URIs containing additional fields, particularly when using commas, behavior is undefined.
|
||||
type: str
|
||||
default: ldapi:///
|
||||
start_tls:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2017 Lenovo, Inc.
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -5,35 +5,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: counter
|
||||
short_description: Counts hashable elements in a sequence
|
||||
version_added: 4.3.0
|
||||
author: Rémy Keil (@keilr)
|
||||
description:
|
||||
- Counts hashable elements in a sequence.
|
||||
options:
|
||||
_input:
|
||||
description: A sequence.
|
||||
type: list
|
||||
elements: any
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Count occurrences
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ [1, 'a', 2, 2, 'a', 'b', 'a'] | community.general.counter }}
|
||||
# Produces: {1: 1, 'a': 3, 2: 2, 'b': 1}
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: A dictionary with the elements of the sequence as keys, and their number of occurrences in the sequence as values.
|
||||
type: dictionary
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common._collections_compat import Sequence
|
||||
from collections import Counter
|
||||
|
||||
@@ -6,60 +6,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: dict
|
||||
short_description: Convert a list of tuples into a dictionary
|
||||
version_added: 3.0.0
|
||||
author: Felix Fontein (@felixfontein)
|
||||
description:
|
||||
- Convert a list of tuples into a dictionary. This is a filter version of the C(dict) function.
|
||||
options:
|
||||
_input:
|
||||
description: A list of tuples (with exactly two elements).
|
||||
type: list
|
||||
elements: tuple
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Convert list of tuples into dictionary
|
||||
ansible.builtin.set_fact:
|
||||
dictionary: "{{ [[1, 2], ['a', 'b']] | community.general.dict }}"
|
||||
# Result is {1: 2, 'a': 'b'}
|
||||
|
||||
- name: Create a list of dictionaries with map and the community.general.dict filter
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ values | map('zip', ['k1', 'k2', 'k3'])
|
||||
| map('map', 'reverse')
|
||||
| map('community.general.dict') }}
|
||||
vars:
|
||||
values:
|
||||
- - foo
|
||||
- 23
|
||||
- a
|
||||
- - bar
|
||||
- 42
|
||||
- b
|
||||
# Produces the following list of dictionaries:
|
||||
# {
|
||||
# "k1": "foo",
|
||||
# "k2": 23,
|
||||
# "k3": "a"
|
||||
# },
|
||||
# {
|
||||
# "k1": "bar",
|
||||
# "k2": 42,
|
||||
# "k3": "b"
|
||||
# }
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The dictionary having the provided key-value pairs.
|
||||
type: boolean
|
||||
'''
|
||||
|
||||
|
||||
def dict_filter(sequence):
|
||||
'''Convert a list of tuples to a dictionary.
|
||||
|
||||
@@ -5,38 +5,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: dict_kv
|
||||
short_description: Convert a value to a dictionary with a single key-value pair
|
||||
version_added: 1.3.0
|
||||
author: Stanislav German-Evtushenko (@giner)
|
||||
description:
|
||||
- Convert a value to a dictionary with a single key-value pair.
|
||||
positional: key
|
||||
options:
|
||||
_input:
|
||||
description: The value for the single key-value pair.
|
||||
type: any
|
||||
required: true
|
||||
key:
|
||||
description: The key for the single key-value pair.
|
||||
type: any
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a one-element dictionary from a value
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ 'myvalue' | dict_kv('mykey') }}"
|
||||
# Produces the dictionary {'mykey': 'myvalue'}
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: A dictionary with a single key-value pair.
|
||||
type: dictionary
|
||||
'''
|
||||
|
||||
|
||||
def dict_kv(value, key):
|
||||
'''Return a dictionary with a single key-value pair
|
||||
|
||||
@@ -7,78 +7,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: from_csv
|
||||
short_description: Converts CSV text input into list of dicts
|
||||
version_added: 2.3.0
|
||||
author: Andrew Pantuso (@Ajpantuso)
|
||||
description:
|
||||
- Converts CSV text input into list of dictionaries.
|
||||
options:
|
||||
_input:
|
||||
description: A string containing a CSV document.
|
||||
type: string
|
||||
required: true
|
||||
dialect:
|
||||
description:
|
||||
- The CSV dialect to use when parsing the CSV file.
|
||||
- Possible values include C(excel), C(excel-tab) or C(unix).
|
||||
type: str
|
||||
default: excel
|
||||
fieldnames:
|
||||
description:
|
||||
- A list of field names for every column.
|
||||
- This is needed if the CSV does not have a header.
|
||||
type: list
|
||||
elements: str
|
||||
delimiter:
|
||||
description:
|
||||
- A one-character string used to separate fields.
|
||||
- When using this parameter, you change the default value used by I(dialect).
|
||||
- The default value depends on the dialect used.
|
||||
type: str
|
||||
skipinitialspace:
|
||||
description:
|
||||
- Whether to ignore any whitespaces immediately following the delimiter.
|
||||
- When using this parameter, you change the default value used by I(dialect).
|
||||
- The default value depends on the dialect used.
|
||||
type: bool
|
||||
strict:
|
||||
description:
|
||||
- Whether to raise an exception on bad CSV input.
|
||||
- When using this parameter, you change the default value used by I(dialect).
|
||||
- The default value depends on the dialect used.
|
||||
type: bool
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Parse a CSV file's contents
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ csv_data | community.genera.from_csv(dialect='unix') }}
|
||||
vars:
|
||||
csv_data: |
|
||||
Column 1,Value
|
||||
foo,23
|
||||
bar,42
|
||||
# Produces the following list of dictionaries:
|
||||
# {
|
||||
# "Column 1": "foo",
|
||||
# "Value": "23",
|
||||
# },
|
||||
# {
|
||||
# "Column 1": "bar",
|
||||
# "Value": "42",
|
||||
# }
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: A list with one dictionary per row.
|
||||
type: list
|
||||
elements: dictionary
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
@@ -5,52 +5,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: groupby_as_dict
|
||||
short_description: Transform a sequence of dictionaries to a dictionary where the dictionaries are indexed by an attribute
|
||||
version_added: 3.1.0
|
||||
author: Felix Fontein (@felixfontein)
|
||||
description:
|
||||
- Transform a sequence of dictionaries to a dictionary where the dictionaries are indexed by an attribute.
|
||||
positional: attribute
|
||||
options:
|
||||
_input:
|
||||
description: A list of dictionaries
|
||||
type: list
|
||||
elements: dictionary
|
||||
required: true
|
||||
attribute:
|
||||
description: The attribute to use as the key.
|
||||
type: str
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Arrange a list of dictionaries as a dictionary of dictionaries
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ sequence | community.general.groupby_as_dict('key') }}"
|
||||
vars:
|
||||
sequence:
|
||||
- key: value
|
||||
foo: bar
|
||||
- key: other_value
|
||||
baz: bar
|
||||
# Produces the following nested structure:
|
||||
#
|
||||
# value:
|
||||
# key: value
|
||||
# foo: bar
|
||||
# other_value:
|
||||
# key: other_value
|
||||
# baz: bar
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: A dictionary containing the dictionaries from the list as values.
|
||||
type: dictionary
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common._collections_compat import Mapping, Sequence
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: hashids_decode
|
||||
short_description: Decodes a sequence of numbers from a YouTube-like hash
|
||||
version_added: 3.0.0
|
||||
author: Andrew Pantuso (@Ajpantuso)
|
||||
description:
|
||||
- Decodes a sequence of numbers from a YouTube-like hash.
|
||||
options:
|
||||
_input:
|
||||
description: A YouTube-like hash.
|
||||
type: string
|
||||
required: true
|
||||
salt:
|
||||
description:
|
||||
- String to use as salt when hashing.
|
||||
type: str
|
||||
default: excel
|
||||
alphabet:
|
||||
description:
|
||||
- String of 16 or more unique characters to produce a hash.
|
||||
type: list
|
||||
elements: str
|
||||
min_length:
|
||||
description:
|
||||
- Minimum length of hash produced.
|
||||
type: integer
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert hash to list of integers
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ 'o2fXhV' | community.general.hashids_decode }}"
|
||||
# Produces: [1, 2, 3]
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: A list of integers.
|
||||
type: list
|
||||
elements: integer
|
||||
@@ -1,38 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: hashids_encode
|
||||
short_description: Encodes YouTube-like hashes from a sequence of integers
|
||||
version_added: 3.0.0
|
||||
author: Andrew Pantuso (@Ajpantuso)
|
||||
description:
|
||||
- Encodes YouTube-like hashes from a sequence of integers.
|
||||
options:
|
||||
_input:
|
||||
description: A list of integers.
|
||||
type: list
|
||||
elements: integer
|
||||
required: true
|
||||
salt:
|
||||
description:
|
||||
- String to use as salt when hashing.
|
||||
type: str
|
||||
default: excel
|
||||
alphabet:
|
||||
description:
|
||||
- String of 16 or more unique characters to produce a hash.
|
||||
type: list
|
||||
elements: str
|
||||
min_length:
|
||||
description:
|
||||
- Minimum length of hash produced.
|
||||
type: integer
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert list of integers to hash
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ [1, 2, 3] | community.general.hashids_encode }}"
|
||||
# Produces: 'o2fXhV'
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: A YouTube-like hash.
|
||||
type: string
|
||||
@@ -21,67 +21,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: jc
|
||||
short_description: Convert output of many shell commands and file-types to JSON
|
||||
version_added: 1.1.0
|
||||
author: Kelly Brazil (@kellyjonbrazil)
|
||||
description:
|
||||
- Convert output of many shell commands and file-types to JSON.
|
||||
- Uses the L(jc library,https://github.com/kellyjonbrazil/jc).
|
||||
positional: parser
|
||||
options:
|
||||
_input:
|
||||
description: The data to convert.
|
||||
type: string
|
||||
required: true
|
||||
parser:
|
||||
description:
|
||||
- The correct parser for the input data.
|
||||
- For example C(ifconfig).
|
||||
- See U(https://github.com/kellyjonbrazil/jc#parsers) for the latest list of parsers.
|
||||
type: string
|
||||
required: true
|
||||
quiet:
|
||||
description: Set to C(false) to not suppress warnings.
|
||||
type: boolean
|
||||
default: true
|
||||
raw:
|
||||
description: Set to C(true) to return pre-processed JSON.
|
||||
type: boolean
|
||||
default: false
|
||||
requirements:
|
||||
- jc (https://github.com/kellyjonbrazil/jc)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Run command
|
||||
ansible.builtin.command: uname -a
|
||||
register: result
|
||||
|
||||
- name: Convert command's result to JSON
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ result.stdout | community.general.jc('uname') }}"
|
||||
# Possible output:
|
||||
#
|
||||
# "msg": {
|
||||
# "hardware_platform": "x86_64",
|
||||
# "kernel_name": "Linux",
|
||||
# "kernel_release": "4.15.0-112-generic",
|
||||
# "kernel_version": "#113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020",
|
||||
# "machine": "x86_64",
|
||||
# "node_name": "kbrazil-ubuntu",
|
||||
# "operating_system": "GNU/Linux",
|
||||
# "processor": "x86_64"
|
||||
# }
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The processed output.
|
||||
type: any
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleFilterError
|
||||
import importlib
|
||||
|
||||
|
||||
@@ -19,107 +19,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: json_query
|
||||
short_description: Select a single element or a data subset from a complex data structure
|
||||
description:
|
||||
- This filter lets you query a complex JSON structure and iterate over it using a loop structure.
|
||||
positional: expr
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The JSON data to query.
|
||||
type: any
|
||||
required: true
|
||||
expr:
|
||||
description:
|
||||
- The query expression.
|
||||
- See U(http://jmespath.org/examples.html) for examples.
|
||||
type: string
|
||||
required: true
|
||||
requirements:
|
||||
- jmespath
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Define data to work on in the examples below
|
||||
ansible.builtin.set_fact:
|
||||
domain_definition:
|
||||
domain:
|
||||
cluster:
|
||||
- name: cluster1
|
||||
- name: cluster2
|
||||
server:
|
||||
- name: server11
|
||||
cluster: cluster1
|
||||
port: '8080'
|
||||
- name: server12
|
||||
cluster: cluster1
|
||||
port: '8090'
|
||||
- name: server21
|
||||
cluster: cluster2
|
||||
port: '9080'
|
||||
- name: server22
|
||||
cluster: cluster2
|
||||
port: '9090'
|
||||
library:
|
||||
- name: lib1
|
||||
target: cluster1
|
||||
- name: lib2
|
||||
target: cluster2
|
||||
|
||||
- name: Display all cluster names
|
||||
ansible.builtin.debug:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}"
|
||||
|
||||
- name: Display all server names
|
||||
ansible.builtin.debug:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}"
|
||||
|
||||
- name: Display all ports from cluster1
|
||||
ansible.builtin.debug:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
|
||||
vars:
|
||||
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"
|
||||
|
||||
- name: Display all ports from cluster1 as a string
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}"
|
||||
|
||||
- name: Display all ports from cluster1
|
||||
ansible.builtin.debug:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}"
|
||||
|
||||
- name: Display all server ports and names from cluster1
|
||||
ansible.builtin.debug:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
|
||||
vars:
|
||||
server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"
|
||||
|
||||
- name: Display all ports from cluster1
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
|
||||
vars:
|
||||
server_name_query: "domain.server[?starts_with(name,'server1')].port"
|
||||
|
||||
- name: Display all ports from cluster1
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
|
||||
vars:
|
||||
server_name_query: "domain.server[?contains(name,'server1')].port"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The result of the query.
|
||||
type: any
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleFilterError
|
||||
|
||||
try:
|
||||
|
||||
@@ -5,98 +5,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: lists_mergeby
|
||||
short_description: Merge two or more lists of dictionaries by a given attribute
|
||||
version_added: 2.0.0
|
||||
author: Vladimir Botka (@vbotka)
|
||||
description:
|
||||
- Merge two or more lists by attribute I(index). Optional parameters 'recursive' and 'list_merge'
|
||||
control the merging of the lists in values. The function merge_hash from ansible.utils.vars
|
||||
is used. To learn details on how to use the parameters 'recursive' and 'list_merge' see
|
||||
Ansible User's Guide chapter "Using filters to manipulate data" section "Combining
|
||||
hashes/dictionaries".
|
||||
positional: another_list, index
|
||||
options:
|
||||
_input:
|
||||
description: A list of dictionaries.
|
||||
type: list
|
||||
elements: dictionary
|
||||
required: true
|
||||
another_list:
|
||||
description: Another list of dictionaries. This parameter can be specified multiple times.
|
||||
type: list
|
||||
elements: dictionary
|
||||
index:
|
||||
description:
|
||||
- The dictionary key that must be present in every dictionary in every list that is used to
|
||||
merge the lists.
|
||||
type: string
|
||||
required: true
|
||||
recursive:
|
||||
description:
|
||||
- Should the combine recursively merge nested dictionaries (hashes).
|
||||
- "B(Note:) It does not depend on the value of the C(hash_behaviour) setting in C(ansible.cfg)."
|
||||
type: boolean
|
||||
default: false
|
||||
list_merge:
|
||||
description:
|
||||
- Modifies the behaviour when the dictionaries (hashes) to merge contain arrays/lists.
|
||||
type: string
|
||||
default: replace
|
||||
choices:
|
||||
- replace
|
||||
- keep
|
||||
- append
|
||||
- prepend
|
||||
- append_rp
|
||||
- prepend_rp
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Merge two lists
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ list1 | community.general.lists_mergeby(
|
||||
list2,
|
||||
'index',
|
||||
recursive=True,
|
||||
list_merge='append'
|
||||
) }}"
|
||||
vars:
|
||||
list1:
|
||||
- index: a
|
||||
value: 123
|
||||
- index: b
|
||||
value: 42
|
||||
list2:
|
||||
- index: a
|
||||
foo: bar
|
||||
- index: c
|
||||
foo: baz
|
||||
# Produces the following list of dictionaries:
|
||||
# {
|
||||
# "index": "a",
|
||||
# "foo": "bar",
|
||||
# "value": 123
|
||||
# },
|
||||
# {
|
||||
# "index": "b",
|
||||
# "value": 42
|
||||
# },
|
||||
# {
|
||||
# "index": "c",
|
||||
# "foo": "baz"
|
||||
# }
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The merged list.
|
||||
type: list
|
||||
elements: dictionary
|
||||
'''
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.common._collections_compat import Mapping, Sequence
|
||||
@@ -108,6 +16,22 @@ from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
def merge_hash_wrapper(x, y, recursive=False, list_merge='replace'):
|
||||
''' Wrapper of the function merge_hash from ansible.utils.vars. Only 2 paramaters are allowed
|
||||
for Ansible 2.9 and lower.'''
|
||||
|
||||
if LooseVersion(ansible_version) < LooseVersion('2.10'):
|
||||
if list_merge != 'replace' or recursive:
|
||||
msg = ("Non default options of list_merge(default=replace) or recursive(default=False) "
|
||||
"are not allowed in Ansible version 2.9 or lower. Ansible version is %s, "
|
||||
"recursive=%s, and list_merge=%s.")
|
||||
raise AnsibleFilterError(msg % (ansible_version, recursive, list_merge))
|
||||
else:
|
||||
return merge_hash(x, y)
|
||||
else:
|
||||
return merge_hash(x, y, recursive, list_merge)
|
||||
|
||||
|
||||
def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
|
||||
''' Merge 2 lists by attribute 'index'. The function merge_hash from ansible.utils.vars is used.
|
||||
This function is used by the function lists_mergeby.
|
||||
@@ -120,7 +44,7 @@ def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
|
||||
msg = "Elements of list arguments for lists_mergeby must be dictionaries. %s is %s"
|
||||
raise AnsibleFilterError(msg % (elem, type(elem)))
|
||||
if index in elem.keys():
|
||||
d[elem[index]].update(merge_hash(d[elem[index]], elem, recursive, list_merge))
|
||||
d[elem[index]].update(merge_hash_wrapper(d[elem[index]], elem, recursive, list_merge))
|
||||
return sorted(d.values(), key=itemgetter(index))
|
||||
|
||||
|
||||
28
plugins/filter/path_join_shim.py
Normal file
28
plugins/filter/path_join_shim.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2020-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
|
||||
|
||||
|
||||
import os.path
|
||||
|
||||
|
||||
def path_join(list):
|
||||
'''Join list of paths.
|
||||
|
||||
This is a minimal shim for ansible.builtin.path_join included in ansible-base 2.10.
|
||||
This should only be called by Ansible 2.9 or earlier. See meta/runtime.yml for details.
|
||||
'''
|
||||
return os.path.join(*list)
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
'''Ansible jinja2 filters'''
|
||||
|
||||
def filters(self):
|
||||
return {
|
||||
'path_join': path_join,
|
||||
}
|
||||
@@ -20,41 +20,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: random_mac
|
||||
short_description: Generate a random MAC address
|
||||
description:
|
||||
- Generates random networking interfaces MAC addresses for a given prefix.
|
||||
options:
|
||||
_input:
|
||||
description: A string prefix to use as a basis for the random MAC generated.
|
||||
type: string
|
||||
required: true
|
||||
seed:
|
||||
description:
|
||||
- A randomization seed to initialize the process, used to get repeatable results.
|
||||
- If no seed is provided, a system random source such as C(/dev/urandom) is used.
|
||||
required: false
|
||||
type: string
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Random MAC given a prefix
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '52:54:00' | community.general.random_mac }}"
|
||||
# => '52:54:00:ef:1c:03'
|
||||
|
||||
- name: With a seed
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The generated MAC.
|
||||
type: string
|
||||
'''
|
||||
|
||||
import re
|
||||
from random import Random, SystemRandom
|
||||
|
||||
|
||||
@@ -46,11 +46,6 @@ def multiply(factors):
|
||||
|
||||
def to_time_unit(human_time, unit='ms', **kwargs):
|
||||
''' Return a time unit from a human readable string '''
|
||||
|
||||
# No need to handle 0
|
||||
if human_time == "0":
|
||||
return 0
|
||||
|
||||
unit_to_short_form = UNIT_TO_SHORT_FORM
|
||||
unit_factors = UNIT_FACTORS
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_days
|
||||
short_description: Converte a duration string to days
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to days.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into days
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '1y 7m 5d 30h' | community.general.to_days }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of days.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_hours
|
||||
short_description: Converte a duration string to hours
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to hours.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into hours
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '7d 30h 20m 10s 123ms' | community.general.to_hours }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of hours.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_milliseconds
|
||||
short_description: Converte a duration string to milliseconds
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to milliseconds.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into milliseconds
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '30h 20m 10s 123ms' | community.general.to_milliseconds }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of milliseconds.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_minutes
|
||||
short_description: Converte a duration string to minutes
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to minutes.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into minutes
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '30h 20m 10s 123ms' | community.general.to_minutes }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of minutes.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_months
|
||||
short_description: Converte a duration string to months
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to months.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into months
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '1y 7m 5d 30h' | community.general.to_months }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of months.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_seconds
|
||||
short_description: Converte a duration string to seconds
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to seconds.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into seconds
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '30h 20m 10s 123ms' | community.general.to_seconds }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of seconds.
|
||||
type: float
|
||||
@@ -1,84 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_time_unit
|
||||
short_description: Converte a duration string to the given time unit
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to the given time unit.
|
||||
positional: unit
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
unit:
|
||||
description:
|
||||
- Time unit to convert the duration to.
|
||||
default: ms
|
||||
choices:
|
||||
- millisecond
|
||||
- milliseconds
|
||||
- ms
|
||||
- msec
|
||||
- msecs
|
||||
- msecond
|
||||
- mseconds
|
||||
- s
|
||||
- sec
|
||||
- secs
|
||||
- second
|
||||
- seconds
|
||||
- h
|
||||
- hour
|
||||
- hours
|
||||
- hs
|
||||
- m
|
||||
- min
|
||||
- mins
|
||||
- minute
|
||||
- minutes
|
||||
- d
|
||||
- ds
|
||||
- day
|
||||
- days
|
||||
- w
|
||||
- ws
|
||||
- week
|
||||
- weeks
|
||||
- mo
|
||||
- mos
|
||||
- month
|
||||
- months
|
||||
- y
|
||||
- ys
|
||||
- year
|
||||
- years
|
||||
type: string
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into seconds
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '1053d 17h 53m -10s 391ms' | community.general.to_time_unit('s') }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of time units.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_weeks
|
||||
short_description: Converte a duration string to weeks
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to weeks.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into weeks
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '1y 7m 5d 30h' | community.general.to_weeks }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of weeks.
|
||||
type: float
|
||||
@@ -1,40 +0,0 @@
|
||||
DOCUMENTATION:
|
||||
name: to_years
|
||||
short_description: Converte a duration string to years
|
||||
version_added: 0.2.0
|
||||
description:
|
||||
- Parse a human readable time duration string and convert to years.
|
||||
options:
|
||||
_input:
|
||||
description:
|
||||
- The time string to convert.
|
||||
- Can use the units C(y) and C(year) for a year, C(mo) and C(month) for a month, C(w) and C(week) for a week,
|
||||
C(d) and C(day) for a day, C(h) and C(hour) for a hour, C(m), C(min) and C(minute) for minutes, C(s), C(sec)
|
||||
and C(second) for seconds, C(ms), C(msec), C(msecond) and C(millisecond) for milliseconds. The suffix C(s)
|
||||
can be added to a unit as well, so C(seconds) is the same as C(second).
|
||||
- Valid strings are space separated combinations of an integer with an optional minus sign and a unit.
|
||||
- Examples are C(1h), C(-5m), and C(3h -5m 6s).
|
||||
type: string
|
||||
required: true
|
||||
year:
|
||||
description:
|
||||
- Number of days per year.
|
||||
default: 365
|
||||
type: float
|
||||
month:
|
||||
description:
|
||||
- Number of days per month.
|
||||
default: 30
|
||||
type: float
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
|
||||
EXAMPLES: |
|
||||
- name: Convert a duration into years
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ '1053d 30h' | community.general.to_years }}"
|
||||
|
||||
RETURN:
|
||||
_value:
|
||||
description: Number of years.
|
||||
type: float
|
||||
@@ -6,46 +6,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: unicode_normalize
|
||||
short_description: Normalizes unicode strings to facilitate comparison of characters with normalized forms
|
||||
version_added: 3.7.0
|
||||
author: Andrew Pantuso (@Ajpantuso)
|
||||
description:
|
||||
- Normalizes unicode strings to facilitate comparison of characters with normalized forms.
|
||||
positional: form
|
||||
options:
|
||||
_input:
|
||||
description: A unicode string.
|
||||
type: string
|
||||
required: true
|
||||
form:
|
||||
description:
|
||||
- The normal form to use.
|
||||
- See U(https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize) for details.
|
||||
type: string
|
||||
default: NFC
|
||||
choices:
|
||||
- NFC
|
||||
- NFD
|
||||
- NFKC
|
||||
- NFKD
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Normalize unicode string
|
||||
ansible.builtin.set_fact:
|
||||
dictionary: "{{ 'ä' | community.general.unicode_normalize('NFKD') }}"
|
||||
# The resulting string has length 2: one letter is 'a', the other
|
||||
# the diacritic combiner.
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The normalized unicode string of the specified normal form.
|
||||
type: string
|
||||
'''
|
||||
|
||||
from unicodedata import normalize
|
||||
|
||||
from ansible.errors import AnsibleFilterError, AnsibleFilterTypeError
|
||||
|
||||
@@ -5,35 +5,6 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: version_sort
|
||||
short_description: Sort a list according to version order instead of pure alphabetical one
|
||||
version_added: 2.2.0
|
||||
author: Eric L. (@ericzolf)
|
||||
description:
|
||||
- Sort a list according to version order instead of pure alphabetical one.
|
||||
options:
|
||||
_input:
|
||||
description: A list of strings to sort.
|
||||
type: list
|
||||
elements: string
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Convert list of tuples into dictionary
|
||||
ansible.builtin.set_fact:
|
||||
dictionary: "{{ ['2.1', '2.10', '2.9'] | community.general.version_sort }}"
|
||||
# Result is ['2.1', '2.9', '2.10']
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: The list of strings sorted by version.
|
||||
type: list
|
||||
elements: string
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
|
||||
self.inventory.add_child(parent_group_name, group_name)
|
||||
else:
|
||||
self.display.vvvv('Processing profile %s without parent\n' % profile['name'])
|
||||
# Create a hierarchy of profile names
|
||||
# Create a heirarchy of profile names
|
||||
profile_elements = profile['name'].split('-')
|
||||
i = 0
|
||||
while i < len(profile_elements) - 1:
|
||||
|
||||
@@ -522,7 +522,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
"""Helper to save data
|
||||
|
||||
Helper to save the data in self.data
|
||||
Detect if data is already in branch and use dict_merge() to prevent that branch is overwritten.
|
||||
Detect if data is allready in branch and use dict_merge() to prevent that branch is overwritten.
|
||||
|
||||
Args:
|
||||
str(instance_name): name of instance
|
||||
|
||||
@@ -21,11 +21,6 @@ DOCUMENTATION = '''
|
||||
description: token that ensures this is a source file for the 'nmap' plugin.
|
||||
required: True
|
||||
choices: ['nmap', 'community.general.nmap']
|
||||
sudo:
|
||||
description: Set to C(true) to execute a C(sudo nmap) plugin scan.
|
||||
version_added: 4.8.0
|
||||
default: false
|
||||
type: boolean
|
||||
address:
|
||||
description: Network IP or range of IPs to scan, you can use a simple range (10.2.2.15-25) or CIDR notation.
|
||||
required: True
|
||||
@@ -54,13 +49,6 @@ EXAMPLES = '''
|
||||
plugin: community.general.nmap
|
||||
strict: False
|
||||
address: 192.168.0.0/24
|
||||
|
||||
|
||||
# a sudo nmap scan to fully use nmap scan power.
|
||||
plugin: community.general.nmap
|
||||
sudo: true
|
||||
strict: False
|
||||
address: 192.168.0.0/24
|
||||
'''
|
||||
|
||||
import os
|
||||
@@ -147,10 +135,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
if not user_cache_setting or cache_needs_update:
|
||||
# setup command
|
||||
cmd = [self._nmap]
|
||||
|
||||
if self._options['sudo']:
|
||||
cmd.insert(0, 'sudo')
|
||||
|
||||
if not self._options['ports']:
|
||||
cmd.append('-sP')
|
||||
|
||||
|
||||
@@ -206,40 +206,28 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||
def _populate(self):
|
||||
hostname_preference = self.get_option('hostname')
|
||||
group_by_labels = self.get_option('group_by_labels')
|
||||
strict = self.get_option('strict')
|
||||
|
||||
# Add a top group 'one'
|
||||
self.inventory.add_group(group='all')
|
||||
|
||||
filter_by_label = self.get_option('filter_by_label')
|
||||
servers = self._retrieve_servers(filter_by_label)
|
||||
for server in servers:
|
||||
hostname = server['name']
|
||||
for server in self._retrieve_servers(filter_by_label):
|
||||
# check for labels
|
||||
if group_by_labels and server['LABELS']:
|
||||
for label in server['LABELS']:
|
||||
self.inventory.add_group(group=label)
|
||||
self.inventory.add_host(host=hostname, group=label)
|
||||
self.inventory.add_host(host=server['name'], group=label)
|
||||
|
||||
self.inventory.add_host(host=hostname, group='all')
|
||||
self.inventory.add_host(host=server['name'], group='all')
|
||||
|
||||
for attribute, value in server.items():
|
||||
self.inventory.set_variable(hostname, attribute, value)
|
||||
self.inventory.set_variable(server['name'], attribute, value)
|
||||
|
||||
if hostname_preference != 'name':
|
||||
self.inventory.set_variable(hostname, 'ansible_host', server[hostname_preference])
|
||||
self.inventory.set_variable(server['name'], 'ansible_host', server[hostname_preference])
|
||||
|
||||
if server.get('SSH_PORT'):
|
||||
self.inventory.set_variable(hostname, 'ansible_port', server['SSH_PORT'])
|
||||
|
||||
# handle construcable implementation: get composed variables if any
|
||||
self._set_composite_vars(self.get_option('compose'), server, hostname, strict=strict)
|
||||
|
||||
# groups based on jinja conditionals get added to specific groups
|
||||
self._add_host_to_composed_groups(self.get_option('groups'), server, hostname, strict=strict)
|
||||
|
||||
# groups based on variables associated with them in the inventory
|
||||
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), server, hostname, strict=strict)
|
||||
self.inventory.set_variable(server['name'], 'ansible_port', server['SSH_PORT'])
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
if not HAS_PYONE:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# Copyright (c) 2018 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 = '''
|
||||
@@ -32,7 +31,6 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- URL to Proxmox cluster.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_URL) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(url).
|
||||
default: 'http://localhost:8006'
|
||||
type: str
|
||||
env:
|
||||
@@ -42,7 +40,6 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- Proxmox authentication user.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_USER) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(user).
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
@@ -52,33 +49,11 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- Proxmox authentication password.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_PASSWORD) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(password).
|
||||
- If you do not specify a password, you must set I(token_id) and I(token_secret) instead.
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
- name: PROXMOX_PASSWORD
|
||||
version_added: 2.0.0
|
||||
token_id:
|
||||
description:
|
||||
- Proxmox authentication token ID.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_TOKEN_ID) will be used instead.
|
||||
- To use token authentication, you must also specify I(token_secret). If you do not specify I(token_id) and I(token_secret),
|
||||
you must set a password instead.
|
||||
- Make sure to grant explicit pve permissions to the token or disable 'privilege separation' to use the users' privileges instead.
|
||||
version_added: 4.8.0
|
||||
type: str
|
||||
env:
|
||||
- name: PROXMOX_TOKEN_ID
|
||||
token_secret:
|
||||
description:
|
||||
- Proxmox authentication token secret.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_TOKEN_SECRET) will be used instead.
|
||||
- To use token authentication, you must also specify I(token_id). If you do not specify I(token_id) and I(token_secret),
|
||||
you must set a password instead.
|
||||
version_added: 4.8.0
|
||||
type: str
|
||||
env:
|
||||
- name: PROXMOX_TOKEN_SECRET
|
||||
validate_certs:
|
||||
description: Verify SSL certificate if using HTTPS.
|
||||
type: boolean
|
||||
@@ -92,29 +67,15 @@ DOCUMENTATION = '''
|
||||
default: proxmox_
|
||||
type: str
|
||||
want_facts:
|
||||
description:
|
||||
- Gather LXC/QEMU configuration facts.
|
||||
- When I(want_facts) is set to C(true) more details about QEMU VM status are possible, besides the running and stopped states.
|
||||
Currently if the VM is running and it is suspended, the status will be running and the machine will be in C(running) group,
|
||||
but its actual state will be paused. See I(qemu_extended_statuses) for how to retrieve the real status.
|
||||
description: Gather LXC/QEMU configuration facts.
|
||||
default: no
|
||||
type: bool
|
||||
qemu_extended_statuses:
|
||||
description:
|
||||
- Requires I(want_facts) to be set to C(true) to function. This will allow you to differentiate betweend C(paused) and C(prelaunch)
|
||||
statuses of the QEMU VMs.
|
||||
- This introduces multiple groups [prefixed with I(group_prefix)] C(prelaunch) and C(paused).
|
||||
default: no
|
||||
type: bool
|
||||
version_added: 5.1.0
|
||||
want_proxmox_nodes_ansible_host:
|
||||
version_added: 3.0.0
|
||||
description:
|
||||
- Whether to set C(ansbile_host) for proxmox nodes.
|
||||
- When set to C(true) (default), will use the first available interface. This can be different from what you expect.
|
||||
- This currently defaults to C(true), but the default is deprecated since community.general 4.8.0.
|
||||
The default will change to C(false) in community.general 6.0.0. To avoid a deprecation warning, please
|
||||
set this parameter explicitly.
|
||||
default: true
|
||||
type: bool
|
||||
filters:
|
||||
version_added: 4.6.0
|
||||
@@ -139,25 +100,6 @@ EXAMPLES = '''
|
||||
plugin: community.general.proxmox
|
||||
user: ansible@pve
|
||||
password: secure
|
||||
# Note that this can easily give you wrong values as ansible_host. See further below for
|
||||
# an example where this is set to `false` and where ansible_host is set with `compose`.
|
||||
want_proxmox_nodes_ansible_host: true
|
||||
|
||||
# Instead of login with password, proxmox supports api token authentication since release 6.2.
|
||||
plugin: community.general.proxmox
|
||||
user: ci@pve
|
||||
token_id: gitlab-1
|
||||
token_secret: fa256e9c-26ab-41ec-82da-707a2c079829
|
||||
|
||||
# The secret can also be a vault string or passed via the environment variable TOKEN_SECRET.
|
||||
token_secret: !vault |
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
62353634333163633336343265623632626339313032653563653165313262343931643431656138
|
||||
6134333736323265656466646539663134306166666237630a653363623262636663333762316136
|
||||
34616361326263383766366663393837626437316462313332663736623066656237386531663731
|
||||
3037646432383064630a663165303564623338666131353366373630656661333437393937343331
|
||||
32643131386134396336623736393634373936356332623632306561356361323737313663633633
|
||||
6231313333666361656537343562333337323030623732323833
|
||||
|
||||
# More complete example demonstrating the use of 'want_facts' and the constructed options
|
||||
# Note that using facts returned by 'want_facts' in constructed options requires 'want_facts=true'
|
||||
@@ -178,9 +120,6 @@ groups:
|
||||
mailservers: "'mail' in (proxmox_tags_parsed|list)"
|
||||
compose:
|
||||
ansible_port: 2222
|
||||
# Note that this can easily give you wrong values as ansible_host. See further below for
|
||||
# an example where this is set to `false` and where ansible_host is set with `compose`.
|
||||
want_proxmox_nodes_ansible_host: true
|
||||
|
||||
# Using the inventory to allow ansible to connect via the first IP address of the VM / Container
|
||||
# (Default is connection by name of QEMU/LXC guests)
|
||||
@@ -192,23 +131,11 @@ user: ansible@pve
|
||||
password: secure
|
||||
validate_certs: false
|
||||
want_facts: true
|
||||
want_proxmox_nodes_ansible_host: false
|
||||
compose:
|
||||
ansible_host: proxmox_ipconfig0.ip | default(proxmox_net0.ip) | ipaddr('address')
|
||||
my_inv_var_1: "'my_var1_value'"
|
||||
my_inv_var_2: >
|
||||
"my_var_2_value"
|
||||
|
||||
# Specify the url, user and password using templating
|
||||
# my.proxmox.yml
|
||||
plugin: community.general.proxmox
|
||||
url: "{{ lookup('ansible.builtin.ini', 'url', section='proxmox', file='file.ini') }}"
|
||||
user: "{{ lookup('ansible.builtin.env','PM_USER') | default('ansible@pve') }}"
|
||||
password: "{{ lookup('community.general.random_string', base64=True) }}"
|
||||
# Note that this can easily give you wrong values as ansible_host. See further up for
|
||||
# an example where this is set to `false` and where ansible_host is set with `compose`.
|
||||
want_proxmox_nodes_ansible_host: true
|
||||
|
||||
'''
|
||||
|
||||
import itertools
|
||||
@@ -219,10 +146,8 @@ from ansible.module_utils.common._collections_compat import MutableMapping
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.utils.display import Display
|
||||
from ansible.template import Templar
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
@@ -273,24 +198,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _get_auth(self):
|
||||
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
|
||||
|
||||
if self.proxmox_password:
|
||||
a = self._get_session()
|
||||
ret = a.post('%s/api2/json/access/ticket' % self.proxmox_url, data=credentials)
|
||||
|
||||
credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, })
|
||||
json = ret.json()
|
||||
|
||||
a = self._get_session()
|
||||
ret = a.post('%s/api2/json/access/ticket' % self.proxmox_url, data=credentials)
|
||||
|
||||
json = ret.json()
|
||||
|
||||
self.headers = {
|
||||
# only required for POST/PUT/DELETE methods, which we are not using currently
|
||||
# 'CSRFPreventionToken': json['data']['CSRFPreventionToken'],
|
||||
'Cookie': 'PVEAuthCookie={0}'.format(json['data']['ticket'])
|
||||
}
|
||||
|
||||
else:
|
||||
|
||||
self.headers = {'Authorization': 'PVEAPIToken={0}!{1}={2}'.format(self.proxmox_user, self.proxmox_token_id, self.proxmox_token_secret)}
|
||||
self.credentials = {
|
||||
'ticket': json['data']['ticket'],
|
||||
'CSRFPreventionToken': json['data']['CSRFPreventionToken'],
|
||||
}
|
||||
|
||||
def _get_json(self, url, ignore_errors=None):
|
||||
|
||||
@@ -302,7 +218,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
data = []
|
||||
s = self._get_session()
|
||||
while True:
|
||||
ret = s.get(url, headers=self.headers)
|
||||
headers = {'Cookie': 'PVEAuthCookie={0}'.format(self.credentials['ticket'])}
|
||||
ret = s.get(url, headers=headers)
|
||||
if ignore_errors and ret.status_code in ignore_errors:
|
||||
break
|
||||
ret.raise_for_status()
|
||||
@@ -406,37 +323,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
# Additional field containing parsed tags as list
|
||||
if config == 'tags':
|
||||
stripped_value = value.strip()
|
||||
if stripped_value:
|
||||
parsed_key = key + "_parsed"
|
||||
properties[parsed_key] = [tag.strip() for tag in stripped_value.split(",")]
|
||||
parsed_key = self.to_safe('%s%s' % (key, "_parsed"))
|
||||
properties[parsed_key] = [tag.strip() for tag in value.split(",")]
|
||||
|
||||
# The first field in the agent string tells you whether the agent is enabled
|
||||
# the rest of the comma separated string is extra config for the agent.
|
||||
# In some (newer versions of proxmox) instances it can be 'enabled=1'.
|
||||
if config == 'agent':
|
||||
agent_enabled = 0
|
||||
try:
|
||||
agent_enabled = int(value.split(',')[0])
|
||||
except ValueError:
|
||||
if value.split(',')[0] == "enabled=1":
|
||||
agent_enabled = 1
|
||||
if agent_enabled:
|
||||
agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype)
|
||||
if agent_iface_value:
|
||||
agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces"))
|
||||
properties[agent_iface_key] = agent_iface_value
|
||||
# the rest of the comma separated string is extra config for the agent
|
||||
if config == 'agent' and int(value.split(',')[0]):
|
||||
agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype)
|
||||
if agent_iface_value:
|
||||
agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces"))
|
||||
properties[agent_iface_key] = agent_iface_value
|
||||
|
||||
if config == 'lxc':
|
||||
out_val = {}
|
||||
for k, v in value:
|
||||
if k.startswith('lxc.'):
|
||||
k = k[len('lxc.'):]
|
||||
out_val[k] = v
|
||||
value = out_val
|
||||
|
||||
if config not in plaintext_configs and isinstance(value, string_types) \
|
||||
and all("=" in v for v in value.split(",")):
|
||||
if config not in plaintext_configs and not isinstance(value, int) and all("=" in v for v in value.split(",")):
|
||||
# split off strings with commas to a dict
|
||||
# skip over any keys that cannot be processed
|
||||
try:
|
||||
@@ -451,8 +349,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _get_vm_status(self, properties, node, vmid, vmtype, name):
|
||||
ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/status/current" % (self.proxmox_url, node, vmtype, vmid))
|
||||
properties[self._fact('status')] = ret['status']
|
||||
if vmtype == 'qemu':
|
||||
properties[self._fact('qmpstatus')] = ret['qmpstatus']
|
||||
|
||||
def _get_vm_snapshots(self, properties, node, vmid, vmtype, name):
|
||||
ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/snapshot" % (self.proxmox_url, node, vmtype, vmid))
|
||||
@@ -511,8 +407,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
name, vmid = item['name'], item['vmid']
|
||||
|
||||
# get status, config and snapshots if want_facts == True
|
||||
want_facts = self.get_option('want_facts')
|
||||
if want_facts:
|
||||
if self.get_option('want_facts'):
|
||||
self._get_vm_status(properties, node, vmid, ittype, name)
|
||||
self._get_vm_config(properties, node, vmid, ittype, name)
|
||||
self._get_vm_snapshots(properties, node, vmid, ittype, name)
|
||||
@@ -526,13 +421,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
node_type_group = self._group('%s_%s' % (node, ittype))
|
||||
self.inventory.add_child(self._group('all_' + ittype), name)
|
||||
self.inventory.add_child(node_type_group, name)
|
||||
|
||||
item_status = item['status']
|
||||
if item_status == 'running':
|
||||
if want_facts and ittype == 'qemu' and self.get_option('qemu_extended_statuses'):
|
||||
# get more details about the status of the qemu VM
|
||||
item_status = properties.get(self._fact('qmpstatus'), item_status)
|
||||
self.inventory.add_child(self._group('all_%s' % (item_status, )), name)
|
||||
if item['status'] == 'stopped':
|
||||
self.inventory.add_child(self._group('all_stopped'), name)
|
||||
elif item['status'] == 'running':
|
||||
self.inventory.add_child(self._group('all_running'), name)
|
||||
|
||||
return name
|
||||
|
||||
@@ -554,27 +446,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _populate(self):
|
||||
|
||||
# create common groups
|
||||
default_groups = ['lxc', 'qemu', 'running', 'stopped']
|
||||
|
||||
if self.get_option('qemu_extended_statuses'):
|
||||
default_groups.extend(['prelaunch', 'paused'])
|
||||
|
||||
for group in default_groups:
|
||||
self.inventory.add_group(self._group('all_%s' % (group)))
|
||||
|
||||
self.inventory.add_group(self._group('all_lxc'))
|
||||
self.inventory.add_group(self._group('all_qemu'))
|
||||
self.inventory.add_group(self._group('all_running'))
|
||||
self.inventory.add_group(self._group('all_stopped'))
|
||||
nodes_group = self._group('nodes')
|
||||
self.inventory.add_group(nodes_group)
|
||||
|
||||
want_proxmox_nodes_ansible_host = self.get_option("want_proxmox_nodes_ansible_host")
|
||||
if want_proxmox_nodes_ansible_host is None:
|
||||
display.deprecated(
|
||||
'The want_proxmox_nodes_ansible_host option of the community.general.proxmox inventory plugin'
|
||||
' currently defaults to `true`, but this default has been deprecated and will change to `false`'
|
||||
' in community.general 6.0.0. To keep the current behavior and remove this deprecation warning,'
|
||||
' explicitly set `want_proxmox_nodes_ansible_host` to `true` in your inventory configuration',
|
||||
version='6.0.0', collection_name='community.general')
|
||||
want_proxmox_nodes_ansible_host = True
|
||||
|
||||
# gather vm's on nodes
|
||||
self._get_auth()
|
||||
hosts = []
|
||||
@@ -590,7 +468,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
continue
|
||||
|
||||
# get node IP address
|
||||
if want_proxmox_nodes_ansible_host:
|
||||
if self.get_option("want_proxmox_nodes_ansible_host"):
|
||||
ip = self._get_node_ip(node['node'])
|
||||
self.inventory.set_variable(node['node'], 'ansible_host', ip)
|
||||
|
||||
@@ -620,40 +498,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
# read config from file, this sets 'options'
|
||||
self._read_config_data(path)
|
||||
|
||||
t = Templar(loader=loader)
|
||||
|
||||
# read options
|
||||
proxmox_url = self.get_option('url')
|
||||
if t.is_template(proxmox_url):
|
||||
proxmox_url = t.template(variable=proxmox_url, disable_lookups=False)
|
||||
self.proxmox_url = proxmox_url.rstrip('/')
|
||||
|
||||
proxmox_user = self.get_option('user')
|
||||
if t.is_template(proxmox_user):
|
||||
proxmox_user = t.template(variable=proxmox_user, disable_lookups=False)
|
||||
self.proxmox_user = proxmox_user
|
||||
|
||||
proxmox_password = self.get_option('password')
|
||||
if t.is_template(proxmox_password):
|
||||
proxmox_password = t.template(variable=proxmox_password, disable_lookups=False)
|
||||
self.proxmox_password = proxmox_password
|
||||
|
||||
proxmox_token_id = self.get_option('token_id')
|
||||
if t.is_template(proxmox_token_id):
|
||||
proxmox_token_id = t.template(variable=proxmox_token_id, disable_lookups=False)
|
||||
self.proxmox_token_id = proxmox_token_id
|
||||
|
||||
proxmox_token_secret = self.get_option('token_secret')
|
||||
if t.is_template(proxmox_token_secret):
|
||||
proxmox_token_secret = t.template(variable=proxmox_token_secret, disable_lookups=False)
|
||||
self.proxmox_token_secret = proxmox_token_secret
|
||||
|
||||
if proxmox_password is None and (proxmox_token_id is None or proxmox_token_secret is None):
|
||||
raise AnsibleError('You must specify either a password or both token_id and token_secret.')
|
||||
|
||||
if self.get_option('qemu_extended_statuses') and not self.get_option('want_facts'):
|
||||
raise AnsibleError('You must set want_facts to True if you want to use qemu_extended_statuses.')
|
||||
|
||||
self.proxmox_url = self.get_option('url').rstrip('/')
|
||||
self.proxmox_user = self.get_option('user')
|
||||
self.proxmox_password = self.get_option('password')
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
self.host_filters = self.get_option('filters')
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Jonathan Lung <lungj@heresjono.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 = """
|
||||
name: bitwarden
|
||||
author:
|
||||
- Jonathan Lung (@lungj) <lungj@heresjono.com>
|
||||
requirements:
|
||||
- bw (command line utility)
|
||||
- be logged into bitwarden
|
||||
short_description: Retrieve secrets from Bitwarden
|
||||
version_added: 5.4.0
|
||||
description:
|
||||
- Retrieve secrets from Bitwarden.
|
||||
options:
|
||||
_terms:
|
||||
description: Key(s) to fetch values for from login info.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
field:
|
||||
description: Field to fetch; leave unset to fetch whole response.
|
||||
type: str
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: "Get 'password' from Bitwarden record named 'a_test'"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ lookup('community.general.bitwarden', 'a_test', field='password') }}
|
||||
|
||||
- name: "Get full Bitwarden record named 'a_test'"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ lookup('community.general.bitwarden', 'a_test') }}
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: List of requested field or JSON object of list of matches.
|
||||
type: list
|
||||
elements: raw
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
from ansible.parsing.ajson import AnsibleJSONDecoder
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class BitwardenException(AnsibleError):
|
||||
pass
|
||||
|
||||
|
||||
class Bitwarden(object):
|
||||
|
||||
def __init__(self, path='bw'):
|
||||
self._cli_path = path
|
||||
|
||||
@property
|
||||
def cli_path(self):
|
||||
return self._cli_path
|
||||
|
||||
@property
|
||||
def logged_in(self):
|
||||
out, err = self._run(['status'], stdin="")
|
||||
decoded = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
return decoded['status'] == 'unlocked'
|
||||
|
||||
def _run(self, args, stdin=None, expected_rc=0):
|
||||
p = Popen([self.cli_path] + args, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
out, err = p.communicate(to_bytes(stdin))
|
||||
rc = p.wait()
|
||||
if rc != expected_rc:
|
||||
raise BitwardenException(err)
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict')
|
||||
|
||||
def _get_matches(self, search_value, search_field="name"):
|
||||
"""Return matching records whose search_field is equal to key.
|
||||
"""
|
||||
out, err = self._run(['list', 'items', '--search', search_value])
|
||||
|
||||
# This includes things that matched in different fields.
|
||||
initial_matches = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
|
||||
# Filter to only include results from the right field.
|
||||
return [item for item in initial_matches if item[search_field] == search_value]
|
||||
|
||||
def get_field(self, field, search_value, search_field="name"):
|
||||
"""Return a list of the specified field for records whose search_field match search_value.
|
||||
|
||||
If field is None, return the whole record for each match.
|
||||
"""
|
||||
matches = self._get_matches(search_value)
|
||||
|
||||
if field:
|
||||
return [match['login'][field] for match in matches]
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
if not _bitwarden.logged_in:
|
||||
raise AnsibleError("Not logged into Bitwarden. Run 'bw login'.")
|
||||
|
||||
return [_bitwarden.get_field(field, term) for term in terms]
|
||||
|
||||
|
||||
_bitwarden = Bitwarden()
|
||||
@@ -16,7 +16,7 @@ DOCUMENTATION = '''
|
||||
The lookup order mirrors the one from Chef, all folders in the base path are walked back looking for the following configuration
|
||||
file in order : .chef/knife.rb, ~/.chef/knife.rb, /etc/chef/client.rb"
|
||||
requirements:
|
||||
- "pychef (L(Python library, https://pychef.readthedocs.io), C(pip install pychef))"
|
||||
- "pychef (python library https://pychef.readthedocs.io `pip install pychef`)"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
|
||||
@@ -57,19 +57,14 @@ EXAMPLES = """
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_result:
|
||||
description: A list containing one dictionary.
|
||||
type: list
|
||||
elements: dictionary
|
||||
contains:
|
||||
password:
|
||||
description:
|
||||
- The actual value stored
|
||||
passprops:
|
||||
description: properties assigned to the entry
|
||||
type: dictionary
|
||||
passwordchangeinprocess:
|
||||
description: did the password change?
|
||||
password:
|
||||
description:
|
||||
- The actual value stored
|
||||
passprops:
|
||||
description: properties assigned to the entry
|
||||
type: dictionary
|
||||
passwordchangeinprocess:
|
||||
description: did the password change?
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
@@ -27,30 +27,19 @@ DOCUMENTATION = '''
|
||||
This needs to be passed-in as an additional parameter to the lookup
|
||||
options:
|
||||
_terms:
|
||||
description: Domain(s) to query.
|
||||
description: domain(s) to query
|
||||
qtype:
|
||||
description:
|
||||
- Record type to query.
|
||||
- C(DLV) is deprecated and will be removed in community.general 6.0.0.
|
||||
description: record type to query
|
||||
default: 'A'
|
||||
choices: [A, ALL, AAAA, CNAME, DNAME, DLV, DNSKEY, DS, HINFO, LOC, MX, NAPTR, NS, NSEC3PARAM, PTR, RP, RRSIG, SOA, SPF, SRV, SSHFP, TLSA, TXT]
|
||||
flat:
|
||||
description: If 0 each record is returned as a dictionary, otherwise a string.
|
||||
description: If 0 each record is returned as a dictionary, otherwise a string
|
||||
default: 1
|
||||
retry_servfail:
|
||||
description: Retry a nameserver if it returns SERVFAIL.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 3.6.0
|
||||
fail_on_error:
|
||||
description:
|
||||
- Abort execution on lookup errors.
|
||||
- The default for this option will likely change to C(true) in the future.
|
||||
The current default, C(false), is used for backwards compatibility, and will result in empty strings
|
||||
or the string C(NXDOMAIN) in the result in case of errors.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 5.4.0
|
||||
notes:
|
||||
- ALL is not a record per-se, merely the listed fields are available for any record results you retrieve in the form of a dictionary.
|
||||
- While the 'dig' lookup plugin supports anything which dnspython supports out of the box, only a subset can be converted into a dictionary.
|
||||
@@ -174,7 +163,6 @@ RETURN = """
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.utils.display import Display
|
||||
import socket
|
||||
|
||||
try:
|
||||
@@ -190,9 +178,6 @@ except ImportError:
|
||||
HAVE_DNS = False
|
||||
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
def make_rdata_dict(rdata):
|
||||
''' While the 'dig' lookup plugin supports anything which dnspython supports
|
||||
out of the box, the following supported_types list describes which
|
||||
@@ -288,7 +273,6 @@ class LookupModule(LookupBase):
|
||||
domain = None
|
||||
qtype = 'A'
|
||||
flat = True
|
||||
fail_on_error = False
|
||||
rdclass = dns.rdataclass.from_text('IN')
|
||||
|
||||
for t in terms:
|
||||
@@ -327,8 +311,6 @@ class LookupModule(LookupBase):
|
||||
raise AnsibleError("dns lookup illegal CLASS: %s" % to_native(e))
|
||||
elif opt == 'retry_servfail':
|
||||
myres.retry_servfail = bool(arg)
|
||||
elif opt == 'fail_on_error':
|
||||
fail_on_error = bool(arg)
|
||||
|
||||
continue
|
||||
|
||||
@@ -344,11 +326,6 @@ class LookupModule(LookupBase):
|
||||
|
||||
ret = []
|
||||
|
||||
if qtype.upper() == 'DLV':
|
||||
display.deprecated('The DLV record type has been decommissioned in 2017 and support for'
|
||||
' it will be removed from community.general 6.0.0',
|
||||
version='6.0.0', collection_name='community.general')
|
||||
|
||||
if qtype.upper() == 'PTR':
|
||||
try:
|
||||
n = dns.reversename.from_address(domain)
|
||||
@@ -376,24 +353,16 @@ class LookupModule(LookupBase):
|
||||
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
|
||||
ret.append(rd)
|
||||
except Exception as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append(str(err))
|
||||
except Exception as e:
|
||||
ret.append(str(e))
|
||||
|
||||
except dns.resolver.NXDOMAIN as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
except dns.resolver.NXDOMAIN:
|
||||
ret.append('NXDOMAIN')
|
||||
except dns.resolver.NoAnswer as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
except dns.resolver.NoAnswer:
|
||||
ret.append("")
|
||||
except dns.resolver.Timeout as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
except dns.resolver.Timeout:
|
||||
ret.append('')
|
||||
except dns.exception.DNSException as err:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
|
||||
except dns.exception.DNSException as e:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(e))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -105,15 +105,11 @@ display = Display()
|
||||
class LookupModule(LookupBase):
|
||||
@staticmethod
|
||||
def Client(vault_parameters):
|
||||
try:
|
||||
vault = SecretsVault(**vault_parameters)
|
||||
return vault
|
||||
except TypeError:
|
||||
raise AnsibleError("python-dsv-sdk==0.0.1 must be installed to use this plugin")
|
||||
return SecretsVault(**vault_parameters)
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
if sdk_is_missing:
|
||||
raise AnsibleError("python-dsv-sdk==0.0.1 must be installed to use this plugin")
|
||||
raise AnsibleError("python-dsv-sdk must be installed to use this plugin")
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
@@ -122,7 +118,6 @@ class LookupModule(LookupBase):
|
||||
"tenant": self.get_option("tenant"),
|
||||
"client_id": self.get_option("client_id"),
|
||||
"client_secret": self.get_option("client_secret"),
|
||||
"tld": self.get_option("tld"),
|
||||
"url_template": self.get_option("url_template"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ DOCUMENTATION = '''
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: output secrets to screen (BAD IDEA)
|
||||
- name : output secrets to screen (BAD IDEA)
|
||||
ansible.builtin.debug:
|
||||
msg: "Password: {{item}}"
|
||||
with_community.general.keyring:
|
||||
|
||||
@@ -11,24 +11,21 @@ DOCUMENTATION = '''
|
||||
- Andrew Zenk (!UNKNOWN) <azenk@umn.edu>
|
||||
requirements:
|
||||
- lpass (command line utility)
|
||||
- must have already logged into LastPass
|
||||
short_description: fetch data from LastPass
|
||||
- must have already logged into lastpass
|
||||
short_description: fetch data from lastpass
|
||||
description:
|
||||
- Use the lpass command line utility to fetch specific fields from LastPass.
|
||||
- use the lpass command line utility to fetch specific fields from lastpass
|
||||
options:
|
||||
_terms:
|
||||
description: Key from which you want to retrieve the field.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
description: key from which you want to retrieve the field
|
||||
required: True
|
||||
field:
|
||||
description: Field to return from LastPass.
|
||||
description: field to return from lastpass
|
||||
default: 'password'
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: get 'custom_field' from LastPass entry 'entry-name'
|
||||
- name: get 'custom_field' from lastpass entry 'entry-name'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.lastpass', 'entry-name', field='custom_field') }}"
|
||||
"""
|
||||
@@ -91,14 +88,12 @@ class LPass(object):
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
|
||||
lp = LPass()
|
||||
|
||||
if not lp.logged_in:
|
||||
raise AnsibleError("Not logged into LastPass: please run 'lpass login' first")
|
||||
raise AnsibleError("Not logged into lastpass: please run 'lpass login' first")
|
||||
|
||||
field = kwargs.get('field', 'password')
|
||||
values = []
|
||||
for term in terms:
|
||||
values.append(lp.get_field(term, field))
|
||||
|
||||
@@ -45,8 +45,8 @@ DOCUMENTATION = '''
|
||||
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
|
||||
notes:
|
||||
- This lookup will use an existing 1Password session if one exists. If not, and you have already
|
||||
performed an initial sign in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the
|
||||
C(master_password) is required. You may optionally specify C(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
|
||||
performed an initial sign in (meaning C(~/.op/config exists)), then only the C(master_password) is required.
|
||||
You may optionally specify C(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
|
||||
- This lookup can perform an initial login by providing C(subdomain), C(username), C(secret_key), and C(master_password).
|
||||
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials
|
||||
needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength
|
||||
@@ -105,12 +105,12 @@ from ansible.plugins.lookup import LookupBase
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.onepassword import OnePasswordConfig
|
||||
|
||||
|
||||
class OnePass(object):
|
||||
|
||||
def __init__(self, path='op'):
|
||||
self.cli_path = path
|
||||
self.config_file_path = os.path.expanduser('~/.op/config')
|
||||
self.logged_in = False
|
||||
self.token = None
|
||||
self.subdomain = None
|
||||
@@ -119,11 +119,9 @@ class OnePass(object):
|
||||
self.secret_key = None
|
||||
self.master_password = None
|
||||
|
||||
self._config = OnePasswordConfig()
|
||||
|
||||
def get_token(self):
|
||||
# If the config file exists, assume an initial signin has taken place and try basic sign in
|
||||
if os.path.isfile(self._config.config_file_path):
|
||||
if os.path.isfile(self.config_file_path):
|
||||
|
||||
if not self.master_password:
|
||||
raise AnsibleLookupError('Unable to sign in to 1Password. master_password is required.')
|
||||
@@ -283,5 +281,4 @@ class LookupModule(LookupBase):
|
||||
values = []
|
||||
for term in terms:
|
||||
values.append(op.get_field(term, field, section, vault))
|
||||
|
||||
return values
|
||||
|
||||
@@ -21,14 +21,8 @@ DOCUMENTATION = '''
|
||||
description: query key.
|
||||
required: True
|
||||
passwordstore:
|
||||
description:
|
||||
- Location of the password store.
|
||||
- 'The value is decided by checking the following in order:'
|
||||
- If set, this value is used.
|
||||
- If C(directory) is set, that value will be used.
|
||||
- If I(backend=pass), then C(~/.password-store) is used.
|
||||
- If I(backend=gopass), then the C(path) field in C(~/.config/gopass/config.yml) is used,
|
||||
falling back to C(~/.local/share/gopass/stores/root) if not defined.
|
||||
description: location of the password store.
|
||||
default: '~/.password-store'
|
||||
directory:
|
||||
description: The directory of the password store.
|
||||
env:
|
||||
@@ -112,22 +106,6 @@ DOCUMENTATION = '''
|
||||
type: str
|
||||
default: 15m
|
||||
version_added: 4.5.0
|
||||
backend:
|
||||
description:
|
||||
- Specify which backend to use.
|
||||
- Defaults to C(pass), passwordstore.org's original pass utility.
|
||||
- C(gopass) support is incomplete.
|
||||
ini:
|
||||
- section: passwordstore_lookup
|
||||
key: backend
|
||||
vars:
|
||||
- name: passwordstore_backend
|
||||
type: str
|
||||
default: pass
|
||||
choices:
|
||||
- pass
|
||||
- gopass
|
||||
version_added: 5.2.0
|
||||
'''
|
||||
EXAMPLES = """
|
||||
ansible.cfg: |
|
||||
@@ -253,24 +231,6 @@ def check_output2(*popenargs, **kwargs):
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def __init__(self, loader=None, templar=None, **kwargs):
|
||||
|
||||
super(LookupModule, self).__init__(loader, templar, **kwargs)
|
||||
self.realpass = None
|
||||
|
||||
def is_real_pass(self):
|
||||
if self.realpass is None:
|
||||
try:
|
||||
passoutput = to_text(
|
||||
check_output2([self.pass_cmd, "--version"], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
)
|
||||
self.realpass = 'pass: the standard unix password manager' in passoutput
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
|
||||
return self.realpass
|
||||
|
||||
def parse_params(self, term):
|
||||
# I went with the "traditional" param followed with space separated KV pairs.
|
||||
# Waiting for final implementation of lookup parameter parsing.
|
||||
@@ -310,12 +270,10 @@ class LookupModule(LookupBase):
|
||||
self.env = os.environ.copy()
|
||||
self.env['LANGUAGE'] = 'C' # make sure to get errors in English as required by check_output2
|
||||
|
||||
if self.backend == 'gopass':
|
||||
self.env['GOPASS_NO_REMINDER'] = "YES"
|
||||
elif os.path.isdir(self.paramvals['directory']):
|
||||
# Set PASSWORD_STORE_DIR
|
||||
# Set PASSWORD_STORE_DIR
|
||||
if os.path.isdir(self.paramvals['directory']):
|
||||
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
|
||||
elif self.is_real_pass():
|
||||
else:
|
||||
raise AnsibleError('Passwordstore directory \'{0}\' does not exist'.format(self.paramvals['directory']))
|
||||
|
||||
# Set PASSWORD_STORE_UMASK if umask is set
|
||||
@@ -330,8 +288,7 @@ class LookupModule(LookupBase):
|
||||
def check_pass(self):
|
||||
try:
|
||||
self.passoutput = to_text(
|
||||
check_output2([self.pass_cmd, 'show'] +
|
||||
[self.passname], env=self.env),
|
||||
check_output2(["pass", "show", self.passname], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
).splitlines()
|
||||
self.password = self.passoutput[0]
|
||||
@@ -345,10 +302,8 @@ class LookupModule(LookupBase):
|
||||
if ':' in line:
|
||||
name, value = line.split(':', 1)
|
||||
self.passdict[name.strip()] = value.strip()
|
||||
if (self.backend == 'gopass' or
|
||||
os.path.isfile(os.path.join(self.paramvals['directory'], self.passname + ".gpg"))
|
||||
or not self.is_real_pass()):
|
||||
# When using real pass, only accept password as found if there is a .gpg file for it (might be a tree node otherwise)
|
||||
if os.path.isfile(os.path.join(self.paramvals['directory'], self.passname + ".gpg")):
|
||||
# Only accept password as found, if there a .gpg file for it (might be a tree node otherwise)
|
||||
return True
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
# 'not in password store' is the expected error if a password wasn't found
|
||||
@@ -384,7 +339,7 @@ class LookupModule(LookupBase):
|
||||
if self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
return newpass
|
||||
@@ -396,7 +351,7 @@ class LookupModule(LookupBase):
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass + '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
return newpass
|
||||
@@ -425,30 +380,17 @@ class LookupModule(LookupBase):
|
||||
yield
|
||||
|
||||
def setup(self, variables):
|
||||
self.backend = self.get_option('backend')
|
||||
self.pass_cmd = self.backend # pass and gopass are commands as well
|
||||
self.locked = None
|
||||
timeout = self.get_option('locktimeout')
|
||||
if not re.match('^[0-9]+[smh]$', timeout):
|
||||
raise AnsibleError("{0} is not a correct value for locktimeout".format(timeout))
|
||||
unit_to_seconds = {"s": 1, "m": 60, "h": 3600}
|
||||
self.lock_timeout = int(timeout[:-1]) * unit_to_seconds[timeout[-1]]
|
||||
|
||||
directory = variables.get('passwordstore', os.environ.get('PASSWORD_STORE_DIR', None))
|
||||
|
||||
if directory is None:
|
||||
if self.backend == 'gopass':
|
||||
try:
|
||||
with open(os.path.expanduser('~/.config/gopass/config.yml')) as f:
|
||||
directory = yaml.safe_load(f)['path']
|
||||
except (FileNotFoundError, KeyError, yaml.YAMLError):
|
||||
directory = os.path.expanduser('~/.local/share/gopass/stores/root')
|
||||
else:
|
||||
directory = os.path.expanduser('~/.password-store')
|
||||
|
||||
self.paramvals = {
|
||||
'subkey': 'password',
|
||||
'directory': directory,
|
||||
'directory': variables.get('passwordstore', os.environ.get(
|
||||
'PASSWORD_STORE_DIR',
|
||||
os.path.expanduser('~/.password-store'))),
|
||||
'create': False,
|
||||
'returnall': False,
|
||||
'overwrite': False,
|
||||
@@ -460,7 +402,6 @@ class LookupModule(LookupBase):
|
||||
}
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
self.setup(variables)
|
||||
result = []
|
||||
|
||||
|
||||
@@ -3,7 +3,51 @@
|
||||
# This particular file snippet, and this file snippet only, is based on
|
||||
# Lib/posixpath.py of cpython
|
||||
# It is licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
# (See PSF-license.txt in this collection)
|
||||
#
|
||||
# 1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
# ("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
# otherwise using this software ("Python") in source or binary form and
|
||||
# its associated documentation.
|
||||
#
|
||||
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
# analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
# distribute, and otherwise use Python alone or in any derivative version,
|
||||
# provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
# i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved"
|
||||
# are retained in Python alone or in any derivative version prepared by Licensee.
|
||||
#
|
||||
# 3. In the event Licensee prepares a derivative work that is based on
|
||||
# or incorporates Python or any part thereof, and wants to make
|
||||
# the derivative work available to others as provided herein, then
|
||||
# Licensee hereby agrees to include in any such work a brief summary of
|
||||
# the changes made to Python.
|
||||
#
|
||||
# 4. PSF is making Python available to Licensee on an "AS IS"
|
||||
# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
# INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
#
|
||||
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
#
|
||||
# 6. This License Agreement will automatically terminate upon a material
|
||||
# breach of its terms and conditions.
|
||||
#
|
||||
# 7. Nothing in this License Agreement shall be deemed to create any
|
||||
# relationship of agency, partnership, or joint venture between PSF and
|
||||
# Licensee. This License Agreement does not grant permission to use PSF
|
||||
# trademarks or trade name in a trademark sense to endorse or promote
|
||||
# products or services of Licensee, or any third party.
|
||||
#
|
||||
# 8. By copying, installing or otherwise using Python, Licensee
|
||||
# agrees to be bound by the terms and conditions of this License
|
||||
# Agreement.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
343
plugins/module_utils/_version.py
Normal file
343
plugins/module_utils/_version.py
Normal file
@@ -0,0 +1,343 @@
|
||||
# Vendored copy of distutils/version.py from CPython 3.9.5
|
||||
#
|
||||
# Implements multiple version numbering conventions for the
|
||||
# Python Module Distribution Utilities.
|
||||
#
|
||||
# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
||||
#
|
||||
|
||||
"""Provides classes to represent module version numbers (one class for
|
||||
each style of version numbering). There are currently two such classes
|
||||
implemented: StrictVersion and LooseVersion.
|
||||
|
||||
Every version number class implements the following interface:
|
||||
* the 'parse' method takes a string and parses it to some internal
|
||||
representation; if the string is an invalid version number,
|
||||
'parse' raises a ValueError exception
|
||||
* the class constructor takes an optional string argument which,
|
||||
if supplied, is passed to 'parse'
|
||||
* __str__ reconstructs the string that was passed to 'parse' (or
|
||||
an equivalent string -- ie. one that will generate an equivalent
|
||||
version number instance)
|
||||
* __repr__ generates Python code to recreate the version number instance
|
||||
* _cmp compares the current instance with either another instance
|
||||
of the same class or a string (which will be parsed to an instance
|
||||
of the same class, thus must follow the same rules)
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
|
||||
try:
|
||||
RE_FLAGS = re.VERBOSE | re.ASCII
|
||||
except AttributeError:
|
||||
RE_FLAGS = re.VERBOSE
|
||||
|
||||
|
||||
class Version:
|
||||
"""Abstract base class for version numbering classes. Just provides
|
||||
constructor (__init__) and reproducer (__repr__), because those
|
||||
seem to be the same for all version numbering classes; and route
|
||||
rich comparisons to _cmp.
|
||||
"""
|
||||
|
||||
def __init__(self, vstring=None):
|
||||
if vstring:
|
||||
self.parse(vstring)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s ('%s')" % (self.__class__.__name__, str(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c == 0
|
||||
|
||||
def __lt__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c < 0
|
||||
|
||||
def __le__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c <= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c > 0
|
||||
|
||||
def __ge__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c >= 0
|
||||
|
||||
|
||||
# Interface for version-number classes -- must be implemented
|
||||
# by the following classes (the concrete ones -- Version should
|
||||
# be treated as an abstract class).
|
||||
# __init__ (string) - create and take same action as 'parse'
|
||||
# (string parameter is optional)
|
||||
# parse (string) - convert a string representation to whatever
|
||||
# internal representation is appropriate for
|
||||
# this style of version numbering
|
||||
# __str__ (self) - convert back to a string; should be very similar
|
||||
# (if not identical to) the string supplied to parse
|
||||
# __repr__ (self) - generate Python code to recreate
|
||||
# the instance
|
||||
# _cmp (self, other) - compare two version numbers ('other' may
|
||||
# be an unparsed version string, or another
|
||||
# instance of your version class)
|
||||
|
||||
|
||||
class StrictVersion(Version):
|
||||
"""Version numbering for anal retentives and software idealists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of two or three
|
||||
dot-separated numeric components, with an optional "pre-release" tag
|
||||
on the end. The pre-release tag consists of the letter 'a' or 'b'
|
||||
followed by a number. If the numeric components of two version
|
||||
numbers are equal, then one with a pre-release tag will always
|
||||
be deemed earlier (lesser) than one without.
|
||||
|
||||
The following are valid version numbers (shown in the order that
|
||||
would be obtained by sorting according to the supplied cmp function):
|
||||
|
||||
0.4 0.4.0 (these two are equivalent)
|
||||
0.4.1
|
||||
0.5a1
|
||||
0.5b3
|
||||
0.5
|
||||
0.9.6
|
||||
1.0
|
||||
1.0.4a3
|
||||
1.0.4b1
|
||||
1.0.4
|
||||
|
||||
The following are examples of invalid version numbers:
|
||||
|
||||
1
|
||||
2.7.2.2
|
||||
1.3.a4
|
||||
1.3pl1
|
||||
1.3c4
|
||||
|
||||
The rationale for this version numbering system will be explained
|
||||
in the distutils documentation.
|
||||
"""
|
||||
|
||||
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
|
||||
RE_FLAGS)
|
||||
|
||||
def parse(self, vstring):
|
||||
match = self.version_re.match(vstring)
|
||||
if not match:
|
||||
raise ValueError("invalid version number '%s'" % vstring)
|
||||
|
||||
(major, minor, patch, prerelease, prerelease_num) = \
|
||||
match.group(1, 2, 4, 5, 6)
|
||||
|
||||
if patch:
|
||||
self.version = tuple(map(int, [major, minor, patch]))
|
||||
else:
|
||||
self.version = tuple(map(int, [major, minor])) + (0,)
|
||||
|
||||
if prerelease:
|
||||
self.prerelease = (prerelease[0], int(prerelease_num))
|
||||
else:
|
||||
self.prerelease = None
|
||||
|
||||
def __str__(self):
|
||||
if self.version[2] == 0:
|
||||
vstring = '.'.join(map(str, self.version[0:2]))
|
||||
else:
|
||||
vstring = '.'.join(map(str, self.version))
|
||||
|
||||
if self.prerelease:
|
||||
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
|
||||
|
||||
return vstring
|
||||
|
||||
def _cmp(self, other):
|
||||
if isinstance(other, str):
|
||||
other = StrictVersion(other)
|
||||
elif not isinstance(other, StrictVersion):
|
||||
return NotImplemented
|
||||
|
||||
if self.version != other.version:
|
||||
# numeric versions don't match
|
||||
# prerelease stuff doesn't matter
|
||||
if self.version < other.version:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
|
||||
# have to compare prerelease
|
||||
# case 1: neither has prerelease; they're equal
|
||||
# case 2: self has prerelease, other doesn't; other is greater
|
||||
# case 3: self doesn't have prerelease, other does: self is greater
|
||||
# case 4: both have prerelease: must compare them!
|
||||
|
||||
if (not self.prerelease and not other.prerelease):
|
||||
return 0
|
||||
elif (self.prerelease and not other.prerelease):
|
||||
return -1
|
||||
elif (not self.prerelease and other.prerelease):
|
||||
return 1
|
||||
elif (self.prerelease and other.prerelease):
|
||||
if self.prerelease == other.prerelease:
|
||||
return 0
|
||||
elif self.prerelease < other.prerelease:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
raise AssertionError("never get here")
|
||||
|
||||
# end class StrictVersion
|
||||
|
||||
# The rules according to Greg Stein:
|
||||
# 1) a version number has 1 or more numbers separated by a period or by
|
||||
# sequences of letters. If only periods, then these are compared
|
||||
# left-to-right to determine an ordering.
|
||||
# 2) sequences of letters are part of the tuple for comparison and are
|
||||
# compared lexicographically
|
||||
# 3) recognize the numeric components may have leading zeroes
|
||||
#
|
||||
# The LooseVersion class below implements these rules: a version number
|
||||
# string is split up into a tuple of integer and string components, and
|
||||
# comparison is a simple tuple comparison. This means that version
|
||||
# numbers behave in a predictable and obvious way, but a way that might
|
||||
# not necessarily be how people *want* version numbers to behave. There
|
||||
# wouldn't be a problem if people could stick to purely numeric version
|
||||
# numbers: just split on period and compare the numbers as tuples.
|
||||
# However, people insist on putting letters into their version numbers;
|
||||
# the most common purpose seems to be:
|
||||
# - indicating a "pre-release" version
|
||||
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
|
||||
# - indicating a post-release patch ('p', 'pl', 'patch')
|
||||
# but of course this can't cover all version number schemes, and there's
|
||||
# no way to know what a programmer means without asking him.
|
||||
#
|
||||
# The problem is what to do with letters (and other non-numeric
|
||||
# characters) in a version number. The current implementation does the
|
||||
# obvious and predictable thing: keep them as strings and compare
|
||||
# lexically within a tuple comparison. This has the desired effect if
|
||||
# an appended letter sequence implies something "post-release":
|
||||
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
|
||||
#
|
||||
# However, if letters in a version number imply a pre-release version,
|
||||
# the "obvious" thing isn't correct. Eg. you would expect that
|
||||
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
|
||||
# implemented here, this just isn't so.
|
||||
#
|
||||
# Two possible solutions come to mind. The first is to tie the
|
||||
# comparison algorithm to a particular set of semantic rules, as has
|
||||
# been done in the StrictVersion class above. This works great as long
|
||||
# as everyone can go along with bondage and discipline. Hopefully a
|
||||
# (large) subset of Python module programmers will agree that the
|
||||
# particular flavour of bondage and discipline provided by StrictVersion
|
||||
# provides enough benefit to be worth using, and will submit their
|
||||
# version numbering scheme to its domination. The free-thinking
|
||||
# anarchists in the lot will never give in, though, and something needs
|
||||
# to be done to accommodate them.
|
||||
#
|
||||
# Perhaps a "moderately strict" version class could be implemented that
|
||||
# lets almost anything slide (syntactically), and makes some heuristic
|
||||
# assumptions about non-digits in version number strings. This could
|
||||
# sink into special-case-hell, though; if I was as talented and
|
||||
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
|
||||
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
|
||||
# just as happy dealing with things like "2g6" and "1.13++". I don't
|
||||
# think I'm smart enough to do it right though.
|
||||
#
|
||||
# In any case, I've coded the test suite for this module (see
|
||||
# ../test/test_version.py) specifically to fail on things like comparing
|
||||
# "1.2a2" and "1.2". That's not because the *code* is doing anything
|
||||
# wrong, it's because the simple, obvious design doesn't match my
|
||||
# complicated, hairy expectations for real-world version numbers. It
|
||||
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
|
||||
# the Right Thing" (ie. the code matches the conception). But I'd rather
|
||||
# have a conception that matches common notions about version numbers.
|
||||
|
||||
|
||||
class LooseVersion(Version):
|
||||
"""Version numbering for anarchists and software realists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of a series of numbers,
|
||||
separated by either periods or strings of letters. When comparing
|
||||
version numbers, the numeric components will be compared
|
||||
numerically, and the alphabetic components lexically. The following
|
||||
are all valid version numbers, in no particular order:
|
||||
|
||||
1.5.1
|
||||
1.5.2b2
|
||||
161
|
||||
3.10a
|
||||
8.02
|
||||
3.4j
|
||||
1996.07.12
|
||||
3.2.pl0
|
||||
3.1.1.6
|
||||
2g6
|
||||
11g
|
||||
0.960923
|
||||
2.2beta29
|
||||
1.13++
|
||||
5.5.kw
|
||||
2.0b1pl0
|
||||
|
||||
In fact, there is no such thing as an invalid version number under
|
||||
this scheme; the rules for comparison are simple and predictable,
|
||||
but may not always give the results you want (for some definition
|
||||
of "want").
|
||||
"""
|
||||
|
||||
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
|
||||
|
||||
def __init__(self, vstring=None):
|
||||
if vstring:
|
||||
self.parse(vstring)
|
||||
|
||||
def parse(self, vstring):
|
||||
# I've given up on thinking I can reconstruct the version string
|
||||
# from the parsed tuple -- so I just store the string here for
|
||||
# use by __str__
|
||||
self.vstring = vstring
|
||||
components = [x for x in self.component_re.split(vstring) if x and x != '.']
|
||||
for i, obj in enumerate(components):
|
||||
try:
|
||||
components[i] = int(obj)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.version = components
|
||||
|
||||
def __str__(self):
|
||||
return self.vstring
|
||||
|
||||
def __repr__(self):
|
||||
return "LooseVersion ('%s')" % str(self)
|
||||
|
||||
def _cmp(self, other):
|
||||
if isinstance(other, str):
|
||||
other = LooseVersion(other)
|
||||
elif not isinstance(other, LooseVersion):
|
||||
return NotImplemented
|
||||
|
||||
if self.version == other.version:
|
||||
return 0
|
||||
if self.version < other.version:
|
||||
return -1
|
||||
if self.version > other.version:
|
||||
return 1
|
||||
|
||||
# end class LooseVersion
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Alexei Znamensky <russoz@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
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from ansible.module_utils.common.collections import is_sequence
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
def _ensure_list(value):
|
||||
return list(value) if is_sequence(value) else [value]
|
||||
|
||||
|
||||
def _process_as_is(rc, out, err):
|
||||
return rc, out, err
|
||||
|
||||
|
||||
class CmdRunnerException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MissingArgumentFormat(CmdRunnerException):
|
||||
def __init__(self, arg, args_order, args_formats):
|
||||
self.args_order = args_order
|
||||
self.arg = arg
|
||||
self.args_formats = args_formats
|
||||
|
||||
def __repr__(self):
|
||||
return "MissingArgumentFormat({0!r}, {1!r}, {2!r})".format(
|
||||
self.arg,
|
||||
self.args_order,
|
||||
self.args_formats,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "Cannot find format for parameter {0} {1} in: {2}".format(
|
||||
self.arg,
|
||||
self.args_order,
|
||||
self.args_formats,
|
||||
)
|
||||
|
||||
|
||||
class MissingArgumentValue(CmdRunnerException):
|
||||
def __init__(self, args_order, arg):
|
||||
self.args_order = args_order
|
||||
self.arg = arg
|
||||
|
||||
def __repr__(self):
|
||||
return "MissingArgumentValue({0!r}, {1!r})".format(
|
||||
self.args_order,
|
||||
self.arg,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "Cannot find value for parameter {0} in {1}".format(
|
||||
self.arg,
|
||||
self.args_order,
|
||||
)
|
||||
|
||||
|
||||
class FormatError(CmdRunnerException):
|
||||
def __init__(self, name, value, args_formats, exc):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.args_formats = args_formats
|
||||
self.exc = exc
|
||||
super(FormatError, self).__init__()
|
||||
|
||||
def __repr__(self):
|
||||
return "FormatError({0!r}, {1!r}, {2!r}, {3!r})".format(
|
||||
self.name,
|
||||
self.value,
|
||||
self.args_formats,
|
||||
self.exc,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "Failed to format parameter {0} with value {1}: {2}".format(
|
||||
self.name,
|
||||
self.value,
|
||||
self.exc,
|
||||
)
|
||||
|
||||
|
||||
class _ArgFormat(object):
|
||||
def __init__(self, func, ignore_none=None):
|
||||
self.func = func
|
||||
self.ignore_none = ignore_none
|
||||
|
||||
def __call__(self, value, ctx_ignore_none):
|
||||
ignore_none = self.ignore_none if self.ignore_none is not None else ctx_ignore_none
|
||||
if value is None and ignore_none:
|
||||
return []
|
||||
f = self.func
|
||||
return [str(x) for x in f(value)]
|
||||
|
||||
|
||||
class _Format(object):
|
||||
@staticmethod
|
||||
def as_bool(args):
|
||||
return _ArgFormat(lambda value: _ensure_list(args) if value else [])
|
||||
|
||||
@staticmethod
|
||||
def as_bool_not(args):
|
||||
return _ArgFormat(lambda value: [] if value else _ensure_list(args), ignore_none=False)
|
||||
|
||||
@staticmethod
|
||||
def as_optval(arg, ignore_none=None):
|
||||
return _ArgFormat(lambda value: ["{0}{1}".format(arg, value)], ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_opt_val(arg, ignore_none=None):
|
||||
return _ArgFormat(lambda value: [arg, value], ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_opt_eq_val(arg, ignore_none=None):
|
||||
return _ArgFormat(lambda value: ["{0}={1}".format(arg, value)], ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_list(ignore_none=None):
|
||||
return _ArgFormat(_ensure_list, ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_fixed(args):
|
||||
return _ArgFormat(lambda value: _ensure_list(args), ignore_none=False)
|
||||
|
||||
@staticmethod
|
||||
def as_func(func, ignore_none=None):
|
||||
return _ArgFormat(func, ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_map(_map, default=None, ignore_none=None):
|
||||
return _ArgFormat(lambda value: _ensure_list(_map.get(value, default)), ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def as_default_type(_type, arg="", ignore_none=None):
|
||||
fmt = _Format
|
||||
if _type == "dict":
|
||||
return fmt.as_func(lambda d: ["--{0}={1}".format(*a) for a in iteritems(d)],
|
||||
ignore_none=ignore_none)
|
||||
if _type == "list":
|
||||
return fmt.as_func(lambda value: ["--{0}".format(x) for x in value], ignore_none=ignore_none)
|
||||
if _type == "bool":
|
||||
return fmt.as_bool("--{0}".format(arg))
|
||||
|
||||
return fmt.as_opt_val("--{0}".format(arg), ignore_none=ignore_none)
|
||||
|
||||
@staticmethod
|
||||
def unpack_args(func):
|
||||
@wraps(func)
|
||||
def wrapper(v):
|
||||
return func(*v)
|
||||
return wrapper
|
||||
|
||||
@staticmethod
|
||||
def unpack_kwargs(func):
|
||||
@wraps(func)
|
||||
def wrapper(v):
|
||||
return func(**v)
|
||||
return wrapper
|
||||
|
||||
|
||||
class CmdRunner(object):
|
||||
"""
|
||||
Wrapper for ``AnsibleModule.run_command()``.
|
||||
|
||||
It aims to provide a reusable runner with consistent argument formatting
|
||||
and sensible defaults.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _prepare_args_order(order):
|
||||
return tuple(order) if is_sequence(order) else tuple(order.split())
|
||||
|
||||
def __init__(self, module, command, arg_formats=None, default_args_order=(),
|
||||
check_rc=False, force_lang="C", path_prefix=None, environ_update=None):
|
||||
self.module = module
|
||||
self.command = _ensure_list(command)
|
||||
self.default_args_order = self._prepare_args_order(default_args_order)
|
||||
if arg_formats is None:
|
||||
arg_formats = {}
|
||||
self.arg_formats = dict(arg_formats)
|
||||
self.check_rc = check_rc
|
||||
self.force_lang = force_lang
|
||||
self.path_prefix = path_prefix
|
||||
if environ_update is None:
|
||||
environ_update = {}
|
||||
self.environ_update = environ_update
|
||||
|
||||
self.command[0] = module.get_bin_path(self.command[0], opt_dirs=path_prefix, required=True)
|
||||
|
||||
for mod_param_name, spec in iteritems(module.argument_spec):
|
||||
if mod_param_name not in self.arg_formats:
|
||||
self.arg_formats[mod_param_name] = _Format.as_default_type(spec['type'], mod_param_name)
|
||||
|
||||
def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs):
|
||||
if output_process is None:
|
||||
output_process = _process_as_is
|
||||
if args_order is None:
|
||||
args_order = self.default_args_order
|
||||
args_order = self._prepare_args_order(args_order)
|
||||
for p in args_order:
|
||||
if p not in self.arg_formats:
|
||||
raise MissingArgumentFormat(p, args_order, tuple(self.arg_formats.keys()))
|
||||
return _CmdRunnerContext(runner=self,
|
||||
args_order=args_order,
|
||||
output_process=output_process,
|
||||
ignore_value_none=ignore_value_none,
|
||||
check_mode_skip=check_mode_skip,
|
||||
check_mode_return=check_mode_return, **kwargs)
|
||||
|
||||
def has_arg_format(self, arg):
|
||||
return arg in self.arg_formats
|
||||
|
||||
# not decided whether to keep it or not, but if deprecating it will happen in a farther future.
|
||||
context = __call__
|
||||
|
||||
|
||||
class _CmdRunnerContext(object):
|
||||
def __init__(self, runner, args_order, output_process, ignore_value_none, check_mode_skip, check_mode_return, **kwargs):
|
||||
self.runner = runner
|
||||
self.args_order = tuple(args_order)
|
||||
self.output_process = output_process
|
||||
self.ignore_value_none = ignore_value_none
|
||||
self.check_mode_skip = check_mode_skip
|
||||
self.check_mode_return = check_mode_return
|
||||
self.run_command_args = dict(kwargs)
|
||||
|
||||
self.environ_update = runner.environ_update
|
||||
self.environ_update.update(self.run_command_args.get('environ_update', {}))
|
||||
if runner.force_lang:
|
||||
self.environ_update.update({
|
||||
'LANGUAGE': runner.force_lang,
|
||||
'LC_ALL': runner.force_lang,
|
||||
})
|
||||
self.run_command_args['environ_update'] = self.environ_update
|
||||
|
||||
if 'check_rc' not in self.run_command_args:
|
||||
self.run_command_args['check_rc'] = runner.check_rc
|
||||
self.check_rc = self.run_command_args['check_rc']
|
||||
|
||||
self.cmd = None
|
||||
self.results_rc = None
|
||||
self.results_out = None
|
||||
self.results_err = None
|
||||
self.results_processed = None
|
||||
|
||||
def run(self, **kwargs):
|
||||
runner = self.runner
|
||||
module = self.runner.module
|
||||
self.cmd = list(runner.command)
|
||||
self.context_run_args = dict(kwargs)
|
||||
|
||||
named_args = dict(module.params)
|
||||
named_args.update(kwargs)
|
||||
for arg_name in self.args_order:
|
||||
value = None
|
||||
try:
|
||||
value = named_args[arg_name]
|
||||
self.cmd.extend(runner.arg_formats[arg_name](value, ctx_ignore_none=self.ignore_value_none))
|
||||
except KeyError:
|
||||
raise MissingArgumentValue(self.args_order, arg_name)
|
||||
except Exception as e:
|
||||
raise FormatError(arg_name, value, runner.arg_formats[arg_name], e)
|
||||
|
||||
if self.check_mode_skip and module.check_mode:
|
||||
return self.check_mode_return
|
||||
results = module.run_command(self.cmd, **self.run_command_args)
|
||||
self.results_rc, self.results_out, self.results_err = results
|
||||
self.results_processed = self.output_process(*results)
|
||||
return self.results_processed
|
||||
|
||||
@property
|
||||
def run_info(self):
|
||||
return dict(
|
||||
ignore_value_none=self.ignore_value_none,
|
||||
check_rc=self.check_rc,
|
||||
environ_update=self.environ_update,
|
||||
args_order=self.args_order,
|
||||
cmd=self.cmd,
|
||||
run_command_args=self.run_command_args,
|
||||
context_run_args=self.context_run_args,
|
||||
results_rc=self.results_rc,
|
||||
results_out=self.results_out,
|
||||
results_err=self.results_err,
|
||||
results_processed=self.results_processed,
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
return False
|
||||
|
||||
|
||||
cmd_runner_fmt = _Format()
|
||||
|
||||
#
|
||||
# The fmt form is deprecated and will be removed in community.general 7.0.0
|
||||
# Please use:
|
||||
# cmd_runner_fmt
|
||||
# Or, to retain the same effect, use:
|
||||
# from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt as fmt
|
||||
fmt = cmd_runner_fmt
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2019 Gregory Thiemonge <gregory.thiemonge@gmail.com>
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Alexei Znamensky <russoz@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
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
|
||||
|
||||
|
||||
def gconftool2_runner(module, **kwargs):
|
||||
return CmdRunner(
|
||||
module,
|
||||
command='gconftool-2',
|
||||
arg_formats=dict(
|
||||
key=fmt.as_list(),
|
||||
value_type=fmt.as_opt_val("--type"),
|
||||
value=fmt.as_list(),
|
||||
direct=fmt.as_bool("--direct"),
|
||||
config_source=fmt.as_opt_val("--config-source"),
|
||||
get=fmt.as_bool("--get"),
|
||||
set_arg=fmt.as_bool("--set"),
|
||||
unset=fmt.as_bool("--unset"),
|
||||
),
|
||||
**kwargs
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c), Google Inc, 2017
|
||||
# Simplified BSD License (see simplified_bsd.txt or
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or
|
||||
# https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
@@ -351,7 +351,7 @@ def wait_to_finish(target, pending, refresh, timeout, min_interval=1, delay=3):
|
||||
|
||||
if pending and status not in pending:
|
||||
raise HwcModuleException(
|
||||
"unexpect status(%s) occurred" % status)
|
||||
"unexpect status(%s) occured" % status)
|
||||
|
||||
if not is_last_time:
|
||||
wait *= 2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright (C) 2018 IBM CORPORATION
|
||||
# Author(s): Tzur Eliyahu <tzure@il.ibm.com>
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -104,7 +104,6 @@ def keycloak_argument_spec():
|
||||
validate_certs=dict(type='bool', default=True),
|
||||
connection_timeout=dict(type='int', default=10),
|
||||
token=dict(type='str', no_log=True),
|
||||
http_agent=dict(type='str', default='Ansible'),
|
||||
)
|
||||
|
||||
|
||||
@@ -124,7 +123,6 @@ def get_token(module_params):
|
||||
"""
|
||||
token = module_params.get('token')
|
||||
base_url = module_params.get('auth_keycloak_url')
|
||||
http_agent = module_params.get('http_agent')
|
||||
|
||||
if not base_url.lower().startswith(('http', 'https')):
|
||||
raise KeycloakError("auth_url '%s' should either start with 'http' or 'https'." % base_url)
|
||||
@@ -151,7 +149,7 @@ def get_token(module_params):
|
||||
(k, v) for k, v in temp_payload.items() if v is not None)
|
||||
try:
|
||||
r = json.loads(to_native(open_url(auth_url, method='POST',
|
||||
validate_certs=validate_certs, http_agent=http_agent, timeout=connection_timeout,
|
||||
validate_certs=validate_certs, timeout=connection_timeout,
|
||||
data=urlencode(payload)).read()))
|
||||
except ValueError as e:
|
||||
raise KeycloakError(
|
||||
@@ -235,7 +233,6 @@ class KeycloakAPI(object):
|
||||
self.validate_certs = self.module.params.get('validate_certs')
|
||||
self.connection_timeout = self.module.params.get('connection_timeout')
|
||||
self.restheaders = connection_header
|
||||
self.http_agent = self.module.params.get('http_agent')
|
||||
|
||||
def get_realm_info_by_id(self, realm='master'):
|
||||
""" Obtain realm public info by id
|
||||
@@ -246,8 +243,7 @@ class KeycloakAPI(object):
|
||||
realm_info_url = URL_REALM_INFO.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(realm_info_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(realm_info_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -272,7 +268,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(realm_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(realm_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -297,7 +293,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update realm %s: %s' % (realm, str(e)),
|
||||
@@ -311,7 +307,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALMS.format(url=self.baseurl)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create realm %s: %s' % (realmrep['id'], str(e)),
|
||||
@@ -326,7 +322,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete realm %s: %s' % (realm, str(e)),
|
||||
@@ -344,8 +340,7 @@ class KeycloakAPI(object):
|
||||
clientlist_url += '?clientId=%s' % filter
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientlist_url, http_agent=self.http_agent, method='GET', headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientlist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of clients for realm %s: %s'
|
||||
@@ -376,8 +371,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(client_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(client_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -416,7 +410,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client %s in realm %s: %s'
|
||||
@@ -431,7 +425,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENTS.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client %s in realm %s: %s'
|
||||
@@ -447,7 +441,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client %s in realm %s: %s'
|
||||
@@ -462,8 +456,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
client_roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(client_roles_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(client_roles_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
||||
@@ -495,8 +488,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
for role in rolemappings:
|
||||
if rid == role['id']:
|
||||
@@ -516,8 +508,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -533,8 +524,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -551,7 +541,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
open_url(available_rolemappings_url, method="POST", headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -568,7 +558,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders,
|
||||
open_url(available_rolemappings_url, method="DELETE", headers=self.restheaders,
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -583,7 +573,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATES.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of client templates for realm %s: %s'
|
||||
@@ -602,7 +592,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, id=id, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client templates %s for realm %s: %s'
|
||||
@@ -648,7 +638,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client template %s in realm %s: %s'
|
||||
@@ -663,7 +653,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATES.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client template %s in realm %s: %s'
|
||||
@@ -679,7 +669,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client template %s in realm %s: %s'
|
||||
@@ -696,8 +686,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of clientscopes in realm %s: %s"
|
||||
@@ -714,8 +703,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscope_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientscope_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -760,7 +748,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(clientscopes_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscopes_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create clientscope %s in realm %s: %s"
|
||||
@@ -775,7 +763,7 @@ class KeycloakAPI(object):
|
||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=clientscoperep['id'])
|
||||
|
||||
try:
|
||||
return open_url(clientscope_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscope_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -813,7 +801,7 @@ class KeycloakAPI(object):
|
||||
# should have a good cid by here.
|
||||
clientscope_url = URL_CLIENTSCOPE.format(realm=realm, id=cid, url=self.baseurl)
|
||||
try:
|
||||
return open_url(clientscope_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscope_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -831,8 +819,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(protocolmappers_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(protocolmappers_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of protocolmappers in realm %s: %s"
|
||||
@@ -851,8 +838,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(protocolmapper_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(protocolmapper_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -899,7 +885,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm)
|
||||
try:
|
||||
return open_url(protocolmappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(protocolmappers_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create protocolmapper %s in realm %s: %s"
|
||||
@@ -915,7 +901,7 @@ class KeycloakAPI(object):
|
||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=mapper_rep['id'])
|
||||
|
||||
try:
|
||||
return open_url(protocolmapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(protocolmapper_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -932,8 +918,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of groups in realm %s: %s"
|
||||
@@ -950,8 +935,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=gid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -997,7 +981,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(groups_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(groups_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create group %s in realm %s: %s"
|
||||
@@ -1012,7 +996,7 @@ class KeycloakAPI(object):
|
||||
group_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=grouprep['id'])
|
||||
|
||||
try:
|
||||
return open_url(group_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(group_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update group %s in realm %s: %s'
|
||||
@@ -1049,7 +1033,7 @@ class KeycloakAPI(object):
|
||||
# should have a good groupid by here.
|
||||
group_url = URL_GROUP.format(realm=realm, groupid=groupid, url=self.baseurl)
|
||||
try:
|
||||
return open_url(group_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(group_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e)))
|
||||
@@ -1062,8 +1046,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
rolelist_url = URL_REALM_ROLES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for realm %s: %s'
|
||||
@@ -1081,7 +1064,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name))
|
||||
try:
|
||||
return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(role_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1101,7 +1084,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
roles_url = URL_REALM_ROLES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(roles_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s in realm %s: %s'
|
||||
@@ -1115,7 +1098,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(rolerep['name']))
|
||||
try:
|
||||
return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s in realm %s: %s'
|
||||
@@ -1129,7 +1112,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name))
|
||||
try:
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s in realm %s: %s'
|
||||
@@ -1148,8 +1131,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
rolelist_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for client %s in realm %s: %s'
|
||||
@@ -1173,7 +1155,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name))
|
||||
try:
|
||||
return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(role_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1199,7 +1181,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(roles_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s for client %s in realm %s: %s'
|
||||
@@ -1219,7 +1201,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep['name']))
|
||||
try:
|
||||
return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s for client %s in realm %s: %s'
|
||||
@@ -1238,7 +1220,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name))
|
||||
try:
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s for client %s in realm %s: %s'
|
||||
@@ -1255,8 +1237,7 @@ class KeycloakAPI(object):
|
||||
authentication_flow = {}
|
||||
# Check if the authentication flow exists on the Keycloak serveraders
|
||||
authentications = json.load(open_url(URL_AUTHENTICATION_FLOWS.format(url=self.baseurl, realm=realm), method='GET',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs))
|
||||
headers=self.restheaders, timeout=self.connection_timeout))
|
||||
for authentication in authentications:
|
||||
if authentication["alias"] == alias:
|
||||
authentication_flow = authentication
|
||||
@@ -1275,7 +1256,7 @@ class KeycloakAPI(object):
|
||||
flow_url = URL_AUTHENTICATION_FLOW.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(flow_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(flow_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete authentication flow %s in realm %s: %s'
|
||||
@@ -1298,18 +1279,16 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
copyfrom=quote(config["copyFrom"])),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_name),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
@@ -1337,19 +1316,17 @@ class KeycloakAPI(object):
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_flow),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
@@ -1372,10 +1349,9 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='PUT',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(updatedExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
|
||||
|
||||
@@ -1393,10 +1369,9 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(authenticationConfig),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
|
||||
@@ -1418,10 +1393,9 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newSubFlow),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new subflow %s: %s" % (subflowName, str(e)))
|
||||
|
||||
@@ -1442,10 +1416,9 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e)))
|
||||
|
||||
@@ -1466,9 +1439,8 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout)
|
||||
elif diff < 0:
|
||||
for i in range(-diff):
|
||||
open_url(
|
||||
@@ -1477,9 +1449,8 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to change execution priority %s: %s" % (executionId, str(e)))
|
||||
|
||||
@@ -1499,9 +1470,8 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(config["alias"])),
|
||||
method='GET',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
for execution in executions:
|
||||
if "authenticationConfig" in execution:
|
||||
execConfigId = execution["authenticationConfig"]
|
||||
@@ -1512,9 +1482,8 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=execConfigId),
|
||||
method='GET',
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
execution["authenticationConfig"] = execConfig
|
||||
return executions
|
||||
except Exception as e:
|
||||
@@ -1528,7 +1497,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idps_url = URL_IDENTITY_PROVIDERS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(idps_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(idps_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity providers for realm %s: %s'
|
||||
@@ -1545,7 +1514,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return json.loads(to_native(open_url(idp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(idp_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1565,7 +1534,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idps_url = URL_IDENTITY_PROVIDERS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(idps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idps_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider %s in realm %s: %s'
|
||||
@@ -1579,7 +1548,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=idprep['alias'])
|
||||
try:
|
||||
return open_url(idp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idp_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update identity provider %s in realm %s: %s'
|
||||
@@ -1592,7 +1561,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return open_url(idp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idp_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete identity provider %s in realm %s: %s'
|
||||
@@ -1606,8 +1575,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mappers_url = URL_IDENTITY_PROVIDER_MAPPERS.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return json.loads(to_native(open_url(mappers_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(mappers_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity provider mappers for idp %s in realm %s: %s'
|
||||
@@ -1625,8 +1593,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(mapper_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(mapper_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1647,7 +1614,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mappers_url = URL_IDENTITY_PROVIDER_MAPPERS.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return open_url(mappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mappers_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider mapper %s for idp %s in realm %s: %s'
|
||||
@@ -1662,7 +1629,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mapper['id'])
|
||||
try:
|
||||
return open_url(mapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mapper_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update mapper %s for identity provider %s in realm %s: %s'
|
||||
@@ -1676,7 +1643,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mid)
|
||||
try:
|
||||
return open_url(mapper_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mapper_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete mapper %s for identity provider %s in realm %s: %s'
|
||||
@@ -1693,7 +1660,7 @@ class KeycloakAPI(object):
|
||||
comps_url += '?%s' % filter
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(comps_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comps_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of components for realm %s: %s'
|
||||
@@ -1710,7 +1677,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1730,13 +1697,13 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
comps_url = URL_COMPONENTS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
resp = open_url(comps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
resp = open_url(comps_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(comprep), validate_certs=self.validate_certs)
|
||||
comp_url = resp.getheader('Location')
|
||||
if comp_url is None:
|
||||
self.module.fail_json(msg='Could not create component in realm %s: %s'
|
||||
% (realm, 'unexpected response'))
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create component in realm %s: %s'
|
||||
@@ -1753,7 +1720,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='Cannot update component without id')
|
||||
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return open_url(comp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(comp_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(comprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update component %s in realm %s: %s'
|
||||
@@ -1766,7 +1733,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return open_url(comp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(comp_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2016 Thomas Krahn (@Nosmoht)
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), Luke Murphy @decentral1se
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2018, Simon Weald <ansible@simonweald.com>
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
@@ -14,9 +14,6 @@ from ansible_collections.community.general.plugins.module_utils.mh.deco import m
|
||||
class ModuleHelperBase(object):
|
||||
module = None
|
||||
ModuleHelperException = _MHE
|
||||
_delegated_to_module = (
|
||||
'check_mode', 'get_bin_path', 'warn', 'deprecate',
|
||||
)
|
||||
|
||||
def __init__(self, module=None):
|
||||
self._changed = False
|
||||
@@ -27,22 +24,6 @@ class ModuleHelperBase(object):
|
||||
if not isinstance(self.module, AnsibleModule):
|
||||
self.module = AnsibleModule(**self.module)
|
||||
|
||||
@property
|
||||
def diff_mode(self):
|
||||
return self.module._diff
|
||||
|
||||
@property
|
||||
def verbosity(self):
|
||||
return self.module._verbosity
|
||||
|
||||
def do_raise(self, *args, **kwargs):
|
||||
raise _MHE(*args, **kwargs)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr in self._delegated_to_module:
|
||||
return getattr(self.module, attr)
|
||||
raise AttributeError("ModuleHelperBase has no attribute '%s'" % (attr, ))
|
||||
|
||||
def __init_module__(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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.common.text.converters import to_native
|
||||
|
||||
|
||||
class ModuleHelperException(Exception):
|
||||
def __init__(self, msg, update_output=None, *args, **kwargs):
|
||||
self.msg = to_native(msg or "Module failed with exception: {0}".format(self))
|
||||
if update_output is None:
|
||||
update_output = {}
|
||||
self.update_output = update_output
|
||||
@staticmethod
|
||||
def _get_remove(key, kwargs):
|
||||
if key in kwargs:
|
||||
result = kwargs[key]
|
||||
del kwargs[key]
|
||||
return result
|
||||
return None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.msg = self._get_remove('msg', kwargs) or "Module failed with exception: {0}".format(self)
|
||||
self.update_output = self._get_remove('update_output', kwargs) or {}
|
||||
super(ModuleHelperException, self).__init__(*args)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
@@ -128,7 +128,8 @@ class CmdMixin(object):
|
||||
for param in param_list:
|
||||
if isinstance(param, dict):
|
||||
if len(param) != 1:
|
||||
self.do_raise("run_command parameter as a dict must contain only one key: {0}".format(param))
|
||||
raise self.ModuleHelperException("run_command parameter as a dict must "
|
||||
"contain only one key: {0}".format(param))
|
||||
_param = list(param.keys())[0]
|
||||
fmt = find_format(_param)
|
||||
value = param[_param]
|
||||
@@ -140,9 +141,9 @@ class CmdMixin(object):
|
||||
fmt = find_format(param)
|
||||
value = extra_params[param]
|
||||
else:
|
||||
self.do_raise('Cannot determine value for parameter: {0}'.format(param))
|
||||
raise self.ModuleHelperException('Cannot determine value for parameter: {0}'.format(param))
|
||||
else:
|
||||
self.do_raise("run_command parameter must be either a str or a dict: {0}".format(param))
|
||||
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)
|
||||
|
||||
return cmd_args
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
@@ -44,8 +44,7 @@ class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelper
|
||||
version="6.0.0",
|
||||
collection_name="community.general",
|
||||
target=ModuleHelper,
|
||||
module=self.module,
|
||||
)
|
||||
module=self.module)
|
||||
|
||||
def update_output(self, **kwargs):
|
||||
self.update_vars(meta={"output": True}, **kwargs)
|
||||
@@ -66,7 +65,7 @@ class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelper
|
||||
facts = self.vars.facts()
|
||||
if facts is not None:
|
||||
result['ansible_facts'] = {self.facts_name: facts}
|
||||
if self.diff_mode:
|
||||
if self.module._diff:
|
||||
diff = result.get('diff', {})
|
||||
vars_diff = self.vars.diff() or {}
|
||||
result['diff'] = dict_merge(dict(diff), vars_diff)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
@@ -337,6 +337,7 @@ def pritunl_auth_request(
|
||||
|
||||
auth_string = "&".join(
|
||||
[api_token, auth_timestamp, auth_nonce, method.upper(), path]
|
||||
+ ([data] if data else [])
|
||||
)
|
||||
|
||||
auth_signature = base64.b64encode(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# 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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user