mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-01 10:53:20 +00:00
Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8505bd8d9 | ||
|
|
aa0df4d81d | ||
|
|
c349861190 | ||
|
|
a853e561f1 | ||
|
|
1d9367ebea | ||
|
|
f1cf0c1949 | ||
|
|
5b26e09319 | ||
|
|
3cc5a5bc8d | ||
|
|
cd0818e488 | ||
|
|
ad197a303a | ||
|
|
cdbf70d781 | ||
|
|
c77661c184 | ||
|
|
d99a7974ad | ||
|
|
0fcd23a3f1 | ||
|
|
49fc40b275 | ||
|
|
c6c65ee554 | ||
|
|
d385caa73f | ||
|
|
2b52822043 | ||
|
|
9ebb5b270a | ||
|
|
7b9ba8d2e2 | ||
|
|
d33467611e | ||
|
|
9e0eeb0b94 | ||
|
|
5fd97399b0 | ||
|
|
eb14bd572d | ||
|
|
beff4063b3 | ||
|
|
b6cd89c677 | ||
|
|
9c535cb17d | ||
|
|
772b5b1ad3 | ||
|
|
e5ab4be82d | ||
|
|
65dd204dab | ||
|
|
5112994fd4 | ||
|
|
0d4d3f6b73 | ||
|
|
4a7b93df64 | ||
|
|
ab20c90929 | ||
|
|
721ea50420 | ||
|
|
23b646d5c2 | ||
|
|
1e78ff58d5 | ||
|
|
280a5b0e61 | ||
|
|
21a840eab7 | ||
|
|
6f98adf602 | ||
|
|
8c45cba53c | ||
|
|
9fe31235d8 | ||
|
|
fc7609628e | ||
|
|
a1d4051a12 | ||
|
|
acdf19c9e6 | ||
|
|
e2513b318e | ||
|
|
cc2794ad05 | ||
|
|
154b5f86fd | ||
|
|
e302058e2d | ||
|
|
1affd48260 | ||
|
|
dc5a89b040 | ||
|
|
71d8109275 | ||
|
|
73362a1e43 | ||
|
|
bc4fda8b14 | ||
|
|
2766898ea8 | ||
|
|
02f123877a | ||
|
|
d4c29e19c0 | ||
|
|
200ab045fa | ||
|
|
9b4decd831 | ||
|
|
566ec0a002 | ||
|
|
2e72051b6c | ||
|
|
89d33bbd7b | ||
|
|
76b6c8e184 | ||
|
|
fdc279def9 | ||
|
|
a471fa88b8 | ||
|
|
bf4e5dc3c0 | ||
|
|
185bdaaa39 | ||
|
|
55d44975dd | ||
|
|
43772cfbbb | ||
|
|
e93b6231ec | ||
|
|
605a557a8d | ||
|
|
d97f1a31ba | ||
|
|
5949b29a12 | ||
|
|
993d580adc | ||
|
|
1ac7783c5c | ||
|
|
f0724c0975 | ||
|
|
f3aca8a575 | ||
|
|
ad1cf82a34 | ||
|
|
42d0a55984 | ||
|
|
4f4d962f7c | ||
|
|
5343880fa5 | ||
|
|
5ea44edc64 | ||
|
|
8152cb3e1f | ||
|
|
eae0c4f92b | ||
|
|
57277e0661 | ||
|
|
53a941cee7 | ||
|
|
e6edf9cdea | ||
|
|
5550ba1946 | ||
|
|
374378beeb | ||
|
|
5222df306b | ||
|
|
788c722b3e | ||
|
|
2ddbda2aa7 | ||
|
|
cb939cbb75 | ||
|
|
7db1613730 | ||
|
|
38a16b421d | ||
|
|
bd8b7e3737 | ||
|
|
057f7196c7 | ||
|
|
8d8fc3d3ba | ||
|
|
5f7a3ac896 | ||
|
|
23ac1b62c3 | ||
|
|
186d410f63 | ||
|
|
1978100d25 | ||
|
|
6fec5a7005 | ||
|
|
aca2afc6f8 | ||
|
|
adda8d3113 | ||
|
|
21ed66a097 | ||
|
|
73a1ac2f1c | ||
|
|
7c3b441246 | ||
|
|
19613ce111 | ||
|
|
594ca4f983 | ||
|
|
af08ea33b1 | ||
|
|
7f729d99a2 | ||
|
|
5b62e0edd6 | ||
|
|
b935dcedd8 | ||
|
|
c58181bdc9 | ||
|
|
f71af21287 | ||
|
|
1a48ebd699 | ||
|
|
5e1be68b01 | ||
|
|
812431e7fe | ||
|
|
04f36f0bac | ||
|
|
2f3d3aaf76 | ||
|
|
e7024c3f97 | ||
|
|
0c2642aa2e | ||
|
|
2f62b6bbc6 | ||
|
|
5c10ed5f5c | ||
|
|
a4bac47520 | ||
|
|
b396ceb6c3 | ||
|
|
e4911a0278 | ||
|
|
1b3e9d8e99 | ||
|
|
6243112067 | ||
|
|
7d23c90c6e | ||
|
|
825bd81e44 | ||
|
|
e10f95836e | ||
|
|
176f6a62ae | ||
|
|
b1b6134f69 | ||
|
|
5103c3cea0 | ||
|
|
8444902289 | ||
|
|
2e1040bfa5 | ||
|
|
5c5ae113c9 | ||
|
|
1d7843465d | ||
|
|
b79f286a66 | ||
|
|
45e1d0a01a | ||
|
|
c7213ec1b5 | ||
|
|
7c9dd8d8ad | ||
|
|
9842b057b0 | ||
|
|
63eca8e68c | ||
|
|
569bd30148 | ||
|
|
70acdf1f6d | ||
|
|
86caa19f78 | ||
|
|
f9acef140f | ||
|
|
31a147f0eb | ||
|
|
9b0ebef408 | ||
|
|
4c734ea134 | ||
|
|
ccf71fb483 | ||
|
|
585d4e8784 | ||
|
|
47f235dc68 | ||
|
|
08200954e9 | ||
|
|
9e66e2c653 | ||
|
|
510ad54062 | ||
|
|
a89ca23ecc | ||
|
|
2261bd62de | ||
|
|
3726b50a92 | ||
|
|
037948fde9 | ||
|
|
9597b7c110 | ||
|
|
780a7c6a38 | ||
|
|
4c24a12462 | ||
|
|
990fffb563 | ||
|
|
1d9bfc206b | ||
|
|
3b74e9b646 | ||
|
|
37308c929b | ||
|
|
2945509a55 | ||
|
|
57be1e8be4 | ||
|
|
086b4e4fb8 | ||
|
|
92dcf1e0b2 | ||
|
|
78a02b84f3 | ||
|
|
7846cddab8 | ||
|
|
b0af1e9c75 | ||
|
|
f6349578c6 | ||
|
|
e4287becb1 | ||
|
|
005e8b151a | ||
|
|
cd2e55e2ab | ||
|
|
1ff276ec34 | ||
|
|
c16c4a32d1 | ||
|
|
3a01ceb355 | ||
|
|
8ca91ab283 | ||
|
|
c88a40d3e3 | ||
|
|
0dfd02c1ca | ||
|
|
5d9072030e | ||
|
|
49765f103b | ||
|
|
1782efb09e | ||
|
|
d8807e9b51 | ||
|
|
d823d71442 | ||
|
|
46d2cddbde | ||
|
|
069b785cb2 | ||
|
|
090d3f3709 | ||
|
|
68a9b66966 | ||
|
|
f96c6476fe | ||
|
|
fc0f677535 | ||
|
|
9a986473bd | ||
|
|
e9f0e49283 | ||
|
|
5eff31e760 | ||
|
|
39c58d5469 | ||
|
|
20d7be4f38 | ||
|
|
2d26fba0b9 | ||
|
|
d6168a196b | ||
|
|
02de81c39e | ||
|
|
4096b9fa5a | ||
|
|
fe3a3a7638 | ||
|
|
7cac741e77 | ||
|
|
f84ebed63f | ||
|
|
f905a1bc94 | ||
|
|
b0470f2e59 | ||
|
|
42175e38b2 | ||
|
|
8e79844b75 | ||
|
|
1338db358a | ||
|
|
06c4439a1c | ||
|
|
16d5d5fc57 | ||
|
|
71af3226f3 | ||
|
|
b3037a46be | ||
|
|
f7df19adbd | ||
|
|
3bca21aa1b | ||
|
|
1bb3d41e15 | ||
|
|
f214f206c3 | ||
|
|
9b8011d692 | ||
|
|
f227038f38 | ||
|
|
86a2996814 | ||
|
|
eb154003cf | ||
|
|
212871fcaf | ||
|
|
1795a67b8e | ||
|
|
a71c0af9cc | ||
|
|
569cde6c3e | ||
|
|
f0db1d1f6b | ||
|
|
5a36e84b86 | ||
|
|
a74c6db77f | ||
|
|
9a14980ca7 | ||
|
|
8c9effce1f | ||
|
|
51ec3594dd | ||
|
|
802f8ea224 | ||
|
|
23af148021 | ||
|
|
1a2c2d0a64 | ||
|
|
80243f8180 | ||
|
|
13b5c4092a |
@@ -29,20 +29,22 @@ schedules:
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-11
|
||||
- stable-10
|
||||
- stable-9
|
||||
- cron: 0 11 * * 0
|
||||
displayName: Weekly (old stable branches)
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- stable-9
|
||||
- stable-8
|
||||
|
||||
variables:
|
||||
- name: checkoutPath
|
||||
value: ansible_collections/community/general
|
||||
- name: coverageBranches
|
||||
value: main
|
||||
- name: pipelinesCoverage
|
||||
value: coverage
|
||||
- name: entryPoint
|
||||
value: tests/utils/shippable/shippable.sh
|
||||
- name: fetchDepth
|
||||
@@ -51,7 +53,7 @@ variables:
|
||||
resources:
|
||||
containers:
|
||||
- container: default
|
||||
image: quay.io/ansible/azure-pipelines-test-container:7.0.0
|
||||
image: quay.io/ansible/azure-pipelines-test-container:6.0.0
|
||||
|
||||
pool: Standard
|
||||
|
||||
@@ -70,19 +72,7 @@ stages:
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_19
|
||||
displayName: Sanity 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.19/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- test: extra
|
||||
- stage: Sanity_2_18
|
||||
displayName: Sanity 2.18
|
||||
dependsOn: []
|
||||
@@ -109,6 +99,19 @@ stages:
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_16
|
||||
displayName: Sanity 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.16/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
### Units
|
||||
- stage: Units_devel
|
||||
displayName: Units devel
|
||||
@@ -119,24 +122,12 @@ stages:
|
||||
nameFormat: Python {0}
|
||||
testFormat: devel/units/{0}/1
|
||||
targets:
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- test: '3.10'
|
||||
- test: '3.11'
|
||||
- test: '3.12'
|
||||
- test: '3.13'
|
||||
- test: '3.14'
|
||||
- stage: Units_2_19
|
||||
displayName: Units 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.19/units/{0}/1
|
||||
targets:
|
||||
- test: 3.8
|
||||
- test: "3.11"
|
||||
- test: "3.13"
|
||||
- stage: Units_2_18
|
||||
displayName: Units 2.18
|
||||
dependsOn: []
|
||||
@@ -147,7 +138,6 @@ stages:
|
||||
testFormat: 2.18/units/{0}/1
|
||||
targets:
|
||||
- test: 3.8
|
||||
- test: "3.11"
|
||||
- test: "3.13"
|
||||
- stage: Units_2_17
|
||||
displayName: Units 2.17
|
||||
@@ -159,8 +149,19 @@ stages:
|
||||
testFormat: 2.17/units/{0}/1
|
||||
targets:
|
||||
- test: 3.7
|
||||
- test: "3.10"
|
||||
- test: "3.12"
|
||||
- stage: Units_2_16
|
||||
displayName: Units 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.16/units/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- test: "3.11"
|
||||
|
||||
## Remote
|
||||
- stage: Remote_devel_extra_vms
|
||||
@@ -171,10 +172,10 @@ stages:
|
||||
parameters:
|
||||
testFormat: devel/{0}
|
||||
targets:
|
||||
- name: Alpine 3.22
|
||||
test: alpine/3.22
|
||||
# - name: Fedora 42
|
||||
# test: fedora/42
|
||||
- name: Alpine 3.20
|
||||
test: alpine/3.20
|
||||
# - name: Fedora 40
|
||||
# test: fedora/40
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu/22.04
|
||||
- name: Ubuntu 24.04
|
||||
@@ -189,34 +190,14 @@ stages:
|
||||
parameters:
|
||||
testFormat: devel/{0}
|
||||
targets:
|
||||
- name: macOS 15.3
|
||||
test: macos/15.3
|
||||
- name: RHEL 10.0
|
||||
test: rhel/10.0
|
||||
- name: RHEL 9.6
|
||||
test: rhel/9.6
|
||||
- name: FreeBSD 14.3
|
||||
test: freebsd/14.3
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_19
|
||||
displayName: Remote 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.19/{0}
|
||||
targets:
|
||||
- name: RHEL 9.5
|
||||
test: rhel/9.5
|
||||
- name: RHEL 10.0
|
||||
test: rhel/10.0
|
||||
- name: FreeBSD 14.2
|
||||
test: freebsd/14.2
|
||||
- name: macOS 14.3
|
||||
test: macos/14.3
|
||||
- name: RHEL 9.4
|
||||
test: rhel/9.4
|
||||
- name: FreeBSD 14.1
|
||||
test: freebsd/14.1
|
||||
- name: FreeBSD 13.4
|
||||
test: freebsd/13.4
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -229,12 +210,8 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.18/{0}
|
||||
targets:
|
||||
- name: macOS 14.3
|
||||
test: macos/14.3
|
||||
- name: RHEL 9.4
|
||||
test: rhel/9.4
|
||||
- name: FreeBSD 14.1
|
||||
test: freebsd/14.1
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -247,10 +224,34 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.17/{0}
|
||||
targets:
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- name: FreeBSD 13.3
|
||||
test: freebsd/13.3
|
||||
- name: RHEL 9.3
|
||||
test: rhel/9.3
|
||||
- name: FreeBSD 14.0
|
||||
test: freebsd/14.0
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_16
|
||||
displayName: Remote 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.16/{0}
|
||||
targets:
|
||||
- name: macOS 13.2
|
||||
test: macos/13.2
|
||||
- name: RHEL 9.2
|
||||
test: rhel/9.2
|
||||
- name: RHEL 8.8
|
||||
test: rhel/8.8
|
||||
- name: RHEL 7.9
|
||||
test: rhel/7.9
|
||||
# - name: FreeBSD 13.2
|
||||
# test: freebsd/13.2
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -265,10 +266,10 @@ stages:
|
||||
parameters:
|
||||
testFormat: devel/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 42
|
||||
test: fedora42
|
||||
- name: Alpine 3.22
|
||||
test: alpine322
|
||||
- name: Fedora 40
|
||||
test: fedora40
|
||||
- name: Alpine 3.20
|
||||
test: alpine320
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
@@ -277,22 +278,6 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_19
|
||||
displayName: Docker 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.19/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 41
|
||||
test: fedora41
|
||||
- name: Alpine 3.21
|
||||
test: alpine321
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_18
|
||||
displayName: Docker 2.18
|
||||
dependsOn: []
|
||||
@@ -301,10 +286,6 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.18/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 40
|
||||
test: fedora40
|
||||
- name: Alpine 3.20
|
||||
test: alpine320
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
groups:
|
||||
@@ -329,6 +310,26 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_16
|
||||
displayName: Docker 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.16/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 38
|
||||
test: fedora38
|
||||
- name: openSUSE 15
|
||||
test: opensuse15
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
|
||||
### Community Docker
|
||||
- stage: Docker_community_devel
|
||||
@@ -344,7 +345,7 @@ stages:
|
||||
- name: Debian Bookworm
|
||||
test: debian-bookworm/3.11
|
||||
- name: ArchLinux
|
||||
test: archlinux/3.13
|
||||
test: archlinux/3.12
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -361,19 +362,8 @@ stages:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: devel/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '3.9'
|
||||
# - test: '3.12'
|
||||
# - test: '3.14'
|
||||
# - stage: Generic_2_19
|
||||
# displayName: Generic 2.19
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: 2.19/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '3.9'
|
||||
# - test: '3.8'
|
||||
# - test: '3.11'
|
||||
# - test: '3.13'
|
||||
# - stage: Generic_2_18
|
||||
# displayName: Generic 2.18
|
||||
@@ -397,32 +387,44 @@ stages:
|
||||
# targets:
|
||||
# - test: '3.7'
|
||||
# - test: '3.12'
|
||||
# - stage: Generic_2_16
|
||||
# displayName: Generic 2.16
|
||||
# dependsOn: []
|
||||
# jobs:
|
||||
# - template: templates/matrix.yml
|
||||
# parameters:
|
||||
# nameFormat: Python {0}
|
||||
# testFormat: 2.16/generic/{0}/1
|
||||
# targets:
|
||||
# - test: '2.7'
|
||||
# - test: '3.6'
|
||||
# - test: '3.11'
|
||||
|
||||
- stage: Summary
|
||||
condition: succeededOrFailed()
|
||||
dependsOn:
|
||||
- Sanity_devel
|
||||
- Sanity_2_19
|
||||
- Sanity_2_18
|
||||
- Sanity_2_17
|
||||
- Sanity_2_16
|
||||
- Units_devel
|
||||
- Units_2_19
|
||||
- Units_2_18
|
||||
- Units_2_17
|
||||
- Units_2_16
|
||||
- Remote_devel_extra_vms
|
||||
- Remote_devel
|
||||
- Remote_2_19
|
||||
- Remote_2_18
|
||||
- Remote_2_17
|
||||
- Remote_2_16
|
||||
- Docker_devel
|
||||
- Docker_2_19
|
||||
- Docker_2_18
|
||||
- Docker_2_17
|
||||
- Docker_2_16
|
||||
- Docker_community_devel
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - Generic_devel
|
||||
# - Generic_2_19
|
||||
# - Generic_2_18
|
||||
# - Generic_2_17
|
||||
# - Generic_2_16
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
||||
@@ -28,6 +28,16 @@ jobs:
|
||||
- bash: .azure-pipelines/scripts/report-coverage.sh
|
||||
displayName: Generate Coverage Report
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
# Azure Pipelines only accepts a single coverage data file.
|
||||
# That means only Python or PowerShell coverage can be uploaded, but not both.
|
||||
# Set the "pipelinesCoverage" variable to determine which type is uploaded.
|
||||
# Use "coverage" for Python and "coverage-powershell" for PowerShell.
|
||||
summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
|
||||
displayName: Publish to Azure Pipelines
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
- bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
|
||||
displayName: Publish to codecov.io
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
|
||||
@@ -50,11 +50,11 @@ jobs:
|
||||
parameters:
|
||||
jobs:
|
||||
- ${{ if eq(length(parameters.groups), 0) }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }}
|
||||
test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }}
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }}
|
||||
test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }}
|
||||
- ${{ if not(eq(length(parameters.groups), 0)) }}:
|
||||
- ${{ each group in parameters.groups }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }}
|
||||
test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }}
|
||||
- ${{ each group in parameters.groups }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }}
|
||||
test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }}
|
||||
|
||||
@@ -14,37 +14,37 @@ parameters:
|
||||
|
||||
jobs:
|
||||
- ${{ each job in parameters.jobs }}:
|
||||
- job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }}
|
||||
displayName: ${{ job.name }}
|
||||
container: default
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
|
||||
displayName: Run Tests
|
||||
- bash: .azure-pipelines/scripts/process-results.sh
|
||||
condition: succeededOrFailed()
|
||||
displayName: Process Results
|
||||
- bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Aggregate Coverage Data
|
||||
- task: PublishTestResults@2
|
||||
condition: eq(variables.haveTestResults, 'true')
|
||||
inputs:
|
||||
testResultsFiles: "$(outputPath)/junit/*.xml"
|
||||
displayName: Publish Test Results
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveBotResults, 'true')
|
||||
displayName: Publish Bot Results
|
||||
inputs:
|
||||
targetPath: "$(outputPath)/bot/"
|
||||
artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Publish Coverage Data
|
||||
inputs:
|
||||
targetPath: "$(Agent.TempDirectory)/coverage/"
|
||||
artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
- job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }}
|
||||
displayName: ${{ job.name }}
|
||||
container: default
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
|
||||
displayName: Run Tests
|
||||
- bash: .azure-pipelines/scripts/process-results.sh
|
||||
condition: succeededOrFailed()
|
||||
displayName: Process Results
|
||||
- bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Aggregate Coverage Data
|
||||
- task: PublishTestResults@2
|
||||
condition: eq(variables.haveTestResults, 'true')
|
||||
inputs:
|
||||
testResultsFiles: "$(outputPath)/junit/*.xml"
|
||||
displayName: Publish Test Results
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveBotResults, 'true')
|
||||
displayName: Publish Bot Results
|
||||
inputs:
|
||||
targetPath: "$(outputPath)/bot/"
|
||||
artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Publish Coverage Data
|
||||
inputs:
|
||||
targetPath: "$(Agent.TempDirectory)/coverage/"
|
||||
artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# YAML reformatting
|
||||
d032de3b16eed11ea3a31cd3d96d78f7c46a2ee0
|
||||
e8f965fbf8154ea177c6622da149f2ae8533bd3c
|
||||
e938ca5f20651abc160ee6aba10014013d04dcc1
|
||||
eaa5e07b2866e05b6c7b5628ca92e9cb1142d008
|
||||
147
.github/BOTMETA.yml
vendored
147
.github/BOTMETA.yml
vendored
@@ -61,6 +61,7 @@ files:
|
||||
$callbacks/elastic.py:
|
||||
keywords: apm observability
|
||||
maintainers: v1v
|
||||
$callbacks/hipchat.py: {}
|
||||
$callbacks/jabber.py: {}
|
||||
$callbacks/log_plays.py: {}
|
||||
$callbacks/loganalytics.py:
|
||||
@@ -77,8 +78,6 @@ files:
|
||||
$callbacks/opentelemetry.py:
|
||||
keywords: opentelemetry observability
|
||||
maintainers: v1v
|
||||
$callbacks/print_task.py:
|
||||
maintainers: demonpig
|
||||
$callbacks/say.py:
|
||||
keywords: brew cask darwin homebrew macosx macports osx
|
||||
labels: macos say
|
||||
@@ -92,8 +91,6 @@ files:
|
||||
maintainers: ryancurrah
|
||||
$callbacks/syslog_json.py:
|
||||
maintainers: imjoseangel
|
||||
$callbacks/tasks_only.py:
|
||||
maintainers: felixfontein
|
||||
$callbacks/timestamp.py:
|
||||
maintainers: kurokobo
|
||||
$callbacks/unixy.py:
|
||||
@@ -120,8 +117,6 @@ files:
|
||||
$connections/saltstack.py:
|
||||
labels: saltstack
|
||||
maintainers: mscherer
|
||||
$connections/wsl.py:
|
||||
maintainers: rgl
|
||||
$connections/zone.py:
|
||||
maintainers: $team_ansible_core
|
||||
$doc_fragments/:
|
||||
@@ -141,8 +136,6 @@ files:
|
||||
$doc_fragments/xenserver.py:
|
||||
labels: xenserver
|
||||
maintainers: bvitnik
|
||||
$filters/accumulate.py:
|
||||
maintainers: VannTen
|
||||
$filters/counter.py:
|
||||
maintainers: keilr
|
||||
$filters/crc32.py:
|
||||
@@ -165,14 +158,6 @@ files:
|
||||
maintainers: Ajpantuso
|
||||
$filters/jc.py:
|
||||
maintainers: kellyjonbrazil
|
||||
$filters/json_diff.yml:
|
||||
maintainers: numo68
|
||||
$filters/json_patch.py:
|
||||
maintainers: numo68
|
||||
$filters/json_patch.yml:
|
||||
maintainers: numo68
|
||||
$filters/json_patch_recipe.yml:
|
||||
maintainers: numo68
|
||||
$filters/json_query.py: {}
|
||||
$filters/keep_keys.py:
|
||||
maintainers: vbotka
|
||||
@@ -209,8 +194,6 @@ files:
|
||||
maintainers: resmo
|
||||
$filters/to_months.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_prettytable.py:
|
||||
maintainers: tgadiev
|
||||
$filters/to_seconds.yml:
|
||||
maintainers: resmo
|
||||
$filters/to_time_unit.yml:
|
||||
@@ -229,8 +212,6 @@ files:
|
||||
maintainers: opoplawski
|
||||
$inventories/gitlab_runners.py:
|
||||
maintainers: morph027
|
||||
$inventories/iocage.py:
|
||||
maintainers: vbotka
|
||||
$inventories/icinga2.py:
|
||||
maintainers: BongoEADGC6
|
||||
$inventories/linode.py:
|
||||
@@ -246,9 +227,13 @@ files:
|
||||
keywords: opennebula dynamic inventory script
|
||||
labels: cloud opennebula
|
||||
maintainers: feldsam
|
||||
$inventories/proxmox.py:
|
||||
maintainers: $team_virt ilijamt krauthosting
|
||||
$inventories/scaleway.py:
|
||||
labels: cloud scaleway
|
||||
maintainers: $team_scaleway
|
||||
$inventories/stackpath_compute.py:
|
||||
maintainers: shayrybak
|
||||
$inventories/virtualbox.py: {}
|
||||
$inventories/xen_orchestra.py:
|
||||
maintainers: ddelnano shinuza
|
||||
@@ -292,6 +277,9 @@ files:
|
||||
$lookups/lastpass.py: {}
|
||||
$lookups/lmdb_kv.py:
|
||||
maintainers: jpmens
|
||||
$lookups/manifold.py:
|
||||
labels: manifold
|
||||
maintainers: galanoff
|
||||
$lookups/merge_variables.py:
|
||||
maintainers: rlenferink m-a-r-k-e alpex8
|
||||
$lookups/onepass:
|
||||
@@ -303,8 +291,6 @@ files:
|
||||
$lookups/onepassword_raw.py:
|
||||
ignore: scottsb
|
||||
maintainers: azenk
|
||||
$lookups/onepassword_ssh_key.py:
|
||||
maintainers: mohammedbabelly20
|
||||
$lookups/passwordstore.py: {}
|
||||
$lookups/random_pet.py:
|
||||
maintainers: Akasurde
|
||||
@@ -322,12 +308,8 @@ files:
|
||||
maintainers: delineaKrehl tylerezimmerman
|
||||
$module_utils/:
|
||||
labels: module_utils
|
||||
$module_utils/android_sdkmanager.py:
|
||||
maintainers: shamilovstas
|
||||
$module_utils/btrfs.py:
|
||||
maintainers: gnfzdz
|
||||
$module_utils/cmd_runner_fmt.py:
|
||||
maintainers: russoz
|
||||
$module_utils/cmd_runner.py:
|
||||
maintainers: russoz
|
||||
$module_utils/deps.py:
|
||||
@@ -374,13 +356,9 @@ files:
|
||||
$module_utils/oracle/oci_utils.py:
|
||||
labels: cloud
|
||||
maintainers: $team_oracle
|
||||
$module_utils/pacemaker.py:
|
||||
maintainers: munchtoast
|
||||
$module_utils/pipx.py:
|
||||
labels: pipx
|
||||
maintainers: russoz
|
||||
$module_utils/pkg_req.py:
|
||||
maintainers: russoz
|
||||
$module_utils/python_runner.py:
|
||||
maintainers: russoz
|
||||
$module_utils/puppet.py:
|
||||
@@ -402,8 +380,6 @@ files:
|
||||
maintainers: russoz
|
||||
$module_utils/ssh.py:
|
||||
maintainers: russoz
|
||||
$module_utils/systemd.py:
|
||||
maintainers: NomakCooper
|
||||
$module_utils/storage/hpe3par/hpe3par.py:
|
||||
maintainers: farhan7500 gautamphegde
|
||||
$module_utils/utm_utils.py:
|
||||
@@ -415,8 +391,6 @@ files:
|
||||
$module_utils/wdc_redfish_utils.py:
|
||||
labels: wdc_redfish_utils
|
||||
maintainers: $team_wdc
|
||||
$module_utils/xdg_mime.py:
|
||||
maintainers: mhalano
|
||||
$module_utils/xenserver.py:
|
||||
labels: xenserver
|
||||
maintainers: bvitnik
|
||||
@@ -443,8 +417,6 @@ files:
|
||||
ignore: DavidWittman jiuka
|
||||
labels: alternatives
|
||||
maintainers: mulby
|
||||
$modules/android_sdk.py:
|
||||
maintainers: shamilovstas
|
||||
$modules/ansible_galaxy_install.py:
|
||||
maintainers: russoz
|
||||
$modules/apache2_mod_proxy.py:
|
||||
@@ -475,7 +447,7 @@ files:
|
||||
$modules/bearychat.py:
|
||||
maintainers: tonyseek
|
||||
$modules/bigpanda.py:
|
||||
ignore: hkariti
|
||||
maintainers: hkariti
|
||||
$modules/bitbucket_:
|
||||
maintainers: catcombo
|
||||
$modules/bootc_manage.py:
|
||||
@@ -500,6 +472,8 @@ files:
|
||||
maintainers: NickatEpic
|
||||
$modules/cisco_webex.py:
|
||||
maintainers: drew-russell
|
||||
$modules/clc_:
|
||||
maintainers: clc-runner
|
||||
$modules/cloud_init_data_facts.py:
|
||||
maintainers: resmo
|
||||
$modules/cloudflare_dns.py:
|
||||
@@ -531,8 +505,6 @@ files:
|
||||
ignore: skornehl
|
||||
$modules/dconf.py:
|
||||
maintainers: azaghal
|
||||
$modules/decompress.py:
|
||||
maintainers: shamilovstas
|
||||
$modules/deploy_helper.py:
|
||||
maintainers: ramondelafuente
|
||||
$modules/dimensiondata_network.py:
|
||||
@@ -658,6 +630,8 @@ files:
|
||||
maintainers: marns93
|
||||
$modules/hg.py:
|
||||
maintainers: yeukhon
|
||||
$modules/hipchat.py:
|
||||
maintainers: pb8226 shirou
|
||||
$modules/homebrew.py:
|
||||
ignore: ryansb
|
||||
keywords: brew cask darwin homebrew macosx macports osx
|
||||
@@ -783,14 +757,10 @@ files:
|
||||
maintainers: brettmilford unnecessary-username juanmcasanova
|
||||
$modules/jenkins_build_info.py:
|
||||
maintainers: juanmcasanova
|
||||
$modules/jenkins_credential.py:
|
||||
maintainers: YoussefKhalidAli
|
||||
$modules/jenkins_job.py:
|
||||
maintainers: sermilrod
|
||||
$modules/jenkins_job_info.py:
|
||||
maintainers: stpierre
|
||||
$modules/jenkins_node.py:
|
||||
maintainers: phyrwork
|
||||
$modules/jenkins_plugin.py:
|
||||
maintainers: jtyr
|
||||
$modules/jenkins_script.py:
|
||||
@@ -827,8 +797,6 @@ files:
|
||||
maintainers: fynncfchen johncant
|
||||
$modules/keycloak_clientsecret_regenerate.py:
|
||||
maintainers: fynncfchen johncant
|
||||
$modules/keycloak_component.py:
|
||||
maintainers: fivetide
|
||||
$modules/keycloak_group.py:
|
||||
maintainers: adamgoossens
|
||||
$modules/keycloak_identity_provider.py:
|
||||
@@ -861,8 +829,6 @@ files:
|
||||
maintainers: ahussey-redhat
|
||||
$modules/kibana_plugin.py:
|
||||
maintainers: barryib
|
||||
$modules/krb_ticket.py:
|
||||
maintainers: abakanovskii
|
||||
$modules/launchd.py:
|
||||
maintainers: martinm82
|
||||
$modules/layman.py:
|
||||
@@ -873,8 +839,6 @@ files:
|
||||
maintainers: drybjed jtyr noles
|
||||
$modules/ldap_entry.py:
|
||||
maintainers: jtyr
|
||||
$modules/ldap_inc.py:
|
||||
maintainers: pduveau
|
||||
$modules/ldap_passwd.py:
|
||||
maintainers: KellerFuchs jtyr
|
||||
$modules/ldap_search.py:
|
||||
@@ -901,8 +865,6 @@ files:
|
||||
maintainers: nerzhul
|
||||
$modules/lvg.py:
|
||||
maintainers: abulimov
|
||||
$modules/lvm_pv.py:
|
||||
maintainers: klention
|
||||
$modules/lvg_rename.py:
|
||||
maintainers: lszomor
|
||||
$modules/lvol.py:
|
||||
@@ -1057,9 +1019,7 @@ files:
|
||||
$modules/ovh_monthly_billing.py:
|
||||
maintainers: fraff
|
||||
$modules/pacemaker_cluster.py:
|
||||
maintainers: matbu munchtoast
|
||||
$modules/pacemaker_resource.py:
|
||||
maintainers: munchtoast
|
||||
maintainers: matbu
|
||||
$modules/packet_:
|
||||
maintainers: nurfet-becirevic t0mk
|
||||
$modules/packet_device.py:
|
||||
@@ -1135,6 +1095,32 @@ files:
|
||||
maintainers: $team_bsd berenddeboer
|
||||
$modules/pritunl_:
|
||||
maintainers: Lowess
|
||||
$modules/profitbricks:
|
||||
maintainers: baldwinSPC
|
||||
$modules/proxmox:
|
||||
keywords: kvm libvirt proxmox qemu
|
||||
labels: proxmox virt
|
||||
maintainers: $team_virt UnderGreen krauthosting
|
||||
ignore: tleguern
|
||||
$modules/proxmox.py:
|
||||
ignore: skvidal
|
||||
maintainers: UnderGreen krauthosting
|
||||
$modules/proxmox_disk.py:
|
||||
maintainers: castorsky krauthosting
|
||||
$modules/proxmox_kvm.py:
|
||||
ignore: skvidal
|
||||
maintainers: helldorado krauthosting
|
||||
$modules/proxmox_nic.py:
|
||||
maintainers: Kogelvis krauthosting
|
||||
$modules/proxmox_node_info.py:
|
||||
maintainers: jwbernin krauthosting
|
||||
$modules/proxmox_storage_contents_info.py:
|
||||
maintainers: l00ptr krauthosting
|
||||
$modules/proxmox_tasks_info:
|
||||
maintainers: paginabianca krauthosting
|
||||
$modules/proxmox_template.py:
|
||||
ignore: skvidal
|
||||
maintainers: UnderGreen krauthosting
|
||||
$modules/pubnub_blocks.py:
|
||||
maintainers: parfeon pubnub
|
||||
$modules/pulp_repo.py:
|
||||
@@ -1173,6 +1159,12 @@ files:
|
||||
keywords: kvm libvirt proxmox qemu
|
||||
labels: rhevm virt
|
||||
maintainers: $team_virt TimothyVandenbrande
|
||||
$modules/rhn_channel.py:
|
||||
labels: rhn_channel
|
||||
maintainers: vincentvdk alikins $team_rhn
|
||||
$modules/rhn_register.py:
|
||||
labels: rhn_register
|
||||
maintainers: jlaska $team_rhn
|
||||
$modules/rhsm_release.py:
|
||||
maintainers: seandst $team_rhsm
|
||||
$modules/rhsm_repository.py:
|
||||
@@ -1205,9 +1197,9 @@ files:
|
||||
$modules/scaleway_compute_private_network.py:
|
||||
maintainers: pastral
|
||||
$modules/scaleway_container.py:
|
||||
maintainers: Lunik
|
||||
maintainers: Lunik
|
||||
$modules/scaleway_container_info.py:
|
||||
maintainers: Lunik
|
||||
maintainers: Lunik
|
||||
$modules/scaleway_container_namespace.py:
|
||||
maintainers: Lunik
|
||||
$modules/scaleway_container_namespace_info.py:
|
||||
@@ -1336,12 +1328,6 @@ files:
|
||||
maintainers: precurse
|
||||
$modules/sysrc.py:
|
||||
maintainers: dlundgren
|
||||
$modules/systemd_creds_decrypt.py:
|
||||
maintainers: konstruktoid
|
||||
$modules/systemd_creds_encrypt.py:
|
||||
maintainers: konstruktoid
|
||||
$modules/systemd_info.py:
|
||||
maintainers: NomakCooper
|
||||
$modules/sysupgrade.py:
|
||||
maintainers: precurse
|
||||
$modules/taiga_issue.py:
|
||||
@@ -1373,19 +1359,16 @@ files:
|
||||
keywords: sophos utm
|
||||
maintainers: $team_e_spirit
|
||||
$modules/utm_ca_host_key_cert.py:
|
||||
ignore: stearz
|
||||
maintainers: $team_e_spirit
|
||||
maintainers: stearz
|
||||
$modules/utm_ca_host_key_cert_info.py:
|
||||
ignore: stearz
|
||||
maintainers: $team_e_spirit
|
||||
maintainers: stearz
|
||||
$modules/utm_network_interface_address.py:
|
||||
maintainers: steamx
|
||||
$modules/utm_network_interface_address_info.py:
|
||||
maintainers: steamx
|
||||
$modules/utm_proxy_auth_profile.py:
|
||||
keywords: sophos utm
|
||||
ignore: stearz
|
||||
maintainers: $team_e_spirit
|
||||
maintainers: $team_e_spirit stearz
|
||||
$modules/utm_proxy_exception.py:
|
||||
keywords: sophos utm
|
||||
maintainers: $team_e_spirit RickS-C137
|
||||
@@ -1415,8 +1398,6 @@ files:
|
||||
maintainers: dinoocch the-maldridge
|
||||
$modules/xcc_:
|
||||
maintainers: panyy3 renxulei
|
||||
$modules/xdg_mime.py:
|
||||
maintainers: mhalano
|
||||
$modules/xenserver_:
|
||||
maintainers: bvitnik
|
||||
$modules/xenserver_facts.py:
|
||||
@@ -1449,8 +1430,6 @@ files:
|
||||
maintainers: natefoo
|
||||
$modules/znode.py:
|
||||
maintainers: treyperry
|
||||
$modules/zpool.py:
|
||||
maintainers: tomhesse
|
||||
$modules/zpool_facts:
|
||||
keywords: beadm dladm illumos ipadm nexenta omnios openindiana pfexec smartos solaris sunos zfs zpool
|
||||
labels: solaris
|
||||
@@ -1465,9 +1444,6 @@ files:
|
||||
maintainers: $team_suse
|
||||
$plugin_utils/ansible_type.py:
|
||||
maintainers: vbotka
|
||||
$modules/zypper_repository_info.py:
|
||||
labels: zypper
|
||||
maintainers: $team_suse TobiasZeuch181
|
||||
$plugin_utils/keys_filter.py:
|
||||
maintainers: vbotka
|
||||
$plugin_utils/unsafe.py:
|
||||
@@ -1517,22 +1493,6 @@ files:
|
||||
maintainers: russoz
|
||||
docs/docsite/rst/guide_deps.rst:
|
||||
maintainers: russoz
|
||||
docs/docsite/rst/guide_iocage.rst:
|
||||
maintainers: russoz felixfontein
|
||||
docs/docsite/rst/guide_iocage_inventory.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_aliases.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_basics.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_dhcp.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_hooks.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_properties.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_iocage_inventory_tags.rst:
|
||||
maintainers: vbotka
|
||||
docs/docsite/rst/guide_modulehelper.rst:
|
||||
maintainers: russoz
|
||||
docs/docsite/rst/guide_online.rst:
|
||||
@@ -1541,8 +1501,6 @@ files:
|
||||
maintainers: baldwinSPC nurfet-becirevic t0mk teebes
|
||||
docs/docsite/rst/guide_scaleway.rst:
|
||||
maintainers: $team_scaleway
|
||||
docs/docsite/rst/guide_uthelper.rst:
|
||||
maintainers: russoz
|
||||
docs/docsite/rst/guide_vardict.rst:
|
||||
maintainers: russoz
|
||||
docs/docsite/rst/test_guide.rst:
|
||||
@@ -1594,9 +1552,10 @@ macros:
|
||||
team_oracle: manojmeda mross22 nalsaber
|
||||
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
|
||||
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt
|
||||
team_rhn: FlossWare alikins barnabycourt vritant
|
||||
team_rhsm: cnsnyder ptoscano
|
||||
team_scaleway: remyleone abarbare
|
||||
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
|
||||
team_suse: commel evrardjp lrupp AnderEnder alxgu andytom sealor
|
||||
team_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
|
||||
team_virt: joshainglis karmab Thulium-Drake Ajpantuso
|
||||
team_wdc: mikemoerk
|
||||
|
||||
278
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
278
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -7,147 +7,147 @@ name: Bug report
|
||||
description: Create a report to help us improve
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Explain the problem briefly below.
|
||||
placeholder: >-
|
||||
When I try to do X with the collection from the main branch on GitHub, Y
|
||||
breaks in a way Z under the env E. Here are all the details I know
|
||||
about this problem...
|
||||
validations:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Explain the problem briefly below.
|
||||
placeholder: >-
|
||||
When I try to do X with the collection from the main branch on GitHub, Y
|
||||
breaks in a way Z under the env E. Here are all the details I know
|
||||
about this problem...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Bug Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
# For smaller collections we could use a multi-select and hardcode the list
|
||||
# May generate this list via GitHub action and walking files under https://github.com/ansible-collections/community.general/tree/main/plugins
|
||||
# Select from list, filter as you type (`mysql` would only show the 3 mysql components)
|
||||
# OR freeform - doesn't seem to be supported in adaptivecards
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` between
|
||||
tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible --version
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
If this issue has an example piece of YAML that can help to reproduce this problem, please provide it.
|
||||
This can be a piece of YAML from, e.g., an automation, script, scene or configuration.
|
||||
Paste verbatim output from `ansible-config dump --only-changed` between quotes
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-config dump --only-changed
|
||||
|
||||
```
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. target OS versions,
|
||||
network device firmware, etc.
|
||||
placeholder: RHEL 8, CentOS Stream etc.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
|
||||
- type: textarea
|
||||
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.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Results
|
||||
description: >-
|
||||
Describe what you expected to happen when running the steps above.
|
||||
placeholder: >-
|
||||
I expected X to happen because I assumed Y.
|
||||
that it did not.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Results
|
||||
description: |
|
||||
Describe what actually happened. If possible run with extra verbosity (`-vvvv`).
|
||||
|
||||
Paste verbatim command output between quotes.
|
||||
value: |
|
||||
```console (paste below)
|
||||
|
||||
```
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Bug Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
# For smaller collections we could use a multi-select and hardcode the list
|
||||
# May generate this list via GitHub action and walking files under https://github.com/ansible-collections/community.general/tree/main/plugins
|
||||
# Select from list, filter as you type (`mysql` would only show the 3 mysql components)
|
||||
# OR freeform - doesn't seem to be supported in adaptivecards
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` between
|
||||
tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible --version
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
If this issue has an example piece of YAML that can help to reproduce this problem, please provide it.
|
||||
This can be a piece of YAML from, e.g., an automation, script, scene or configuration.
|
||||
Paste verbatim output from `ansible-config dump --only-changed` between quotes
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-config dump --only-changed
|
||||
|
||||
```
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. target OS versions,
|
||||
network device firmware, etc.
|
||||
placeholder: RHEL 8, CentOS Stream etc.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
|
||||
- type: textarea
|
||||
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.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Results
|
||||
description: >-
|
||||
Describe what you expected to happen when running the steps above.
|
||||
placeholder: >-
|
||||
I expected X to happen because I assumed Y.
|
||||
that it did not.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Results
|
||||
description: |
|
||||
Describe what actually happened. If possible run with extra verbosity (`-vvvv`).
|
||||
|
||||
Paste verbatim command output between quotes.
|
||||
value: |
|
||||
```console (paste below)
|
||||
|
||||
```
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
|
||||
42
.github/ISSUE_TEMPLATE/config.yml
vendored
42
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -6,26 +6,26 @@
|
||||
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
|
||||
blank_issues_enabled: false # default: true
|
||||
contact_links:
|
||||
- name: Security bug report
|
||||
url: https://docs.ansible.com/ansible-core/devel/community/reporting_bugs_and_features.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: |
|
||||
Please learn how to report security vulnerabilities here.
|
||||
- name: Security bug report
|
||||
url: https://docs.ansible.com/ansible-core/devel/community/reporting_bugs_and_features.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: |
|
||||
Please learn how to report security vulnerabilities here.
|
||||
|
||||
For all security related bugs, email security@ansible.com
|
||||
instead of using this issue tracker and you will receive
|
||||
a prompt response.
|
||||
For all security related bugs, email security@ansible.com
|
||||
instead of using this issue tracker and you will receive
|
||||
a prompt response.
|
||||
|
||||
For more information, see
|
||||
https://docs.ansible.com/ansible/latest/community/reporting_bugs_and_features.html
|
||||
- name: Ansible Code of Conduct
|
||||
url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: Be nice to other members of the community.
|
||||
- name: Talks to the community
|
||||
url: https://docs.ansible.com/ansible/latest/community/communication.html?utm_medium=github&utm_source=issue_template_chooser#mailing-list-information
|
||||
about: Please ask and answer usage questions here
|
||||
- name: Working groups
|
||||
url: https://github.com/ansible/community/wiki
|
||||
about: Interested in improving a specific area? Become a part of a working group!
|
||||
- name: For Enterprise
|
||||
url: https://www.ansible.com/products/engine?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: Red Hat offers support for the Ansible Automation Platform
|
||||
For more information, see
|
||||
https://docs.ansible.com/ansible/latest/community/reporting_bugs_and_features.html
|
||||
- name: Ansible Code of Conduct
|
||||
url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: Be nice to other members of the community.
|
||||
- name: Talks to the community
|
||||
url: https://docs.ansible.com/ansible/latest/community/communication.html?utm_medium=github&utm_source=issue_template_chooser#mailing-list-information
|
||||
about: Please ask and answer usage questions here
|
||||
- name: Working groups
|
||||
url: https://github.com/ansible/community/wiki
|
||||
about: Interested in improving a specific area? Become a part of a working group!
|
||||
- name: For Enterprise
|
||||
url: https://www.ansible.com/products/engine?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
|
||||
about: Red Hat offers support for the Ansible Automation Platform
|
||||
|
||||
226
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
226
.github/ISSUE_TEMPLATE/documentation_report.yml
vendored
@@ -8,122 +8,122 @@ description: Ask us about docs
|
||||
# NOTE: issue body is enabled to allow screenshots
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: |
|
||||
Explain the problem briefly below, add suggestions to wording or structure.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: |
|
||||
Explain the problem briefly below, add suggestions to wording or structure.
|
||||
|
||||
**HINT:** Did you know the documentation has an `Edit on GitHub` link on every page?
|
||||
placeholder: >-
|
||||
I was reading the Collection documentation of version X and I'm having
|
||||
problems understanding Y. It would be very helpful if that got
|
||||
rephrased as Z.
|
||||
validations:
|
||||
**HINT:** Did you know the documentation has an `Edit on GitHub` link on every page?
|
||||
placeholder: >-
|
||||
I was reading the Collection documentation of version X and I'm having
|
||||
problems understanding Y. It would be very helpful if that got
|
||||
rephrased as Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Documentation Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the file, module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: mysql_user
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` between
|
||||
tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible --version
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
Paste verbatim output from `ansible-config dump --only-changed` between quotes.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-config dump --only-changed
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. OS version,
|
||||
browser, etc.
|
||||
placeholder: Fedora 33, Firefox etc.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how this improves the documentation, e.g. before/after situation or screenshots.
|
||||
|
||||
**Tip:** It's not possible to upload the screenshot via this field directly but you can use the last textarea in this form to attach them.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
placeholder: >-
|
||||
When the improvement is applied, it makes it more straightforward
|
||||
to understand X.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Documentation Report
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the file, module, plugin, task or feature below,
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: mysql_user
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ansible Version
|
||||
description: >-
|
||||
Paste verbatim output from `ansible --version` between
|
||||
tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible --version
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Community.general Version
|
||||
description: >-
|
||||
Paste verbatim output from "ansible-galaxy collection list community.general"
|
||||
between tripple backticks.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-galaxy collection list community.general
|
||||
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: >-
|
||||
Paste verbatim output from `ansible-config dump --only-changed` between quotes.
|
||||
value: |
|
||||
```console (paste below)
|
||||
$ ansible-config dump --only-changed
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS / Environment
|
||||
description: >-
|
||||
Provide all relevant information below, e.g. OS version,
|
||||
browser, etc.
|
||||
placeholder: Fedora 33, Firefox etc.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how this improves the documentation, e.g. before/after situation or screenshots.
|
||||
|
||||
**Tip:** It's not possible to upload the screenshot via this field directly but you can use the last textarea in this form to attach them.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
placeholder: >-
|
||||
When the improvement is applied, it makes it more straightforward
|
||||
to understand X.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
|
||||
118
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
118
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -7,67 +7,67 @@ name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠
|
||||
Verify first that your issue is not [already reported on GitHub][issue search].
|
||||
Also test if the latest release and devel branch are affected too.
|
||||
*Complete **all** sections as described, this form is processed automatically.*
|
||||
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the new feature/improvement briefly below.
|
||||
placeholder: >-
|
||||
I am trying to do X with the collection from the main branch on GitHub and
|
||||
I think that implementing a feature Y would be very helpful for me and
|
||||
every other user of community.general because of Z.
|
||||
validations:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Summary
|
||||
description: Describe the new feature/improvement briefly below.
|
||||
placeholder: >-
|
||||
I am trying to do X with the collection from the main branch on GitHub and
|
||||
I think that implementing a feature Y would be very helpful for me and
|
||||
every other user of community.general because of Z.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Feature Idea
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module or plugin, or which other part(s) of the collection this feature affects.
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how the feature would be used, why it is needed and what it would solve.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Issue Type
|
||||
# FIXME: Once GitHub allows defining the default choice, update this
|
||||
options:
|
||||
- Feature Idea
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Component Name
|
||||
description: >-
|
||||
Write the short name of the module or plugin, or which other part(s) of the collection this feature affects.
|
||||
*use your best guess if unsure*. Do not include `community.general.`!
|
||||
placeholder: dnf, apt, yum, pip, user etc.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Describe how the feature would be used, why it is needed and what it would solve.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
<!--- Paste example playbooks or commands between quotes below -->
|
||||
```yaml (paste below)
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: |
|
||||
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
|
||||
options:
|
||||
- label: I agree to follow the Ansible Code of Conduct
|
||||
required: true
|
||||
...
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -9,7 +9,3 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
ci:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
117
.github/workflows/ansible-test.yml
vendored
117
.github/workflows/ansible-test.yml
vendored
@@ -7,7 +7,7 @@
|
||||
# https://github.com/marketplace/actions/ansible-test
|
||||
|
||||
name: EOL CI
|
||||
"on":
|
||||
on:
|
||||
# Run EOL CI against all pushes (direct commits, also merged PRs), Pull Requests
|
||||
push:
|
||||
branches:
|
||||
@@ -29,7 +29,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
ansible:
|
||||
- '2.16'
|
||||
- '2.13'
|
||||
- '2.14'
|
||||
- '2.15'
|
||||
# Ansible-test on various stable branches does not yet work well with cgroups v2.
|
||||
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
|
||||
# image for these stable branches. The list of branches where this is necessary will
|
||||
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
|
||||
# for the latest list.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Perform sanity testing
|
||||
@@ -40,10 +47,13 @@ jobs:
|
||||
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
|
||||
pull-request-change-detection: 'true'
|
||||
testing-type: sanity
|
||||
pre-test-cmd: >-
|
||||
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools
|
||||
|
||||
units:
|
||||
# Ansible-test on various stable branches does not yet work well with cgroups v2.
|
||||
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
|
||||
# image for these stable branches. The list of branches where this is necessary will
|
||||
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
|
||||
# for the latest list.
|
||||
runs-on: ubuntu-latest
|
||||
name: EOL Units (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }})
|
||||
strategy:
|
||||
@@ -57,12 +67,20 @@ jobs:
|
||||
exclude:
|
||||
- ansible: ''
|
||||
include:
|
||||
- ansible: '2.16'
|
||||
- ansible: '2.13'
|
||||
python: '2.7'
|
||||
- ansible: '2.16'
|
||||
python: '3.6'
|
||||
- ansible: '2.16'
|
||||
python: '3.11'
|
||||
- ansible: '2.13'
|
||||
python: '3.8'
|
||||
- ansible: '2.13'
|
||||
python: '2.7'
|
||||
- ansible: '2.13'
|
||||
python: '3.8'
|
||||
- ansible: '2.14'
|
||||
python: '3.9'
|
||||
- ansible: '2.15'
|
||||
python: '3.5'
|
||||
- ansible: '2.15'
|
||||
python: '3.10'
|
||||
|
||||
steps:
|
||||
- name: >-
|
||||
@@ -82,6 +100,11 @@ jobs:
|
||||
testing-type: units
|
||||
|
||||
integration:
|
||||
# Ansible-test on various stable branches does not yet work well with cgroups v2.
|
||||
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
|
||||
# image for these stable branches. The list of branches where this is necessary will
|
||||
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
|
||||
# for the latest list.
|
||||
runs-on: ubuntu-latest
|
||||
name: EOL I (Ⓐ${{ matrix.ansible }}+${{ matrix.docker }}+py${{ matrix.python }}:${{ matrix.target }})
|
||||
strategy:
|
||||
@@ -98,56 +121,81 @@ jobs:
|
||||
exclude:
|
||||
- ansible: ''
|
||||
include:
|
||||
# 2.16
|
||||
# CentOS 7 does not work in GHA, that's why it's not listed here.
|
||||
- ansible: '2.16'
|
||||
docker: fedora38
|
||||
# 2.13
|
||||
- ansible: '2.13'
|
||||
docker: fedora35
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.16'
|
||||
docker: fedora38
|
||||
- ansible: '2.13'
|
||||
docker: fedora35
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.16'
|
||||
docker: fedora38
|
||||
- ansible: '2.13'
|
||||
docker: fedora35
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.16'
|
||||
docker: opensuse15
|
||||
- ansible: '2.13'
|
||||
docker: opensuse15py2
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.16'
|
||||
docker: opensuse15
|
||||
- ansible: '2.13'
|
||||
docker: opensuse15py2
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.16'
|
||||
docker: opensuse15
|
||||
- ansible: '2.13'
|
||||
docker: opensuse15py2
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.16'
|
||||
- ansible: '2.13'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.16'
|
||||
- ansible: '2.13'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.16'
|
||||
- ansible: '2.13'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# 2.14
|
||||
- ansible: '2.14'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.14'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.14'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# 2.15
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.15'
|
||||
docker: fedora37
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - ansible: '2.16'
|
||||
# - ansible: '2.13'
|
||||
# docker: default
|
||||
# python: '2.7'
|
||||
# python: '3.9'
|
||||
# target: azp/generic/1/
|
||||
# - ansible: '2.16'
|
||||
# - ansible: '2.14'
|
||||
# docker: default
|
||||
# python: '3.6'
|
||||
# python: '3.10'
|
||||
# target: azp/generic/1/
|
||||
# - ansible: '2.16'
|
||||
# - ansible: '2.15'
|
||||
# docker: default
|
||||
# python: '3.11'
|
||||
# python: '3.9'
|
||||
# target: azp/generic/1/
|
||||
|
||||
steps:
|
||||
@@ -164,15 +212,12 @@ jobs:
|
||||
integration-continue-on-error: 'false'
|
||||
integration-diff: 'false'
|
||||
integration-retry-on-error: 'true'
|
||||
# TODO: remove "--branch stable-2" from community.crypto install once we're only using ansible-core 2.17 or newer!
|
||||
pre-test-cmd: >-
|
||||
mkdir -p ../../ansible
|
||||
;
|
||||
git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git ../../ansible/posix
|
||||
;
|
||||
git clone --depth=1 --single-branch --branch stable-2 https://github.com/ansible-collections/community.crypto.git ../../community/crypto
|
||||
;
|
||||
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.docker.git ../../community/docker
|
||||
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.crypto.git ../../community/crypto
|
||||
;
|
||||
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools
|
||||
pull-request-change-detection: 'true'
|
||||
|
||||
22
.github/workflows/codeql-analysis.yml
vendored
22
.github/workflows/codeql-analysis.yml
vendored
@@ -5,7 +5,7 @@
|
||||
|
||||
name: "Code scanning - action"
|
||||
|
||||
"on":
|
||||
on:
|
||||
schedule:
|
||||
- cron: '26 19 * * 1'
|
||||
workflow_dispatch:
|
||||
@@ -23,16 +23,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: python
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: python
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
20
.github/workflows/import-galaxy.yml
vendored
Normal file
20
.github/workflows/import-galaxy.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: import-galaxy
|
||||
'on':
|
||||
# Run CI against all pushes (direct commits, also merged PRs) to main, and all Pull Requests
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
import-galaxy:
|
||||
permissions:
|
||||
contents: read
|
||||
name: Test to import built collection artifact with Galaxy importer
|
||||
uses: ansible-community/github-action-test-galaxy-import/.github/workflows/test-galaxy-import.yml@main
|
||||
28
.github/workflows/nox.yml
vendored
28
.github/workflows/nox.yml
vendored
@@ -1,28 +0,0 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: nox
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
pull_request:
|
||||
# Run CI once per day (at 08:00 UTC)
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
nox:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Run extra sanity tests"
|
||||
steps:
|
||||
- name: Check out collection
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run nox
|
||||
uses: ansible-community/antsibull-nox@main
|
||||
34
.github/workflows/reuse.yml
vendored
Normal file
34
.github/workflows/reuse.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: Verify REUSE
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches:
|
||||
- main
|
||||
- stable-*
|
||||
# Run CI once per day (at 07:30 UTC)
|
||||
schedule:
|
||||
- cron: '30 7 * * *'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || '' }}
|
||||
|
||||
- name: REUSE Compliance Check
|
||||
uses: fsfe/reuse-action@v5
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -383,16 +383,6 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
@@ -492,10 +482,6 @@ tags
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
|
||||
5
.reuse/dep5
Normal file
5
.reuse/dep5
Normal file
@@ -0,0 +1,5 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
|
||||
Files: changelogs/fragments/*
|
||||
Copyright: Ansible Project
|
||||
License: GPL-3.0-or-later
|
||||
52
.yamllint
52
.yamllint
@@ -1,52 +0,0 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
extends: default
|
||||
|
||||
ignore: |
|
||||
/changelogs/
|
||||
|
||||
rules:
|
||||
line-length:
|
||||
max: 1000
|
||||
level: error
|
||||
document-start: disable
|
||||
document-end: disable
|
||||
truthy:
|
||||
level: error
|
||||
allowed-values:
|
||||
- 'true'
|
||||
- 'false'
|
||||
indentation:
|
||||
spaces: 2
|
||||
indent-sequences: true
|
||||
key-duplicates: enable
|
||||
trailing-spaces: enable
|
||||
new-line-at-end-of-file: disable
|
||||
hyphens:
|
||||
max-spaces-after: 1
|
||||
empty-lines:
|
||||
max: 2
|
||||
max-start: 0
|
||||
max-end: 0
|
||||
commas:
|
||||
max-spaces-before: 0
|
||||
min-spaces-after: 1
|
||||
max-spaces-after: 1
|
||||
colons:
|
||||
max-spaces-before: 0
|
||||
max-spaces-after: 1
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
braces:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 1
|
||||
octal-values:
|
||||
forbid-implicit-octal: true
|
||||
forbid-explicit-octal: true
|
||||
comments:
|
||||
min-spaces-from-content: 1
|
||||
comments-indentation: false
|
||||
1513
CHANGELOG.md
1513
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
1453
CHANGELOG.rst
1453
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -44,49 +44,7 @@ If you want to test a PR locally, refer to [our testing guide](https://github.co
|
||||
|
||||
If you find any inconsistencies or places in this document which can be improved, feel free to raise an issue or pull request to fix it.
|
||||
|
||||
## Run sanity or unit locally (with antsibull-nox)
|
||||
|
||||
The easiest way to run sanity and unit tests locally is to use [antsibull-nox](https://ansible.readthedocs.io/projects/antsibull-nox/).
|
||||
(If you have [nox](https://nox.thea.codes/en/stable/) installed, it will automatically install antsibull-nox in a virtual environment for you.)
|
||||
|
||||
### Sanity tests
|
||||
|
||||
The following commands show how to run ansible-test sanity tests:
|
||||
|
||||
```.bash
|
||||
# Run basic sanity tests for all files in the collection:
|
||||
nox -Re ansible-test-sanity-devel
|
||||
|
||||
# Run basic sanity tests for the given files and directories:
|
||||
nox -Re ansible-test-sanity-devel -- plugins/modules/system/pids.py tests/integration/targets/pids/
|
||||
|
||||
# Run all other sanity tests for all files in the collection:
|
||||
nox -R
|
||||
```
|
||||
|
||||
If you replace `-Re` with `-e`, respectively. If you leave `-R` away, then the virtual environments will be re-created. The `-R` re-uses them (if they already exist).
|
||||
|
||||
### Unit tests
|
||||
|
||||
The following commands show how to run unit tests:
|
||||
|
||||
```.bash
|
||||
# Run all unit tests:
|
||||
nox -Re ansible-test-units-devel
|
||||
|
||||
# Run all unit tests for one Python version (a lot faster):
|
||||
nox -Re ansible-test-units-devel -- --python 3.13
|
||||
|
||||
# Run a specific unit test (for the nmcli module) for one Python version:
|
||||
nox -Re ansible-test-units-devel -- --python 3.13 tests/unit/plugins/modules/net_tools/test_nmcli.py
|
||||
```
|
||||
|
||||
If you replace `-Re` with `-e`, then the virtual environments will be re-created. The `-R` re-uses them (if they already exist).
|
||||
|
||||
## Run basic sanity, unit or integration tests locally (with ansible-test)
|
||||
|
||||
Instead of using antsibull-nox, you can also run sanity and unit tests with ansible-test directly.
|
||||
This also allows you to run integration tests.
|
||||
## Run sanity, unit or integration tests locally
|
||||
|
||||
You have to check out the repository into a specific path structure to be able to run `ansible-test`. The path to the git checkout must end with `.../ansible_collections/community/general`. Please see [our testing guide](https://github.com/ansible/community-docs/blob/main/test_pr_locally_guide.rst) for instructions on how to check out the repository into a correct path structure. The short version of these instructions is:
|
||||
|
||||
@@ -98,27 +56,20 @@ cd ~/dev/ansible_collections/community/general
|
||||
|
||||
Then you can run `ansible-test` (which is a part of [ansible-core](https://pypi.org/project/ansible-core/)) inside the checkout. The following example commands expect that you have installed Docker or Podman. Note that Podman has only been supported by more recent ansible-core releases. If you are using Docker, the following will work with Ansible 2.9+.
|
||||
|
||||
### Basic sanity tests
|
||||
### Sanity tests
|
||||
|
||||
The following commands show how to run basic sanity tests:
|
||||
The following commands show how to run sanity tests:
|
||||
|
||||
```.bash
|
||||
# Run basic sanity tests for all files in the collection:
|
||||
# Run sanity tests for all files in the collection:
|
||||
ansible-test sanity --docker -v
|
||||
|
||||
# Run basic sanity tests for the given files and directories:
|
||||
# Run sanity tests for the given files and directories:
|
||||
ansible-test sanity --docker -v plugins/modules/system/pids.py tests/integration/targets/pids/
|
||||
```
|
||||
|
||||
### Unit tests
|
||||
|
||||
Note that for running unit tests, you need to install required collections in the same folder structure that `community.general` is checked out in.
|
||||
Right now, you need to install [`community.internal_test_tools`](https://github.com/ansible-collections/community.internal_test_tools).
|
||||
If you want to use the latest version from GitHub, you can run:
|
||||
```
|
||||
git clone https://github.com/ansible-collections/community.internal_test_tools.git ~/dev/ansible_collections/community/internal_test_tools
|
||||
```
|
||||
|
||||
The following commands show how to run unit tests:
|
||||
|
||||
```.bash
|
||||
@@ -134,16 +85,6 @@ ansible-test units --docker -v --python 3.8 tests/unit/plugins/modules/net_tools
|
||||
|
||||
### Integration tests
|
||||
|
||||
Note that for running integration tests, you need to install required collections in the same folder structure that `community.general` is checked out in.
|
||||
Right now, depending on the test, you need to install [`ansible.posix`](https://github.com/ansible-collections/ansible.posix), [`community.crypto`](https://github.com/ansible-collections/community.crypto), and [`community.docker`](https://github.com/ansible-collections/community.docker):
|
||||
If you want to use the latest versions from GitHub, you can run:
|
||||
```
|
||||
mkdir -p ~/dev/ansible_collections/ansible
|
||||
git clone https://github.com/ansible-collections/ansible.posix.git ~/dev/ansible_collections/ansible/posix
|
||||
git clone https://github.com/ansible-collections/community.crypto.git ~/dev/ansible_collections/community/crypto
|
||||
git clone https://github.com/ansible-collections/community.docker.git ~/dev/ansible_collections/community/docker
|
||||
```
|
||||
|
||||
The following commands show how to run integration tests:
|
||||
|
||||
#### In Docker
|
||||
@@ -151,8 +92,8 @@ The following commands show how to run integration tests:
|
||||
Integration tests on Docker have the following parameters:
|
||||
- `image_name` (required): The name of the Docker image. To get the list of supported Docker images, run
|
||||
`ansible-test integration --help` and look for _target docker images_.
|
||||
- `test_name` (optional): The name of the integration test.
|
||||
For modules, this equals the short name of the module; for example, `pacman` in case of `community.general.pacman`.
|
||||
- `test_name` (optional): The name of the integration test.
|
||||
For modules, this equals the short name of the module; for example, `pacman` in case of `community.general.pacman`.
|
||||
For plugins, the plugin type is added before the plugin's short name, for example `callback_yaml` for the `community.general.yaml` callback.
|
||||
```.bash
|
||||
# Test all plugins/modules on fedora40
|
||||
|
||||
16
README.md
16
README.md
@@ -6,10 +6,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Community General Collection
|
||||
|
||||
[](https://docs.ansible.com/ansible/devel/collections/community/general/)
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://github.com/ansible-collections/community.general/actions)
|
||||
[](https://github.com/ansible-collections/community.general/actions)
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://github.com/ansible-collections/community.general/actions)
|
||||
[](https://codecov.io/gh/ansible-collections/community.general)
|
||||
[](https://api.reuse.software/info/github.com/ansible-collections/community.general)
|
||||
|
||||
@@ -39,7 +37,7 @@ For more information about communication, see the [Ansible communication guide](
|
||||
|
||||
## Tested with Ansible
|
||||
|
||||
Tested with the current ansible-core 2.16, ansible-core 2.17, ansible-core 2.18, ansible-core 2.19 releases and the current development version of ansible-core. Ansible-core versions before 2.16.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
|
||||
## External requirements
|
||||
|
||||
@@ -118,7 +116,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-11/CHANGELOG.md).
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-9/CHANGELOG.md).
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -137,8 +135,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues
|
||||
|
||||
This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later.
|
||||
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-11/COPYING) for the full text.
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-9/COPYING) for the full text.
|
||||
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-11/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-11/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-11/LICENSES/PSF-2.0.txt).
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/PSF-2.0.txt).
|
||||
|
||||
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/).
|
||||
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/).
|
||||
|
||||
11
REUSE.toml
11
REUSE.toml
@@ -1,11 +0,0 @@
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
version = 1
|
||||
|
||||
[[annotations]]
|
||||
path = "changelogs/fragments/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "Ansible Project"
|
||||
SPDX-License-Identifier = "GPL-3.0-or-later"
|
||||
@@ -1,89 +0,0 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
|
||||
|
||||
[collection_sources]
|
||||
"ansible.posix" = "git+https://github.com/ansible-collections/ansible.posix.git,main"
|
||||
"community.crypto" = "git+https://github.com/ansible-collections/community.crypto.git,main"
|
||||
"community.docker" = "git+https://github.com/ansible-collections/community.docker.git,main"
|
||||
"community.internal_test_tools" = "git+https://github.com/ansible-collections/community.internal_test_tools.git,main"
|
||||
|
||||
[collection_sources_per_ansible.'2.16']
|
||||
# community.crypto's main branch needs ansible-core >= 2.17
|
||||
"community.crypto" = "git+https://github.com/ansible-collections/community.crypto.git,stable-2"
|
||||
|
||||
[sessions]
|
||||
|
||||
[sessions.lint]
|
||||
run_isort = false
|
||||
run_black = false
|
||||
run_flake8 = false
|
||||
run_pylint = false
|
||||
run_yamllint = true
|
||||
yamllint_config = ".yamllint"
|
||||
# yamllint_config_plugins = ".yamllint-docs"
|
||||
# yamllint_config_plugins_examples = ".yamllint-examples"
|
||||
run_mypy = false
|
||||
|
||||
[sessions.docs_check]
|
||||
validate_collection_refs="all"
|
||||
codeblocks_restrict_types = [
|
||||
"ansible-output",
|
||||
"console",
|
||||
"ini",
|
||||
"json",
|
||||
"python",
|
||||
"shell",
|
||||
"yaml",
|
||||
"yaml+jinja",
|
||||
"text",
|
||||
]
|
||||
codeblocks_restrict_type_exact_case = true
|
||||
codeblocks_allow_without_type = false
|
||||
codeblocks_allow_literal_blocks = false
|
||||
|
||||
[sessions.license_check]
|
||||
|
||||
[sessions.extra_checks]
|
||||
run_no_unwanted_files = true
|
||||
no_unwanted_files_module_extensions = [".py"]
|
||||
no_unwanted_files_yaml_extensions = [".yml"]
|
||||
run_action_groups = true
|
||||
run_no_trailing_whitespace = true
|
||||
no_trailing_whitespace_skip_paths = [
|
||||
"tests/integration/targets/iso_extract/files/test.iso",
|
||||
"tests/integration/targets/java_cert/files/testpkcs.p12",
|
||||
"tests/integration/targets/one_host/files/testhost/tmp/opennebula-fixtures.json.gz",
|
||||
"tests/integration/targets/one_template/files/testhost/tmp/opennebula-fixtures.json.gz",
|
||||
"tests/integration/targets/setup_flatpak_remote/files/repo.tar.xz",
|
||||
]
|
||||
no_trailing_whitespace_skip_directories = [
|
||||
"tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/",
|
||||
"tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/",
|
||||
]
|
||||
|
||||
[[sessions.extra_checks.action_groups_config]]
|
||||
name = "consul"
|
||||
pattern = "^consul_.*$"
|
||||
exclusions = [
|
||||
"consul_acl_bootstrap",
|
||||
"consul_kv",
|
||||
]
|
||||
doc_fragment = "community.general.consul.actiongroup_consul"
|
||||
|
||||
[[sessions.extra_checks.action_groups_config]]
|
||||
name = "keycloak"
|
||||
pattern = "^keycloak_.*$"
|
||||
exclusions = [
|
||||
"keycloak_realm_info",
|
||||
]
|
||||
doc_fragment = "community.general.keycloak.actiongroup_keycloak"
|
||||
|
||||
[sessions.build_import_check]
|
||||
run_galaxy_importer = true
|
||||
|
||||
[sessions.ansible_test_sanity]
|
||||
include_devel = true
|
||||
|
||||
[sessions.ansible_test_units]
|
||||
include_devel = true
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,9 +7,9 @@ changelog_filename_template: ../CHANGELOG.rst
|
||||
changelog_filename_version_depth: 0
|
||||
changes_file: changelog.yaml
|
||||
changes_format: combined
|
||||
ignore_other_fragment_extensions: true
|
||||
keep_fragments: false
|
||||
mention_ancestor: true
|
||||
flatmap: true
|
||||
new_plugins_after_name: removed_features
|
||||
notesdir: fragments
|
||||
output_formats:
|
||||
@@ -40,4 +40,3 @@ use_fqcn: true
|
||||
add_plugin_period: true
|
||||
changelog_nice_yaml: true
|
||||
changelog_sort: version
|
||||
vcs: auto
|
||||
|
||||
@@ -8,10 +8,9 @@ sections:
|
||||
toctree:
|
||||
- filter_guide
|
||||
- test_guide
|
||||
- title: Technology Guides
|
||||
- title: Cloud Guides
|
||||
toctree:
|
||||
- guide_alicloud
|
||||
- guide_iocage
|
||||
- guide_online
|
||||
- guide_packet
|
||||
- guide_scaleway
|
||||
@@ -21,4 +20,3 @@ sections:
|
||||
- guide_vardict
|
||||
- guide_cmdrunner
|
||||
- guide_modulehelper
|
||||
- guide_uthelper
|
||||
|
||||
@@ -36,7 +36,7 @@ gives
|
||||
|
||||
result:
|
||||
{{ tests.0.result | to_yaml(indent=2) | indent(5) }}
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-5 are all the same:
|
||||
|
||||
@@ -36,7 +36,7 @@ gives
|
||||
|
||||
result:
|
||||
{{ tests.0.result | to_yaml(indent=2) | indent(5) }}
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-5 are all the same:
|
||||
|
||||
@@ -37,7 +37,7 @@ gives
|
||||
|
||||
result:
|
||||
{{ tests.0.result | to_yaml(indent=2) | indent(5) }}
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-3 are all the same:
|
||||
|
||||
@@ -44,7 +44,7 @@ gives
|
||||
- {k0_x0: A0, k1_x1: B0}
|
||||
- {k0_x0: A1, k1_x1: B1}
|
||||
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-5 are all the same:
|
||||
|
||||
@@ -46,7 +46,7 @@ gives
|
||||
- k2_x2: [C1]
|
||||
k3_x3: bar
|
||||
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-5 are all the same:
|
||||
|
||||
@@ -53,7 +53,7 @@ gives
|
||||
k2_x2: [C1]
|
||||
k3_x3: bar
|
||||
|
||||
|
||||
|
||||
.. versionadded:: 9.1.0
|
||||
|
||||
* The results of the below examples 1-3 are all the same:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.filter_guide.filter_guide_abstract_informations.lists_of_dicts:
|
||||
|
||||
|
||||
Lists of dictionaries
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
community.general Filter Guide
|
||||
==============================
|
||||
|
||||
The :anscollection:`community.general collection <community.general#collection>` offers several useful filter plugins.
|
||||
The :ref:`community.general collection <plugins_in_community.general>` offers several useful filter plugins.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
@@ -26,8 +26,8 @@ You can use the :ansplugin:`community.general.dict_kv filter <community.general.
|
||||
type: host
|
||||
database: all
|
||||
myservers:
|
||||
- server1
|
||||
- server2
|
||||
- server1
|
||||
- server2
|
||||
|
||||
This produces:
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ All three statements are equivalent and give:
|
||||
|
||||
.. note:: Be aware that in most cases, filter calls without any argument require ``flatten=true``, otherwise the input is returned as result. The reason for this is, that the input is considered as a variable argument and is wrapped by an additional outer list. ``flatten=true`` ensures that this list is removed before the input is processed by the filter logic.
|
||||
|
||||
The filters :ansplugin:`community.general.lists_difference#filter` or :ansplugin:`community.general.lists_symmetric_difference#filter` can be used in the same way as the filters in the examples above. They calculate the difference or the symmetric difference between two or more lists and preserve the item order.
|
||||
The filters ansplugin:`community.general.lists_difference#filter` or :ansplugin:`community.general.lists_symmetric_difference#filter` can be used in the same way as the filters in the examples above. They calculate the difference or the symmetric difference between two or more lists and preserve the item order.
|
||||
|
||||
For example, the symmetric difference of ``A``, ``B`` and ``C`` may be written as:
|
||||
|
||||
|
||||
@@ -17,50 +17,50 @@ Consider this data structure:
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
{
|
||||
"domain_definition": {
|
||||
"domain": {
|
||||
"cluster": [
|
||||
{
|
||||
"name": "cluster1"
|
||||
},
|
||||
{
|
||||
"name": "cluster2"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
To extract all clusters from this structure, you can use the following query:
|
||||
@@ -124,7 +124,7 @@ To get a hash map with all ports and names of a cluster:
|
||||
var: item
|
||||
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
|
||||
vars:
|
||||
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].{name: name, port: port}"
|
||||
server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"
|
||||
|
||||
To extract ports from all clusters with name starting with 'server1':
|
||||
|
||||
|
||||
@@ -78,17 +78,17 @@ If you do not specify a ``count_tag``, the task creates the number of instances
|
||||
tasks:
|
||||
- name: Create a set of instances
|
||||
community.general.ali_instance:
|
||||
instance_type: ecs.n4.small
|
||||
image_id: "{{ ami_id }}"
|
||||
instance_name: "My-new-instance"
|
||||
instance_tags:
|
||||
Name: NewECS
|
||||
Version: 0.0.1
|
||||
count: 5
|
||||
count_tag:
|
||||
Name: NewECS
|
||||
allocate_public_ip: true
|
||||
max_bandwidth_out: 50
|
||||
instance_type: ecs.n4.small
|
||||
image_id: "{{ ami_id }}"
|
||||
instance_name: "My-new-instance"
|
||||
instance_tags:
|
||||
Name: NewECS
|
||||
Version: 0.0.1
|
||||
count: 5
|
||||
count_tag:
|
||||
Name: NewECS
|
||||
allocate_public_ip: true
|
||||
max_bandwidth_out: 50
|
||||
register: create_instance
|
||||
|
||||
In the example playbook above, data about the instances created by this playbook is saved in the variable defined by the ``register`` keyword in the task.
|
||||
|
||||
@@ -267,54 +267,24 @@ In these descriptions ``value`` refers to the single parameter passed to the for
|
||||
+------------+-------------------------+
|
||||
|
||||
- ``cmd_runner_fmt.as_fixed()``
|
||||
This method defines one or more fixed arguments that are returned by the generated function
|
||||
regardless whether ``value`` is passed to it or not.
|
||||
|
||||
This method accepts these arguments in one of three forms:
|
||||
|
||||
* one scalar parameter ``arg``, which will be returned as ``[arg]`` by the function, or
|
||||
* one sequence parameter, such as a list, ``arg``, which will be returned by the function as ``arg[0]``, or
|
||||
* multiple parameters ``args``, which will be returned as ``args`` directly by the function.
|
||||
|
||||
See the examples below for each one of those forms. And, stressing that the generated function expects no ``value`` - if one
|
||||
This method receives one parameter ``arg``, the function expects no ``value`` - if one
|
||||
is provided then it is ignored.
|
||||
The function returns ``arg`` as-is.
|
||||
|
||||
- Creation (one scalar argument):
|
||||
* ``cmd_runner_fmt.as_fixed("--version")``
|
||||
- Creation:
|
||||
``cmd_runner_fmt.as_fixed("--version")``
|
||||
- Examples:
|
||||
+---------+--------------------------------------+
|
||||
| Value | Outcome |
|
||||
+=========+======================================+
|
||||
| | * ``["--version"]`` |
|
||||
+---------+--------------------------------------+
|
||||
| 57 | * ``["--version"]`` |
|
||||
+---------+--------------------------------------+
|
||||
|
||||
- Creation (one sequence argument):
|
||||
* ``cmd_runner_fmt.as_fixed(["--list", "--json"])``
|
||||
- Examples:
|
||||
+---------+--------------------------------------+
|
||||
| Value | Outcome |
|
||||
+=========+======================================+
|
||||
| | * ``["--list", "--json"]`` |
|
||||
+---------+--------------------------------------+
|
||||
| True | * ``["--list", "--json"]`` |
|
||||
+---------+--------------------------------------+
|
||||
|
||||
- Creation (multiple arguments):
|
||||
* ``cmd_runner_fmt.as_fixed("--one", "--two", "--three")``
|
||||
- Examples:
|
||||
+---------+--------------------------------------+
|
||||
| Value | Outcome |
|
||||
+=========+======================================+
|
||||
| | * ``["--one", "--two", "--three"]`` |
|
||||
+---------+--------------------------------------+
|
||||
| False | * ``["--one", "--two", "--three"]`` |
|
||||
+---------+--------------------------------------+
|
||||
+---------+-----------------------+
|
||||
| Value | Outcome |
|
||||
+=========+=======================+
|
||||
| | ``["--version"]`` |
|
||||
+---------+-----------------------+
|
||||
| 57 | ``["--version"]`` |
|
||||
+---------+-----------------------+
|
||||
|
||||
- Note:
|
||||
This is the only special case in which a value can be missing for the formatting function.
|
||||
The first example here comes from the code in `Quickstart`_.
|
||||
The example also comes from the code in `Quickstart`_.
|
||||
In that case, the module has code to determine the command's version so that it can assert compatibility.
|
||||
There is no *value* to be passed for that CLI argument.
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage:
|
||||
|
||||
************
|
||||
Iocage Guide
|
||||
************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
guide_iocage_inventory
|
||||
@@ -1,31 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory:
|
||||
|
||||
community.general.iocage inventory plugin
|
||||
=========================================
|
||||
|
||||
The inventory plugin :ansplugin:`community.general.iocage#inventory` gets the inventory hosts from the iocage jail manager.
|
||||
|
||||
See:
|
||||
|
||||
* `iocage - A FreeBSD Jail Manager <https://iocage.readthedocs.io/en/latest>`_
|
||||
* `man iocage <https://man.freebsd.org/cgi/man.cgi?query=iocage>`_
|
||||
* `Jails and Containers <https://docs.freebsd.org/en/books/handbook/jails>`_
|
||||
|
||||
.. note::
|
||||
The output of the examples is YAML formatted. See the option :ansopt:`ansible.builtin.default#callback:result_format`.
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of Contents
|
||||
:maxdepth: 1
|
||||
|
||||
guide_iocage_inventory_basics
|
||||
guide_iocage_inventory_dhcp
|
||||
guide_iocage_inventory_hooks
|
||||
guide_iocage_inventory_properties
|
||||
guide_iocage_inventory_tags
|
||||
guide_iocage_inventory_aliases
|
||||
@@ -1,200 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_aliases:
|
||||
|
||||
Aliases
|
||||
-------
|
||||
|
||||
Quoting :ref:`inventory_aliases`:
|
||||
|
||||
The ``inventory_hostname`` is the unique identifier for a host in Ansible, this can be an IP or a hostname, but also just an 'alias' or short name for the host.
|
||||
|
||||
As root at the iocage host, stop and destroy all jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage stop ALL
|
||||
* Stopping srv_1
|
||||
+ Executing prestop OK
|
||||
+ Stopping services OK
|
||||
+ Tearing down VNET OK
|
||||
+ Removing devfs_ruleset: 1000 OK
|
||||
+ Removing jail process OK
|
||||
+ Executing poststop OK
|
||||
* Stopping srv_2
|
||||
+ Executing prestop OK
|
||||
+ Stopping services OK
|
||||
+ Tearing down VNET OK
|
||||
+ Removing devfs_ruleset: 1001 OK
|
||||
+ Removing jail process OK
|
||||
+ Executing poststop OK
|
||||
* Stopping srv_3
|
||||
+ Executing prestop OK
|
||||
+ Stopping services OK
|
||||
+ Tearing down VNET OK
|
||||
+ Removing devfs_ruleset: 1002 OK
|
||||
+ Removing jail process OK
|
||||
+ Executing poststop OK
|
||||
ansible_client is not running!
|
||||
|
||||
shell> iocage destroy -f srv_1 srv_2 srv_3
|
||||
Destroying srv_1
|
||||
Destroying srv_2
|
||||
Destroying srv_3
|
||||
|
||||
Create three VNET jails with a DHCP interface from the template *ansible_client*. Use the option ``--count``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage create --short --template ansible_client --count 3 bpf=1 dhcp=1 vnet=1
|
||||
1c11de2d successfully created!
|
||||
9d94cc9e successfully created!
|
||||
052b9557 successfully created!
|
||||
|
||||
The names are random. Start the jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage start ALL
|
||||
No default gateway found for ipv6.
|
||||
* Starting 052b9557
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1000 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.137/24
|
||||
No default gateway found for ipv6.
|
||||
* Starting 1c11de2d
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1001 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.146/24
|
||||
No default gateway found for ipv6.
|
||||
* Starting 9d94cc9e
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1002 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.115/24
|
||||
Please convert back to a jail before trying to start ansible_client
|
||||
|
||||
List the jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage list -l
|
||||
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+=====+==========+======+=======+======+=================+====================+=====+================+==========+
|
||||
| 207 | 052b9557 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.137 | - | ansible_client | no |
|
||||
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 208 | 1c11de2d | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.146 | - | ansible_client | no |
|
||||
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 209 | 9d94cc9e | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.115 | - | ansible_client | no |
|
||||
+-----+----------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
|
||||
Set notes. The tag *alias* will be used to create inventory aliases:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage set notes="vmm=iocage_02 project=foo alias=srv_1" 052b9557
|
||||
notes: none -> vmm=iocage_02 project=foo alias=srv_1
|
||||
shell> iocage set notes="vmm=iocage_02 project=foo alias=srv_2" 1c11de2d
|
||||
notes: none -> vmm=iocage_02 project=foo alias=srv_2
|
||||
shell> iocage set notes="vmm=iocage_02 project=bar alias=srv_3" 9d94cc9e
|
||||
notes: none -> vmm=iocage_02 project=bar alias=srv_3
|
||||
|
||||
Update the inventory configuration. Set the option
|
||||
:ansopt:`community.general.iocage#inventory:inventory_hostname_tag` to :ansval:`alias`. This tag keeps the
|
||||
value of the alias. The option :ansopt:`community.general.iocage#inventory:get_properties` must be
|
||||
enabled. For example, ``hosts/02_iocage.yml`` contains:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
get_properties: true
|
||||
inventory_hostname_tag: alias
|
||||
hooks_results:
|
||||
- /var/db/dhclient-hook.address.epair0b
|
||||
compose:
|
||||
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
|
||||
iocage_tags: dict(iocage_properties.notes | split | map('split', '='))
|
||||
keyed_groups:
|
||||
- prefix: vmm
|
||||
key: iocage_tags.vmm
|
||||
- prefix: project
|
||||
key: iocage_tags.project
|
||||
|
||||
Display tags and groups. Create a playbook ``pb-test-groups.yml`` with the following content:
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
- hosts: all
|
||||
remote_user: admin
|
||||
|
||||
vars:
|
||||
|
||||
ansible_python_interpreter: auto_silent
|
||||
|
||||
tasks:
|
||||
|
||||
- debug:
|
||||
var: iocage_tags
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{% for group in groups %}
|
||||
{{ group }}: {{ groups[group] }}
|
||||
{% endfor %}
|
||||
run_once: true
|
||||
|
||||
Run the playbook:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-playbook -i hosts/02_iocage.yml pb-test-groups.yml
|
||||
|
||||
PLAY [all] **********************************************************************************************************
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_1] =>
|
||||
iocage_tags:
|
||||
alias: srv_1
|
||||
project: foo
|
||||
vmm: iocage_02
|
||||
ok: [srv_2] =>
|
||||
iocage_tags:
|
||||
alias: srv_2
|
||||
project: foo
|
||||
vmm: iocage_02
|
||||
ok: [srv_3] =>
|
||||
iocage_tags:
|
||||
alias: srv_3
|
||||
project: bar
|
||||
vmm: iocage_02
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_1] =>
|
||||
msg: |-
|
||||
all: ['srv_1', 'srv_2', 'srv_3']
|
||||
ungrouped: []
|
||||
vmm_iocage_02: ['srv_1', 'srv_2', 'srv_3']
|
||||
project_foo: ['srv_1', 'srv_2']
|
||||
project_bar: ['srv_3']
|
||||
|
||||
PLAY RECAP **********************************************************************************************************
|
||||
srv_1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
@@ -1,128 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_basics:
|
||||
|
||||
Basics
|
||||
------
|
||||
|
||||
As root at the iocage host, create three VNET jails with a DHCP interface from the template
|
||||
*ansible_client*:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage create --template ansible_client --name srv_1 bpf=1 dhcp=1 vnet=1
|
||||
srv_1 successfully created!
|
||||
shell> iocage create --template ansible_client --name srv_2 bpf=1 dhcp=1 vnet=1
|
||||
srv_2 successfully created!
|
||||
shell> iocage create --template ansible_client --name srv_3 bpf=1 dhcp=1 vnet=1
|
||||
srv_3 successfully created!
|
||||
|
||||
See: `Configuring a VNET Jail <https://iocage.readthedocs.io/en/latest/networking.html#configuring-a-vnet-jail>`_.
|
||||
|
||||
As admin at the controller, list the jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ssh admin@10.1.0.73 iocage list -l
|
||||
+------+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+======+=======+======+=======+======+=================+====================+=====+================+==========+
|
||||
| None | srv_1 | off | down | jail | 14.2-RELEASE-p3 | DHCP (not running) | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| None | srv_2 | off | down | jail | 14.2-RELEASE-p3 | DHCP (not running) | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| None | srv_3 | off | down | jail | 14.2-RELEASE-p3 | DHCP (not running) | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
|
||||
Create the inventory file ``hosts/02_iocage.yml``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
|
||||
Display the inventory:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-inventory -i hosts/02_iocage.yml --list --yaml
|
||||
all:
|
||||
children:
|
||||
ungrouped:
|
||||
hosts:
|
||||
srv_1:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (not running)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: None
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: down
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_2:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (not running)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: None
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: down
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_3:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (not running)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: None
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: down
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
|
||||
Optionally, create shared IP jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage create --template ansible_client --name srv_1 ip4_addr="em0|10.1.0.101/24"
|
||||
srv_1 successfully created!
|
||||
shell> iocage create --template ansible_client --name srv_2 ip4_addr="em0|10.1.0.102/24"
|
||||
srv_2 successfully created!
|
||||
shell> iocage create --template ansible_client --name srv_3 ip4_addr="em0|10.1.0.103/24"
|
||||
srv_3 successfully created!
|
||||
shell> iocage list -l
|
||||
+------+-------+------+-------+------+-----------------+-------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+======+=======+======+=======+======+=================+===================+=====+================+==========+
|
||||
| None | srv_1 | off | down | jail | 14.2-RELEASE-p3 | em0|10.1.0.101/24 | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+-------------------+-----+----------------+----------+
|
||||
| None | srv_2 | off | down | jail | 14.2-RELEASE-p3 | em0|10.1.0.102/24 | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+-------------------+-----+----------------+----------+
|
||||
| None | srv_3 | off | down | jail | 14.2-RELEASE-p3 | em0|10.1.0.103/24 | - | ansible_client | no |
|
||||
+------+-------+------+-------+------+-----------------+-------------------+-----+----------------+----------+
|
||||
|
||||
See: `Configuring a Shared IP Jail <https://iocage.readthedocs.io/en/latest/networking.html#configuring-a-shared-ip-jail>`_
|
||||
|
||||
If iocage needs environment variable(s), use the option :ansopt:`community.general.iocage#inventory:env`. For example,
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
env:
|
||||
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
|
||||
@@ -1,175 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_dhcp:
|
||||
|
||||
DHCP
|
||||
----
|
||||
|
||||
As root at the iocage host, start the jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage start ALL
|
||||
No default gateway found for ipv6.
|
||||
* Starting srv_1
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1000 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.183/24
|
||||
No default gateway found for ipv6.
|
||||
* Starting srv_2
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1001 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.204/24
|
||||
No default gateway found for ipv6.
|
||||
* Starting srv_3
|
||||
+ Started OK
|
||||
+ Using devfs_ruleset: 1002 (iocage generated default)
|
||||
+ Configuring VNET OK
|
||||
+ Using IP options: vnet
|
||||
+ Starting services OK
|
||||
+ Executing poststart OK
|
||||
+ DHCP Address: 10.1.0.169/24
|
||||
Please convert back to a jail before trying to start ansible_client
|
||||
|
||||
List the jails:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage list -l
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+=====+=======+======+=======+======+=================+====================+=====+================+==========+
|
||||
| 204 | srv_1 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.183 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 205 | srv_2 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.204 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 206 | srv_3 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.169 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
|
||||
As admin at the controller, list the jails. The IP4 tab says "... address requires root":
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ssh admin@10.1.0.73 iocage list -l
|
||||
+-----+-------+------+-------+------+-----------------+-----------------------------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+=====+=======+======+=======+======+=================+=========================================+=====+================+==========+
|
||||
| 204 | srv_1 | off | up | jail | 14.2-RELEASE-p3 | DHCP (running -- address requires root) | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+-----------------------------------------+-----+----------------+----------+
|
||||
| 205 | srv_2 | off | up | jail | 14.2-RELEASE-p3 | DHCP (running -- address requires root) | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+-----------------------------------------+-----+----------------+----------+
|
||||
| 206 | srv_3 | off | up | jail | 14.2-RELEASE-p3 | DHCP (running -- address requires root) | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+-----------------------------------------+-----+----------------+----------+
|
||||
|
||||
Use sudo if enabled:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ssh admin@10.1.0.73 sudo iocage list -l
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| JID | NAME | BOOT | STATE | TYPE | RELEASE | IP4 | IP6 | TEMPLATE | BASEJAIL |
|
||||
+=====+=======+======+=======+======+=================+====================+=====+================+==========+
|
||||
| 204 | srv_1 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.183 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 205 | srv_2 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.204 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
| 206 | srv_3 | off | up | jail | 14.2-RELEASE-p3 | epair0b|10.1.0.169 | - | ansible_client | no |
|
||||
+-----+-------+------+-------+------+-----------------+--------------------+-----+----------------+----------+
|
||||
|
||||
Create the inventory file ``hosts/02_iocage.yml``. Use the option
|
||||
:ansopt:`community.general.iocage#inventory:sudo`:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
sudo: true
|
||||
|
||||
Display the inventory:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-inventory -i hosts/02_iocage.yml --list --yaml
|
||||
all:
|
||||
children:
|
||||
ungrouped:
|
||||
hosts:
|
||||
srv_1:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: 10.1.0.183
|
||||
iocage_ip4_dict:
|
||||
ip4:
|
||||
- ifc: epair0b
|
||||
ip: 10.1.0.183
|
||||
mask: '-'
|
||||
msg: ''
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '204'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_2:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: 10.1.0.204
|
||||
iocage_ip4_dict:
|
||||
ip4:
|
||||
- ifc: epair0b
|
||||
ip: 10.1.0.204
|
||||
mask: '-'
|
||||
msg: ''
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '205'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_3:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_ip4: 10.1.0.169
|
||||
iocage_ip4_dict:
|
||||
ip4:
|
||||
- ifc: epair0b
|
||||
ip: 10.1.0.169
|
||||
mask: '-'
|
||||
msg: ''
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '206'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
|
||||
Note: If the option :ansopt:`community.general.iocage#inventory:env` is used and :ansopt:`community.general.iocage#inventory:sudo` is enabled, enable also :ansopt:`community.general.iocage#inventory:sudo_preserve_env`. For example,
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
env:
|
||||
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
|
||||
sudo: true
|
||||
sudo_preserve_env: true
|
||||
|
||||
In this case, make sure the sudo tag ``SETENV`` is used:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ssh admin@10.1.0.73 sudo cat /usr/local/etc/sudoers | grep admin
|
||||
admin ALL=(ALL) NOPASSWD:SETENV: ALL
|
||||
@@ -1,187 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_hooks:
|
||||
|
||||
Hooks
|
||||
-----
|
||||
|
||||
The iocage utility internally opens a console to a jail to get the jail's DHCP address. This
|
||||
requires root. If you run the command ``iocage list -l`` as unprivileged user, you'll see the
|
||||
message ``DHCP (running -- address requires root)``. If you are not granted the root privilege, use
|
||||
``/etc/dhclient-exit-hooks``. For example, in the jail *srv_1*, create the file
|
||||
``/zroot/iocage/jails/srv_1/root/etc/dhclient-exit-hooks``
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
case "$reason" in
|
||||
"BOUND"|"REBIND"|"REBOOT"|"RENEW")
|
||||
echo $new_ip_address > /var/db/dhclient-hook.address.$interface
|
||||
;;
|
||||
esac
|
||||
|
||||
where ``/zroot/iocage`` is the activated pool.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> zfs list | grep /zroot/iocage
|
||||
zroot/iocage 4.69G 446G 5.08M /zroot/iocage
|
||||
zroot/iocage/download 927M 446G 384K /zroot/iocage/download
|
||||
zroot/iocage/download/14.1-RELEASE 465M 446G 465M /zroot/iocage/download/14.1-RELEASE
|
||||
zroot/iocage/download/14.2-RELEASE 462M 446G 462M /zroot/iocage/download/14.2-RELEASE
|
||||
zroot/iocage/images 384K 446G 384K /zroot/iocage/images
|
||||
zroot/iocage/jails 189M 446G 480K /zroot/iocage/jails
|
||||
zroot/iocage/jails/srv_1 62.9M 446G 464K /zroot/iocage/jails/srv_1
|
||||
zroot/iocage/jails/srv_1/root 62.4M 446G 3.53G /zroot/iocage/jails/srv_1/root
|
||||
zroot/iocage/jails/srv_2 62.8M 446G 464K /zroot/iocage/jails/srv_2
|
||||
zroot/iocage/jails/srv_2/root 62.3M 446G 3.53G /zroot/iocage/jails/srv_2/root
|
||||
zroot/iocage/jails/srv_3 62.8M 446G 464K /zroot/iocage/jails/srv_3
|
||||
zroot/iocage/jails/srv_3/root 62.3M 446G 3.53G /zroot/iocage/jails/srv_3/root
|
||||
zroot/iocage/log 688K 446G 688K /zroot/iocage/log
|
||||
zroot/iocage/releases 2.93G 446G 384K /zroot/iocage/releases
|
||||
zroot/iocage/releases/14.2-RELEASE 2.93G 446G 384K /zroot/iocage/releases/14.2-RELEASE
|
||||
zroot/iocage/releases/14.2-RELEASE/root 2.93G 446G 2.88G /zroot/iocage/releases/14.2-RELEASE/root
|
||||
zroot/iocage/templates 682M 446G 416K /zroot/iocage/templates
|
||||
zroot/iocage/templates/ansible_client 681M 446G 432K /zroot/iocage/templates/ansible_client
|
||||
zroot/iocage/templates/ansible_client/root 681M 446G 3.53G /zroot/iocage/templates/ansible_client/root
|
||||
|
||||
See: `man dhclient-script <https://man.freebsd.org/cgi/man.cgi?dhclient-script>`_
|
||||
|
||||
Create the inventory configuration. Use the option :ansopt:`community.general.iocage#inventory:hooks_results` instead of :ansopt:`community.general.iocage#inventory:sudo`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> cat hosts/02_iocage.yml
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
hooks_results:
|
||||
- /var/db/dhclient-hook.address.epair0b
|
||||
|
||||
.. note::
|
||||
|
||||
The option :ansopt:`community.general.iocage#inventory:hooks_results` expects the poolname to be mounted to ``/poolname``. For example, if you
|
||||
activate the pool iocage, this plugin expects to find the :ansopt:`community.general.iocage#inventory:hooks_results` items in the path
|
||||
/iocage/iocage/jails/<name>/root. If you mount the poolname to a different path, the easiest
|
||||
remedy is to create a symlink.
|
||||
|
||||
As admin at the controller, display the inventory:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-inventory -i hosts/02_iocage.yml --list --yaml
|
||||
all:
|
||||
children:
|
||||
ungrouped:
|
||||
hosts:
|
||||
srv_1:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_hooks:
|
||||
- 10.1.0.183
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (running -- address requires root)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '204'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_2:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_hooks:
|
||||
- 10.1.0.204
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (running -- address requires root)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '205'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
srv_3:
|
||||
iocage_basejail: 'no'
|
||||
iocage_boot: 'off'
|
||||
iocage_hooks:
|
||||
- 10.1.0.169
|
||||
iocage_ip4: '-'
|
||||
iocage_ip4_dict:
|
||||
ip4: []
|
||||
msg: DHCP (running -- address requires root)
|
||||
iocage_ip6: '-'
|
||||
iocage_jid: '206'
|
||||
iocage_release: 14.2-RELEASE-p3
|
||||
iocage_state: up
|
||||
iocage_template: ansible_client
|
||||
iocage_type: jail
|
||||
|
||||
Compose the variable ``ansible_host``. For example, ``hosts/02_iocage.yml`` could look like:
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
hooks_results:
|
||||
- /var/db/dhclient-hook.address.epair0b
|
||||
compose:
|
||||
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
|
||||
|
||||
Test the jails. Create a playbook ``pb-test-uname.yml``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- hosts: all
|
||||
remote_user: admin
|
||||
|
||||
vars:
|
||||
|
||||
ansible_python_interpreter: auto_silent
|
||||
|
||||
tasks:
|
||||
|
||||
- command: uname -a
|
||||
register: out
|
||||
|
||||
- debug:
|
||||
var: out.stdout
|
||||
|
||||
See: :ref:`working_with_bsd`
|
||||
|
||||
Run the playbook:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-playbook -i hosts/02_iocage.yml pb-test-uname.yml
|
||||
|
||||
PLAY [all] **********************************************************************************************************
|
||||
|
||||
TASK [command] ******************************************************************************************************
|
||||
changed: [srv_3]
|
||||
changed: [srv_1]
|
||||
changed: [srv_2]
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_1] =>
|
||||
out.stdout: FreeBSD srv-1 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
|
||||
ok: [srv_3] =>
|
||||
out.stdout: FreeBSD srv-3 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
|
||||
ok: [srv_2] =>
|
||||
out.stdout: FreeBSD srv-2 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
|
||||
|
||||
PLAY RECAP **********************************************************************************************************
|
||||
srv_1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_3 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
|
||||
Note: This playbook and the inventory configuration works also for the *Shared IP Jails*.
|
||||
@@ -1,201 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_properties:
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
Optionally, in the inventory file ``hosts/02_iocage.yml``, get the iocage properties. Enable
|
||||
:ansopt:`community.general.iocage#inventory:get_properties`:
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
get_properties: true
|
||||
hooks_results:
|
||||
- /var/db/dhclient-hook.address.epair0b
|
||||
compose:
|
||||
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
|
||||
|
||||
Display the properties. Create the playbook ``pb-test-properties.yml``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- hosts: all
|
||||
remote_user: admin
|
||||
|
||||
vars:
|
||||
|
||||
ansible_python_interpreter: auto_silent
|
||||
|
||||
tasks:
|
||||
|
||||
- debug:
|
||||
var: iocage_properties
|
||||
|
||||
Run the playbook. Limit the inventory to *srv_3*:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-playbook -i hosts/02_iocage.yml -l srv_3 pb-test-properties.yml
|
||||
|
||||
PLAY [all] **********************************************************************************************************
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_3] =>
|
||||
iocage_properties:
|
||||
CONFIG_VERSION: '33'
|
||||
allow_chflags: '0'
|
||||
allow_mlock: '0'
|
||||
allow_mount: '1'
|
||||
allow_mount_devfs: '0'
|
||||
allow_mount_fdescfs: '0'
|
||||
allow_mount_fusefs: '0'
|
||||
allow_mount_linprocfs: '0'
|
||||
allow_mount_linsysfs: '0'
|
||||
allow_mount_nullfs: '0'
|
||||
allow_mount_procfs: '0'
|
||||
allow_mount_tmpfs: '0'
|
||||
allow_mount_zfs: '0'
|
||||
allow_nfsd: '0'
|
||||
allow_quotas: '0'
|
||||
allow_raw_sockets: '0'
|
||||
allow_set_hostname: '1'
|
||||
allow_socket_af: '0'
|
||||
allow_sysvipc: '0'
|
||||
allow_tun: '0'
|
||||
allow_vmm: '0'
|
||||
assign_localhost: '0'
|
||||
available: readonly
|
||||
basejail: '0'
|
||||
boot: '0'
|
||||
bpf: '1'
|
||||
children_max: '0'
|
||||
cloned_release: 14.2-RELEASE
|
||||
comment: none
|
||||
compression: 'on'
|
||||
compressratio: readonly
|
||||
coredumpsize: 'off'
|
||||
count: '1'
|
||||
cpuset: 'off'
|
||||
cputime: 'off'
|
||||
datasize: 'off'
|
||||
dedup: 'off'
|
||||
defaultrouter: auto
|
||||
defaultrouter6: auto
|
||||
depends: none
|
||||
devfs_ruleset: '4'
|
||||
dhcp: '1'
|
||||
enforce_statfs: '2'
|
||||
exec_clean: '1'
|
||||
exec_created: /usr/bin/true
|
||||
exec_fib: '0'
|
||||
exec_jail_user: root
|
||||
exec_poststart: /usr/bin/true
|
||||
exec_poststop: /usr/bin/true
|
||||
exec_prestart: /usr/bin/true
|
||||
exec_prestop: /usr/bin/true
|
||||
exec_start: /bin/sh /etc/rc
|
||||
exec_stop: /bin/sh /etc/rc.shutdown
|
||||
exec_system_jail_user: '0'
|
||||
exec_system_user: root
|
||||
exec_timeout: '60'
|
||||
host_domainname: none
|
||||
host_hostname: srv-3
|
||||
host_hostuuid: srv_3
|
||||
host_time: '1'
|
||||
hostid: ea2ba7d1-4fcd-f13f-82e4-8b32c0a03403
|
||||
hostid_strict_check: '0'
|
||||
interfaces: vnet0:bridge0
|
||||
ip4: new
|
||||
ip4_addr: none
|
||||
ip4_saddrsel: '1'
|
||||
ip6: new
|
||||
ip6_addr: none
|
||||
ip6_saddrsel: '1'
|
||||
ip_hostname: '0'
|
||||
jail_zfs: '0'
|
||||
jail_zfs_dataset: iocage/jails/srv_3/data
|
||||
jail_zfs_mountpoint: none
|
||||
last_started: '2025-06-11 04:29:23'
|
||||
localhost_ip: none
|
||||
login_flags: -f root
|
||||
mac_prefix: 02a098
|
||||
maxproc: 'off'
|
||||
memorylocked: 'off'
|
||||
memoryuse: 'off'
|
||||
min_dyn_devfs_ruleset: '1000'
|
||||
mount_devfs: '1'
|
||||
mount_fdescfs: '1'
|
||||
mount_linprocfs: '0'
|
||||
mount_procfs: '0'
|
||||
mountpoint: readonly
|
||||
msgqqueued: 'off'
|
||||
msgqsize: 'off'
|
||||
nat: '0'
|
||||
nat_backend: ipfw
|
||||
nat_forwards: none
|
||||
nat_interface: none
|
||||
nat_prefix: '172.16'
|
||||
nmsgq: 'off'
|
||||
notes: none
|
||||
nsem: 'off'
|
||||
nsemop: 'off'
|
||||
nshm: 'off'
|
||||
nthr: 'off'
|
||||
openfiles: 'off'
|
||||
origin: readonly
|
||||
owner: root
|
||||
pcpu: 'off'
|
||||
plugin_name: none
|
||||
plugin_repository: none
|
||||
priority: '99'
|
||||
pseudoterminals: 'off'
|
||||
quota: none
|
||||
readbps: 'off'
|
||||
readiops: 'off'
|
||||
release: 14.2-RELEASE-p3
|
||||
reservation: none
|
||||
resolver: /etc/resolv.conf
|
||||
rlimits: 'off'
|
||||
rtsold: '0'
|
||||
securelevel: '2'
|
||||
shmsize: 'off'
|
||||
source_template: ansible_client
|
||||
stacksize: 'off'
|
||||
state: up
|
||||
stop_timeout: '30'
|
||||
swapuse: 'off'
|
||||
sync_state: none
|
||||
sync_target: none
|
||||
sync_tgt_zpool: none
|
||||
sysvmsg: new
|
||||
sysvsem: new
|
||||
sysvshm: new
|
||||
template: '0'
|
||||
type: jail
|
||||
used: readonly
|
||||
vmemoryuse: 'off'
|
||||
vnet: '1'
|
||||
vnet0_mac: 02a0983da05d 02a0983da05e
|
||||
vnet0_mtu: auto
|
||||
vnet1_mac: none
|
||||
vnet1_mtu: auto
|
||||
vnet2_mac: none
|
||||
vnet2_mtu: auto
|
||||
vnet3_mac: none
|
||||
vnet3_mtu: auto
|
||||
vnet_default_interface: auto
|
||||
vnet_default_mtu: '1500'
|
||||
vnet_interfaces: none
|
||||
wallclock: 'off'
|
||||
writebps: 'off'
|
||||
writeiops: 'off'
|
||||
|
||||
PLAY RECAP **********************************************************************************************************
|
||||
srv_3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
@@ -1,117 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_iocage.guide_iocage_inventory.guide_iocage_inventory_tags:
|
||||
|
||||
Tags
|
||||
----
|
||||
|
||||
Quoting `man iocage <https://man.freebsd.org/cgi/man.cgi?query=iocage>`_
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
PROPERTIES
|
||||
...
|
||||
notes="any string"
|
||||
Custom notes for miscellaneous tagging.
|
||||
Default: none
|
||||
Source: local
|
||||
|
||||
We will use the format `notes="tag1=value1 tag2=value2 ..."`.
|
||||
|
||||
.. note::
|
||||
|
||||
The iocage tags have nothing to do with the :ref:`tags`.
|
||||
|
||||
As root at the iocage host, set notes. For example,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> iocage set notes="vmm=iocage_02 project=foo" srv_1
|
||||
notes: none -> vmm=iocage_02 project=foo
|
||||
shell> iocage set notes="vmm=iocage_02 project=foo" srv_2
|
||||
notes: none -> vmm=iocage_02 project=foo
|
||||
shell> iocage set notes="vmm=iocage_02 project=bar" srv_3
|
||||
notes: none -> vmm=iocage_02 project=bar
|
||||
|
||||
Update the inventory configuration. Compose a dictionary *iocage_tags* and create groups. The option
|
||||
:ansopt:`community.general.iocage#inventory:get_properties` must be enabled.
|
||||
For example, ``hosts/02_iocage.yml`` could look like:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
plugin: community.general.iocage
|
||||
host: 10.1.0.73
|
||||
user: admin
|
||||
get_properties: true
|
||||
hooks_results:
|
||||
- /var/db/dhclient-hook.address.epair0b
|
||||
compose:
|
||||
ansible_host: (iocage_hooks.0 == '-') | ternary(iocage_ip4, iocage_hooks.0)
|
||||
iocage_tags: dict(iocage_properties.notes | split | map('split', '='))
|
||||
keyed_groups:
|
||||
- prefix: vmm
|
||||
key: iocage_tags.vmm
|
||||
- prefix: project
|
||||
key: iocage_tags.project
|
||||
|
||||
Display tags and groups. Create a playbook ``pb-test-groups.yml``:
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
- hosts: all
|
||||
remote_user: admin
|
||||
|
||||
vars:
|
||||
|
||||
ansible_python_interpreter: auto_silent
|
||||
|
||||
tasks:
|
||||
|
||||
- debug:
|
||||
var: iocage_tags
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{% for group in groups %}
|
||||
{{ group }}: {{ groups[group] }}
|
||||
{% endfor %}
|
||||
run_once: true
|
||||
|
||||
Run the playbook:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
shell> ansible-playbook -i hosts/02_iocage.yml pb-test-groups.yml
|
||||
|
||||
PLAY [all] **********************************************************************************************************
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_1] =>
|
||||
iocage_tags:
|
||||
project: foo
|
||||
vmm: iocage_02
|
||||
ok: [srv_2] =>
|
||||
iocage_tags:
|
||||
project: foo
|
||||
vmm: iocage_02
|
||||
ok: [srv_3] =>
|
||||
iocage_tags:
|
||||
project: bar
|
||||
vmm: iocage_02
|
||||
|
||||
TASK [debug] ********************************************************************************************************
|
||||
ok: [srv_1] =>
|
||||
msg: |-
|
||||
all: ['srv_1', 'srv_2', 'srv_3']
|
||||
ungrouped: []
|
||||
vmm_iocage_02: ['srv_1', 'srv_2', 'srv_3']
|
||||
project_foo: ['srv_1', 'srv_2']
|
||||
project_bar: ['srv_3']
|
||||
|
||||
PLAY RECAP **********************************************************************************************************
|
||||
srv_1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
srv_3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
||||
@@ -38,6 +38,7 @@ But bear in mind that it does not showcase all of MH's features:
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
use_old_vardict = False
|
||||
|
||||
def __run__(self):
|
||||
self.vars.original_message = ''
|
||||
@@ -75,14 +76,13 @@ section above, but there are more elements that will take part in it.
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
|
||||
|
||||
class MyTest(ModuleHelper):
|
||||
# behavior for module paramaters ONLY, see below for further information
|
||||
output_params = ()
|
||||
change_params = ()
|
||||
diff_params = ()
|
||||
facts_name = None
|
||||
facts_params = ()
|
||||
|
||||
facts_name = None # used if generating facts, from parameters or otherwise
|
||||
|
||||
use_old_vardict = True
|
||||
mute_vardict_deprecation = False
|
||||
module = dict(
|
||||
argument_spec=dict(...),
|
||||
# ...
|
||||
@@ -202,14 +202,27 @@ By using ``self.vars``, you get a central mechanism to access the parameters but
|
||||
As described in :ref:`ansible_collections.community.general.docsite.guide_vardict`, variables in ``VarDict`` have metadata associated to them.
|
||||
One of the attributes in that metadata marks the variable for output, and MH makes use of that to generate the module's return values.
|
||||
|
||||
.. note::
|
||||
.. important::
|
||||
|
||||
The ``VarDict`` class was introduced in community.general 7.1.0, as part of ``ModuleHelper`` itself.
|
||||
However, it has been factored out to become an utility on its own, described in :ref:`ansible_collections.community.general.docsite.guide_vardict`,
|
||||
and the older implementation was removed in community.general 11.0.0.
|
||||
The ``VarDict`` feature described was introduced in community.general 7.1.0, but there was a first
|
||||
implementation of it embedded within ``ModuleHelper``.
|
||||
That older implementation is now deprecated and will be removed in community.general 11.0.0.
|
||||
After community.general 7.1.0, MH modules generate a deprecation message about *using the old VarDict*.
|
||||
There are two ways to prevent that from happening:
|
||||
|
||||
Some code might still refer to the class variables ``use_old_vardict`` and ``mute_vardict_deprecation``, used for the transtition to the new
|
||||
implementation but from community.general 11.0.0 onwards they are no longer used and can be safely removed from the code.
|
||||
#. Set ``mute_vardict_deprecation = True`` and the deprecation will be silenced. If the module still uses the old ``VarDict``,
|
||||
it will not be able to update to community.general 11.0.0 (Spring 2026) upon its release.
|
||||
#. Set ``use_old_vardict = False`` to make the MH module use the new ``VarDict`` immediatelly.
|
||||
The new ``VarDict`` and its use is documented and this is the recommended way to handle this.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyTest(ModuleHelper):
|
||||
use_old_vardict = False
|
||||
mute_vardict_deprecation = True
|
||||
...
|
||||
|
||||
These two settings are mutually exclusive, but that is not enforced and the behavior when setting both is not specified.
|
||||
|
||||
Contrary to new variables created in ``VarDict``, module parameters are not set for output by default.
|
||||
If you want to include some module parameters in the output, list them in the ``output_params`` class variable.
|
||||
@@ -220,11 +233,6 @@ If you want to include some module parameters in the output, list them in the ``
|
||||
output_params = ('state', 'name')
|
||||
...
|
||||
|
||||
.. important::
|
||||
|
||||
The variable names listed in ``output_params`` **must be module parameters**, as in parameters listed in the module's ``argument_spec``.
|
||||
Names not found in ``argument_spec`` are silently ignored.
|
||||
|
||||
Another neat feature provided by MH by using ``VarDict`` is the automatic tracking of changes when setting the metadata ``change=True``.
|
||||
Again, to enable this feature for module parameters, you must list them in the ``change_params`` class variable.
|
||||
|
||||
@@ -235,11 +243,6 @@ Again, to enable this feature for module parameters, you must list them in the `
|
||||
change_params = ('value', )
|
||||
...
|
||||
|
||||
.. important::
|
||||
|
||||
The variable names listed in ``change_params`` **must be module parameters**, as in parameters listed in the module's ``argument_spec``.
|
||||
Names not found in ``argument_spec`` are silently ignored.
|
||||
|
||||
.. seealso::
|
||||
|
||||
See more about this in
|
||||
@@ -257,11 +260,6 @@ With that, MH will automatically generate the diff output for variables that hav
|
||||
# example from community.general.gio_mime
|
||||
self.vars.set_meta("handler", initial_value=gio_mime_get(self.runner, self.vars.mime_type), diff=True, change=True)
|
||||
|
||||
.. important::
|
||||
|
||||
The variable names listed in ``diff_params`` **must be module parameters**, as in parameters listed in the module's ``argument_spec``.
|
||||
Names not found in ``argument_spec`` are silently ignored.
|
||||
|
||||
Moreover, if a module is set to return *facts* instead of return values, then again use the metadata ``fact=True`` and ``fact_params`` for module parameters.
|
||||
Additionally, you must specify ``facts_name``, as in:
|
||||
|
||||
@@ -285,11 +283,6 @@ That generates an Ansible fact like:
|
||||
debug:
|
||||
msg: Volume fact is {{ ansible_facts.volume_facts.volume }}
|
||||
|
||||
.. important::
|
||||
|
||||
The variable names listed in ``fact_params`` **must be module parameters**, as in parameters listed in the module's ``argument_spec``.
|
||||
Names not found in ``argument_spec`` are silently ignored.
|
||||
|
||||
.. important::
|
||||
|
||||
If ``facts_name`` is not set, the module does not generate any facts.
|
||||
@@ -353,8 +346,6 @@ However, you can set output variables specifically for that exception, if you so
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelperException
|
||||
|
||||
def __init_module__(self):
|
||||
if not complex_validation():
|
||||
self.do_raise("Validation failed!")
|
||||
@@ -363,16 +354,11 @@ However, you can set output variables specifically for that exception, if you so
|
||||
awesomeness = calculate_awesomeness()
|
||||
if awesomeness > 1000:
|
||||
self.do_raise("Over awesome, I cannot handle it!", update_output={"awesomeness": awesomeness})
|
||||
# which is just a convenience shortcut for
|
||||
raise ModuleHelperException("...", update_output={...})
|
||||
|
||||
All exceptions derived from ``Exception`` are captured and translated into a ``fail_json()`` call.
|
||||
However, if you do want to call ``self.module.fail_json()`` yourself it will work,
|
||||
just keep in mind that there will be no automatic handling of output variables in that case.
|
||||
|
||||
Behind the curtains, all ``do_raise()`` does is to raise a ``ModuleHelperException``.
|
||||
If you want to create specialized error handling for your code, the best way is to extend that clas and raise it when needed.
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_modulehelper.statemh:
|
||||
|
||||
StateModuleHelper
|
||||
@@ -391,6 +377,7 @@ By using ``StateModuleHelper`` you can make your code like the excerpt from the
|
||||
module = dict(
|
||||
...
|
||||
)
|
||||
use_old_vardict = False
|
||||
|
||||
def __init_module__(self):
|
||||
self.runner = gconftool2_runner(self.module, check_rc=True)
|
||||
@@ -474,11 +461,6 @@ Additionally, MH will also delegate:
|
||||
- ``diff_mode`` to ``self.module._diff``
|
||||
- ``verbosity`` to ``self.module._verbosity``
|
||||
|
||||
Starting in community.general 10.3.0, MH will also delegate the method ``debug`` to ``self.module``.
|
||||
If any existing module already has a ``debug`` attribute defined, a warning message will be generated,
|
||||
requesting it to be renamed. Upon the release of community.general 12.0.0, the delegation will be
|
||||
preemptive and will override any existing method or property in the subclasses.
|
||||
|
||||
Decorators
|
||||
""""""""""
|
||||
|
||||
|
||||
@@ -67,16 +67,16 @@ The following code block is a simple playbook that creates one `Type 0 <https://
|
||||
hosts: localhost
|
||||
tasks:
|
||||
|
||||
- community.general.packet_sshkey:
|
||||
key_file: ./id_rsa.pub
|
||||
label: tutorial key
|
||||
- community.general.packet_sshkey:
|
||||
key_file: ./id_rsa.pub
|
||||
label: tutorial key
|
||||
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
hostnames: myserver
|
||||
operating_system: ubuntu_16_04
|
||||
plan: baremetal_0
|
||||
facility: sjc1
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
hostnames: myserver
|
||||
operating_system: ubuntu_16_04
|
||||
plan: baremetal_0
|
||||
facility: sjc1
|
||||
|
||||
After running ``ansible-playbook playbook_create.yml``, you should have a server provisioned on Packet. You can verify through a CLI or in the `Packet portal <https://app.packet.net/portal#/projects/list/table>`__.
|
||||
|
||||
@@ -110,10 +110,10 @@ If your playbook acts on existing Packet devices, you can only pass the ``hostna
|
||||
hosts: localhost
|
||||
tasks:
|
||||
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
hostnames: myserver
|
||||
state: rebooted
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
hostnames: myserver
|
||||
state: rebooted
|
||||
|
||||
You can also identify specific Packet devices with the ``device_ids`` parameter. The device's UUID can be found in the `Packet Portal <https://app.packet.net/portal>`_ or by using a `CLI <https://www.packet.net/developers/integrations/>`_. The following playbook removes a Packet device using the ``device_ids`` field:
|
||||
|
||||
@@ -125,10 +125,10 @@ You can also identify specific Packet devices with the ``device_ids`` parameter.
|
||||
hosts: localhost
|
||||
tasks:
|
||||
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
device_ids: <myserver_device_id>
|
||||
state: absent
|
||||
- community.general.packet_device:
|
||||
project_id: <your_project_id>
|
||||
device_ids: <myserver_device_id>
|
||||
state: absent
|
||||
|
||||
|
||||
More Complex Playbooks
|
||||
@@ -153,43 +153,43 @@ The following playbook will create an SSH key, 3 Packet servers, and then wait u
|
||||
hosts: localhost
|
||||
tasks:
|
||||
|
||||
- community.general.packet_sshkey:
|
||||
key_file: ./id_rsa.pub
|
||||
label: new
|
||||
- community.general.packet_sshkey:
|
||||
key_file: ./id_rsa.pub
|
||||
label: new
|
||||
|
||||
- community.general.packet_device:
|
||||
hostnames: [coreos-one, coreos-two, coreos-three]
|
||||
operating_system: coreos_beta
|
||||
plan: baremetal_0
|
||||
facility: ewr1
|
||||
project_id: <your_project_id>
|
||||
wait_for_public_IPv: 4
|
||||
user_data: |
|
||||
# cloud-config
|
||||
coreos:
|
||||
etcd2:
|
||||
discovery: https://discovery.etcd.io/<token>
|
||||
advertise-client-urls: http://$private_ipv4:2379,http://$private_ipv4:4001
|
||||
initial-advertise-peer-urls: http://$private_ipv4:2380
|
||||
listen-client-urls: http://0.0.0.0:2379,http://0.0.0.0:4001
|
||||
listen-peer-urls: http://$private_ipv4:2380
|
||||
fleet:
|
||||
public-ip: $private_ipv4
|
||||
units:
|
||||
- name: etcd2.service
|
||||
command: start
|
||||
- name: fleet.service
|
||||
command: start
|
||||
register: newhosts
|
||||
- community.general.packet_device:
|
||||
hostnames: [coreos-one, coreos-two, coreos-three]
|
||||
operating_system: coreos_beta
|
||||
plan: baremetal_0
|
||||
facility: ewr1
|
||||
project_id: <your_project_id>
|
||||
wait_for_public_IPv: 4
|
||||
user_data: |
|
||||
#cloud-config
|
||||
coreos:
|
||||
etcd2:
|
||||
discovery: https://discovery.etcd.io/<token>
|
||||
advertise-client-urls: http://$private_ipv4:2379,http://$private_ipv4:4001
|
||||
initial-advertise-peer-urls: http://$private_ipv4:2380
|
||||
listen-client-urls: http://0.0.0.0:2379,http://0.0.0.0:4001
|
||||
listen-peer-urls: http://$private_ipv4:2380
|
||||
fleet:
|
||||
public-ip: $private_ipv4
|
||||
units:
|
||||
- name: etcd2.service
|
||||
command: start
|
||||
- name: fleet.service
|
||||
command: start
|
||||
register: newhosts
|
||||
|
||||
- name: wait for ssh
|
||||
ansible.builtin.wait_for:
|
||||
delay: 1
|
||||
host: "{{ item.public_ipv4 }}"
|
||||
port: 22
|
||||
state: started
|
||||
timeout: 500
|
||||
loop: "{{ newhosts.results[0].devices }}"
|
||||
- name: wait for ssh
|
||||
ansible.builtin.wait_for:
|
||||
delay: 1
|
||||
host: "{{ item.public_ipv4 }}"
|
||||
port: 22
|
||||
state: started
|
||||
timeout: 500
|
||||
loop: "{{ newhosts.results[0].devices }}"
|
||||
|
||||
|
||||
As with most Ansible modules, the default states of the Packet modules are idempotent, meaning the resources in your project will remain the same after re-runs of a playbook. Thus, we can keep the ``packet_sshkey`` module call in our playbook. If the public key is already in your Packet account, the call will have no effect.
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
..
|
||||
Copyright (c) Ansible Project
|
||||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
.. _ansible_collections.community.general.docsite.guide_uthelper:
|
||||
|
||||
UTHelper Guide
|
||||
==============
|
||||
|
||||
Introduction
|
||||
^^^^^^^^^^^^
|
||||
|
||||
``UTHelper`` was written to reduce the boilerplate code used in unit tests for modules.
|
||||
It was originally written to handle tests of modules that run external commands using ``AnsibleModule.run_command()``.
|
||||
At the time of writing (Feb 2025) that remains the only type of tests you can use
|
||||
``UTHelper`` for, but it aims to provide support for other types of interactions.
|
||||
|
||||
Until now, there are many different ways to implement unit tests that validate a module based on the execution of external commands. See some examples:
|
||||
|
||||
* `test_apk.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_apk.py>`_ - A very simple one
|
||||
* `test_bootc_manage.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_bootc_manage.py>`_ -
|
||||
This one has more test cases, but do notice how the code is repeated amongst them.
|
||||
* `test_modprobe.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_modprobe.py>`_ -
|
||||
This one has 15 tests in it, but to achieve that it declares 8 classes repeating quite a lot of code.
|
||||
|
||||
As you can notice, there is no consistency in the way these tests are executed -
|
||||
they all do the same thing eventually, but each one is written in a very distinct way.
|
||||
|
||||
``UTHelper`` aims to:
|
||||
|
||||
* provide a consistent idiom to define unit tests
|
||||
* reduce the code to a bare minimal, and
|
||||
* define tests as data instead
|
||||
* allow the test cases definition to be expressed not only as a Python data structure but also as YAML content
|
||||
|
||||
Quickstart
|
||||
""""""""""
|
||||
|
||||
To use UTHelper, your test module will need only a bare minimal of code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# tests/unit/plugin/modules/test_ansible_module.py
|
||||
from ansible_collections.community.general.plugins.modules import ansible_module
|
||||
from .uthelper import UTHelper, RunCommandMock
|
||||
|
||||
|
||||
UTHelper.from_module(ansible_module, __name__, mocks=[RunCommandMock])
|
||||
|
||||
Then, in the test specification file, you have:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# tests/unit/plugin/modules/test_ansible_module.yaml
|
||||
test_cases:
|
||||
- id: test_ansible_module
|
||||
flags:
|
||||
diff: true
|
||||
input:
|
||||
state: present
|
||||
name: Roger the Shrubber
|
||||
output:
|
||||
shrubbery:
|
||||
looks: nice
|
||||
price: not too expensive
|
||||
changed: true
|
||||
diff:
|
||||
before:
|
||||
shrubbery: null
|
||||
after:
|
||||
shrubbery:
|
||||
looks: nice
|
||||
price: not too expensive
|
||||
mocks:
|
||||
run_command:
|
||||
- command: [/testbin/shrubber, --version]
|
||||
rc: 0
|
||||
out: "2.80.0\n"
|
||||
err: ''
|
||||
- command: [/testbin/shrubber, --make-shrubbery]
|
||||
rc: 0
|
||||
out: 'Shrubbery created'
|
||||
err: ''
|
||||
|
||||
.. note::
|
||||
|
||||
If you prefer to pick a different YAML file for the test cases, or if you prefer to define them in plain Python,
|
||||
you can use the convenience methods ``UTHelper.from_file()`` and ``UTHelper.from_spec()``, respectively.
|
||||
See more details below.
|
||||
|
||||
|
||||
Using ``UTHelper``
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Test Module
|
||||
"""""""""""
|
||||
|
||||
``UTHelper`` is **strictly for unit tests**. To use it, you import the ``.uthelper.UTHelper`` class.
|
||||
As mentioned in different parts of this guide, there are three different mechanisms to load the test cases.
|
||||
|
||||
.. seealso::
|
||||
|
||||
See the UTHelper class reference below for API details on the three different mechanisms.
|
||||
|
||||
|
||||
The easies and most recommended way of using ``UTHelper`` is literally the example shown.
|
||||
See a real world example at
|
||||
`test_gconftool2.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_gconftool2.py>`_.
|
||||
|
||||
The ``from_module()`` method will pick the filename of the test module up (in the example above, ``tests/unit/plugins/modules/test_gconftool2.py``)
|
||||
and it will search for ``tests/unit/plugins/modules/test_gconftool2.yaml`` (or ``.yml`` if that is not found).
|
||||
In that file it will expect to find the test specification expressed in YAML format, conforming to the structure described below LINK LINK LINK.
|
||||
|
||||
If you prefer to read the test specifications a different file path, use ``from_file()`` passing the file handle for the YAML file.
|
||||
|
||||
And, if for any reason you prefer or need to pass the data structure rather than dealing with YAML files, use the ``from_spec()`` method.
|
||||
A real world example for that can be found at
|
||||
`test_snap.py <https://github.com/ansible-collections/community.general/blob/main/tests/unit/plugins/modules/test_snap.py>`_.
|
||||
|
||||
|
||||
Test Specification
|
||||
""""""""""""""""""
|
||||
|
||||
The structure of the test specification data is described below.
|
||||
|
||||
Top level
|
||||
---------
|
||||
|
||||
At the top level there are two accepted keys:
|
||||
|
||||
- ``anchors: dict``
|
||||
Optional. Placeholder for you to define YAML anchors that can be repeated in the test cases.
|
||||
Its contents are never accessed directly by test Helper.
|
||||
- ``test_cases: list``
|
||||
Mandatory. List of test cases, see below for definition.
|
||||
|
||||
Test cases
|
||||
----------
|
||||
|
||||
You write the test cases with five elements:
|
||||
|
||||
- ``id: str``
|
||||
Mandatory. Used to identify the test case.
|
||||
|
||||
- ``flags: dict``
|
||||
Optional. Flags controling the behavior of the test case. All flags are optional. Accepted flags:
|
||||
|
||||
* ``check: bool``: set to ``true`` if the module is to be executed in **check mode**.
|
||||
* ``diff: bool``: set to ``true`` if the module is to be executed in **diff mode**.
|
||||
* ``skip: str``: set the test case to be skipped, providing the message for ``pytest.skip()``.
|
||||
* ``xfail: str``: set the test case to expect failure, providing the message for ``pytest.xfail()``.
|
||||
|
||||
- ``input: dict``
|
||||
Optional. Parameters for the Ansible module, it can be empty.
|
||||
|
||||
- ``output: dict``
|
||||
Optional. Expected return values from the Ansible module.
|
||||
All RV names are used here are expected to be found in the module output, but not all RVs in the output must be here.
|
||||
It can include special RVs such as ``changed`` and ``diff``.
|
||||
It can be empty.
|
||||
|
||||
- ``mocks: dict``
|
||||
Optional. Mocked interactions, ``run_command`` being the only one supported for now.
|
||||
Each key in this dictionary refers to one subclass of ``TestCaseMock`` and its
|
||||
structure is dictated by the ``TestCaseMock`` subclass implementation.
|
||||
All keys are expected to be named using snake case, as in ``run_command``.
|
||||
The ``TestCaseMock`` subclass is responsible for defining the name used in the test specification.
|
||||
The structure for that specification is dependent on the implementing class.
|
||||
See more details below for the implementation of ``RunCommandMock``
|
||||
|
||||
Example using YAML
|
||||
------------------
|
||||
|
||||
We recommend you use ``UTHelper`` reading the test specifications from a YAML file.
|
||||
See an example below of how one actually looks like (excerpt from ``test_opkg.yaml``):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
---
|
||||
anchors:
|
||||
environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
test_cases:
|
||||
- id: install_zlibdev
|
||||
input:
|
||||
name: zlib-dev
|
||||
state: present
|
||||
output:
|
||||
msg: installed 1 package(s)
|
||||
mocks:
|
||||
run_command:
|
||||
- command: [/testbin/opkg, --version]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: ''
|
||||
err: ''
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: ''
|
||||
err: ''
|
||||
- command: [/testbin/opkg, install, zlib-dev]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: |
|
||||
Installing zlib-dev (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
|
||||
Installing zlib (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
|
||||
Configuring zlib.
|
||||
Configuring zlib-dev.
|
||||
err: ''
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ''
|
||||
- id: install_zlibdev_present
|
||||
input:
|
||||
name: zlib-dev
|
||||
state: present
|
||||
output:
|
||||
msg: package(s) already present
|
||||
mocks:
|
||||
run_command:
|
||||
- command: [/testbin/opkg, --version]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: ''
|
||||
err: ''
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: *env-def
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ''
|
||||
|
||||
TestCaseMocks Specifications
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``TestCaseMock`` subclass is free to define the expected data structure.
|
||||
|
||||
RunCommandMock Specification
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
``RunCommandMock`` mocks can be specified with the key ``run_command`` and it expects a ``list`` in which elements follow the structure:
|
||||
|
||||
- ``command: Union[list, str]``
|
||||
Mandatory. The command that is expected to be executed by the module. It corresponds to the parameter ``args`` of the ``AnsibleModule.run_command()`` call.
|
||||
It can be either a list or a string, though the list form is generally recommended.
|
||||
- ``environ: dict``
|
||||
Mandatory. All other parameters passed to the ``AnsibleModule.run_command()`` call.
|
||||
Most commonly used are ``environ_update`` and ``check_rc``.
|
||||
Must include all parameters the Ansible module uses in the ``AnsibleModule.run_command()`` call, otherwise the test will fail.
|
||||
- ``rc: int``
|
||||
Mandatory. The return code for the command execution.
|
||||
As per usual in bash scripting, a value of ``0`` means success, whereas any other number is an error code.
|
||||
- ``out: str``
|
||||
Mandatory. The *stdout* result of the command execution, as one single string containing zero or more lines.
|
||||
- ``err: str``
|
||||
Mandatory. The *stderr* result of the command execution, as one single string containing zero or more lines.
|
||||
|
||||
|
||||
``UTHelper`` Reference
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. py:module:: .uthelper
|
||||
|
||||
.. py:class:: UTHelper
|
||||
|
||||
A class to encapsulate unit tests.
|
||||
|
||||
.. py:staticmethod:: from_spec(ansible_module, test_module, test_spec, mocks=None)
|
||||
|
||||
Creates an ``UTHelper`` instance from a given test specification.
|
||||
|
||||
:param ansible_module: The Ansible module to be tested.
|
||||
:type ansible_module: module
|
||||
:param test_module: The test module.
|
||||
:type test_module: module
|
||||
:param test_spec: The test specification.
|
||||
:type test_spec: dict
|
||||
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
|
||||
:type mocks: list or None
|
||||
:return: An ``UTHelper`` instance.
|
||||
:rtype: UTHelper
|
||||
|
||||
Example usage of ``from_spec()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import ansible_module
|
||||
from .uthelper import UTHelper, RunCommandMock
|
||||
|
||||
TEST_SPEC = dict(
|
||||
test_cases=[
|
||||
...
|
||||
]
|
||||
)
|
||||
|
||||
helper = UTHelper.from_spec(ansible_module, sys.modules[__name__], TEST_SPEC, mocks=[RunCommandMock])
|
||||
|
||||
.. py:staticmethod:: from_file(ansible_module, test_module, test_spec_filehandle, mocks=None)
|
||||
|
||||
Creates an ``UTHelper`` instance from a test specification file.
|
||||
|
||||
:param ansible_module: The Ansible module to be tested.
|
||||
:type ansible_module: module
|
||||
:param test_module: The test module.
|
||||
:type test_module: module
|
||||
:param test_spec_filehandle: A file handle to an file stream handle providing the test specification in YAML format.
|
||||
:type test_spec_filehandle: file
|
||||
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
|
||||
:type mocks: list or None
|
||||
:return: An ``UTHelper`` instance.
|
||||
:rtype: UTHelper
|
||||
|
||||
Example usage of ``from_file()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import ansible_module
|
||||
from .uthelper import UTHelper, RunCommandMock
|
||||
|
||||
with open("test_spec.yaml", "r") as test_spec_filehandle:
|
||||
helper = UTHelper.from_file(ansible_module, sys.modules[__name__], test_spec_filehandle, mocks=[RunCommandMock])
|
||||
|
||||
.. py:staticmethod:: from_module(ansible_module, test_module_name, mocks=None)
|
||||
|
||||
Creates an ``UTHelper`` instance from a given Ansible module and test module.
|
||||
|
||||
:param ansible_module: The Ansible module to be tested.
|
||||
:type ansible_module: module
|
||||
:param test_module_name: The name of the test module. It works if passed ``__name__``.
|
||||
:type test_module_name: str
|
||||
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
|
||||
:type mocks: list or None
|
||||
:return: An ``UTHelper`` instance.
|
||||
:rtype: UTHelper
|
||||
|
||||
Example usage of ``from_module()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import ansible_module
|
||||
from .uthelper import UTHelper, RunCommandMock
|
||||
|
||||
# Example usage
|
||||
helper = UTHelper.from_module(ansible_module, __name__, mocks=[RunCommandMock])
|
||||
|
||||
|
||||
Creating TestCaseMocks
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To create a new ``TestCaseMock`` you must extend that class and implement the relevant parts:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class ShrubberyMock(TestCaseMock):
|
||||
# this name is mandatory, it is the name used in the test specification
|
||||
name = "shrubbery"
|
||||
|
||||
def setup(self, mocker):
|
||||
# perform setup, commonly using mocker to patch some other piece of code
|
||||
...
|
||||
|
||||
def check(self, test_case, results):
|
||||
# verify the tst execution met the expectations of the test case
|
||||
# for example the function was called as many times as it should
|
||||
...
|
||||
|
||||
def fixtures(self):
|
||||
# returns a dict mapping names to pytest fixtures that should be used for the test case
|
||||
# for example, in RunCommandMock it creates a fixture that patches AnsibleModule.get_bin_path
|
||||
...
|
||||
|
||||
Caveats
|
||||
^^^^^^^
|
||||
|
||||
Known issues/opportunities for improvement:
|
||||
|
||||
* Only one ``UTHelper`` per test module: UTHelper injects a test function with a fixed name into the module's namespace,
|
||||
so placing a second ``UTHelper`` instance is going to overwrite the function created by the first one.
|
||||
* Order of elements in module's namespace is not consistent across executions in Python 3.5, so if adding more tests to the test module
|
||||
might make Test Helper add its function before or after the other test functions.
|
||||
In the community.general collection the CI processes uses ``pytest-xdist`` to paralellize and distribute the tests,
|
||||
and it requires the order of the tests to be consistent.
|
||||
|
||||
.. versionadded:: 7.5.0
|
||||
@@ -51,7 +51,7 @@ And by the time the module is about to exit:
|
||||
|
||||
That makes the return value of the module:
|
||||
|
||||
.. code-block:: json
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"abc": 123,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
community.general Test (Plugin) Guide
|
||||
=====================================
|
||||
|
||||
The :anscollection:`community.general collection <community.general#collection>` offers currently one test plugin.
|
||||
The :ref:`community.general collection <plugins_in_community.general>` offers currently one test plugin.
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace: community
|
||||
name: general
|
||||
version: 11.1.2
|
||||
version: 9.5.2
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
548
meta/runtime.yml
548
meta/runtime.yml
@@ -3,7 +3,7 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
requires_ansible: '>=2.16.0'
|
||||
requires_ansible: '>=2.13.0'
|
||||
action_groups:
|
||||
consul:
|
||||
- consul_agent_check
|
||||
@@ -15,37 +15,22 @@ action_groups:
|
||||
- consul_session
|
||||
- consul_token
|
||||
proxmox:
|
||||
- metadata:
|
||||
extend_group:
|
||||
- community.proxmox.proxmox
|
||||
keycloak:
|
||||
- keycloak_authentication
|
||||
- keycloak_authentication_required_actions
|
||||
- keycloak_authz_authorization_scope
|
||||
- keycloak_authz_custom_policy
|
||||
- keycloak_authz_permission
|
||||
- keycloak_authz_permission_info
|
||||
- keycloak_client
|
||||
- keycloak_client_rolemapping
|
||||
- keycloak_client_rolescope
|
||||
- keycloak_clientscope
|
||||
- keycloak_clientscope_type
|
||||
- keycloak_clientsecret_info
|
||||
- keycloak_clientsecret_regenerate
|
||||
- keycloak_clienttemplate
|
||||
- keycloak_component
|
||||
- keycloak_component_info
|
||||
- keycloak_group
|
||||
- keycloak_identity_provider
|
||||
- keycloak_realm
|
||||
- keycloak_realm_key
|
||||
- keycloak_realm_keys_metadata_info
|
||||
- keycloak_realm_rolemapping
|
||||
- keycloak_role
|
||||
- keycloak_user
|
||||
- keycloak_user_federation
|
||||
- keycloak_user_rolemapping
|
||||
- keycloak_userprofile
|
||||
- proxmox
|
||||
- proxmox_disk
|
||||
- proxmox_domain_info
|
||||
- proxmox_group_info
|
||||
- proxmox_kvm
|
||||
- proxmox_nic
|
||||
- proxmox_node_info
|
||||
- proxmox_pool
|
||||
- proxmox_pool_member
|
||||
- proxmox_snap
|
||||
- proxmox_storage_contents_info
|
||||
- proxmox_storage_info
|
||||
- proxmox_tasks_info
|
||||
- proxmox_template
|
||||
- proxmox_user_info
|
||||
- proxmox_vm_info
|
||||
plugin_routing:
|
||||
callback:
|
||||
actionable:
|
||||
@@ -59,7 +44,7 @@ plugin_routing:
|
||||
warning_text: Use the 'default' callback plugin with 'display_skipped_hosts
|
||||
= no' option.
|
||||
hipchat:
|
||||
tombstone:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
osx_say:
|
||||
@@ -69,30 +54,16 @@ plugin_routing:
|
||||
removal_version: 2.0.0
|
||||
warning_text: Use the 'default' callback plugin with 'display_failed_stderr
|
||||
= yes' option.
|
||||
yaml:
|
||||
deprecation:
|
||||
removal_version: 12.0.0
|
||||
warning_text: >-
|
||||
The plugin has been superseded by the the option `result_format=yaml` in callback plugin ansible.builtin.default from ansible-core 2.13 onwards.
|
||||
connection:
|
||||
docker:
|
||||
redirect: community.docker.docker
|
||||
oc:
|
||||
redirect: community.okd.oc
|
||||
proxmox_pct_remote:
|
||||
redirect: community.proxmox.proxmox_pct_remote
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
lookup:
|
||||
gcp_storage_file:
|
||||
redirect: community.google.gcp_storage_file
|
||||
hashi_vault:
|
||||
redirect: community.hashi_vault.hashi_vault
|
||||
manifold:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Company was acquired in 2021 and service was ceased afterwards.
|
||||
nios:
|
||||
redirect: infoblox.nios_modules.nios_lookup
|
||||
nios_next_ip:
|
||||
@@ -100,68 +71,140 @@ plugin_routing:
|
||||
nios_next_network:
|
||||
redirect: infoblox.nios_modules.nios_next_network
|
||||
modules:
|
||||
consul_acl:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
|
||||
hipchat:
|
||||
deprecation:
|
||||
removal_version: 11.0.0
|
||||
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
rax_cbs_attachments:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cbs:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb_database:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb_user:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb_nodes:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb_ssl:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_dns_record:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_dns:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_facts:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_files_objects:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_files:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_identity:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_keypair:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_meta:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_alarm:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_check:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_entity:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_notification_plan:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_notification:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_network:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_queue:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_scaling_group:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_scaling_policy:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rhn_channel:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL, please contact the community.general maintainers
|
||||
if still using this; see the module documentation for more details.
|
||||
rhn_register:
|
||||
deprecation:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL, please contact the community.general maintainers
|
||||
if still using this; see the module documentation for more details.
|
||||
stackdriver:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on HTTPS APIs that do not exist anymore,
|
||||
and any new development in the direction of providing an alternative should
|
||||
happen in the context of the google.cloud collection.
|
||||
ali_instance_facts:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.ali_instance_info instead.
|
||||
atomic_container:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Project Atomic was sunset by the end of 2019.
|
||||
atomic_host:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Project Atomic was sunset by the end of 2019.
|
||||
atomic_image:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Project Atomic was sunset by the end of 2019.
|
||||
catapult:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: DNS fails to resolve the API endpoint used by the module since Oct 2024. See https://github.com/ansible-collections/community.general/issues/10318 for details.
|
||||
cisco_spark:
|
||||
redirect: community.general.cisco_webex
|
||||
clc_alert_policy:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_blueprint_package:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_firewall_policy:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_group:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_loadbalancer:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_modify_server:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_publicip:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_server:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
clc_server_snapshot:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: CenturyLink Cloud services went EOL in September 2023.
|
||||
consul_acl:
|
||||
tombstone:
|
||||
removal_version: 10.0.0
|
||||
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
|
||||
docker_compose:
|
||||
redirect: community.docker.docker_compose
|
||||
docker_config:
|
||||
@@ -216,10 +259,6 @@ plugin_routing:
|
||||
redirect: community.docker.docker_volume
|
||||
docker_volume_info:
|
||||
redirect: community.docker.docker_volume_info
|
||||
facter:
|
||||
deprecation:
|
||||
removal_version: 12.0.0
|
||||
warning_text: Use community.general.facter_facts instead.
|
||||
flowdock:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
@@ -313,10 +352,6 @@ plugin_routing:
|
||||
redirect: community.hrobot.firewall
|
||||
hetzner_firewall_info:
|
||||
redirect: community.hrobot.firewall_info
|
||||
hipchat:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
hpilo_facts:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
@@ -638,116 +673,6 @@ plugin_routing:
|
||||
redirect: community.postgresql.postgresql_user
|
||||
postgresql_user_obj_stat_info:
|
||||
redirect: community.postgresql.postgresql_user_obj_stat_info
|
||||
profitbricks:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Supporting library is unsupported since 2021.
|
||||
profitbricks_datacenter:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Supporting library is unsupported since 2021.
|
||||
profitbricks_nic:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Supporting library is unsupported since 2021.
|
||||
profitbricks_volume:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Supporting library is unsupported since 2021.
|
||||
profitbricks_volume_attachments:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: Supporting library is unsupported since 2021.
|
||||
proxmox:
|
||||
redirect: community.proxmox.proxmox
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_backup:
|
||||
redirect: community.proxmox.proxmox_backup
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_backup_info:
|
||||
redirect: community.proxmox.proxmox_backup_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_disk:
|
||||
redirect: community.proxmox.proxmox_disk
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_domain_info:
|
||||
redirect: community.proxmox.proxmox_domain_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_group_info:
|
||||
redirect: community.proxmox.proxmox_group_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_kvm:
|
||||
redirect: community.proxmox.proxmox_kvm
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_nic:
|
||||
redirect: community.proxmox.proxmox_nic
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_node_info:
|
||||
redirect: community.proxmox.proxmox_node_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_pool:
|
||||
redirect: community.proxmox.proxmox_pool
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_pool_member:
|
||||
redirect: community.proxmox.proxmox_pool_member
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_snap:
|
||||
redirect: community.proxmox.proxmox_snap
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_storage_contents_info:
|
||||
redirect: community.proxmox.proxmox_storage_contents_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_storage_info:
|
||||
redirect: community.proxmox.proxmox_storage_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_tasks_info:
|
||||
redirect: community.proxmox.proxmox_tasks_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_template:
|
||||
redirect: community.proxmox.proxmox_template
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_user_info:
|
||||
redirect: community.proxmox.proxmox_user_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
proxmox_vm_info:
|
||||
redirect: community.proxmox.proxmox_vm_info
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
purefa_facts:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
@@ -760,122 +685,10 @@ plugin_routing:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.python_requirements_info instead.
|
||||
rax_cbs_attachments:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cbs:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb_database:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb_user:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_cdb:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb_nodes:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb_ssl:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_clb:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_dns_record:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_dns:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_facts:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_files_objects:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_files:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_identity:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_keypair:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_meta:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_alarm:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_check:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_entity:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_notification_plan:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_mon_notification:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_network:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_queue:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_scaling_group:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
rax_scaling_policy:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on the deprecated package pyrax.
|
||||
redfish_facts:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.redfish_info instead.
|
||||
rhn_channel:
|
||||
tombstone:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL.
|
||||
rhn_register:
|
||||
tombstone:
|
||||
removal_version: 10.0.0
|
||||
warning_text: RHN is EOL.
|
||||
sapcar_extract:
|
||||
redirect: community.sap_libs.sapcar_extract
|
||||
sap_task_list_execute:
|
||||
@@ -908,26 +721,6 @@ plugin_routing:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.scaleway_volume_info instead.
|
||||
sensu_check:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
|
||||
sensu_client:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
|
||||
sensu_handler:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
|
||||
sensu_silence:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
|
||||
sensu_subscription:
|
||||
deprecation:
|
||||
removal_version: 13.0.0
|
||||
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
|
||||
sf_account_manager:
|
||||
tombstone:
|
||||
removal_version: 2.0.0
|
||||
@@ -952,12 +745,6 @@ plugin_routing:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.smartos_image_info instead.
|
||||
stackdriver:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module relied on HTTPS APIs that do not exist anymore,
|
||||
and any new development in the direction of providing an alternative should
|
||||
happen in the context of the google.cloud collection.
|
||||
vertica_facts:
|
||||
tombstone:
|
||||
removal_version: 3.0.0
|
||||
@@ -992,6 +779,11 @@ plugin_routing:
|
||||
removal_version: 3.0.0
|
||||
warning_text: Use community.general.xenserver_guest_info instead.
|
||||
doc_fragments:
|
||||
rackspace:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This doc fragment was used by rax modules, that relied on the deprecated
|
||||
package pyrax.
|
||||
_gcp:
|
||||
redirect: community.google._gcp
|
||||
docker:
|
||||
@@ -1006,21 +798,11 @@ plugin_routing:
|
||||
redirect: infoblox.nios_modules.nios
|
||||
postgresql:
|
||||
redirect: community.postgresql.postgresql
|
||||
proxmox:
|
||||
redirect: community.proxmox.proxmox
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
purestorage:
|
||||
deprecation:
|
||||
removal_version: 12.0.0
|
||||
warning_text: The modules for purestorage were removed in community.general 3.0.0, this document fragment was left behind.
|
||||
rackspace:
|
||||
module_utils:
|
||||
rax:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This doc fragment was used by rax modules, that relied on the deprecated
|
||||
package pyrax.
|
||||
module_utils:
|
||||
warning_text: This module util relied on the deprecated package pyrax.
|
||||
docker.common:
|
||||
redirect: community.docker.common
|
||||
docker.swarm:
|
||||
@@ -1039,19 +821,6 @@ plugin_routing:
|
||||
redirect: infoblox.nios_modules.api
|
||||
postgresql:
|
||||
redirect: community.postgresql.postgresql
|
||||
proxmox:
|
||||
redirect: community.proxmox.proxmox
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
pure:
|
||||
deprecation:
|
||||
removal_version: 12.0.0
|
||||
warning_text: The modules for purestorage were removed in community.general 3.0.0, this module util was left behind.
|
||||
rax:
|
||||
tombstone:
|
||||
removal_version: 9.0.0
|
||||
warning_text: This module util relied on the deprecated package pyrax.
|
||||
remote_management.dellemc.dellemc_idrac:
|
||||
redirect: dellemc.openmanage.dellemc_idrac
|
||||
remote_management.dellemc.ome:
|
||||
@@ -1061,17 +830,8 @@ plugin_routing:
|
||||
redirect: community.docker.docker_machine
|
||||
docker_swarm:
|
||||
redirect: community.docker.docker_swarm
|
||||
proxmox:
|
||||
redirect: community.proxmox.proxmox
|
||||
deprecation:
|
||||
removal_version: 15.0.0
|
||||
warning_text: The proxmox content has been moved to community.proxmox.
|
||||
kubevirt:
|
||||
redirect: community.kubevirt.kubevirt
|
||||
stackpath_compute:
|
||||
tombstone:
|
||||
removal_version: 11.0.0
|
||||
warning_text: The company and the service were sunset in June 2024.
|
||||
filter:
|
||||
path_join:
|
||||
# The ansible.builtin.path_join filter has been added in ansible-base 2.10.
|
||||
|
||||
38
noxfile.py
38
noxfile.py
@@ -1,38 +0,0 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
|
||||
|
||||
# /// script
|
||||
# dependencies = ["nox>=2025.02.09", "antsibull-nox"]
|
||||
# ///
|
||||
|
||||
import sys
|
||||
|
||||
import nox
|
||||
|
||||
|
||||
try:
|
||||
import antsibull_nox
|
||||
except ImportError:
|
||||
print("You need to install antsibull-nox in the same Python environment as nox.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
antsibull_nox.load_antsibull_nox_toml()
|
||||
|
||||
|
||||
@nox.session(name="aliases", python=False, default=True)
|
||||
def aliases(session: nox.Session) -> None:
|
||||
session.run("python", "tests/sanity/extra/aliases.py")
|
||||
|
||||
|
||||
@nox.session(name="botmeta", default=True)
|
||||
def botmeta(session: nox.Session) -> None:
|
||||
session.install("PyYAML", "voluptuous")
|
||||
session.run("python", "tests/sanity/extra/botmeta.py")
|
||||
|
||||
|
||||
# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar.
|
||||
# Requires nox >= 2025.02.09
|
||||
if __name__ == "__main__":
|
||||
nox.main()
|
||||
@@ -3,7 +3,8 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import time
|
||||
|
||||
@@ -21,33 +22,25 @@ class ActionModule(ActionBase):
|
||||
_VALID_ARGS = frozenset(('path', 'state', 'table', 'noflush', 'counters', 'modprobe', 'ip_version', 'wait'))
|
||||
DEFAULT_SUDOABLE = True
|
||||
|
||||
@staticmethod
|
||||
def msg_error__async_and_poll_not_zero(task_poll, task_async, max_timeout):
|
||||
return (
|
||||
"This module doesn't support async>0 and poll>0 when its 'state' param "
|
||||
"is set to 'restored'. To enable its rollback feature (that needs the "
|
||||
"module to run asynchronously on the remote), please set task attribute "
|
||||
f"'poll' (={task_poll}) to 0, and 'async' (={task_async}) to a value >2 and not greater than "
|
||||
f"'ansible_timeout' (={max_timeout}) (recommended).")
|
||||
|
||||
@staticmethod
|
||||
def msg_warning__no_async_is_no_rollback(task_poll, task_async, max_timeout):
|
||||
return (
|
||||
"Attempts to restore iptables state without rollback in case of mistake "
|
||||
"may lead the ansible controller to loose access to the hosts and never "
|
||||
"regain it before fixing firewall rules through a serial console, or any "
|
||||
f"other way except SSH. Please set task attribute 'poll' (={task_poll}) to 0, and "
|
||||
f"'async' (={task_async}) to a value >2 and not greater than 'ansible_timeout' (={max_timeout}) "
|
||||
"(recommended).")
|
||||
|
||||
@staticmethod
|
||||
def msg_warning__async_greater_than_timeout(task_poll, task_async, max_timeout):
|
||||
return (
|
||||
"You attempt to restore iptables state with rollback in case of mistake, "
|
||||
"but with settings that will lead this rollback to happen AFTER that the "
|
||||
"controller will reach its own timeout. Please set task attribute 'poll' "
|
||||
f"(={task_poll}) to 0, and 'async' (={task_async}) to a value >2 and not greater than "
|
||||
f"'ansible_timeout' (={max_timeout}) (recommended).")
|
||||
MSG_ERROR__ASYNC_AND_POLL_NOT_ZERO = (
|
||||
"This module doesn't support async>0 and poll>0 when its 'state' param "
|
||||
"is set to 'restored'. To enable its rollback feature (that needs the "
|
||||
"module to run asynchronously on the remote), please set task attribute "
|
||||
"'poll' (=%s) to 0, and 'async' (=%s) to a value >2 and not greater than "
|
||||
"'ansible_timeout' (=%s) (recommended).")
|
||||
MSG_WARNING__NO_ASYNC_IS_NO_ROLLBACK = (
|
||||
"Attempts to restore iptables state without rollback in case of mistake "
|
||||
"may lead the ansible controller to loose access to the hosts and never "
|
||||
"regain it before fixing firewall rules through a serial console, or any "
|
||||
"other way except SSH. Please set task attribute 'poll' (=%s) to 0, and "
|
||||
"'async' (=%s) to a value >2 and not greater than 'ansible_timeout' (=%s) "
|
||||
"(recommended).")
|
||||
MSG_WARNING__ASYNC_GREATER_THAN_TIMEOUT = (
|
||||
"You attempt to restore iptables state with rollback in case of mistake, "
|
||||
"but with settings that will lead this rollback to happen AFTER that the "
|
||||
"controller will reach its own timeout. Please set task attribute 'poll' "
|
||||
"(=%s) to 0, and 'async' (=%s) to a value >2 and not greater than "
|
||||
"'ansible_timeout' (=%s) (recommended).")
|
||||
|
||||
def _async_result(self, async_status_args, task_vars, timeout):
|
||||
'''
|
||||
@@ -102,18 +95,18 @@ class ActionModule(ActionBase):
|
||||
if module_args.get('state', None) == 'restored':
|
||||
if not wrap_async:
|
||||
if not check_mode:
|
||||
display.warning(self.msg_error__async_and_poll_not_zero(
|
||||
display.warning(self.MSG_WARNING__NO_ASYNC_IS_NO_ROLLBACK % (
|
||||
task_poll,
|
||||
task_async,
|
||||
max_timeout))
|
||||
elif task_poll:
|
||||
raise AnsibleActionFail(self.msg_warning__no_async_is_no_rollback(
|
||||
raise AnsibleActionFail(self.MSG_ERROR__ASYNC_AND_POLL_NOT_ZERO % (
|
||||
task_poll,
|
||||
task_async,
|
||||
max_timeout))
|
||||
else:
|
||||
if task_async > max_timeout and not check_mode:
|
||||
display.warning(self.msg_warning__async_greater_than_timeout(
|
||||
display.warning(self.MSG_WARNING__ASYNC_GREATER_THAN_TIMEOUT % (
|
||||
task_poll,
|
||||
task_async,
|
||||
max_timeout))
|
||||
@@ -126,10 +119,10 @@ class ActionModule(ActionBase):
|
||||
# remote and local sides (if not the same, make the loop
|
||||
# longer on the controller); and set a backup file path.
|
||||
module_args['_timeout'] = task_async
|
||||
module_args['_back'] = f'{async_dir}/iptables.state'
|
||||
module_args['_back'] = '%s/iptables.state' % async_dir
|
||||
async_status_args = dict(mode='status')
|
||||
confirm_cmd = f"rm -f {module_args['_back']}"
|
||||
starter_cmd = f"touch {module_args['_back']}.starter"
|
||||
confirm_cmd = 'rm -f %s' % module_args['_back']
|
||||
starter_cmd = 'touch %s.starter' % module_args['_back']
|
||||
remaining_time = max(task_async, max_timeout)
|
||||
|
||||
# do work!
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
@@ -17,10 +18,6 @@ from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
def fmt(mapping, key):
|
||||
return to_native(mapping[key]).strip()
|
||||
|
||||
|
||||
class TimedOutException(Exception):
|
||||
pass
|
||||
|
||||
@@ -87,26 +84,31 @@ class ActionModule(ActionBase):
|
||||
def get_distribution(self, task_vars):
|
||||
# FIXME: only execute the module if we don't already have the facts we need
|
||||
distribution = {}
|
||||
display.debug(f'{self._task.action}: running setup module to get distribution')
|
||||
display.debug('{action}: running setup module to get distribution'.format(action=self._task.action))
|
||||
module_output = self._execute_module(
|
||||
task_vars=task_vars,
|
||||
module_name='ansible.legacy.setup',
|
||||
module_args={'gather_subset': 'min'})
|
||||
try:
|
||||
if module_output.get('failed', False):
|
||||
raise AnsibleError(f"Failed to determine system distribution. {fmt(module_output, 'module_stdout')}, {fmt(module_output, 'module_stderr')}")
|
||||
raise AnsibleError('Failed to determine system distribution. {0}, {1}'.format(
|
||||
to_native(module_output['module_stdout']).strip(),
|
||||
to_native(module_output['module_stderr']).strip()))
|
||||
distribution['name'] = module_output['ansible_facts']['ansible_distribution'].lower()
|
||||
distribution['version'] = to_text(
|
||||
module_output['ansible_facts']['ansible_distribution_version'].split('.')[0])
|
||||
distribution['family'] = to_text(module_output['ansible_facts']['ansible_os_family'].lower())
|
||||
display.debug(f"{self._task.action}: distribution: {distribution}")
|
||||
display.debug("{action}: distribution: {dist}".format(action=self._task.action, dist=distribution))
|
||||
return distribution
|
||||
except KeyError as ke:
|
||||
raise AnsibleError(f'Failed to get distribution information. Missing "{ke.args[0]}" in output.')
|
||||
raise AnsibleError('Failed to get distribution information. Missing "{0}" in output.'.format(ke.args[0]))
|
||||
|
||||
def get_shutdown_command(self, task_vars, distribution):
|
||||
def find_command(command, find_search_paths):
|
||||
display.debug(f'{self._task.action}: running find module looking in {find_search_paths} to get path for "{command}"')
|
||||
display.debug('{action}: running find module looking in {paths} to get path for "{command}"'.format(
|
||||
action=self._task.action,
|
||||
command=command,
|
||||
paths=find_search_paths))
|
||||
find_result = self._execute_module(
|
||||
task_vars=task_vars,
|
||||
# prevent collection search by calling with ansible.legacy (still allows library/ override of find)
|
||||
@@ -128,37 +130,42 @@ class ActionModule(ActionBase):
|
||||
if is_string(search_paths):
|
||||
search_paths = [search_paths]
|
||||
|
||||
# Error if we didn't get a list
|
||||
err_msg = "'search_paths' must be a string or flat list of strings, got {0}"
|
||||
try:
|
||||
incorrect_type = any(not is_string(x) for x in search_paths)
|
||||
if not isinstance(search_paths, list) or incorrect_type:
|
||||
raise TypeError
|
||||
except TypeError:
|
||||
# Error if we didn't get a list
|
||||
err_msg = f"'search_paths' must be a string or flat list of strings, got {search_paths}"
|
||||
raise AnsibleError(err_msg)
|
||||
raise AnsibleError(err_msg.format(search_paths))
|
||||
|
||||
full_path = find_command(shutdown_bin, search_paths) # find the path to the shutdown command
|
||||
if not full_path: # if we could not find the shutdown command
|
||||
|
||||
# tell the user we will try with systemd
|
||||
display.vvv(f'Unable to find command "{shutdown_bin}" in search paths: {search_paths}, will attempt a shutdown using systemd directly.')
|
||||
display.vvv('Unable to find command "{0}" in search paths: {1}, will attempt a shutdown using systemd '
|
||||
'directly.'.format(shutdown_bin, search_paths)) # tell the user we will try with systemd
|
||||
systemctl_search_paths = ['/bin', '/usr/bin']
|
||||
full_path = find_command('systemctl', systemctl_search_paths) # find the path to the systemctl command
|
||||
if not full_path: # if we couldn't find systemctl
|
||||
raise AnsibleError(
|
||||
f'Could not find command "{shutdown_bin}" in search paths: {search_paths} or systemctl'
|
||||
f' command in search paths: {systemctl_search_paths}, unable to shutdown.') # we give up here
|
||||
'Could not find command "{0}" in search paths: {1} or systemctl command in search paths: {2}, unable to shutdown.'.
|
||||
format(shutdown_bin, search_paths, systemctl_search_paths)) # we give up here
|
||||
else:
|
||||
return f"{full_path[0]} poweroff" # done, since we cannot use args with systemd shutdown
|
||||
return "{0} poweroff".format(full_path[0]) # done, since we cannot use args with systemd shutdown
|
||||
|
||||
# systemd case taken care of, here we add args to the command
|
||||
args = self._get_value_from_facts('SHUTDOWN_COMMAND_ARGS', distribution, 'DEFAULT_SHUTDOWN_COMMAND_ARGS')
|
||||
# Convert seconds to minutes. If less that 60, set it to 0.
|
||||
delay_sec = self.delay
|
||||
shutdown_message = self._task.args.get('msg', self.DEFAULT_SHUTDOWN_MESSAGE)
|
||||
|
||||
af = args.format(delay_sec=delay_sec, delay_min=delay_sec // 60, message=shutdown_message)
|
||||
return f'{full_path[0]} {af}'
|
||||
return '{0} {1}'. \
|
||||
format(
|
||||
full_path[0],
|
||||
args.format(
|
||||
delay_sec=delay_sec,
|
||||
delay_min=delay_sec // 60,
|
||||
message=shutdown_message
|
||||
)
|
||||
)
|
||||
|
||||
def perform_shutdown(self, task_vars, distribution):
|
||||
result = {}
|
||||
@@ -167,8 +174,9 @@ class ActionModule(ActionBase):
|
||||
|
||||
self.cleanup(force=True)
|
||||
try:
|
||||
display.vvv(f"{self._task.action}: shutting down server...")
|
||||
display.debug(f"{self._task.action}: shutting down server with command '{shutdown_command_exec}'")
|
||||
display.vvv("{action}: shutting down server...".format(action=self._task.action))
|
||||
display.debug("{action}: shutting down server with command '{command}'".
|
||||
format(action=self._task.action, command=shutdown_command_exec))
|
||||
if self._play_context.check_mode:
|
||||
shutdown_result['rc'] = 0
|
||||
else:
|
||||
@@ -176,13 +184,16 @@ class ActionModule(ActionBase):
|
||||
except AnsibleConnectionFailure as e:
|
||||
# If the connection is closed too quickly due to the system being shutdown, carry on
|
||||
display.debug(
|
||||
f'{self._task.action}: AnsibleConnectionFailure caught and handled: {e}')
|
||||
'{action}: AnsibleConnectionFailure caught and handled: {error}'.format(action=self._task.action,
|
||||
error=to_text(e)))
|
||||
shutdown_result['rc'] = 0
|
||||
|
||||
if shutdown_result['rc'] != 0:
|
||||
result['failed'] = True
|
||||
result['shutdown'] = False
|
||||
result['msg'] = f"Shutdown command failed. Error was {fmt(shutdown_result, 'stdout')}, {fmt(shutdown_result, 'stderr')}"
|
||||
result['msg'] = "Shutdown command failed. Error was {stdout}, {stderr}".format(
|
||||
stdout=to_native(shutdown_result['stdout'].strip()),
|
||||
stderr=to_native(shutdown_result['stderr'].strip()))
|
||||
return result
|
||||
|
||||
result['failed'] = False
|
||||
@@ -195,7 +206,7 @@ class ActionModule(ActionBase):
|
||||
|
||||
# If running with local connection, fail so we don't shutdown ourself
|
||||
if self._connection.transport == 'local' and (not self._play_context.check_mode):
|
||||
msg = f'Running {self._task.action} with local connection would shutdown the control node.'
|
||||
msg = 'Running {0} with local connection would shutdown the control node.'.format(self._task.action)
|
||||
return {'changed': False, 'elapsed': 0, 'shutdown': False, 'failed': True, 'msg': msg}
|
||||
|
||||
if task_vars is None:
|
||||
|
||||
@@ -2,91 +2,89 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: doas
|
||||
short_description: Do As user
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(doas) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: doas_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_doas_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DOAS_USER
|
||||
become_exe:
|
||||
description: C(doas) executable.
|
||||
type: string
|
||||
default: doas
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: doas_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_doas_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DOAS_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(doas).
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: doas_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_doas_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DOAS_FLAGS
|
||||
become_pass:
|
||||
description: Password for C(doas) prompt.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_doas_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DOAS_PASS
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
DOCUMENTATION = '''
|
||||
name: doas
|
||||
short_description: Do As user
|
||||
description:
|
||||
- List of localized strings to match for prompt detection.
|
||||
- If empty the plugin uses the built-in one.
|
||||
type: list
|
||||
elements: string
|
||||
default: []
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_doas_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_DOAS_PROMPT_L10N
|
||||
notes:
|
||||
- This become plugin does not work when connection pipelining is enabled. With ansible-core 2.19+, using it automatically
|
||||
disables pipelining. On ansible-core 2.18 and before, pipelining must explicitly be disabled by the user.
|
||||
"""
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the doas utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: doas_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_doas_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DOAS_USER
|
||||
become_exe:
|
||||
description: Doas executable.
|
||||
type: string
|
||||
default: doas
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: doas_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_doas_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DOAS_EXE
|
||||
become_flags:
|
||||
description: Options to pass to doas.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: doas_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_doas_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DOAS_FLAGS
|
||||
become_pass:
|
||||
description: Password for doas prompt.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_doas_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DOAS_PASS
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
description:
|
||||
- List of localized strings to match for prompt detection.
|
||||
- If empty we will use the built in one.
|
||||
type: list
|
||||
elements: string
|
||||
default: []
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_doas_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_DOAS_PROMPT_L10N
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
@@ -102,10 +100,6 @@ class BecomeModule(BecomeBase):
|
||||
fail = ('Permission denied',)
|
||||
missing = ('Authorization required',)
|
||||
|
||||
# See https://github.com/ansible-collections/community.general/issues/9977,
|
||||
# https://github.com/ansible/ansible/pull/78111
|
||||
pipelining = False
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
''' checks if the expected password prompt exists in b_output '''
|
||||
|
||||
@@ -131,9 +125,9 @@ class BecomeModule(BecomeBase):
|
||||
flags += ' -n'
|
||||
|
||||
become_user = self.get_option('become_user')
|
||||
user = f'-u {become_user}' if become_user else ''
|
||||
user = '-u %s' % (become_user) if become_user else ''
|
||||
|
||||
success_cmd = self._build_success_command(cmd, shell, noexe=True)
|
||||
executable = getattr(shell, 'executable', shell.SHELL_FAMILY)
|
||||
|
||||
return f'{become_exe} {flags} {user} {executable} -c {success_cmd}'
|
||||
return '%s %s %s %s -c %s' % (become_exe, flags, user, executable, success_cmd)
|
||||
|
||||
@@ -2,74 +2,75 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: dzdo
|
||||
short_description: Centrify's Direct Authorize
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(dzdo) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: dzdo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_dzdo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DZDO_USER
|
||||
become_exe:
|
||||
description: C(dzdo) executable.
|
||||
type: string
|
||||
default: dzdo
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: dzdo_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_dzdo_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DZDO_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(dzdo).
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: dzdo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_dzdo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DZDO_FLAGS
|
||||
become_pass:
|
||||
description: Options to pass to C(dzdo).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_dzdo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DZDO_PASS
|
||||
ini:
|
||||
- section: dzdo_become_plugin
|
||||
key: password
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: dzdo
|
||||
short_description: Centrify's Direct Authorize
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the dzdo utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: dzdo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_dzdo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DZDO_USER
|
||||
become_exe:
|
||||
description: Dzdo executable.
|
||||
type: string
|
||||
default: dzdo
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: dzdo_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_dzdo_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DZDO_EXE
|
||||
become_flags:
|
||||
description: Options to pass to dzdo.
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: dzdo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_dzdo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DZDO_FLAGS
|
||||
become_pass:
|
||||
description: Options to pass to dzdo.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_dzdo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DZDO_PASS
|
||||
ini:
|
||||
- section: dzdo_become_plugin
|
||||
key: password
|
||||
'''
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
@@ -91,10 +92,10 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
if self.get_option('become_pass'):
|
||||
self.prompt = f'[dzdo via ansible, key={self._id}] password:'
|
||||
flags = f"{flags.replace('-n', '')} -p \"{self.prompt}\""
|
||||
self.prompt = '[dzdo via ansible, key=%s] password:' % self._id
|
||||
flags = '%s -p "%s"' % (flags.replace('-n', ''), self.prompt)
|
||||
|
||||
become_user = self.get_option('become_user')
|
||||
user = f'-u {become_user}' if become_user else ''
|
||||
user = '-u %s' % (become_user) if become_user else ''
|
||||
|
||||
return f"{becomecmd} {flags} {user} {self._build_success_command(cmd, shell)}"
|
||||
return ' '.join([becomecmd, flags, user, self._build_success_command(cmd, shell)])
|
||||
|
||||
@@ -2,89 +2,90 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: ksu
|
||||
short_description: Kerberos substitute user
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(ksu) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: ksu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_ksu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_KSU_USER
|
||||
required: true
|
||||
become_exe:
|
||||
description: C(ksu) executable.
|
||||
type: string
|
||||
default: ksu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: ksu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_ksu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_KSU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(ksu).
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: ksu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_ksu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_KSU_FLAGS
|
||||
become_pass:
|
||||
description: C(ksu) password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_ksu_pass
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_become_password
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_KSU_PASS
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
DOCUMENTATION = '''
|
||||
name: ksu
|
||||
short_description: Kerberos substitute user
|
||||
description:
|
||||
- List of localized strings to match for prompt detection.
|
||||
- If empty the plugin uses the built-in one.
|
||||
type: list
|
||||
elements: string
|
||||
default: []
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_ksu_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_KSU_PROMPT_L10N
|
||||
"""
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the ksu utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: ksu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_ksu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_KSU_USER
|
||||
required: true
|
||||
become_exe:
|
||||
description: Su executable.
|
||||
type: string
|
||||
default: ksu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: ksu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_ksu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_KSU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to ksu.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: ksu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_ksu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_KSU_FLAGS
|
||||
become_pass:
|
||||
description: Ksu password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_ksu_pass
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_become_password
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_KSU_PASS
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
description:
|
||||
- List of localized strings to match for prompt detection.
|
||||
- If empty we will use the built in one.
|
||||
type: list
|
||||
elements: string
|
||||
default: []
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_ksu_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_KSU_PROMPT_L10N
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
@@ -123,4 +124,4 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
user = self.get_option('become_user')
|
||||
return f'{exe} {user} {flags} -e {self._build_success_command(cmd, shell)} '
|
||||
return '%s %s %s -e %s ' % (exe, user, flags, self._build_success_command(cmd, shell))
|
||||
|
||||
@@ -2,94 +2,94 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: machinectl
|
||||
short_description: Systemd's machinectl privilege escalation
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(machinectl) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: machinectl_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_machinectl_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_MACHINECTL_USER
|
||||
become_exe:
|
||||
description: C(machinectl) executable.
|
||||
type: string
|
||||
default: machinectl
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: machinectl_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_machinectl_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_MACHINECTL_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(machinectl).
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: machinectl_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_machinectl_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_MACHINECTL_FLAGS
|
||||
become_pass:
|
||||
description: Password for C(machinectl).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_machinectl_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_MACHINECTL_PASS
|
||||
ini:
|
||||
- section: machinectl_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- When not using this plugin with user V(root), it only works correctly with a polkit rule which alters the behaviour
|
||||
of C(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 works if it
|
||||
is used in context with the root user, because then no further prompt is shown by C(machinectl).
|
||||
- This become plugin does not work when connection pipelining is enabled. With ansible-core 2.19+, using it automatically
|
||||
disables pipelining. On ansible-core 2.18 and before, pipelining must explicitly be disabled by the user.
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: machinectl
|
||||
short_description: Systemd's machinectl privilege escalation
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the machinectl utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: machinectl_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_machinectl_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_MACHINECTL_USER
|
||||
become_exe:
|
||||
description: Machinectl executable.
|
||||
type: string
|
||||
default: machinectl
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: machinectl_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_machinectl_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_MACHINECTL_EXE
|
||||
become_flags:
|
||||
description: Options to pass to machinectl.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: machinectl_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_machinectl_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_MACHINECTL_FLAGS
|
||||
become_pass:
|
||||
description: Password for machinectl.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_machinectl_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_MACHINECTL_PASS
|
||||
ini:
|
||||
- section: machinectl_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- When not using this plugin with user V(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"""
|
||||
EXAMPLES = r'''
|
||||
# A polkit rule needed to use the module with a non-root user.
|
||||
# See the Notes section for details.
|
||||
/etc/polkit-1/rules.d/60-machinectl-fast-user-auth.rules: |-
|
||||
/etc/polkit-1/rules.d/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
|
||||
|
||||
@@ -109,10 +109,6 @@ class BecomeModule(BecomeBase):
|
||||
success = ('==== AUTHENTICATION COMPLETE ====',)
|
||||
require_tty = True # see https://github.com/ansible-collections/community.general/issues/6932
|
||||
|
||||
# See https://github.com/ansible/ansible/issues/81254,
|
||||
# https://github.com/ansible/ansible/pull/78111
|
||||
pipelining = False
|
||||
|
||||
@staticmethod
|
||||
def remove_ansi_codes(line):
|
||||
return ansi_color_codes.sub(b"", line)
|
||||
@@ -127,7 +123,7 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
user = self.get_option('become_user')
|
||||
return f'{become} -q shell {flags} {user}@ {self._build_success_command(cmd, shell)}'
|
||||
return '%s -q shell %s %s@ %s' % (become, flags, user, self._build_success_command(cmd, shell))
|
||||
|
||||
def check_success(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
|
||||
@@ -2,86 +2,87 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: pbrun
|
||||
short_description: PowerBroker run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(pbrun) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pbrun_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pbrun_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PBRUN_USER
|
||||
become_exe:
|
||||
description: C(pbrun) executable.
|
||||
type: string
|
||||
default: pbrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pbrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pbrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PBRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(pbrun).
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pbrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pbrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PBRUN_FLAGS
|
||||
become_pass:
|
||||
description: Password for C(pbrun).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pbrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PBRUN_PASS
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command C(pbrun) calls in C(shell -c) or not.
|
||||
default: false
|
||||
type: bool
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pbrun_wrap_execution
|
||||
env:
|
||||
- name: ANSIBLE_PBRUN_WRAP_EXECUTION
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: pbrun
|
||||
short_description: PowerBroker run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pbrun utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pbrun_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pbrun_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PBRUN_USER
|
||||
become_exe:
|
||||
description: Sudo executable.
|
||||
type: string
|
||||
default: pbrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pbrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pbrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PBRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pbrun.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pbrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pbrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PBRUN_FLAGS
|
||||
become_pass:
|
||||
description: Password for pbrun.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pbrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PBRUN_PASS
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command pbrun calls in C(shell -c) or not.
|
||||
default: false
|
||||
type: bool
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pbrun_wrap_execution
|
||||
env:
|
||||
- name: ANSIBLE_PBRUN_WRAP_EXECUTION
|
||||
'''
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
@@ -102,7 +103,7 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
become_user = self.get_option('become_user')
|
||||
user = f'-u {become_user}' if become_user else ''
|
||||
user = '-u %s' % (become_user) if become_user else ''
|
||||
noexe = not self.get_option('wrap_exe')
|
||||
|
||||
return f"{become_exe} {flags} {user} {self._build_success_command(cmd, shell, noexe=noexe)}"
|
||||
return ' '.join([become_exe, flags, user, self._build_success_command(cmd, shell, noexe=noexe)])
|
||||
|
||||
@@ -2,91 +2,92 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: pfexec
|
||||
short_description: Profile based execution
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(pfexec) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
DOCUMENTATION = '''
|
||||
name: pfexec
|
||||
short_description: profile based execution
|
||||
description:
|
||||
- User you 'become' to execute the task.
|
||||
- This plugin ignores this setting as pfexec uses its own C(exec_attr) to figure this out, but it is supplied here for
|
||||
Ansible to make decisions needed for the task execution, like file permissions.
|
||||
type: string
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pfexec_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pfexec_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PFEXEC_USER
|
||||
become_exe:
|
||||
description: C(pfexec) executable.
|
||||
type: string
|
||||
default: pfexec
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pfexec_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pfexec_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PFEXEC_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(pfexec).
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pfexec_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pfexec_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PFEXEC_FLAGS
|
||||
become_pass:
|
||||
description: C(pfexec) password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pfexec_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PFEXEC_PASS
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command C(pfexec) calls in C(shell -c) or not.
|
||||
default: false
|
||||
type: bool
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pfexec_wrap_execution
|
||||
env:
|
||||
- name: ANSIBLE_PFEXEC_WRAP_EXECUTION
|
||||
notes:
|
||||
- This plugin ignores O(become_user) as pfexec uses its own C(exec_attr) to figure this out.
|
||||
"""
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pfexec utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_user:
|
||||
description:
|
||||
- User you 'become' to execute the task.
|
||||
- This plugin ignores this setting as pfexec uses it's own C(exec_attr) to figure this out,
|
||||
but it is supplied here for Ansible to make decisions needed for the task execution, like file permissions.
|
||||
type: string
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pfexec_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pfexec_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PFEXEC_USER
|
||||
become_exe:
|
||||
description: Sudo executable.
|
||||
type: string
|
||||
default: pfexec
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pfexec_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pfexec_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PFEXEC_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pfexec.
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pfexec_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pfexec_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PFEXEC_FLAGS
|
||||
become_pass:
|
||||
description: pfexec password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pfexec_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PFEXEC_PASS
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command pfexec calls in C(shell -c) or not.
|
||||
default: false
|
||||
type: bool
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pfexec_wrap_execution
|
||||
env:
|
||||
- name: ANSIBLE_PFEXEC_WRAP_EXECUTION
|
||||
notes:
|
||||
- This plugin ignores O(become_user) as pfexec uses its own C(exec_attr) to figure this out.
|
||||
'''
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
@@ -105,4 +106,4 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
noexe = not self.get_option('wrap_exe')
|
||||
return f'{exe} {flags} {self._build_success_command(cmd, shell, noexe=noexe)}'
|
||||
return '%s %s %s' % (exe, flags, self._build_success_command(cmd, shell, noexe=noexe))
|
||||
|
||||
@@ -2,62 +2,63 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: pmrun
|
||||
short_description: Privilege Manager run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(pmrun) utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_exe:
|
||||
description: C(pmrun) executable.
|
||||
type: string
|
||||
default: pmrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pmrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pmrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PMRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(pmrun).
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pmrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pmrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PMRUN_FLAGS
|
||||
become_pass:
|
||||
description: C(pmrun) password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pmrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PMRUN_PASS
|
||||
ini:
|
||||
- section: pmrun_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- This plugin ignores the C(become_user) supplied and uses C(pmrun)'s own configuration to select the user.
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: pmrun
|
||||
short_description: Privilege Manager run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pmrun utility.
|
||||
author: Ansible Core Team
|
||||
options:
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
type: string
|
||||
default: pmrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pmrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pmrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PMRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pmrun.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pmrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pmrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PMRUN_FLAGS
|
||||
become_pass:
|
||||
description: pmrun password.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pmrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PMRUN_PASS
|
||||
ini:
|
||||
- section: pmrun_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- This plugin ignores the become_user supplied and uses pmrun's own configuration to select the user.
|
||||
'''
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
@@ -77,4 +78,4 @@ class BecomeModule(BecomeBase):
|
||||
become = self.get_option('become_exe')
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
return f'{become} {flags} {shlex_quote(self._build_success_command(cmd, shell))}'
|
||||
return '%s %s %s' % (become, flags, shlex_quote(self._build_success_command(cmd, shell)))
|
||||
|
||||
@@ -3,71 +3,72 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: run0
|
||||
short_description: Systemd's run0
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(run0) utility.
|
||||
author:
|
||||
- Thomas Sjögren (@konstruktoid)
|
||||
version_added: '9.0.0'
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: run0_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_run0_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_RUN0_USER
|
||||
type: string
|
||||
become_exe:
|
||||
description: C(run0) executable.
|
||||
default: run0
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: run0_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_run0_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_RUN0_EXE
|
||||
type: string
|
||||
become_flags:
|
||||
description: Options to pass to C(run0).
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: run0_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_run0_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_RUN0_FLAGS
|
||||
type: string
|
||||
notes:
|
||||
- This plugin only works when a C(polkit) rule is in place.
|
||||
DOCUMENTATION = """
|
||||
name: run0
|
||||
short_description: Systemd's run0
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the C(run0) utility.
|
||||
author:
|
||||
- Thomas Sjögren (@konstruktoid)
|
||||
version_added: '9.0.0'
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: run0_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_run0_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_RUN0_USER
|
||||
type: string
|
||||
become_exe:
|
||||
description: The C(run0) executable.
|
||||
default: run0
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: run0_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_run0_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_RUN0_EXE
|
||||
type: string
|
||||
become_flags:
|
||||
description: Options to pass to run0.
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: run0_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_run0_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_RUN0_FLAGS
|
||||
type: string
|
||||
notes:
|
||||
- This plugin will only work when a polkit rule is in place.
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
# An example polkit rule that allows the user 'ansible' in the 'wheel' group
|
||||
# to execute commands using run0 without authentication.
|
||||
/etc/polkit-1/rules.d/60-run0-fast-user-auth.rules: |-
|
||||
/etc/polkit-1/rules.d/60-run0-fast-user-auth.rules: |
|
||||
polkit.addRule(function(action, subject) {
|
||||
if(action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||
subject.isInGroup("wheel") &&
|
||||
|
||||
@@ -2,75 +2,76 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: sesu
|
||||
short_description: CA Privileged Access Manager
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user using the C(sesu) utility.
|
||||
author: ansible (@nekonyuu)
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sesu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sesu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SESU_USER
|
||||
become_exe:
|
||||
description: C(sesu) executable.
|
||||
type: string
|
||||
default: sesu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: sesu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_sesu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_SESU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to C(sesu).
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sesu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sesu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SESU_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to C(sesu).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sesu_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SESU_PASS
|
||||
ini:
|
||||
- section: sesu_become_plugin
|
||||
key: password
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: sesu
|
||||
short_description: CA Privileged Access Manager
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the sesu utility.
|
||||
author: ansible (@nekonyuu)
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sesu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sesu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SESU_USER
|
||||
become_exe:
|
||||
description: sesu executable.
|
||||
type: string
|
||||
default: sesu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: sesu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_sesu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_SESU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to sesu.
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sesu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sesu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SESU_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to sesu.
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sesu_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SESU_PASS
|
||||
ini:
|
||||
- section: sesu_become_plugin
|
||||
key: password
|
||||
'''
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
@@ -92,4 +93,4 @@ class BecomeModule(BecomeBase):
|
||||
|
||||
flags = self.get_option('become_flags')
|
||||
user = self.get_option('become_user')
|
||||
return f'{become} {flags} {user} -c {self._build_success_command(cmd, shell)}'
|
||||
return '%s %s %s -c %s' % (become, flags, user, self._build_success_command(cmd, shell))
|
||||
|
||||
@@ -2,77 +2,77 @@
|
||||
# Copyright (c) 2021, Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: sudosu
|
||||
short_description: Run tasks using sudo su -
|
||||
description:
|
||||
- This become plugin allows your remote/login user to execute commands as another user using the C(sudo) and C(su) utilities
|
||||
combined.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
version_added: 2.4.0
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sudo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sudo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SUDO_USER
|
||||
become_flags:
|
||||
description: Options to pass to C(sudo).
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sudo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sudo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SUDO_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to C(sudo).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sudo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SUDO_PASS
|
||||
ini:
|
||||
- section: sudo_become_plugin
|
||||
key: password
|
||||
alt_method:
|
||||
DOCUMENTATION = """
|
||||
name: sudosu
|
||||
short_description: Run tasks using sudo su -
|
||||
description:
|
||||
- Whether to use an alternative method to call C(su). Instead of running C(su -l user /path/to/shell -c command), it
|
||||
runs C(su -l user -c command).
|
||||
- Use this when the default one is not working on your system.
|
||||
required: false
|
||||
type: boolean
|
||||
ini:
|
||||
- section: community.general.sudosu
|
||||
key: alternative_method
|
||||
vars:
|
||||
- name: ansible_sudosu_alt_method
|
||||
env:
|
||||
- name: ANSIBLE_SUDOSU_ALT_METHOD
|
||||
version_added: 9.2.0
|
||||
- This become plugin allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
version_added: 2.4.0
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task.
|
||||
type: string
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sudo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sudo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SUDO_USER
|
||||
become_flags:
|
||||
description: Options to pass to C(sudo).
|
||||
type: string
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sudo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sudo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SUDO_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to C(sudo).
|
||||
type: string
|
||||
required: false
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sudo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SUDO_PASS
|
||||
ini:
|
||||
- section: sudo_become_plugin
|
||||
key: password
|
||||
alt_method:
|
||||
description:
|
||||
- Whether to use an alternative method to call C(su). Instead of running C(su -l user /path/to/shell -c command),
|
||||
it runs C(su -l user -c command).
|
||||
- Use this when the default one is not working on your system.
|
||||
required: false
|
||||
type: boolean
|
||||
ini:
|
||||
- section: community.general.sudosu
|
||||
key: alternative_method
|
||||
vars:
|
||||
- name: ansible_sudosu_alt_method
|
||||
env:
|
||||
- name: ANSIBLE_SUDOSU_ALT_METHOD
|
||||
version_added: 9.2.0
|
||||
"""
|
||||
|
||||
|
||||
@@ -98,16 +98,16 @@ class BecomeModule(BecomeBase):
|
||||
flags = self.get_option('become_flags') or ''
|
||||
prompt = ''
|
||||
if self.get_option('become_pass'):
|
||||
self.prompt = f'[sudo via ansible, key={self._id}] password:'
|
||||
self.prompt = '[sudo via ansible, key=%s] password:' % self._id
|
||||
if flags: # this could be simplified, but kept as is for now for backwards string matching
|
||||
flags = flags.replace('-n', '')
|
||||
prompt = f'-p "{self.prompt}"'
|
||||
prompt = '-p "%s"' % (self.prompt)
|
||||
|
||||
user = self.get_option('become_user') or ''
|
||||
if user:
|
||||
user = f'{user}'
|
||||
user = '%s' % (user)
|
||||
|
||||
if self.get_option('alt_method'):
|
||||
return f"{becomecmd} {flags} {prompt} su -l {user} -c {self._build_success_command(cmd, shell, True)}"
|
||||
return ' '.join([becomecmd, flags, prompt, "su -l", user, "-c", self._build_success_command(cmd, shell, True)])
|
||||
else:
|
||||
return f"{becomecmd} {flags} {prompt} su -l {user} {self._build_success_command(cmd, shell)}"
|
||||
return ' '.join([becomecmd, flags, prompt, 'su -l', user, self._build_success_command(cmd, shell)])
|
||||
|
||||
81
plugins/cache/memcached.py
vendored
81
plugins/cache/memcached.py
vendored
@@ -4,48 +4,49 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: memcached
|
||||
short_description: Use memcached DB for cache
|
||||
description:
|
||||
- This cache uses JSON formatted, per host records saved in memcached.
|
||||
requirements:
|
||||
- memcache (python lib)
|
||||
options:
|
||||
_uri:
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: memcached
|
||||
short_description: Use memcached DB for cache
|
||||
description:
|
||||
- List of connection information for the memcached DBs.
|
||||
default: ['127.0.0.1:11211']
|
||||
type: list
|
||||
elements: string
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the DB entries.
|
||||
type: string
|
||||
default: ansible_facts
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
_timeout:
|
||||
default: 86400
|
||||
type: integer
|
||||
- This cache uses JSON formatted, per host records saved in memcached.
|
||||
requirements:
|
||||
- memcache (python lib)
|
||||
options:
|
||||
_uri:
|
||||
description:
|
||||
- List of connection information for the memcached DBs
|
||||
default: ['127.0.0.1:11211']
|
||||
type: list
|
||||
elements: string
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the DB entries
|
||||
type: string
|
||||
default: ansible_facts
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
_timeout:
|
||||
default: 86400
|
||||
type: integer
|
||||
# TODO: determine whether it is OK to change to: type: float
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
"""
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
'''
|
||||
|
||||
import collections
|
||||
import os
|
||||
@@ -190,7 +191,7 @@ class CacheModule(BaseCacheModule):
|
||||
self._keys = CacheModuleKeys(self._db, self._db.get(CacheModuleKeys.PREFIX) or [])
|
||||
|
||||
def _make_key(self, key):
|
||||
return f"{self._prefix}{key}"
|
||||
return "{0}{1}".format(self._prefix, key)
|
||||
|
||||
def _expire_keys(self):
|
||||
if self._timeout > 0:
|
||||
|
||||
72
plugins/cache/pickle.py
vendored
72
plugins/cache/pickle.py
vendored
@@ -5,43 +5,44 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: pickle
|
||||
short_description: Pickle formatted files
|
||||
description:
|
||||
- This cache uses Python's pickle serialization format, in per host files, saved to the filesystem.
|
||||
author: Brian Coca (@bcoca)
|
||||
options:
|
||||
_uri:
|
||||
required: true
|
||||
DOCUMENTATION = '''
|
||||
name: pickle
|
||||
short_description: Pickle formatted files.
|
||||
description:
|
||||
- Path in which the cache plugin saves the files.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
type: path
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the files.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
type: string
|
||||
_timeout:
|
||||
default: 86400
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
type: float
|
||||
"""
|
||||
- This cache uses Python's pickle serialization format, in per host files, saved to the filesystem.
|
||||
author: Brian Coca (@bcoca)
|
||||
options:
|
||||
_uri:
|
||||
required: true
|
||||
description:
|
||||
- Path in which the cache plugin will save the files
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
type: path
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the files
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
type: string
|
||||
_timeout:
|
||||
default: 86400
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
type: float
|
||||
'''
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
@@ -56,7 +57,6 @@ class CacheModule(BaseFileCacheModule):
|
||||
"""
|
||||
A caching module backed by pickle files.
|
||||
"""
|
||||
_persistent = False # prevent unnecessary JSON serialization and key munging
|
||||
|
||||
def _load(self, filepath):
|
||||
# Pickle is a binary format
|
||||
|
||||
128
plugins/cache/redis.py
vendored
128
plugins/cache/redis.py
vendored
@@ -3,75 +3,77 @@
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: redis
|
||||
short_description: Use Redis DB for cache
|
||||
description:
|
||||
- This cache uses JSON formatted, per host records saved in Redis.
|
||||
requirements:
|
||||
- redis>=2.4.5 (python lib)
|
||||
options:
|
||||
_uri:
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: redis
|
||||
short_description: Use Redis DB for cache
|
||||
description:
|
||||
- A colon separated string of connection information for Redis.
|
||||
- The format is V(host:port:db:password), for example V(localhost:6379:0:changeme).
|
||||
- To use encryption in transit, prefix the connection with V(tls://), as in V(tls://localhost:6379:0:changeme).
|
||||
- To use redis sentinel, use separator V(;), for example V(localhost:26379;localhost:26379;0:changeme). Requires redis>=2.9.0.
|
||||
type: string
|
||||
required: true
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the DB entries.
|
||||
type: string
|
||||
default: ansible_facts
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
_keyset_name:
|
||||
description: User defined name for cache keyset name.
|
||||
type: string
|
||||
default: ansible_cache_keys
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_REDIS_KEYSET_NAME
|
||||
ini:
|
||||
- key: fact_caching_redis_keyset_name
|
||||
section: defaults
|
||||
version_added: 1.3.0
|
||||
_sentinel_service_name:
|
||||
description: The redis sentinel service name (or referenced as cluster name).
|
||||
type: string
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_REDIS_SENTINEL
|
||||
ini:
|
||||
- key: fact_caching_redis_sentinel
|
||||
section: defaults
|
||||
version_added: 1.3.0
|
||||
_timeout:
|
||||
default: 86400
|
||||
type: integer
|
||||
- This cache uses JSON formatted, per host records saved in Redis.
|
||||
requirements:
|
||||
- redis>=2.4.5 (python lib)
|
||||
options:
|
||||
_uri:
|
||||
description:
|
||||
- A colon separated string of connection information for Redis.
|
||||
- The format is V(host:port:db:password), for example V(localhost:6379:0:changeme).
|
||||
- To use encryption in transit, prefix the connection with V(tls://), as in V(tls://localhost:6379:0:changeme).
|
||||
- To use redis sentinel, use separator V(;), for example V(localhost:26379;localhost:26379;0:changeme). Requires redis>=2.9.0.
|
||||
type: string
|
||||
required: true
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the DB entries
|
||||
type: string
|
||||
default: ansible_facts
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
_keyset_name:
|
||||
description: User defined name for cache keyset name.
|
||||
type: string
|
||||
default: ansible_cache_keys
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_REDIS_KEYSET_NAME
|
||||
ini:
|
||||
- key: fact_caching_redis_keyset_name
|
||||
section: defaults
|
||||
version_added: 1.3.0
|
||||
_sentinel_service_name:
|
||||
description: The redis sentinel service name (or referenced as cluster name).
|
||||
type: string
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_REDIS_SENTINEL
|
||||
ini:
|
||||
- key: fact_caching_redis_sentinel
|
||||
section: defaults
|
||||
version_added: 1.3.0
|
||||
_timeout:
|
||||
default: 86400
|
||||
type: integer
|
||||
# TODO: determine whether it is OK to change to: type: float
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
"""
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
'''
|
||||
|
||||
import re
|
||||
import time
|
||||
import json
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder
|
||||
from ansible.plugins.cache import BaseCacheModule
|
||||
from ansible.utils.display import Display
|
||||
@@ -129,7 +131,7 @@ class CacheModule(BaseCacheModule):
|
||||
connection = self._parse_connection(self.re_url_conn, uri)
|
||||
self._db = StrictRedis(*connection, **kw)
|
||||
|
||||
display.vv(f'Redis connection: {self._db}')
|
||||
display.vv('Redis connection: %s' % self._db)
|
||||
|
||||
@staticmethod
|
||||
def _parse_connection(re_patt, uri):
|
||||
@@ -162,12 +164,12 @@ class CacheModule(BaseCacheModule):
|
||||
pass # password is optional
|
||||
|
||||
sentinels = [self._parse_connection(self.re_sent_conn, shost) for shost in connections]
|
||||
display.vv(f'\nUsing redis sentinels: {sentinels}')
|
||||
display.vv('\nUsing redis sentinels: %s' % sentinels)
|
||||
scon = Sentinel(sentinels, **kw)
|
||||
try:
|
||||
return scon.master_for(self._sentinel_service_name, socket_timeout=0.2)
|
||||
except Exception as exc:
|
||||
raise AnsibleError(f'Could not connect to redis sentinel: {exc}')
|
||||
raise AnsibleError('Could not connect to redis sentinel: %s' % to_native(exc))
|
||||
|
||||
def _make_key(self, key):
|
||||
return self._prefix + key
|
||||
|
||||
71
plugins/cache/yaml.py
vendored
71
plugins/cache/yaml.py
vendored
@@ -5,44 +5,45 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: yaml
|
||||
short_description: YAML formatted files
|
||||
description:
|
||||
- This cache uses YAML formatted, per host, files saved to the filesystem.
|
||||
author: Brian Coca (@bcoca)
|
||||
options:
|
||||
_uri:
|
||||
required: true
|
||||
DOCUMENTATION = '''
|
||||
name: yaml
|
||||
short_description: YAML formatted files.
|
||||
description:
|
||||
- Path in which the cache plugin saves the files.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
type: string
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the files.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
type: string
|
||||
_timeout:
|
||||
default: 86400
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
type: integer
|
||||
- This cache uses YAML formatted, per host, files saved to the filesystem.
|
||||
author: Brian Coca (@bcoca)
|
||||
options:
|
||||
_uri:
|
||||
required: true
|
||||
description:
|
||||
- Path in which the cache plugin will save the files
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
|
||||
ini:
|
||||
- key: fact_caching_connection
|
||||
section: defaults
|
||||
type: string
|
||||
_prefix:
|
||||
description: User defined prefix to use when creating the files
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
|
||||
ini:
|
||||
- key: fact_caching_prefix
|
||||
section: defaults
|
||||
type: string
|
||||
_timeout:
|
||||
default: 86400
|
||||
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
|
||||
env:
|
||||
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
|
||||
ini:
|
||||
- key: fact_caching_timeout
|
||||
section: defaults
|
||||
type: integer
|
||||
# TODO: determine whether it is OK to change to: type: float
|
||||
"""
|
||||
'''
|
||||
|
||||
|
||||
import codecs
|
||||
|
||||
@@ -4,43 +4,43 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: cgroup_memory_recap
|
||||
type: aggregate
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- cgroups
|
||||
short_description: Profiles maximum memory usage of tasks and full execution using cgroups
|
||||
description:
|
||||
- This is an Ansible callback plugin that profiles maximum memory usage of Ansible and individual tasks, and displays a
|
||||
recap at the end using cgroups.
|
||||
notes:
|
||||
- Requires ansible to be run from within a C(cgroup), such as with C(cgexec -g memory:ansible_profile ansible-playbook ...).
|
||||
- This C(cgroup) should only be used by Ansible to get accurate results.
|
||||
- To create the C(cgroup), first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile).
|
||||
options:
|
||||
max_mem_file:
|
||||
required: true
|
||||
description: Path to cgroups C(memory.max_usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes).
|
||||
type: str
|
||||
env:
|
||||
- name: CGROUP_MAX_MEM_FILE
|
||||
ini:
|
||||
- section: callback_cgroupmemrecap
|
||||
key: max_mem_file
|
||||
cur_mem_file:
|
||||
required: true
|
||||
description: Path to C(memory.usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes).
|
||||
type: str
|
||||
env:
|
||||
- name: CGROUP_CUR_MEM_FILE
|
||||
ini:
|
||||
- section: callback_cgroupmemrecap
|
||||
key: cur_mem_file
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: cgroup_memory_recap
|
||||
type: aggregate
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- cgroups
|
||||
short_description: Profiles maximum memory usage of tasks and full execution using cgroups
|
||||
description:
|
||||
- This is an ansible callback plugin that profiles maximum memory usage of ansible and individual tasks, and displays a recap at the end using cgroups.
|
||||
notes:
|
||||
- Requires ansible to be run from within a cgroup, such as with C(cgexec -g memory:ansible_profile ansible-playbook ...).
|
||||
- This cgroup should only be used by ansible to get accurate results.
|
||||
- To create the cgroup, first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile).
|
||||
options:
|
||||
max_mem_file:
|
||||
required: true
|
||||
description: Path to cgroups C(memory.max_usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes).
|
||||
type: str
|
||||
env:
|
||||
- name: CGROUP_MAX_MEM_FILE
|
||||
ini:
|
||||
- section: callback_cgroupmemrecap
|
||||
key: max_mem_file
|
||||
cur_mem_file:
|
||||
required: true
|
||||
description: Path to C(memory.usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes).
|
||||
type: str
|
||||
env:
|
||||
- name: CGROUP_CUR_MEM_FILE
|
||||
ini:
|
||||
- section: callback_cgroupmemrecap
|
||||
key: cur_mem_file
|
||||
'''
|
||||
|
||||
import time
|
||||
import threading
|
||||
@@ -114,7 +114,7 @@ class CallbackModule(CallbackBase):
|
||||
max_results = int(f.read().strip()) / 1024 / 1024
|
||||
|
||||
self._display.banner('CGROUP MEMORY RECAP')
|
||||
self._display.display(f'Execution Maximum: {max_results:0.2f}MB\n\n')
|
||||
self._display.display('Execution Maximum: %0.2fMB\n\n' % max_results)
|
||||
|
||||
for task, memory in self.task_results:
|
||||
self._display.display(f'{task.get_name()} ({task._uuid}): {memory:0.2f}MB')
|
||||
self._display.display('%s (%s): %0.2fMB' % (task.get_name(), task._uuid, memory))
|
||||
|
||||
@@ -4,19 +4,20 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: context_demo
|
||||
type: aggregate
|
||||
short_description: Demo callback that adds play/task context
|
||||
description:
|
||||
- Displays some play and task context along with normal output.
|
||||
- This is mostly for demo purposes.
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: context_demo
|
||||
type: aggregate
|
||||
short_description: demo callback that adds play/task context
|
||||
description:
|
||||
- Displays some play and task context along with normal output.
|
||||
- This is mostly for demo purposes.
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
'''
|
||||
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
@@ -37,15 +38,15 @@ class CallbackModule(CallbackBase):
|
||||
self.play = None
|
||||
|
||||
def v2_on_any(self, *args, **kwargs):
|
||||
self._display.display(f"--- play: {getattr(self.play, 'name', None)} task: {self.task} ---")
|
||||
self._display.display("--- play: {0} task: {1} ---".format(getattr(self.play, 'name', None), self.task))
|
||||
|
||||
self._display.display(" --- ARGS ")
|
||||
for i, a in enumerate(args):
|
||||
self._display.display(f' {i}: {a}')
|
||||
self._display.display(' %s: %s' % (i, a))
|
||||
|
||||
self._display.display(" --- KWARGS ")
|
||||
for k in kwargs:
|
||||
self._display.display(f' {k}: {kwargs[k]}')
|
||||
self._display.display(' %s: %s' % (k, kwargs[k]))
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
self.play = play
|
||||
|
||||
@@ -6,22 +6,23 @@
|
||||
Counter enabled Ansible callback plugin (See DOCUMENTATION for more information)
|
||||
'''
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: counter_enabled
|
||||
type: stdout
|
||||
short_description: Adds counters to the output items (tasks and hosts/task)
|
||||
description:
|
||||
- Use this callback when you need a kind of progress bar on a large environments.
|
||||
- You can see how many tasks has the playbook to run, and which one is actually running.
|
||||
- You can see how many hosts may run a task, and which of them is actually running.
|
||||
extends_documentation_fragment:
|
||||
- default_callback
|
||||
requirements:
|
||||
- set as stdout callback in C(ansible.cfg) (C(stdout_callback = counter_enabled))
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: counter_enabled
|
||||
type: stdout
|
||||
short_description: adds counters to the output items (tasks and hosts/task)
|
||||
description:
|
||||
- Use this callback when you need a kind of progress bar on a large environments.
|
||||
- You will know how many tasks has the playbook to run, and which one is actually running.
|
||||
- You will know how many hosts may run a task, and which of them is actually running.
|
||||
extends_documentation_fragment:
|
||||
- default_callback
|
||||
requirements:
|
||||
- set as stdout callback in C(ansible.cfg) (C(stdout_callback = counter_enabled))
|
||||
'''
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
@@ -70,7 +71,7 @@ class CallbackModule(CallbackBase):
|
||||
if not name:
|
||||
msg = u"play"
|
||||
else:
|
||||
msg = f"PLAY [{name}]"
|
||||
msg = u"PLAY [%s]" % name
|
||||
|
||||
self._play = play
|
||||
|
||||
@@ -90,17 +91,25 @@ class CallbackModule(CallbackBase):
|
||||
for host in hosts:
|
||||
stat = stats.summarize(host)
|
||||
|
||||
self._display.display(
|
||||
f"{hostcolor(host, stat)} : {colorize('ok', stat['ok'], C.COLOR_OK)} {colorize('changed', stat['changed'], C.COLOR_CHANGED)} "
|
||||
f"{colorize('unreachable', stat['unreachable'], C.COLOR_UNREACHABLE)} {colorize('failed', stat['failures'], C.COLOR_ERROR)} "
|
||||
f"{colorize('rescued', stat['rescued'], C.COLOR_OK)} {colorize('ignored', stat['ignored'], C.COLOR_WARN)}",
|
||||
self._display.display(u"%s : %s %s %s %s %s %s" % (
|
||||
hostcolor(host, stat),
|
||||
colorize(u'ok', stat['ok'], C.COLOR_OK),
|
||||
colorize(u'changed', stat['changed'], C.COLOR_CHANGED),
|
||||
colorize(u'unreachable', stat['unreachable'], C.COLOR_UNREACHABLE),
|
||||
colorize(u'failed', stat['failures'], C.COLOR_ERROR),
|
||||
colorize(u'rescued', stat['rescued'], C.COLOR_OK),
|
||||
colorize(u'ignored', stat['ignored'], C.COLOR_WARN)),
|
||||
screen_only=True
|
||||
)
|
||||
|
||||
self._display.display(
|
||||
f"{hostcolor(host, stat, False)} : {colorize('ok', stat['ok'], None)} {colorize('changed', stat['changed'], None)} "
|
||||
f"{colorize('unreachable', stat['unreachable'], None)} {colorize('failed', stat['failures'], None)} "
|
||||
f"{colorize('rescued', stat['rescued'], None)} {colorize('ignored', stat['ignored'], None)}",
|
||||
self._display.display(u"%s : %s %s %s %s %s %s" % (
|
||||
hostcolor(host, stat, False),
|
||||
colorize(u'ok', stat['ok'], None),
|
||||
colorize(u'changed', stat['changed'], None),
|
||||
colorize(u'unreachable', stat['unreachable'], None),
|
||||
colorize(u'failed', stat['failures'], None),
|
||||
colorize(u'rescued', stat['rescued'], None),
|
||||
colorize(u'ignored', stat['ignored'], None)),
|
||||
log_only=True
|
||||
)
|
||||
|
||||
@@ -115,14 +124,12 @@ class CallbackModule(CallbackBase):
|
||||
for k in sorted(stats.custom.keys()):
|
||||
if k == '_run':
|
||||
continue
|
||||
_custom_stats = self._dump_results(stats.custom[k], indent=1).replace('\n', '')
|
||||
self._display.display(f'\t{k}: {_custom_stats}')
|
||||
self._display.display('\t%s: %s' % (k, self._dump_results(stats.custom[k], indent=1).replace('\n', '')))
|
||||
|
||||
# print per run custom stats
|
||||
if '_run' in stats.custom:
|
||||
self._display.display("", screen_only=True)
|
||||
_custom_stats_run = self._dump_results(stats.custom['_run'], indent=1).replace('\n', '')
|
||||
self._display.display(f'\tRUN: {_custom_stats_run}')
|
||||
self._display.display('\tRUN: %s' % self._dump_results(stats.custom['_run'], indent=1).replace('\n', ''))
|
||||
self._display.display("", screen_only=True)
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
@@ -136,13 +143,13 @@ class CallbackModule(CallbackBase):
|
||||
# that they can secure this if they feel that their stdout is insecure
|
||||
# (shoulder surfing, logging stdout straight to a file, etc).
|
||||
if not task.no_log and C.DISPLAY_ARGS_TO_STDOUT:
|
||||
args = ', '.join(('{k}={v}' for k, v in task.args.items()))
|
||||
args = f' {args}'
|
||||
self._display.banner(f"TASK {self._task_counter}/{self._task_total} [{task.get_name().strip()}{args}]")
|
||||
args = ', '.join(('%s=%s' % a for a in task.args.items()))
|
||||
args = ' %s' % args
|
||||
self._display.banner("TASK %d/%d [%s%s]" % (self._task_counter, self._task_total, task.get_name().strip(), args))
|
||||
if self._display.verbosity >= 2:
|
||||
path = task.get_path()
|
||||
if path:
|
||||
self._display.display(f"task path: {path}", color=C.COLOR_DEBUG)
|
||||
self._display.display("task path: %s" % path, color=C.COLOR_DEBUG)
|
||||
self._host_counter = self._previous_batch_total
|
||||
self._task_counter += 1
|
||||
|
||||
@@ -159,15 +166,15 @@ class CallbackModule(CallbackBase):
|
||||
return
|
||||
elif result._result.get('changed', False):
|
||||
if delegated_vars:
|
||||
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
|
||||
msg = "changed: %d/%d [%s -> %s]" % (self._host_counter, self._host_total, result._host.get_name(), delegated_vars['ansible_host'])
|
||||
else:
|
||||
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
|
||||
msg = "changed: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
|
||||
color = C.COLOR_CHANGED
|
||||
else:
|
||||
if delegated_vars:
|
||||
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
|
||||
msg = "ok: %d/%d [%s -> %s]" % (self._host_counter, self._host_total, result._host.get_name(), delegated_vars['ansible_host'])
|
||||
else:
|
||||
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
|
||||
msg = "ok: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
|
||||
color = C.COLOR_OK
|
||||
|
||||
self._handle_warnings(result._result)
|
||||
@@ -178,7 +185,7 @@ class CallbackModule(CallbackBase):
|
||||
self._clean_results(result._result, result._task.action)
|
||||
|
||||
if self._run_is_verbose(result):
|
||||
msg += f" => {self._dump_results(result._result)}"
|
||||
msg += " => %s" % (self._dump_results(result._result),)
|
||||
self._display.display(msg, color=color)
|
||||
|
||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||
@@ -199,16 +206,14 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
else:
|
||||
if delegated_vars:
|
||||
self._display.display(
|
||||
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
|
||||
f"{delegated_vars['ansible_host']}]: FAILED! => {self._dump_results(result._result)}",
|
||||
color=C.COLOR_ERROR
|
||||
)
|
||||
self._display.display("fatal: %d/%d [%s -> %s]: FAILED! => %s" % (self._host_counter, self._host_total,
|
||||
result._host.get_name(), delegated_vars['ansible_host'],
|
||||
self._dump_results(result._result)),
|
||||
color=C.COLOR_ERROR)
|
||||
else:
|
||||
self._display.display(
|
||||
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: FAILED! => {self._dump_results(result._result)}",
|
||||
color=C.COLOR_ERROR
|
||||
)
|
||||
self._display.display("fatal: %d/%d [%s]: FAILED! => %s" % (self._host_counter, self._host_total,
|
||||
result._host.get_name(), self._dump_results(result._result)),
|
||||
color=C.COLOR_ERROR)
|
||||
|
||||
if ignore_errors:
|
||||
self._display.display("...ignoring", color=C.COLOR_SKIP)
|
||||
@@ -226,9 +231,9 @@ class CallbackModule(CallbackBase):
|
||||
if result._task.loop and 'results' in result._result:
|
||||
self._process_items(result)
|
||||
else:
|
||||
msg = f"skipping: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
|
||||
msg = "skipping: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
|
||||
if self._run_is_verbose(result):
|
||||
msg += f" => {self._dump_results(result._result)}"
|
||||
msg += " => %s" % self._dump_results(result._result)
|
||||
self._display.display(msg, color=C.COLOR_SKIP)
|
||||
|
||||
def v2_runner_on_unreachable(self, result):
|
||||
@@ -239,13 +244,11 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
delegated_vars = result._result.get('_ansible_delegated_vars', None)
|
||||
if delegated_vars:
|
||||
self._display.display(
|
||||
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
|
||||
f"{delegated_vars['ansible_host']}]: UNREACHABLE! => {self._dump_results(result._result)}",
|
||||
color=C.COLOR_UNREACHABLE
|
||||
)
|
||||
self._display.display("fatal: %d/%d [%s -> %s]: UNREACHABLE! => %s" % (self._host_counter, self._host_total,
|
||||
result._host.get_name(), delegated_vars['ansible_host'],
|
||||
self._dump_results(result._result)),
|
||||
color=C.COLOR_UNREACHABLE)
|
||||
else:
|
||||
self._display.display(
|
||||
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: UNREACHABLE! => {self._dump_results(result._result)}",
|
||||
color=C.COLOR_UNREACHABLE
|
||||
)
|
||||
self._display.display("fatal: %d/%d [%s]: UNREACHABLE! => %s" % (self._host_counter, self._host_total,
|
||||
result._host.get_name(), self._dump_results(result._result)),
|
||||
color=C.COLOR_UNREACHABLE)
|
||||
|
||||
@@ -4,33 +4,35 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: default_without_diff
|
||||
type: stdout
|
||||
short_description: The default ansible callback without diff output
|
||||
version_added: 8.4.0
|
||||
description:
|
||||
- This is basically the default ansible callback plugin (P(ansible.builtin.default#callback)) without showing diff output.
|
||||
This can be useful when using another callback which sends more detailed information to another service, like the L(ARA,
|
||||
https://ara.recordsansible.org/) callback, and you want diff output sent to that plugin but not shown on the console output.
|
||||
author: Felix Fontein (@felixfontein)
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.default_callback
|
||||
- ansible.builtin.result_format_callback
|
||||
"""
|
||||
DOCUMENTATION = r'''
|
||||
name: default_without_diff
|
||||
type: stdout
|
||||
short_description: The default ansible callback without diff output
|
||||
version_added: 8.4.0
|
||||
description:
|
||||
- This is basically the default ansible callback plugin (P(ansible.builtin.default#callback)) without
|
||||
showing diff output. This can be useful when using another callback which sends more detailed information
|
||||
to another service, like the L(ARA, https://ara.recordsansible.org/) callback, and you want diff output
|
||||
sent to that plugin but not shown on the console output.
|
||||
author: Felix Fontein (@felixfontein)
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.default_callback
|
||||
- ansible.builtin.result_format_callback
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
EXAMPLES = r'''
|
||||
# Enable callback in ansible.cfg:
|
||||
ansible_config: |
|
||||
[defaults]
|
||||
stdout_callback = community.general.default_without_diff
|
||||
|
||||
# Enable callback with environment variables:
|
||||
environment_variable: |-
|
||||
environment_variable: |
|
||||
ANSIBLE_STDOUT_CALLBACK=community.general.default_without_diff
|
||||
"""
|
||||
'''
|
||||
|
||||
from ansible.plugins.callback.default import CallbackModule as Default
|
||||
|
||||
|
||||
@@ -4,21 +4,22 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
DOCUMENTATION = '''
|
||||
name: dense
|
||||
type: stdout
|
||||
short_description: Minimal stdout output
|
||||
short_description: minimal stdout output
|
||||
extends_documentation_fragment:
|
||||
- default_callback
|
||||
- default_callback
|
||||
description:
|
||||
- When in verbose mode it acts the same as the default callback.
|
||||
- When in verbose mode it will act the same as the default callback.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
- Dag Wieers (@dagwieers)
|
||||
requirements:
|
||||
- set as stdout in configuration
|
||||
"""
|
||||
- set as stdout in configuration
|
||||
'''
|
||||
|
||||
HAS_OD = False
|
||||
try:
|
||||
@@ -194,7 +195,7 @@ class CallbackModule(CallbackModule_default):
|
||||
self.disabled = True
|
||||
|
||||
def __del__(self):
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
|
||||
def _add_host(self, result, status):
|
||||
name = result._host.get_name()
|
||||
@@ -242,7 +243,7 @@ class CallbackModule(CallbackModule_default):
|
||||
|
||||
def _handle_exceptions(self, result):
|
||||
if 'exception' in result:
|
||||
# Remove the exception from the result so it is not shown every time
|
||||
# Remove the exception from the result so it's not shown every time
|
||||
del result['exception']
|
||||
|
||||
if self._display.verbosity == 1:
|
||||
@@ -251,7 +252,7 @@ class CallbackModule(CallbackModule_default):
|
||||
def _display_progress(self, result=None):
|
||||
# Always rewrite the complete line
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.nolinewrap + vt100.underline)
|
||||
sys.stdout.write(f'{self.type} {self.count[self.type]}:')
|
||||
sys.stdout.write('%s %d:' % (self.type, self.count[self.type]))
|
||||
sys.stdout.write(vt100.reset)
|
||||
sys.stdout.flush()
|
||||
|
||||
@@ -259,18 +260,22 @@ class CallbackModule(CallbackModule_default):
|
||||
for name in self.hosts:
|
||||
sys.stdout.write(' ')
|
||||
if self.hosts[name].get('delegate', None):
|
||||
sys.stdout.write(f"{self.hosts[name]['delegate']}>")
|
||||
sys.stdout.write(self.hosts[name]['delegate'] + '>')
|
||||
sys.stdout.write(colors[self.hosts[name]['state']] + name + vt100.reset)
|
||||
sys.stdout.flush()
|
||||
|
||||
# if result._result.get('diff', False):
|
||||
# sys.stdout.write('\n' + vt100.linewrap)
|
||||
sys.stdout.write(vt100.linewrap)
|
||||
|
||||
# self.keep = True
|
||||
|
||||
def _display_task_banner(self):
|
||||
if not self.shown_title:
|
||||
self.shown_title = True
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline)
|
||||
sys.stdout.write(f'{self.type} {self.count[self.type]}: {self.task.get_name().strip()}')
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write('%s %d: %s' % (self.type, self.count[self.type], self.task.get_name().strip()))
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
|
||||
@@ -279,7 +284,7 @@ class CallbackModule(CallbackModule_default):
|
||||
def _display_results(self, result, status):
|
||||
# Leave the previous task on screen (as it has changes/errors)
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
|
||||
self.keep = False
|
||||
@@ -304,16 +309,16 @@ class CallbackModule(CallbackModule_default):
|
||||
if result._task.loop and 'results' in result._result:
|
||||
self._process_items(result)
|
||||
else:
|
||||
sys.stdout.write(f"{colors[status] + status}: ")
|
||||
sys.stdout.write(colors[status] + status + ': ')
|
||||
|
||||
delegated_vars = result._result.get('_ansible_delegated_vars', None)
|
||||
if delegated_vars:
|
||||
sys.stdout.write(f"{vt100.reset}{result._host.get_name()}>{colors[status]}{delegated_vars['ansible_host']}")
|
||||
sys.stdout.write(vt100.reset + result._host.get_name() + '>' + colors[status] + delegated_vars['ansible_host'])
|
||||
else:
|
||||
sys.stdout.write(result._host.get_name())
|
||||
|
||||
sys.stdout.write(f": {dump}\n")
|
||||
sys.stdout.write(f"{vt100.reset}{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(': ' + dump + '\n')
|
||||
sys.stdout.write(vt100.reset + vt100.save + vt100.clearline)
|
||||
sys.stdout.flush()
|
||||
|
||||
if status == 'changed':
|
||||
@@ -322,7 +327,7 @@ class CallbackModule(CallbackModule_default):
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
# Leave the previous task on screen (as it has changes/errors)
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.bold}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.bold)
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.bold)
|
||||
|
||||
@@ -336,14 +341,14 @@ class CallbackModule(CallbackModule_default):
|
||||
name = play.get_name().strip()
|
||||
if not name:
|
||||
name = 'unnamed'
|
||||
sys.stdout.write(f"PLAY {self.count['play']}: {name.upper()}")
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write('PLAY %d: %s' % (self.count['play'], name.upper()))
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
sys.stdout.flush()
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
# Leave the previous task on screen (as it has changes/errors)
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.underline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline)
|
||||
else:
|
||||
# Do not clear line, since we want to retain the previous output
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.underline)
|
||||
@@ -360,14 +365,14 @@ class CallbackModule(CallbackModule_default):
|
||||
self.count['task'] += 1
|
||||
|
||||
# Write the next task on screen (behind the prompt is the previous output)
|
||||
sys.stdout.write(f'{self.type} {self.count[self.type]}.')
|
||||
sys.stdout.write('%s %d.' % (self.type, self.count[self.type]))
|
||||
sys.stdout.write(vt100.reset)
|
||||
sys.stdout.flush()
|
||||
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
# Leave the previous task on screen (as it has changes/errors)
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.underline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline)
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline)
|
||||
|
||||
@@ -383,7 +388,7 @@ class CallbackModule(CallbackModule_default):
|
||||
self.count[self.type] += 1
|
||||
|
||||
# Write the next task on screen (behind the prompt is the previous output)
|
||||
sys.stdout.write(f'{self.type} {self.count[self.type]}.')
|
||||
sys.stdout.write('%s %d.' % (self.type, self.count[self.type]))
|
||||
sys.stdout.write(vt100.reset)
|
||||
sys.stdout.flush()
|
||||
|
||||
@@ -446,13 +451,13 @@ class CallbackModule(CallbackModule_default):
|
||||
|
||||
def v2_playbook_on_no_hosts_remaining(self):
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
|
||||
self.keep = False
|
||||
|
||||
sys.stdout.write(f"{vt100.white + vt100.redbg}NO MORE HOSTS LEFT")
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.white + vt100.redbg + 'NO MORE HOSTS LEFT')
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
sys.stdout.flush()
|
||||
|
||||
def v2_playbook_on_include(self, included_file):
|
||||
@@ -460,7 +465,7 @@ class CallbackModule(CallbackModule_default):
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
if self._display.verbosity == 0 and self.keep:
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
else:
|
||||
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
|
||||
|
||||
@@ -471,16 +476,22 @@ class CallbackModule(CallbackModule_default):
|
||||
sys.stdout.write(vt100.bold + vt100.underline)
|
||||
sys.stdout.write('SUMMARY')
|
||||
|
||||
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
|
||||
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
|
||||
sys.stdout.flush()
|
||||
|
||||
hosts = sorted(stats.processed.keys())
|
||||
for h in hosts:
|
||||
t = stats.summarize(h)
|
||||
self._display.display(
|
||||
f"{hostcolor(h, t)} : {colorize('ok', t['ok'], C.COLOR_OK)} {colorize('changed', t['changed'], C.COLOR_CHANGED)} "
|
||||
f"{colorize('unreachable', t['unreachable'], C.COLOR_UNREACHABLE)} {colorize('failed', t['failures'], C.COLOR_ERROR)} "
|
||||
f"{colorize('rescued', t['rescued'], C.COLOR_OK)} {colorize('ignored', t['ignored'], C.COLOR_WARN)}",
|
||||
u"%s : %s %s %s %s %s %s" % (
|
||||
hostcolor(h, t),
|
||||
colorize(u'ok', t['ok'], C.COLOR_OK),
|
||||
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
|
||||
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
|
||||
colorize(u'failed', t['failures'], C.COLOR_ERROR),
|
||||
colorize(u'rescued', t['rescued'], C.COLOR_OK),
|
||||
colorize(u'ignored', t['ignored'], C.COLOR_WARN),
|
||||
),
|
||||
screen_only=True
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,71 +2,72 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
|
||||
name: elastic
|
||||
type: notification
|
||||
short_description: Create distributed traces for each Ansible task in Elastic APM
|
||||
version_added: 3.8.0
|
||||
description:
|
||||
- This callback creates distributed traces for each Ansible task in Elastic APM.
|
||||
- You can configure the plugin with environment variables.
|
||||
- See U(https://www.elastic.co/guide/en/apm/agent/python/current/configuration.html).
|
||||
options:
|
||||
hide_task_arguments:
|
||||
default: false
|
||||
type: bool
|
||||
DOCUMENTATION = '''
|
||||
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
|
||||
name: elastic
|
||||
type: notification
|
||||
short_description: Create distributed traces for each Ansible task in Elastic APM
|
||||
version_added: 3.8.0
|
||||
description:
|
||||
- Hide the arguments for a task.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
|
||||
apm_service_name:
|
||||
default: ansible
|
||||
type: str
|
||||
description:
|
||||
- The service name resource attribute.
|
||||
env:
|
||||
- name: ELASTIC_APM_SERVICE_NAME
|
||||
apm_server_url:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM server and its environment variables.
|
||||
env:
|
||||
- name: ELASTIC_APM_SERVER_URL
|
||||
apm_secret_token:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM server token.
|
||||
env:
|
||||
- name: ELASTIC_APM_SECRET_TOKEN
|
||||
apm_api_key:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM API key.
|
||||
env:
|
||||
- name: ELASTIC_APM_API_KEY
|
||||
apm_verify_server_cert:
|
||||
default: true
|
||||
type: bool
|
||||
description:
|
||||
- Verifies the SSL certificate if an HTTPS connection.
|
||||
env:
|
||||
- name: ELASTIC_APM_VERIFY_SERVER_CERT
|
||||
traceparent:
|
||||
type: str
|
||||
description:
|
||||
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
|
||||
env:
|
||||
- name: TRACEPARENT
|
||||
requirements:
|
||||
- elastic-apm (Python library)
|
||||
"""
|
||||
- This callback creates distributed traces for each Ansible task in Elastic APM.
|
||||
- You can configure the plugin with environment variables.
|
||||
- See U(https://www.elastic.co/guide/en/apm/agent/python/current/configuration.html).
|
||||
options:
|
||||
hide_task_arguments:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- Hide the arguments for a task.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
|
||||
apm_service_name:
|
||||
default: ansible
|
||||
type: str
|
||||
description:
|
||||
- The service name resource attribute.
|
||||
env:
|
||||
- name: ELASTIC_APM_SERVICE_NAME
|
||||
apm_server_url:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM server and its environment variables.
|
||||
env:
|
||||
- name: ELASTIC_APM_SERVER_URL
|
||||
apm_secret_token:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM server token
|
||||
env:
|
||||
- name: ELASTIC_APM_SECRET_TOKEN
|
||||
apm_api_key:
|
||||
type: str
|
||||
description:
|
||||
- Use the APM API key
|
||||
env:
|
||||
- name: ELASTIC_APM_API_KEY
|
||||
apm_verify_server_cert:
|
||||
default: true
|
||||
type: bool
|
||||
description:
|
||||
- Verifies the SSL certificate if an HTTPS connection.
|
||||
env:
|
||||
- name: ELASTIC_APM_VERIFY_SERVER_CERT
|
||||
traceparent:
|
||||
type: str
|
||||
description:
|
||||
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
|
||||
env:
|
||||
- name: TRACEPARENT
|
||||
requirements:
|
||||
- elastic-apm (Python library)
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: |-
|
||||
EXAMPLES = '''
|
||||
examples: |
|
||||
Enable the plugin in ansible.cfg:
|
||||
[defaults]
|
||||
callbacks_enabled = community.general.elastic
|
||||
@@ -75,7 +76,7 @@ examples: |-
|
||||
export ELASTIC_APM_SERVER_URL=<your APM server URL)>
|
||||
export ELASTIC_APM_SERVICE_NAME=your_service_name
|
||||
export ELASTIC_APM_API_KEY=your_APM_API_KEY
|
||||
"""
|
||||
'''
|
||||
|
||||
import getpass
|
||||
import socket
|
||||
@@ -87,7 +88,6 @@ from contextlib import closing
|
||||
from os.path import basename
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleRuntimeError
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
from ansible.module_utils.six import raise_from
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
@@ -118,7 +118,7 @@ class TaskData:
|
||||
if host.uuid in self.host_data:
|
||||
if host.status == 'included':
|
||||
# concatenate task include output from multiple items
|
||||
host.result = f'{self.host_data[host.uuid].result}\n{host.result}'
|
||||
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
|
||||
else:
|
||||
return
|
||||
|
||||
@@ -141,6 +141,7 @@ class HostData:
|
||||
class ElasticSource(object):
|
||||
def __init__(self, display):
|
||||
self.ansible_playbook = ""
|
||||
self.ansible_version = None
|
||||
self.session = str(uuid.uuid4())
|
||||
self.host = socket.gethostname()
|
||||
try:
|
||||
@@ -165,7 +166,7 @@ class ElasticSource(object):
|
||||
args = None
|
||||
|
||||
if not task.no_log and not hide_task_arguments:
|
||||
args = ', '.join((f'{k}={v}' for k, v in task.args.items()))
|
||||
args = ', '.join(('%s=%s' % a for a in task.args.items()))
|
||||
|
||||
tasks_data[uuid] = TaskData(uuid, name, path, play_name, action, args)
|
||||
|
||||
@@ -183,6 +184,9 @@ class ElasticSource(object):
|
||||
|
||||
task = tasks_data[task_uuid]
|
||||
|
||||
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))
|
||||
|
||||
def generate_distributed_traces(self, tasks_data, status, end_time, traceparent, apm_service_name,
|
||||
@@ -206,7 +210,8 @@ class ElasticSource(object):
|
||||
else:
|
||||
apm_cli.begin_transaction("Session", start=parent_start_time)
|
||||
# Populate trace metadata attributes
|
||||
label(ansible_version=ansible_version)
|
||||
if self.ansible_version is not None:
|
||||
label(ansible_version=self.ansible_version)
|
||||
label(ansible_session=self.session, ansible_host_name=self.host, ansible_host_user=self.user)
|
||||
if self.ip_address is not None:
|
||||
label(ansible_host_ip=self.ip_address)
|
||||
@@ -220,7 +225,7 @@ class ElasticSource(object):
|
||||
def create_span_data(self, apm_cli, task_data, host_data):
|
||||
""" create the span with the given TaskData and HostData """
|
||||
|
||||
name = f'[{host_data.name}] {task_data.play}: {task_data.name}'
|
||||
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
|
||||
|
||||
message = "success"
|
||||
status = "success"
|
||||
@@ -254,7 +259,7 @@ class ElasticSource(object):
|
||||
"ansible.task.host.status": host_data.status}) as span:
|
||||
span.outcome = status
|
||||
if 'failure' in status:
|
||||
exception = AnsibleRuntimeError(message=f"{task_data.action}: {name} failed with error message {enriched_error_message}")
|
||||
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, enriched_error_message))
|
||||
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
|
||||
|
||||
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
|
||||
@@ -283,7 +288,7 @@ class ElasticSource(object):
|
||||
message = result.get('msg', 'failed')
|
||||
exception = result.get('exception')
|
||||
stderr = result.get('stderr')
|
||||
return f"message: \"{message}\"\nexception: \"{exception}\"\nstderr: \"{stderr}\""
|
||||
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
|
||||
240
plugins/callback/hipchat.py
Normal file
240
plugins/callback/hipchat.py
Normal file
@@ -0,0 +1,240 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2014, Matt Martz <matt@sivel.net>
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: hipchat
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelist in configuration.
|
||||
- prettytable (python lib)
|
||||
short_description: post task events to hipchat
|
||||
description:
|
||||
- This callback plugin sends status updates to a HipChat channel during playbook execution.
|
||||
- Before 2.4 only environment variables were available for configuring this plugin.
|
||||
deprecated:
|
||||
removed_in: 10.0.0
|
||||
why: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
|
||||
alternative: There is none.
|
||||
options:
|
||||
token:
|
||||
description: HipChat API token for v1 or v2 API.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: HIPCHAT_TOKEN
|
||||
ini:
|
||||
- section: callback_hipchat
|
||||
key: token
|
||||
api_version:
|
||||
description: HipChat API version, v1 or v2.
|
||||
type: str
|
||||
choices:
|
||||
- v1
|
||||
- v2
|
||||
required: false
|
||||
default: v1
|
||||
env:
|
||||
- name: HIPCHAT_API_VERSION
|
||||
ini:
|
||||
- section: callback_hipchat
|
||||
key: api_version
|
||||
room:
|
||||
description: HipChat room to post in.
|
||||
type: str
|
||||
default: ansible
|
||||
env:
|
||||
- name: HIPCHAT_ROOM
|
||||
ini:
|
||||
- section: callback_hipchat
|
||||
key: room
|
||||
from:
|
||||
description: Name to post as
|
||||
type: str
|
||||
default: ansible
|
||||
env:
|
||||
- name: HIPCHAT_FROM
|
||||
ini:
|
||||
- section: callback_hipchat
|
||||
key: from
|
||||
notify:
|
||||
description: Add notify flag to important messages
|
||||
type: bool
|
||||
default: true
|
||||
env:
|
||||
- name: HIPCHAT_NOTIFY
|
||||
ini:
|
||||
- section: callback_hipchat
|
||||
key: notify
|
||||
|
||||
'''
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
try:
|
||||
import prettytable
|
||||
HAS_PRETTYTABLE = True
|
||||
except ImportError:
|
||||
HAS_PRETTYTABLE = False
|
||||
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.urls import open_url
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
"""This is an example ansible callback plugin that sends status
|
||||
updates to a HipChat channel during playbook execution.
|
||||
"""
|
||||
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'notification'
|
||||
CALLBACK_NAME = 'community.general.hipchat'
|
||||
CALLBACK_NEEDS_WHITELIST = True
|
||||
|
||||
API_V1_URL = 'https://api.hipchat.com/v1/rooms/message'
|
||||
API_V2_URL = 'https://api.hipchat.com/v2/'
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super(CallbackModule, self).__init__()
|
||||
|
||||
if not HAS_PRETTYTABLE:
|
||||
self.disabled = True
|
||||
self._display.warning('The `prettytable` python module is not installed. '
|
||||
'Disabling the HipChat callback plugin.')
|
||||
self.printed_playbook = False
|
||||
self.playbook_name = None
|
||||
self.play = None
|
||||
|
||||
def set_options(self, task_keys=None, var_options=None, direct=None):
|
||||
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||
|
||||
self.token = self.get_option('token')
|
||||
self.api_version = self.get_option('api_version')
|
||||
self.from_name = self.get_option('from')
|
||||
self.allow_notify = self.get_option('notify')
|
||||
self.room = self.get_option('room')
|
||||
|
||||
if self.token is None:
|
||||
self.disabled = True
|
||||
self._display.warning('HipChat token could not be loaded. The HipChat '
|
||||
'token can be provided using the `HIPCHAT_TOKEN` '
|
||||
'environment variable.')
|
||||
|
||||
# Pick the request handler.
|
||||
if self.api_version == 'v2':
|
||||
self.send_msg = self.send_msg_v2
|
||||
else:
|
||||
self.send_msg = self.send_msg_v1
|
||||
|
||||
def send_msg_v2(self, msg, msg_format='text', color='yellow', notify=False):
|
||||
"""Method for sending a message to HipChat"""
|
||||
|
||||
headers = {'Authorization': 'Bearer %s' % self.token, 'Content-Type': 'application/json'}
|
||||
|
||||
body = {}
|
||||
body['room_id'] = self.room
|
||||
body['from'] = self.from_name[:15] # max length is 15
|
||||
body['message'] = msg
|
||||
body['message_format'] = msg_format
|
||||
body['color'] = color
|
||||
body['notify'] = self.allow_notify and notify
|
||||
|
||||
data = json.dumps(body)
|
||||
url = self.API_V2_URL + "room/{room_id}/notification".format(room_id=self.room)
|
||||
try:
|
||||
response = open_url(url, data=data, headers=headers, method='POST')
|
||||
return response.read()
|
||||
except Exception as ex:
|
||||
self._display.warning('Could not submit message to hipchat: {0}'.format(ex))
|
||||
|
||||
def send_msg_v1(self, msg, msg_format='text', color='yellow', notify=False):
|
||||
"""Method for sending a message to HipChat"""
|
||||
|
||||
params = {}
|
||||
params['room_id'] = self.room
|
||||
params['from'] = self.from_name[:15] # max length is 15
|
||||
params['message'] = msg
|
||||
params['message_format'] = msg_format
|
||||
params['color'] = color
|
||||
params['notify'] = int(self.allow_notify and notify)
|
||||
|
||||
url = ('%s?auth_token=%s' % (self.API_V1_URL, self.token))
|
||||
try:
|
||||
response = open_url(url, data=urlencode(params))
|
||||
return response.read()
|
||||
except Exception as ex:
|
||||
self._display.warning('Could not submit message to hipchat: {0}'.format(ex))
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
"""Display Playbook and play start messages"""
|
||||
|
||||
self.play = play
|
||||
name = play.name
|
||||
# This block sends information about a playbook when it starts
|
||||
# The playbook object is not immediately available at
|
||||
# playbook_on_start so we grab it via the play
|
||||
#
|
||||
# Displays info about playbook being started by a person on an
|
||||
# inventory, as well as Tags, Skip Tags and Limits
|
||||
if not self.printed_playbook:
|
||||
self.playbook_name, dummy = os.path.splitext(os.path.basename(self.play.playbook.filename))
|
||||
host_list = self.play.playbook.inventory.host_list
|
||||
inventory = os.path.basename(os.path.realpath(host_list))
|
||||
self.send_msg("%s: Playbook initiated by %s against %s" %
|
||||
(self.playbook_name,
|
||||
self.play.playbook.remote_user,
|
||||
inventory), notify=True)
|
||||
self.printed_playbook = True
|
||||
subset = self.play.playbook.inventory._subset
|
||||
skip_tags = self.play.playbook.skip_tags
|
||||
self.send_msg("%s:\nTags: %s\nSkip Tags: %s\nLimit: %s" %
|
||||
(self.playbook_name,
|
||||
', '.join(self.play.playbook.only_tags),
|
||||
', '.join(skip_tags) if skip_tags else None,
|
||||
', '.join(subset) if subset else subset))
|
||||
|
||||
# This is where we actually say we are starting a play
|
||||
self.send_msg("%s: Starting play: %s" %
|
||||
(self.playbook_name, name))
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
"""Display info about playbook statistics"""
|
||||
hosts = sorted(stats.processed.keys())
|
||||
|
||||
t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',
|
||||
'Failures'])
|
||||
|
||||
failures = False
|
||||
unreachable = False
|
||||
|
||||
for h in hosts:
|
||||
s = stats.summarize(h)
|
||||
|
||||
if s['failures'] > 0:
|
||||
failures = True
|
||||
if s['unreachable'] > 0:
|
||||
unreachable = True
|
||||
|
||||
t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',
|
||||
'failures']])
|
||||
|
||||
self.send_msg("%s: Playbook complete" % self.playbook_name,
|
||||
notify=True)
|
||||
|
||||
if failures or unreachable:
|
||||
color = 'red'
|
||||
self.send_msg("%s: Failures detected" % self.playbook_name,
|
||||
color=color, notify=True)
|
||||
else:
|
||||
color = 'green'
|
||||
|
||||
self.send_msg("/code %s:\n%s" % (self.playbook_name, t), color=color)
|
||||
@@ -4,44 +4,45 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: jabber
|
||||
type: notification
|
||||
short_description: Post task events to a Jabber server
|
||||
description:
|
||||
- The chatty part of ChatOps with a Hipchat server as a target.
|
||||
- This callback plugin sends status updates to a HipChat channel during playbook execution.
|
||||
requirements:
|
||||
- xmpp (Python library U(https://github.com/ArchipelProject/xmpppy))
|
||||
options:
|
||||
server:
|
||||
description: Connection info to Jabber server.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_SERV
|
||||
user:
|
||||
description: Jabber user to authenticate as.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_USER
|
||||
password:
|
||||
description: Password for the user to the Jabber server.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_PASS
|
||||
to:
|
||||
description: Chat identifier that receives the message.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_TO
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: jabber
|
||||
type: notification
|
||||
short_description: post task events to a jabber server
|
||||
description:
|
||||
- The chatty part of ChatOps with a Hipchat server as a target.
|
||||
- This callback plugin sends status updates to a HipChat channel during playbook execution.
|
||||
requirements:
|
||||
- xmpp (Python library U(https://github.com/ArchipelProject/xmpppy))
|
||||
options:
|
||||
server:
|
||||
description: connection info to jabber server
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_SERV
|
||||
user:
|
||||
description: Jabber user to authenticate as
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_USER
|
||||
password:
|
||||
description: Password for the user to the jabber server
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_PASS
|
||||
to:
|
||||
description: chat identifier that will receive the message
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: JABBER_TO
|
||||
'''
|
||||
|
||||
import os
|
||||
|
||||
@@ -101,7 +102,7 @@ class CallbackModule(CallbackBase):
|
||||
"""Display Playbook and play start messages"""
|
||||
self.play = play
|
||||
name = play.name
|
||||
self.send_msg(f"Ansible starting play: {name}")
|
||||
self.send_msg("Ansible starting play: %s" % (name))
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
name = self.play
|
||||
@@ -117,7 +118,7 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
if failures or unreachable:
|
||||
out = self.debug
|
||||
self.send_msg(f"{name}: Failures detected \n{self.task} \nHost: {h}\n Failed at:\n{out}")
|
||||
self.send_msg("%s: Failures detected \n%s \nHost: %s\n Failed at:\n%s" % (name, self.task, h, out))
|
||||
else:
|
||||
out = self.debug
|
||||
self.send_msg(f"Great! \n Playbook {name} completed:\n{s} \n Last task debug:\n {out}")
|
||||
self.send_msg("Great! \n Playbook %s completed:\n%s \n Last task debug:\n %s" % (name, s, out))
|
||||
|
||||
@@ -4,29 +4,30 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: log_plays
|
||||
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.
|
||||
requirements:
|
||||
- Whitelist in configuration
|
||||
- A writeable C(/var/log/ansible/hosts) directory by the user executing Ansible on the controller
|
||||
options:
|
||||
log_folder:
|
||||
default: /var/log/ansible/hosts
|
||||
description: The folder where log files are created.
|
||||
type: str
|
||||
env:
|
||||
- name: ANSIBLE_LOG_FOLDER
|
||||
ini:
|
||||
- section: callback_log_plays
|
||||
key: log_folder
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: log_plays
|
||||
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.
|
||||
requirements:
|
||||
- Whitelist in configuration
|
||||
- A writeable C(/var/log/ansible/hosts) directory by the user executing Ansible on the controller
|
||||
options:
|
||||
log_folder:
|
||||
default: /var/log/ansible/hosts
|
||||
description: The folder where log files will be created.
|
||||
type: str
|
||||
env:
|
||||
- name: ANSIBLE_LOG_FOLDER
|
||||
ini:
|
||||
- section: callback_log_plays
|
||||
key: log_folder
|
||||
'''
|
||||
|
||||
import os
|
||||
import time
|
||||
@@ -56,10 +57,7 @@ class CallbackModule(CallbackBase):
|
||||
CALLBACK_NEEDS_WHITELIST = True
|
||||
|
||||
TIME_FORMAT = "%b %d %Y %H:%M:%S"
|
||||
|
||||
@staticmethod
|
||||
def _make_msg(now, playbook, task_name, task_action, category, data):
|
||||
return f"{now} - {playbook} - {task_name} - {task_action} - {category} - {data}\n\n"
|
||||
MSG_FORMAT = "%(now)s - %(playbook)s - %(task_name)s - %(task_action)s - %(category)s - %(data)s\n\n"
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -84,12 +82,22 @@ class CallbackModule(CallbackBase):
|
||||
invocation = data.pop('invocation', None)
|
||||
data = json.dumps(data, cls=AnsibleJSONEncoder)
|
||||
if invocation is not None:
|
||||
data = f"{json.dumps(invocation)} => {data} "
|
||||
data = json.dumps(invocation) + " => %s " % data
|
||||
|
||||
path = os.path.join(self.log_folder, result._host.get_name())
|
||||
now = time.strftime(self.TIME_FORMAT, time.localtime())
|
||||
|
||||
msg = to_bytes(self._make_msg(now, self.playbook, result._task.name, result._task.action, category, data))
|
||||
msg = to_bytes(
|
||||
self.MSG_FORMAT
|
||||
% dict(
|
||||
now=now,
|
||||
playbook=self.playbook,
|
||||
task_name=result._task.name,
|
||||
task_action=result._task.action,
|
||||
category=category,
|
||||
data=data,
|
||||
)
|
||||
)
|
||||
with open(path, "ab") as fd:
|
||||
fd.write(msg)
|
||||
|
||||
|
||||
@@ -3,43 +3,44 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: loganalytics
|
||||
type: notification
|
||||
short_description: Posts task results to Azure Log Analytics
|
||||
author: "Cyrus Li (@zhcli) <cyrus1006@gmail.com>"
|
||||
description:
|
||||
- This callback plugin posts task results in JSON formatted to an Azure Log Analytics workspace.
|
||||
- Credits to authors of splunk callback plugin.
|
||||
version_added: "2.4.0"
|
||||
requirements:
|
||||
- Whitelisting this callback plugin.
|
||||
- An Azure log analytics work space has been established.
|
||||
options:
|
||||
workspace_id:
|
||||
description: Workspace ID of the Azure log analytics workspace.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: WORKSPACE_ID
|
||||
ini:
|
||||
- section: callback_loganalytics
|
||||
key: workspace_id
|
||||
shared_key:
|
||||
description: Shared key to connect to Azure log analytics workspace.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: WORKSPACE_SHARED_KEY
|
||||
ini:
|
||||
- section: callback_loganalytics
|
||||
key: shared_key
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: loganalytics
|
||||
type: notification
|
||||
short_description: Posts task results to Azure Log Analytics
|
||||
author: "Cyrus Li (@zhcli) <cyrus1006@gmail.com>"
|
||||
description:
|
||||
- This callback plugin will post task results in JSON formatted to an Azure Log Analytics workspace.
|
||||
- Credits to authors of splunk callback plugin.
|
||||
version_added: "2.4.0"
|
||||
requirements:
|
||||
- Whitelisting this callback plugin.
|
||||
- An Azure log analytics work space has been established.
|
||||
options:
|
||||
workspace_id:
|
||||
description: Workspace ID of the Azure log analytics workspace.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: WORKSPACE_ID
|
||||
ini:
|
||||
- section: callback_loganalytics
|
||||
key: workspace_id
|
||||
shared_key:
|
||||
description: Shared key to connect to Azure log analytics workspace.
|
||||
type: str
|
||||
required: true
|
||||
env:
|
||||
- name: WORKSPACE_SHARED_KEY
|
||||
ini:
|
||||
- section: callback_loganalytics
|
||||
key: shared_key
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: |-
|
||||
EXAMPLES = '''
|
||||
examples: |
|
||||
Whitelist the plugin in ansible.cfg:
|
||||
[defaults]
|
||||
callback_whitelist = community.general.loganalytics
|
||||
@@ -50,7 +51,7 @@ examples: |-
|
||||
[callback_loganalytics]
|
||||
workspace_id = 01234567-0123-0123-0123-01234567890a
|
||||
shared_key = dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==
|
||||
"""
|
||||
'''
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
@@ -62,7 +63,6 @@ import getpass
|
||||
|
||||
from os.path import basename
|
||||
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
@@ -76,6 +76,7 @@ class AzureLogAnalyticsSource(object):
|
||||
def __init__(self):
|
||||
self.ansible_check_mode = False
|
||||
self.ansible_playbook = ""
|
||||
self.ansible_version = ""
|
||||
self.session = str(uuid.uuid4())
|
||||
self.host = socket.gethostname()
|
||||
self.user = getpass.getuser()
|
||||
@@ -83,17 +84,18 @@ class AzureLogAnalyticsSource(object):
|
||||
|
||||
def __build_signature(self, date, workspace_id, shared_key, content_length):
|
||||
# Build authorisation signature for Azure log analytics API call
|
||||
sigs = f"POST\n{content_length}\napplication/json\nx-ms-date:{date}\n/api/logs"
|
||||
sigs = "POST\n{0}\napplication/json\nx-ms-date:{1}\n/api/logs".format(
|
||||
str(content_length), date)
|
||||
utf8_sigs = sigs.encode('utf-8')
|
||||
decoded_shared_key = base64.b64decode(shared_key)
|
||||
hmac_sha256_sigs = hmac.new(
|
||||
decoded_shared_key, utf8_sigs, digestmod=hashlib.sha256).digest()
|
||||
encoded_hash = base64.b64encode(hmac_sha256_sigs).decode('utf-8')
|
||||
signature = f"SharedKey {workspace_id}:{encoded_hash}"
|
||||
signature = "SharedKey {0}:{1}".format(workspace_id, encoded_hash)
|
||||
return signature
|
||||
|
||||
def __build_workspace_url(self, workspace_id):
|
||||
return f"https://{workspace_id}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01"
|
||||
return "https://{0}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01".format(workspace_id)
|
||||
|
||||
def __rfc1123date(self):
|
||||
return now().strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
@@ -102,6 +104,10 @@ class AzureLogAnalyticsSource(object):
|
||||
if result._task_fields['args'].get('_ansible_check_mode') is True:
|
||||
self.ansible_check_mode = True
|
||||
|
||||
if result._task_fields['args'].get('_ansible_version'):
|
||||
self.ansible_version = \
|
||||
result._task_fields['args'].get('_ansible_version')
|
||||
|
||||
if result._task._role:
|
||||
ansible_role = str(result._task._role)
|
||||
else:
|
||||
@@ -115,7 +121,7 @@ class AzureLogAnalyticsSource(object):
|
||||
data['host'] = self.host
|
||||
data['user'] = self.user
|
||||
data['runtime'] = runtime
|
||||
data['ansible_version'] = ansible_version
|
||||
data['ansible_version'] = self.ansible_version
|
||||
data['ansible_check_mode'] = self.ansible_check_mode
|
||||
data['ansible_host'] = result._host.name
|
||||
data['ansible_playbook'] = self.ansible_playbook
|
||||
|
||||
@@ -3,58 +3,59 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: logdna
|
||||
type: notification
|
||||
short_description: Sends playbook logs to LogDNA
|
||||
description:
|
||||
- This callback reports logs from playbook actions, tasks, and events to LogDNA (U(https://app.logdna.com)).
|
||||
requirements:
|
||||
- LogDNA Python Library (U(https://github.com/logdna/python))
|
||||
- whitelisting in configuration
|
||||
options:
|
||||
conf_key:
|
||||
required: true
|
||||
description: LogDNA Ingestion Key.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_INGESTION_KEY
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_key
|
||||
plugin_ignore_errors:
|
||||
required: false
|
||||
description: Whether to ignore errors on failing or not.
|
||||
type: boolean
|
||||
env:
|
||||
- name: ANSIBLE_IGNORE_ERRORS
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: plugin_ignore_errors
|
||||
default: false
|
||||
conf_hostname:
|
||||
required: false
|
||||
description: Alternative Host Name; the current host name by default.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_HOSTNAME
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_hostname
|
||||
conf_tags:
|
||||
required: false
|
||||
description: Tags.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_TAGS
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_tags
|
||||
default: ansible
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: logdna
|
||||
type: notification
|
||||
short_description: Sends playbook logs to LogDNA
|
||||
description:
|
||||
- This callback will report logs from playbook actions, tasks, and events to LogDNA (U(https://app.logdna.com)).
|
||||
requirements:
|
||||
- LogDNA Python Library (U(https://github.com/logdna/python))
|
||||
- whitelisting in configuration
|
||||
options:
|
||||
conf_key:
|
||||
required: true
|
||||
description: LogDNA Ingestion Key.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_INGESTION_KEY
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_key
|
||||
plugin_ignore_errors:
|
||||
required: false
|
||||
description: Whether to ignore errors on failing or not.
|
||||
type: boolean
|
||||
env:
|
||||
- name: ANSIBLE_IGNORE_ERRORS
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: plugin_ignore_errors
|
||||
default: false
|
||||
conf_hostname:
|
||||
required: false
|
||||
description: Alternative Host Name; the current host name by default.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_HOSTNAME
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_hostname
|
||||
conf_tags:
|
||||
required: false
|
||||
description: Tags.
|
||||
type: string
|
||||
env:
|
||||
- name: LOGDNA_TAGS
|
||||
ini:
|
||||
- section: callback_logdna
|
||||
key: conf_tags
|
||||
default: ansible
|
||||
'''
|
||||
|
||||
import logging
|
||||
import json
|
||||
@@ -72,7 +73,7 @@ except ImportError:
|
||||
|
||||
# Getting MAC Address of system:
|
||||
def get_mac():
|
||||
mac = f"{getnode():012x}"
|
||||
mac = "%012x" % getnode()
|
||||
return ":".join(map(lambda index: mac[index:index + 2], range(int(len(mac) / 2))))
|
||||
|
||||
|
||||
@@ -160,7 +161,7 @@ class CallbackModule(CallbackBase):
|
||||
if ninvalidKeys > 0:
|
||||
for key in invalidKeys:
|
||||
del meta[key]
|
||||
meta['__errors'] = f"These keys have been sanitized: {', '.join(invalidKeys)}"
|
||||
meta['__errors'] = 'These keys have been sanitized: ' + ', '.join(invalidKeys)
|
||||
return meta
|
||||
|
||||
def sanitizeJSON(self, data):
|
||||
|
||||
@@ -3,79 +3,82 @@
|
||||
# Copyright (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: logentries
|
||||
type: notification
|
||||
short_description: Sends events to Logentries
|
||||
description:
|
||||
- This callback plugin generates JSON objects and send them to Logentries using TCP for auditing/debugging purposes.
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- certifi (Python library)
|
||||
- flatdict (Python library), if you want to use the O(flatten) option
|
||||
options:
|
||||
api:
|
||||
description: URI to the Logentries API.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGENTRIES_API
|
||||
default: data.logentries.com
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: api
|
||||
port:
|
||||
description: HTTP port to use when connecting to the API.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGENTRIES_PORT
|
||||
default: 80
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: port
|
||||
tls_port:
|
||||
description: Port to use when connecting to the API when TLS is enabled.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGENTRIES_TLS_PORT
|
||||
default: 443
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: tls_port
|
||||
token:
|
||||
description: The logentries C(TCP token).
|
||||
type: str
|
||||
env:
|
||||
- name: LOGENTRIES_ANSIBLE_TOKEN
|
||||
required: true
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: token
|
||||
use_tls:
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: logentries
|
||||
type: notification
|
||||
short_description: Sends events to Logentries
|
||||
description:
|
||||
- Toggle to decide whether to use TLS to encrypt the communications with the API server.
|
||||
env:
|
||||
- name: LOGENTRIES_USE_TLS
|
||||
default: false
|
||||
type: boolean
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: use_tls
|
||||
flatten:
|
||||
description: Flatten complex data structures into a single dictionary with complex keys.
|
||||
type: boolean
|
||||
default: false
|
||||
env:
|
||||
- name: LOGENTRIES_FLATTEN
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: flatten
|
||||
"""
|
||||
- This callback plugin will generate JSON objects and send them to Logentries via TCP for auditing/debugging purposes.
|
||||
- Before 2.4, if you wanted to use an ini configuration, the file must be placed in the same directory as this plugin and named C(logentries.ini).
|
||||
- In 2.4 and above you can just put it in the main Ansible configuration file.
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- certifi (Python library)
|
||||
- flatdict (Python library), if you want to use the O(flatten) option
|
||||
options:
|
||||
api:
|
||||
description: URI to the Logentries API.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGENTRIES_API
|
||||
default: data.logentries.com
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: api
|
||||
port:
|
||||
description: HTTP port to use when connecting to the API.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGENTRIES_PORT
|
||||
default: 80
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: port
|
||||
tls_port:
|
||||
description: Port to use when connecting to the API when TLS is enabled.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGENTRIES_TLS_PORT
|
||||
default: 443
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: tls_port
|
||||
token:
|
||||
description: The logentries C(TCP token).
|
||||
type: str
|
||||
env:
|
||||
- name: LOGENTRIES_ANSIBLE_TOKEN
|
||||
required: true
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: token
|
||||
use_tls:
|
||||
description:
|
||||
- Toggle to decide whether to use TLS to encrypt the communications with the API server.
|
||||
env:
|
||||
- name: LOGENTRIES_USE_TLS
|
||||
default: false
|
||||
type: boolean
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: use_tls
|
||||
flatten:
|
||||
description: Flatten complex data structures into a single dictionary with complex keys.
|
||||
type: boolean
|
||||
default: false
|
||||
env:
|
||||
- name: LOGENTRIES_FLATTEN
|
||||
ini:
|
||||
- section: callback_logentries
|
||||
key: flatten
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: >-
|
||||
EXAMPLES = '''
|
||||
examples: >
|
||||
To enable, add this to your ansible.cfg file in the defaults block
|
||||
|
||||
[defaults]
|
||||
@@ -94,7 +97,7 @@ examples: >-
|
||||
use_tls = true
|
||||
token = dd21fc88-f00a-43ff-b977-e3a4233c53af
|
||||
flatten = false
|
||||
"""
|
||||
'''
|
||||
|
||||
import os
|
||||
import socket
|
||||
@@ -132,7 +135,7 @@ class PlainTextSocketAppender(object):
|
||||
# Error message displayed when an incorrect Token has been detected
|
||||
self.INVALID_TOKEN = "\n\nIt appears the LOGENTRIES_TOKEN parameter you entered is incorrect!\n\n"
|
||||
# Unicode Line separator character \u2028
|
||||
self.LINE_SEP = '\u2028'
|
||||
self.LINE_SEP = u'\u2028'
|
||||
|
||||
self._display = display
|
||||
self._conn = None
|
||||
@@ -150,7 +153,7 @@ class PlainTextSocketAppender(object):
|
||||
self.open_connection()
|
||||
return
|
||||
except Exception as e:
|
||||
self._display.vvvv(f"Unable to connect to Logentries: {e}")
|
||||
self._display.vvvv(u"Unable to connect to Logentries: %s" % to_text(e))
|
||||
|
||||
root_delay *= 2
|
||||
if root_delay > self.MAX_DELAY:
|
||||
@@ -159,7 +162,7 @@ class PlainTextSocketAppender(object):
|
||||
wait_for = root_delay + random.uniform(0, root_delay)
|
||||
|
||||
try:
|
||||
self._display.vvvv(f"sleeping {wait_for} before retry")
|
||||
self._display.vvvv("sleeping %s before retry" % wait_for)
|
||||
time.sleep(wait_for)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
@@ -172,8 +175,8 @@ class PlainTextSocketAppender(object):
|
||||
# Replace newlines with Unicode line separator
|
||||
# for multi-line events
|
||||
data = to_text(data, errors='surrogate_or_strict')
|
||||
multiline = data.replace('\n', self.LINE_SEP)
|
||||
multiline += "\n"
|
||||
multiline = data.replace(u'\n', self.LINE_SEP)
|
||||
multiline += u"\n"
|
||||
# Send data, reconnect if needed
|
||||
while True:
|
||||
try:
|
||||
@@ -246,7 +249,7 @@ class CallbackModule(CallbackBase):
|
||||
self.use_tls = self.get_option('use_tls')
|
||||
self.flatten = self.get_option('flatten')
|
||||
except KeyError as e:
|
||||
self._display.warning(f"Missing option for Logentries callback plugin: {e}")
|
||||
self._display.warning(u"Missing option for Logentries callback plugin: %s" % to_text(e))
|
||||
self.disabled = True
|
||||
|
||||
try:
|
||||
@@ -265,10 +268,10 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
if not self.disabled:
|
||||
if self.use_tls:
|
||||
self._display.vvvv(f"Connecting to {self.api_url}:{self.api_tls_port} with TLS")
|
||||
self._display.vvvv("Connecting to %s:%s with TLS" % (self.api_url, self.api_tls_port))
|
||||
self._appender = TLSSocketAppender(display=self._display, LE_API=self.api_url, LE_TLS_PORT=self.api_tls_port)
|
||||
else:
|
||||
self._display.vvvv(f"Connecting to {self.api_url}:{self.api_port}")
|
||||
self._display.vvvv("Connecting to %s:%s" % (self.api_url, self.api_port))
|
||||
self._appender = PlainTextSocketAppender(display=self._display, LE_API=self.api_url, LE_PORT=self.api_port)
|
||||
self._appender.reopen_connection()
|
||||
|
||||
@@ -281,7 +284,7 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
def emit(self, record):
|
||||
msg = record.rstrip('\n')
|
||||
msg = f"{self.token} {msg}"
|
||||
msg = "{0} {1}".format(self.token, msg)
|
||||
self._appender.put(msg)
|
||||
self._display.vvvv("Sent event to logentries")
|
||||
|
||||
|
||||
@@ -4,96 +4,98 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Yevhen Khmelenko (@ujenmr)
|
||||
name: logstash
|
||||
type: notification
|
||||
short_description: Sends events to Logstash
|
||||
description:
|
||||
- This callback reports facts and task events to Logstash U(https://www.elastic.co/products/logstash).
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- logstash (Python library)
|
||||
options:
|
||||
server:
|
||||
description: Address of the Logstash server.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGSTASH_SERVER
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: server
|
||||
version_added: 1.0.0
|
||||
default: localhost
|
||||
port:
|
||||
description: Port on which logstash is listening.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGSTASH_PORT
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: port
|
||||
version_added: 1.0.0
|
||||
default: 5000
|
||||
type:
|
||||
description: Message type.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGSTASH_TYPE
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: type
|
||||
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.
|
||||
type: str
|
||||
version_added: 2.0.0
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: pre_command
|
||||
env:
|
||||
- name: LOGSTASH_PRE_COMMAND
|
||||
format_version:
|
||||
description: Logging format.
|
||||
type: str
|
||||
version_added: 2.0.0
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: format_version
|
||||
env:
|
||||
- name: LOGSTASH_FORMAT_VERSION
|
||||
default: v1
|
||||
choices:
|
||||
- v1
|
||||
- v2
|
||||
"""
|
||||
DOCUMENTATION = r'''
|
||||
author: Yevhen Khmelenko (@ujenmr)
|
||||
name: logstash
|
||||
type: notification
|
||||
short_description: Sends events to Logstash
|
||||
description:
|
||||
- This callback will report facts and task events to Logstash U(https://www.elastic.co/products/logstash).
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- logstash (Python library)
|
||||
options:
|
||||
server:
|
||||
description: Address of the Logstash server.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGSTASH_SERVER
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: server
|
||||
version_added: 1.0.0
|
||||
default: localhost
|
||||
port:
|
||||
description: Port on which logstash is listening.
|
||||
type: int
|
||||
env:
|
||||
- name: LOGSTASH_PORT
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: port
|
||||
version_added: 1.0.0
|
||||
default: 5000
|
||||
type:
|
||||
description: Message type.
|
||||
type: str
|
||||
env:
|
||||
- name: LOGSTASH_TYPE
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: type
|
||||
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.
|
||||
type: str
|
||||
version_added: 2.0.0
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: pre_command
|
||||
env:
|
||||
- name: LOGSTASH_PRE_COMMAND
|
||||
format_version:
|
||||
description: Logging format.
|
||||
type: str
|
||||
version_added: 2.0.0
|
||||
ini:
|
||||
- section: callback_logstash
|
||||
key: format_version
|
||||
env:
|
||||
- name: LOGSTASH_FORMAT_VERSION
|
||||
default: v1
|
||||
choices:
|
||||
- v1
|
||||
- v2
|
||||
|
||||
EXAMPLES = r"""
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
ansible.cfg: |
|
||||
# Enable Callback plugin
|
||||
[defaults]
|
||||
callback_whitelist = community.general.logstash
|
||||
# Enable Callback plugin
|
||||
[defaults]
|
||||
callback_whitelist = community.general.logstash
|
||||
|
||||
[callback_logstash]
|
||||
server = logstash.example.com
|
||||
port = 5000
|
||||
pre_command = git rev-parse HEAD
|
||||
type = ansible
|
||||
[callback_logstash]
|
||||
server = logstash.example.com
|
||||
port = 5000
|
||||
pre_command = git rev-parse HEAD
|
||||
type = ansible
|
||||
|
||||
11-input-tcp.conf: |-
|
||||
# Enable Logstash TCP Input
|
||||
input {
|
||||
tcp {
|
||||
port => 5000
|
||||
codec => json
|
||||
add_field => { "[@metadata][beat]" => "notify" }
|
||||
add_field => { "[@metadata][type]" => "ansible" }
|
||||
}
|
||||
}
|
||||
"""
|
||||
11-input-tcp.conf: |
|
||||
# Enable Logstash TCP Input
|
||||
input {
|
||||
tcp {
|
||||
port => 5000
|
||||
codec => json
|
||||
add_field => { "[@metadata][beat]" => "notify" }
|
||||
add_field => { "[@metadata][type]" => "ansible" }
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
import os
|
||||
import json
|
||||
@@ -127,7 +129,9 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
if not HAS_LOGSTASH:
|
||||
self.disabled = True
|
||||
self._display.warning("The required python3-logstash is not installed.")
|
||||
self._display.warning("The required python-logstash/python3-logstash is not installed. "
|
||||
"pip install python-logstash for Python 2"
|
||||
"pip install python3-logstash for Python 3")
|
||||
|
||||
self.start_time = now()
|
||||
|
||||
@@ -180,7 +184,7 @@ class CallbackModule(CallbackBase):
|
||||
data['status'] = "OK"
|
||||
data['ansible_playbook'] = playbook._file_name
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info(
|
||||
"START PLAYBOOK | %s", data['ansible_playbook'], extra=data
|
||||
)
|
||||
@@ -205,7 +209,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_playbook_duration'] = runtime.total_seconds()
|
||||
data['ansible_result'] = json.dumps(summarize_stat) # deprecated field
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info(
|
||||
"FINISH PLAYBOOK | %s", json.dumps(summarize_stat), extra=data
|
||||
)
|
||||
@@ -224,7 +228,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_play_id'] = self.play_id
|
||||
data['ansible_play_name'] = self.play_name
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info("START PLAY | %s", self.play_name, extra=data)
|
||||
else:
|
||||
self.logger.info("ansible play", extra=data)
|
||||
@@ -249,7 +253,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_task'] = task_name
|
||||
data['ansible_facts'] = self._dump_results(result._result)
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info(
|
||||
"SETUP FACTS | %s", self._dump_results(result._result), extra=data
|
||||
)
|
||||
@@ -270,7 +274,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_task_id'] = self.task_id
|
||||
data['ansible_result'] = self._dump_results(result._result)
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info(
|
||||
"TASK OK | %s | RESULT | %s",
|
||||
task_name, self._dump_results(result._result), extra=data
|
||||
@@ -291,7 +295,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_task_id'] = self.task_id
|
||||
data['ansible_result'] = self._dump_results(result._result)
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info("TASK SKIPPED | %s", task_name, extra=data)
|
||||
else:
|
||||
self.logger.info("ansible skipped", extra=data)
|
||||
@@ -305,7 +309,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_play_name'] = self.play_name
|
||||
data['imported_file'] = imported_file
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info("IMPORT | %s", imported_file, extra=data)
|
||||
else:
|
||||
self.logger.info("ansible import", extra=data)
|
||||
@@ -319,7 +323,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_play_name'] = self.play_name
|
||||
data['imported_file'] = missing_file
|
||||
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.info("NOT IMPORTED | %s", missing_file, extra=data)
|
||||
else:
|
||||
self.logger.info("ansible import", extra=data)
|
||||
@@ -343,7 +347,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_result'] = self._dump_results(result._result)
|
||||
|
||||
self.errors += 1
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.error(
|
||||
"TASK FAILED | %s | HOST | %s | RESULT | %s",
|
||||
task_name, self.hostname,
|
||||
@@ -366,7 +370,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_result'] = self._dump_results(result._result)
|
||||
|
||||
self.errors += 1
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.error(
|
||||
"UNREACHABLE | %s | HOST | %s | RESULT | %s",
|
||||
task_name, self.hostname,
|
||||
@@ -389,7 +393,7 @@ class CallbackModule(CallbackBase):
|
||||
data['ansible_result'] = self._dump_results(result._result)
|
||||
|
||||
self.errors += 1
|
||||
if self.ls_format_version == "v2":
|
||||
if (self.ls_format_version == "v2"):
|
||||
self.logger.error(
|
||||
"ASYNC FAILED | %s | HOST | %s | RESULT | %s",
|
||||
task_name, self.hostname,
|
||||
|
||||
@@ -4,82 +4,84 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
DOCUMENTATION = '''
|
||||
name: mail
|
||||
type: notification
|
||||
short_description: Sends failure events through email
|
||||
short_description: Sends failure events via email
|
||||
description:
|
||||
- This callback reports failures through email.
|
||||
- This callback will report failures via email.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
- Dag Wieers (@dagwieers)
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- whitelisting in configuration
|
||||
options:
|
||||
mta:
|
||||
description:
|
||||
- Mail Transfer Agent, server that accepts SMTP.
|
||||
- Mail Transfer Agent, server that accepts SMTP.
|
||||
type: str
|
||||
env:
|
||||
- name: SMTPHOST
|
||||
- name: SMTPHOST
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: smtphost
|
||||
- section: callback_mail
|
||||
key: smtphost
|
||||
default: localhost
|
||||
mtaport:
|
||||
description:
|
||||
- Mail Transfer Agent Port.
|
||||
- Port at which server SMTP.
|
||||
- Mail Transfer Agent Port.
|
||||
- Port at which server SMTP.
|
||||
type: int
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: smtpport
|
||||
- section: callback_mail
|
||||
key: smtpport
|
||||
default: 25
|
||||
to:
|
||||
description:
|
||||
- Mail recipient.
|
||||
- Mail recipient.
|
||||
type: list
|
||||
elements: str
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: to
|
||||
- section: callback_mail
|
||||
key: to
|
||||
default: [root]
|
||||
sender:
|
||||
description:
|
||||
- Mail sender.
|
||||
- This is required since community.general 6.0.0.
|
||||
- Mail sender.
|
||||
- This is required since community.general 6.0.0.
|
||||
type: str
|
||||
required: true
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: sender
|
||||
- section: callback_mail
|
||||
key: sender
|
||||
cc:
|
||||
description:
|
||||
- CC'd recipients.
|
||||
- CC'd recipients.
|
||||
type: list
|
||||
elements: str
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: cc
|
||||
- section: callback_mail
|
||||
key: cc
|
||||
bcc:
|
||||
description:
|
||||
- BCC'd recipients.
|
||||
- BCC'd recipients.
|
||||
type: list
|
||||
elements: str
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: bcc
|
||||
- section: callback_mail
|
||||
key: bcc
|
||||
message_id_domain:
|
||||
description:
|
||||
- The domain name to use for the L(Message-ID header, https://en.wikipedia.org/wiki/Message-ID).
|
||||
- The default is the hostname of the control node.
|
||||
- The domain name to use for the L(Message-ID header, https://en.wikipedia.org/wiki/Message-ID).
|
||||
- The default is the hostname of the control node.
|
||||
type: str
|
||||
ini:
|
||||
- section: callback_mail
|
||||
key: message_id_domain
|
||||
- section: callback_mail
|
||||
key: message_id_domain
|
||||
version_added: 8.2.0
|
||||
"""
|
||||
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
@@ -133,14 +135,14 @@ class CallbackModule(CallbackBase):
|
||||
if self.bcc:
|
||||
bcc_addresses = email.utils.getaddresses(self.bcc)
|
||||
|
||||
content = f'Date: {email.utils.formatdate()}\n'
|
||||
content += f'From: {email.utils.formataddr(sender_address)}\n'
|
||||
content = 'Date: %s\n' % email.utils.formatdate()
|
||||
content += 'From: %s\n' % email.utils.formataddr(sender_address)
|
||||
if self.to:
|
||||
content += f"To: {', '.join([email.utils.formataddr(pair) for pair in to_addresses])}\n"
|
||||
content += 'To: %s\n' % ', '.join([email.utils.formataddr(pair) for pair in to_addresses])
|
||||
if self.cc:
|
||||
content += f"Cc: {', '.join([email.utils.formataddr(pair) for pair in cc_addresses])}\n"
|
||||
content += f"Message-ID: {email.utils.make_msgid(domain=self.get_option('message_id_domain'))}\n"
|
||||
content += f'Subject: {subject.strip()}\n\n'
|
||||
content += 'Cc: %s\n' % ', '.join([email.utils.formataddr(pair) for pair in cc_addresses])
|
||||
content += 'Message-ID: %s\n' % email.utils.make_msgid(domain=self.get_option('message_id_domain'))
|
||||
content += 'Subject: %s\n\n' % subject.strip()
|
||||
content += body
|
||||
|
||||
addresses = to_addresses
|
||||
@@ -157,22 +159,23 @@ class CallbackModule(CallbackBase):
|
||||
smtp.quit()
|
||||
|
||||
def subject_msg(self, multiline, failtype, linenr):
|
||||
msg = multiline.strip('\r\n').splitlines()[linenr]
|
||||
return f'{failtype}: {msg}'
|
||||
return '%s: %s' % (failtype, multiline.strip('\r\n').splitlines()[linenr])
|
||||
|
||||
def indent(self, multiline, indent=8):
|
||||
return re.sub('^', ' ' * indent, multiline, flags=re.MULTILINE)
|
||||
|
||||
def body_blob(self, multiline, texttype):
|
||||
''' Turn some text output in a well-indented block for sending in a mail body '''
|
||||
intro = f'with the following {texttype}:\n\n'
|
||||
blob = "\n".join(multiline.strip('\r\n').splitlines())
|
||||
return f"{intro}{self.indent(blob)}\n"
|
||||
intro = 'with the following %s:\n\n' % texttype
|
||||
blob = ''
|
||||
for line in multiline.strip('\r\n').splitlines():
|
||||
blob += '%s\n' % line
|
||||
return intro + self.indent(blob) + '\n'
|
||||
|
||||
def mail_result(self, result, failtype):
|
||||
host = result._host.get_name()
|
||||
if not self.sender:
|
||||
self.sender = f'"Ansible: {host}" <root>'
|
||||
self.sender = '"Ansible: %s" <root>' % host
|
||||
|
||||
# Add subject
|
||||
if self.itembody:
|
||||
@@ -188,33 +191,31 @@ class CallbackModule(CallbackBase):
|
||||
elif result._result.get('exception'): # Unrelated exceptions are added to output :-/
|
||||
subject = self.subject_msg(result._result['exception'], failtype, -1)
|
||||
else:
|
||||
subject = f'{failtype}: {result._task.name or result._task.action}'
|
||||
subject = '%s: %s' % (failtype, result._task.name or result._task.action)
|
||||
|
||||
# Make playbook name visible (e.g. in Outlook/Gmail condensed view)
|
||||
body = f'Playbook: {os.path.basename(self.playbook._file_name)}\n'
|
||||
body = 'Playbook: %s\n' % os.path.basename(self.playbook._file_name)
|
||||
if result._task.name:
|
||||
body += f'Task: {result._task.name}\n'
|
||||
body += f'Module: {result._task.action}\n'
|
||||
body += f'Host: {host}\n'
|
||||
body += 'Task: %s\n' % result._task.name
|
||||
body += 'Module: %s\n' % result._task.action
|
||||
body += 'Host: %s\n' % host
|
||||
body += '\n'
|
||||
|
||||
# Add task information (as much as possible)
|
||||
body += 'The following task failed:\n\n'
|
||||
if 'invocation' in result._result:
|
||||
body += self.indent(f"{result._task.action}: {json.dumps(result._result['invocation']['module_args'], indent=4)}\n")
|
||||
body += self.indent('%s: %s\n' % (result._task.action, json.dumps(result._result['invocation']['module_args'], indent=4)))
|
||||
elif result._task.name:
|
||||
body += self.indent(f'{result._task.name} ({result._task.action})\n')
|
||||
body += self.indent('%s (%s)\n' % (result._task.name, result._task.action))
|
||||
else:
|
||||
body += self.indent(f'{result._task.action}\n')
|
||||
body += self.indent('%s\n' % result._task.action)
|
||||
body += '\n'
|
||||
|
||||
# Add item / message
|
||||
if self.itembody:
|
||||
body += self.itembody
|
||||
elif result._result.get('failed_when_result') is True:
|
||||
fail_cond_list = '\n- '.join(result._task.failed_when)
|
||||
fail_cond = self.indent(f"failed_when:\n- {fail_cond_list}")
|
||||
body += f"due to the following condition:\n\n{fail_cond}\n\n"
|
||||
body += "due to the following condition:\n\n" + self.indent('failed_when:\n- ' + '\n- '.join(result._task.failed_when)) + '\n\n'
|
||||
elif result._result.get('msg'):
|
||||
body += self.body_blob(result._result['msg'], 'message')
|
||||
|
||||
@@ -227,13 +228,13 @@ class CallbackModule(CallbackBase):
|
||||
body += self.body_blob(result._result['exception'], 'exception')
|
||||
if result._result.get('warnings'):
|
||||
for i in range(len(result._result.get('warnings'))):
|
||||
body += self.body_blob(result._result['warnings'][i], f'exception {i + 1}')
|
||||
body += self.body_blob(result._result['warnings'][i], 'exception %d' % (i + 1))
|
||||
if result._result.get('deprecations'):
|
||||
for i in range(len(result._result.get('deprecations'))):
|
||||
body += self.body_blob(result._result['deprecations'][i], f'exception {i + 1}')
|
||||
body += self.body_blob(result._result['deprecations'][i], 'exception %d' % (i + 1))
|
||||
|
||||
body += 'and a complete dump of the error:\n\n'
|
||||
body += self.indent(f'{failtype}: {json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4)}')
|
||||
body += self.indent('%s: %s' % (failtype, json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4)))
|
||||
|
||||
self.mail(subject=subject, body=body)
|
||||
|
||||
@@ -256,4 +257,4 @@ class CallbackModule(CallbackBase):
|
||||
def v2_runner_item_on_failed(self, result):
|
||||
# Pass item information to task failure
|
||||
self.itemsubject = result._result['msg']
|
||||
self.itembody += self.body_blob(json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4), f"failed item dump '{result._result['item']}'")
|
||||
self.itembody += self.body_blob(json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4), "failed item dump '%(item)s'" % result._result)
|
||||
|
||||
@@ -4,67 +4,68 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: nrdp
|
||||
type: notification
|
||||
author: "Remi VERCHERE (@rverchere)"
|
||||
short_description: Post task results to a Nagios server through nrdp
|
||||
description:
|
||||
- This callback send playbook result to Nagios.
|
||||
- Nagios shall use NRDP to receive passive events.
|
||||
- The passive check is sent to a dedicated host/service for Ansible.
|
||||
options:
|
||||
url:
|
||||
description: URL of the nrdp server.
|
||||
required: true
|
||||
env:
|
||||
- name: NRDP_URL
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: url
|
||||
type: string
|
||||
validate_certs:
|
||||
description: Validate the SSL certificate of the nrdp server. (Used for HTTPS URLs).
|
||||
env:
|
||||
- name: NRDP_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: validate_nrdp_certs
|
||||
- section: callback_nrdp
|
||||
key: validate_certs
|
||||
type: boolean
|
||||
default: false
|
||||
aliases: [validate_nrdp_certs]
|
||||
token:
|
||||
description: Token to be allowed to push nrdp events.
|
||||
required: true
|
||||
env:
|
||||
- name: NRDP_TOKEN
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: token
|
||||
type: string
|
||||
hostname:
|
||||
description: Hostname where the passive check is linked to.
|
||||
required: true
|
||||
env:
|
||||
- name: NRDP_HOSTNAME
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: hostname
|
||||
type: string
|
||||
servicename:
|
||||
description: Service where the passive check is linked to.
|
||||
required: true
|
||||
env:
|
||||
- name: NRDP_SERVICENAME
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: servicename
|
||||
type: string
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
name: nrdp
|
||||
type: notification
|
||||
author: "Remi VERCHERE (@rverchere)"
|
||||
short_description: Post task results to a Nagios server through nrdp
|
||||
description:
|
||||
- This callback send playbook result to Nagios.
|
||||
- Nagios shall use NRDP to receive passive events.
|
||||
- The passive check is sent to a dedicated host/service for Ansible.
|
||||
options:
|
||||
url:
|
||||
description: URL of the nrdp server.
|
||||
required: true
|
||||
env:
|
||||
- name : NRDP_URL
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: url
|
||||
type: string
|
||||
validate_certs:
|
||||
description: Validate the SSL certificate of the nrdp server. (Used for HTTPS URLs.)
|
||||
env:
|
||||
- name: NRDP_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: validate_nrdp_certs
|
||||
- section: callback_nrdp
|
||||
key: validate_certs
|
||||
type: boolean
|
||||
default: false
|
||||
aliases: [ validate_nrdp_certs ]
|
||||
token:
|
||||
description: Token to be allowed to push nrdp events.
|
||||
required: true
|
||||
env:
|
||||
- name: NRDP_TOKEN
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: token
|
||||
type: string
|
||||
hostname:
|
||||
description: Hostname where the passive check is linked to.
|
||||
required: true
|
||||
env:
|
||||
- name : NRDP_HOSTNAME
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: hostname
|
||||
type: string
|
||||
servicename:
|
||||
description: Service where the passive check is linked to.
|
||||
required: true
|
||||
env:
|
||||
- name : NRDP_SERVICENAME
|
||||
ini:
|
||||
- section: callback_nrdp
|
||||
key: servicename
|
||||
type: string
|
||||
'''
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
@@ -131,10 +132,10 @@ class CallbackModule(CallbackBase):
|
||||
xmldata = "<?xml version='1.0'?>\n"
|
||||
xmldata += "<checkresults>\n"
|
||||
xmldata += "<checkresult type='service'>\n"
|
||||
xmldata += f"<hostname>{self.hostname}</hostname>\n"
|
||||
xmldata += f"<servicename>{self.servicename}</servicename>\n"
|
||||
xmldata += f"<state>{state}</state>\n"
|
||||
xmldata += f"<output>{msg}</output>\n"
|
||||
xmldata += "<hostname>%s</hostname>\n" % self.hostname
|
||||
xmldata += "<servicename>%s</servicename>\n" % self.servicename
|
||||
xmldata += "<state>%d</state>\n" % state
|
||||
xmldata += "<output>%s</output>\n" % msg
|
||||
xmldata += "</checkresult>\n"
|
||||
xmldata += "</checkresults>\n"
|
||||
|
||||
@@ -151,7 +152,7 @@ class CallbackModule(CallbackBase):
|
||||
validate_certs=self.validate_nrdp_certs)
|
||||
return response.read()
|
||||
except Exception as ex:
|
||||
self._display.warning(f"NRDP callback cannot send result {ex}")
|
||||
self._display.warning("NRDP callback cannot send result {0}".format(ex))
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
'''
|
||||
@@ -169,16 +170,17 @@ class CallbackModule(CallbackBase):
|
||||
critical = warning = 0
|
||||
for host in hosts:
|
||||
stat = stats.summarize(host)
|
||||
gstats += (
|
||||
f"'{host}_ok'={stat['ok']} '{host}_changed'={stat['changed']} '{host}_unreachable'={stat['unreachable']} '{host}_failed'={stat['failures']} "
|
||||
)
|
||||
gstats += "'%s_ok'=%d '%s_changed'=%d \
|
||||
'%s_unreachable'=%d '%s_failed'=%d " % \
|
||||
(host, stat['ok'], host, stat['changed'],
|
||||
host, stat['unreachable'], host, stat['failures'])
|
||||
# Critical when failed tasks or unreachable host
|
||||
critical += stat['failures']
|
||||
critical += stat['unreachable']
|
||||
# Warning when changed tasks
|
||||
warning += stat['changed']
|
||||
|
||||
msg = f"{name} | {gstats}"
|
||||
msg = "%s | %s" % (name, gstats)
|
||||
if critical:
|
||||
# Send Critical
|
||||
self._send_nrdp(self.CRITICAL, msg)
|
||||
|
||||
@@ -4,18 +4,19 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: 'null'
|
||||
type: stdout
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: Do not display stuff to screen
|
||||
description:
|
||||
- This callback prevents outputting events to screen.
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: 'null'
|
||||
type: stdout
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: Don't display stuff to screen
|
||||
description:
|
||||
- This callback prevents outputting events to screen.
|
||||
'''
|
||||
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
@@ -3,122 +3,122 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
|
||||
name: opentelemetry
|
||||
type: notification
|
||||
short_description: Create distributed traces with OpenTelemetry
|
||||
version_added: 3.7.0
|
||||
description:
|
||||
- This callback creates distributed traces for each Ansible task with OpenTelemetry.
|
||||
- You can configure the OpenTelemetry exporter and SDK with environment variables.
|
||||
- See U(https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html).
|
||||
- See
|
||||
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#opentelemetry-sdk-environment-variables).
|
||||
options:
|
||||
hide_task_arguments:
|
||||
default: false
|
||||
type: bool
|
||||
DOCUMENTATION = '''
|
||||
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
|
||||
name: opentelemetry
|
||||
type: notification
|
||||
short_description: Create distributed traces with OpenTelemetry
|
||||
version_added: 3.7.0
|
||||
description:
|
||||
- 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:
|
||||
- Whether to enable this callback only if the given environment variable exists and it is set to V(true).
|
||||
- This is handy when you use Configuration as Code and want to send distributed traces if running in the CI rather when
|
||||
running Ansible locally.
|
||||
- For such, it evaluates the given O(enable_from_environment) value as environment variable and if set to V(true) this
|
||||
plugin is 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
|
||||
type: str
|
||||
description:
|
||||
- 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
|
||||
description:
|
||||
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
|
||||
env:
|
||||
- name: TRACEPARENT
|
||||
disable_logs:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- Disable sending logs.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_DISABLE_LOGS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: disable_logs
|
||||
version_added: 5.8.0
|
||||
disable_attributes_in_logs:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- Disable populating span attributes to the logs.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_DISABLE_ATTRIBUTES_IN_LOGS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: disable_attributes_in_logs
|
||||
version_added: 7.1.0
|
||||
store_spans_in_file:
|
||||
type: str
|
||||
description:
|
||||
- It stores the exported spans in the given file.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_STORE_SPANS_IN_FILE
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: store_spans_in_file
|
||||
version_added: 9.0.0
|
||||
otel_exporter_otlp_traces_protocol:
|
||||
type: str
|
||||
description:
|
||||
- E(OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) represents the the transport protocol for spans.
|
||||
- See
|
||||
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#envvar-OTEL_EXPORTER_OTLP_TRACES_PROTOCOL).
|
||||
default: grpc
|
||||
choices:
|
||||
- grpc
|
||||
- http/protobuf
|
||||
env:
|
||||
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: otel_exporter_otlp_traces_protocol
|
||||
version_added: 9.0.0
|
||||
requirements:
|
||||
- opentelemetry-api (Python library)
|
||||
- opentelemetry-exporter-otlp (Python library)
|
||||
- opentelemetry-sdk (Python library)
|
||||
"""
|
||||
- This callback creates distributed traces for each Ansible task with OpenTelemetry.
|
||||
- You can configure the OpenTelemetry exporter and SDK with environment variables.
|
||||
- See U(https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html).
|
||||
- See U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#opentelemetry-sdk-environment-variables).
|
||||
options:
|
||||
hide_task_arguments:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- 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:
|
||||
- Whether to enable this callback only if the given environment variable exists and it is set to V(true).
|
||||
- This is handy when you use Configuration as Code and want to send distributed traces
|
||||
if running in the CI rather when running Ansible locally.
|
||||
- For such, it evaluates the given O(enable_from_environment) value as environment variable
|
||||
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
|
||||
type: str
|
||||
description:
|
||||
- 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
|
||||
description:
|
||||
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
|
||||
env:
|
||||
- name: TRACEPARENT
|
||||
disable_logs:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- Disable sending logs.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_DISABLE_LOGS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: disable_logs
|
||||
version_added: 5.8.0
|
||||
disable_attributes_in_logs:
|
||||
default: false
|
||||
type: bool
|
||||
description:
|
||||
- Disable populating span attributes to the logs.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_DISABLE_ATTRIBUTES_IN_LOGS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: disable_attributes_in_logs
|
||||
version_added: 7.1.0
|
||||
store_spans_in_file:
|
||||
type: str
|
||||
description:
|
||||
- It stores the exported spans in the given file
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_STORE_SPANS_IN_FILE
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: store_spans_in_file
|
||||
version_added: 9.0.0
|
||||
otel_exporter_otlp_traces_protocol:
|
||||
type: str
|
||||
description:
|
||||
- E(OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) represents the the transport protocol for spans.
|
||||
- See
|
||||
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#envvar-OTEL_EXPORTER_OTLP_TRACES_PROTOCOL).
|
||||
default: grpc
|
||||
choices:
|
||||
- grpc
|
||||
- http/protobuf
|
||||
env:
|
||||
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: otel_exporter_otlp_traces_protocol
|
||||
version_added: 9.0.0
|
||||
requirements:
|
||||
- opentelemetry-api (Python library)
|
||||
- opentelemetry-exporter-otlp (Python library)
|
||||
- opentelemetry-sdk (Python library)
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: |-
|
||||
EXAMPLES = '''
|
||||
examples: |
|
||||
Enable the plugin in ansible.cfg:
|
||||
[defaults]
|
||||
callbacks_enabled = community.general.opentelemetry
|
||||
@@ -130,20 +130,20 @@ examples: |-
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer your_otel_token"
|
||||
export OTEL_SERVICE_NAME=your_service_name
|
||||
export ANSIBLE_OPENTELEMETRY_ENABLED=true
|
||||
"""
|
||||
'''
|
||||
|
||||
import getpass
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from time import time_ns
|
||||
|
||||
from collections import OrderedDict
|
||||
from os.path import basename
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
from ansible.module_utils.six import raise_from
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
@@ -164,12 +164,31 @@ try:
|
||||
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
|
||||
InMemorySpanExporter
|
||||
)
|
||||
# Support for opentelemetry-api <= 1.12
|
||||
try:
|
||||
from opentelemetry.util._time import _time_ns
|
||||
except ImportError as imp_exc:
|
||||
OTEL_LIBRARY_TIME_NS_ERROR = imp_exc
|
||||
else:
|
||||
OTEL_LIBRARY_TIME_NS_ERROR = None
|
||||
|
||||
except ImportError as imp_exc:
|
||||
OTEL_LIBRARY_IMPORT_ERROR = imp_exc
|
||||
OTEL_LIBRARY_TIME_NS_ERROR = imp_exc
|
||||
else:
|
||||
OTEL_LIBRARY_IMPORT_ERROR = None
|
||||
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
time_ns = time.time_ns
|
||||
elif not OTEL_LIBRARY_TIME_NS_ERROR:
|
||||
time_ns = _time_ns
|
||||
else:
|
||||
def time_ns():
|
||||
# Support versions older than 3.7 with opentelemetry-api > 1.12
|
||||
return int(time.time() * 1e9)
|
||||
|
||||
|
||||
class TaskData:
|
||||
"""
|
||||
Data about an individual task.
|
||||
@@ -190,7 +209,7 @@ class TaskData:
|
||||
if host.uuid in self.host_data:
|
||||
if host.status == 'included':
|
||||
# concatenate task include output from multiple items
|
||||
host.result = f'{self.host_data[host.uuid].result}\n{host.result}'
|
||||
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
|
||||
else:
|
||||
return
|
||||
|
||||
@@ -213,6 +232,7 @@ class HostData:
|
||||
class OpenTelemetrySource(object):
|
||||
def __init__(self, display):
|
||||
self.ansible_playbook = ""
|
||||
self.ansible_version = None
|
||||
self.session = str(uuid.uuid4())
|
||||
self.host = socket.gethostname()
|
||||
try:
|
||||
@@ -260,6 +280,9 @@ 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'):
|
||||
self.ansible_version = result._task_fields['args'].get('_ansible_version')
|
||||
|
||||
task.dump = dump
|
||||
task.add_host(HostData(host_uuid, host_name, status, result))
|
||||
|
||||
@@ -307,7 +330,8 @@ class OpenTelemetrySource(object):
|
||||
start_time=parent_start_time, kind=SpanKind.SERVER) as parent:
|
||||
parent.set_status(status)
|
||||
# Populate trace metadata attributes
|
||||
parent.set_attribute("ansible.version", ansible_version)
|
||||
if self.ansible_version is not None:
|
||||
parent.set_attribute("ansible.version", self.ansible_version)
|
||||
parent.set_attribute("ansible.session", self.session)
|
||||
parent.set_attribute("ansible.host.name", self.host)
|
||||
if self.ip_address is not None:
|
||||
@@ -323,7 +347,7 @@ class OpenTelemetrySource(object):
|
||||
def update_span_data(self, task_data, host_data, span, disable_logs, disable_attributes_in_logs):
|
||||
""" update the span with the given TaskData and HostData """
|
||||
|
||||
name = f'[{host_data.name}] {task_data.play}: {task_data.name}'
|
||||
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
|
||||
|
||||
message = 'success'
|
||||
res = {}
|
||||
@@ -446,7 +470,7 @@ class OpenTelemetrySource(object):
|
||||
def get_error_message_from_results(results, action):
|
||||
for result in results:
|
||||
if result.get('failed', False):
|
||||
return f"{action}({result.get('item', 'none')}) - {OpenTelemetrySource.get_error_message(result)}"
|
||||
return ('{0}({1}) - {2}').format(action, result.get('item', 'none'), OpenTelemetrySource.get_error_message(result))
|
||||
|
||||
@staticmethod
|
||||
def _last_line(text):
|
||||
@@ -458,14 +482,14 @@ class OpenTelemetrySource(object):
|
||||
message = result.get('msg', 'failed')
|
||||
exception = result.get('exception')
|
||||
stderr = result.get('stderr')
|
||||
return f"message: \"{message}\"\nexception: \"{exception}\"\nstderr: \"{stderr}\""
|
||||
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||
|
||||
@staticmethod
|
||||
def enrich_error_message_from_results(results, action):
|
||||
message = ""
|
||||
for result in results:
|
||||
if result.get('failed', False):
|
||||
message = f"{action}({result.get('item', 'none')}) - {OpenTelemetrySource.enrich_error_message(result)}\n{message}"
|
||||
message = ('{0}({1}) - {2}\n{3}').format(action, result.get('item', 'none'), OpenTelemetrySource.enrich_error_message(result), message)
|
||||
return message
|
||||
|
||||
|
||||
@@ -511,9 +535,8 @@ class CallbackModule(CallbackBase):
|
||||
environment_variable = self.get_option('enable_from_environment')
|
||||
if environment_variable is not None and os.environ.get(environment_variable, 'false').lower() != 'true':
|
||||
self.disabled = True
|
||||
self._display.warning(
|
||||
f"The `enable_from_environment` option has been set and {environment_variable} is not enabled. Disabling the `opentelemetry` callback plugin."
|
||||
)
|
||||
self._display.warning("The `enable_from_environment` option has been set and {0} is not enabled. "
|
||||
"Disabling the `opentelemetry` callback plugin.".format(environment_variable))
|
||||
|
||||
self.hide_task_arguments = self.get_option('hide_task_arguments')
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2025, Max Mitschke <maxmitschke@fastmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: print_task
|
||||
type: aggregate
|
||||
short_description: Prints playbook task snippet to job output
|
||||
description:
|
||||
- This plugin prints the currently executing playbook task to the job output.
|
||||
version_added: 10.7.0
|
||||
requirements:
|
||||
- enable in configuration
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
ansible.cfg: |-
|
||||
# Enable plugin
|
||||
[defaults]
|
||||
callbacks_enabled=community.general.print_task
|
||||
"""
|
||||
|
||||
from yaml import load, dump
|
||||
|
||||
try:
|
||||
from yaml import CSafeDumper as SafeDumper
|
||||
from yaml import CSafeLoader as SafeLoader
|
||||
except ImportError:
|
||||
from yaml import SafeDumper, SafeLoader
|
||||
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
"""
|
||||
This callback module tells you how long your plays ran for.
|
||||
"""
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'aggregate'
|
||||
CALLBACK_NAME = 'community.general.print_task'
|
||||
|
||||
CALLBACK_NEEDS_ENABLED = True
|
||||
|
||||
def __init__(self):
|
||||
super(CallbackModule, self).__init__()
|
||||
self._printed_message = False
|
||||
|
||||
def _print_task(self, task):
|
||||
if hasattr(task, '_ds'):
|
||||
task_snippet = load(str([task._ds.copy()]), Loader=SafeLoader)
|
||||
task_yaml = dump(task_snippet, sort_keys=False, Dumper=SafeDumper)
|
||||
self._display.display(f"\n{task_yaml}\n")
|
||||
self._printed_message = True
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
self._printed_message = False
|
||||
|
||||
def v2_runner_on_start(self, host, task):
|
||||
if not self._printed_message:
|
||||
self._print_task(task)
|
||||
@@ -5,19 +5,20 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: say
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- the C(/usr/bin/say) command line program (standard on macOS) or C(espeak) command line program
|
||||
short_description: Notify using software speech synthesizer
|
||||
description:
|
||||
- This plugin uses C(say) or C(espeak) to "speak" about play events.
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: say
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
- the C(/usr/bin/say) command line program (standard on macOS) or C(espeak) command line program
|
||||
short_description: notify using software speech synthesizer
|
||||
description:
|
||||
- This plugin will use the C(say) or C(espeak) program to "speak" about play events.
|
||||
'''
|
||||
|
||||
import platform
|
||||
import subprocess
|
||||
@@ -49,7 +50,7 @@ class CallbackModule(CallbackBase):
|
||||
self.synthesizer = get_bin_path('say')
|
||||
if platform.system() != 'Darwin':
|
||||
# 'say' binary available, it might be GNUstep tool which doesn't support 'voice' parameter
|
||||
self._display.warning(f"'say' executable found but system is '{platform.system()}': ignoring voice parameter")
|
||||
self._display.warning("'say' executable found but system is '%s': ignoring voice parameter" % platform.system())
|
||||
else:
|
||||
self.FAILED_VOICE = 'Zarvox'
|
||||
self.REGULAR_VOICE = 'Trinoids'
|
||||
@@ -68,7 +69,7 @@ class CallbackModule(CallbackBase):
|
||||
# ansible will not call any callback if disabled is set to True
|
||||
if not self.synthesizer:
|
||||
self.disabled = True
|
||||
self._display.warning(f"Unable to find either 'say' or 'espeak' executable, plugin {os.path.basename(__file__)} disabled")
|
||||
self._display.warning("Unable to find either 'say' or 'espeak' executable, plugin %s disabled" % os.path.basename(__file__))
|
||||
|
||||
def say(self, msg, voice):
|
||||
cmd = [self.synthesizer, msg]
|
||||
@@ -77,7 +78,7 @@ class CallbackModule(CallbackBase):
|
||||
subprocess.call(cmd)
|
||||
|
||||
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||
self.say(f"Failure on host {host}", self.FAILED_VOICE)
|
||||
self.say("Failure on host %s" % host, self.FAILED_VOICE)
|
||||
|
||||
def runner_on_ok(self, host, res):
|
||||
self.say("pew", self.LASER_VOICE)
|
||||
@@ -86,13 +87,13 @@ class CallbackModule(CallbackBase):
|
||||
self.say("pew", self.LASER_VOICE)
|
||||
|
||||
def runner_on_unreachable(self, host, res):
|
||||
self.say(f"Failure on host {host}", self.FAILED_VOICE)
|
||||
self.say("Failure on host %s" % host, self.FAILED_VOICE)
|
||||
|
||||
def runner_on_async_ok(self, host, res, jid):
|
||||
self.say("pew", self.LASER_VOICE)
|
||||
|
||||
def runner_on_async_failed(self, host, res, jid):
|
||||
self.say(f"Failure on host {host}", self.FAILED_VOICE)
|
||||
self.say("Failure on host %s" % host, self.FAILED_VOICE)
|
||||
|
||||
def playbook_on_start(self):
|
||||
self.say("Running Playbook", self.REGULAR_VOICE)
|
||||
@@ -102,15 +103,15 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
def playbook_on_task_start(self, name, is_conditional):
|
||||
if not is_conditional:
|
||||
self.say(f"Starting task: {name}", self.REGULAR_VOICE)
|
||||
self.say("Starting task: %s" % name, self.REGULAR_VOICE)
|
||||
else:
|
||||
self.say(f"Notifying task: {name}", self.REGULAR_VOICE)
|
||||
self.say("Notifying task: %s" % name, self.REGULAR_VOICE)
|
||||
|
||||
def playbook_on_setup(self):
|
||||
self.say("Gathering facts", self.REGULAR_VOICE)
|
||||
|
||||
def playbook_on_play_start(self, name):
|
||||
self.say(f"Starting play: {name}", self.HAPPY_VOICE)
|
||||
self.say("Starting play: %s" % name, self.HAPPY_VOICE)
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
self.say("Play complete", self.HAPPY_VOICE)
|
||||
|
||||
@@ -4,37 +4,38 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: selective
|
||||
type: stdout
|
||||
requirements:
|
||||
- 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 allows operators
|
||||
to focus on the tasks that provide value only.
|
||||
- Tasks that are not printed are placed with a C(.).
|
||||
- If you increase verbosity all tasks are printed.
|
||||
options:
|
||||
nocolor:
|
||||
default: false
|
||||
description: This setting allows suppressing colorizing output.
|
||||
env:
|
||||
- name: ANSIBLE_NOCOLOR
|
||||
- name: ANSIBLE_SELECTIVE_DONT_COLORIZE
|
||||
ini:
|
||||
- section: defaults
|
||||
key: nocolor
|
||||
type: boolean
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: selective
|
||||
type: stdout
|
||||
requirements:
|
||||
- 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 allows operators to focus on the tasks that provide value only.
|
||||
- Tasks that are not printed are placed with a C(.).
|
||||
- If you increase verbosity all tasks are printed.
|
||||
options:
|
||||
nocolor:
|
||||
default: false
|
||||
description: This setting allows suppressing colorizing output.
|
||||
env:
|
||||
- name: ANSIBLE_NOCOLOR
|
||||
- name: ANSIBLE_SELECTIVE_DONT_COLORIZE
|
||||
ini:
|
||||
- section: defaults
|
||||
key: nocolor
|
||||
type: boolean
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
- ansible.builtin.debug: msg="This will not be printed"
|
||||
- ansible.builtin.debug: msg="But this will"
|
||||
tags: [print_action]
|
||||
EXAMPLES = """
|
||||
- ansible.builtin.debug: msg="This will not be printed"
|
||||
- ansible.builtin.debug: msg="But this will"
|
||||
tags: [print_action]
|
||||
"""
|
||||
|
||||
import difflib
|
||||
@@ -47,13 +48,13 @@ from ansible.module_utils.common.text.converters import to_text
|
||||
DONT_COLORIZE = False
|
||||
COLORS = {
|
||||
'normal': '\033[0m',
|
||||
'ok': f'\x1b[{C.COLOR_CODES[C.COLOR_OK]}m',
|
||||
'ok': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_OK]),
|
||||
'bold': '\033[1m',
|
||||
'not_so_bold': '\033[1m\033[34m',
|
||||
'changed': f'\x1b[{C.COLOR_CODES[C.COLOR_CHANGED]}m',
|
||||
'failed': f'\x1b[{C.COLOR_CODES[C.COLOR_ERROR]}m',
|
||||
'changed': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_CHANGED]),
|
||||
'failed': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_ERROR]),
|
||||
'endc': '\033[0m',
|
||||
'skipped': f'\x1b[{C.COLOR_CODES[C.COLOR_SKIP]}m',
|
||||
'skipped': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_SKIP]),
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +73,7 @@ def colorize(msg, color):
|
||||
if DONT_COLORIZE:
|
||||
return msg
|
||||
else:
|
||||
return f"{COLORS[color]}{msg}{COLORS['endc']}"
|
||||
return '{0}{1}{2}'.format(COLORS[color], msg, COLORS['endc'])
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
@@ -105,15 +106,15 @@ class CallbackModule(CallbackBase):
|
||||
line_length = 120
|
||||
if self.last_skipped:
|
||||
print()
|
||||
line = f"# {task_name} "
|
||||
msg = colorize(f"{line}{'*' * (line_length - len(line))}", 'bold')
|
||||
line = "# {0} ".format(task_name)
|
||||
msg = colorize("{0}{1}".format(line, '*' * (line_length - len(line))), 'bold')
|
||||
print(msg)
|
||||
|
||||
def _indent_text(self, text, indent_level):
|
||||
lines = text.splitlines()
|
||||
result_lines = []
|
||||
for l in lines:
|
||||
result_lines.append(f"{' ' * indent_level}{l}")
|
||||
result_lines.append("{0}{1}".format(' ' * indent_level, l))
|
||||
return '\n'.join(result_lines)
|
||||
|
||||
def _print_diff(self, diff, indent_level):
|
||||
@@ -146,19 +147,19 @@ class CallbackModule(CallbackBase):
|
||||
change_string = colorize('FAILED!!!', color)
|
||||
else:
|
||||
color = 'changed' if changed else 'ok'
|
||||
change_string = colorize(f"changed={changed}", color)
|
||||
change_string = colorize("changed={0}".format(changed), color)
|
||||
|
||||
msg = colorize(msg, color)
|
||||
|
||||
line_length = 120
|
||||
spaces = ' ' * (40 - len(name) - indent_level)
|
||||
line = f"{' ' * indent_level} * {name}{spaces}- {change_string}"
|
||||
line = "{0} * {1}{2}- {3}".format(' ' * indent_level, name, spaces, change_string)
|
||||
|
||||
if len(msg) < 50:
|
||||
line += f' -- {msg}'
|
||||
print(f"{line} {'-' * (line_length - len(line))}---------")
|
||||
line += ' -- {0}'.format(msg)
|
||||
print("{0} {1}---------".format(line, '-' * (line_length - len(line))))
|
||||
else:
|
||||
print(f"{line} {'-' * (line_length - len(line))}")
|
||||
print("{0} {1}".format(line, '-' * (line_length - len(line))))
|
||||
print(self._indent_text(msg, indent_level + 4))
|
||||
|
||||
if diff:
|
||||
@@ -238,10 +239,8 @@ class CallbackModule(CallbackBase):
|
||||
else:
|
||||
color = 'ok'
|
||||
|
||||
msg = (
|
||||
f"{host} : ok={s['ok']}\tchanged={s['changed']}\tfailed={s['failures']}\tunreachable="
|
||||
f"{s['unreachable']}\trescued={s['rescued']}\tignored={s['ignored']}"
|
||||
)
|
||||
msg = '{0} : ok={1}\tchanged={2}\tfailed={3}\tunreachable={4}\trescued={5}\tignored={6}'.format(
|
||||
host, s['ok'], s['changed'], s['failures'], s['unreachable'], s['rescued'], s['ignored'])
|
||||
print(colorize(msg, color))
|
||||
|
||||
def v2_runner_on_skipped(self, result, **kwargs):
|
||||
@@ -253,15 +252,17 @@ class CallbackModule(CallbackBase):
|
||||
line_length = 120
|
||||
spaces = ' ' * (31 - len(result._host.name) - 4)
|
||||
|
||||
line = f" * {colorize(result._host.name, 'not_so_bold')}{spaces}- {colorize('skipped', 'skipped')}"
|
||||
line = " * {0}{1}- {2}".format(colorize(result._host.name, 'not_so_bold'),
|
||||
spaces,
|
||||
colorize("skipped", 'skipped'),)
|
||||
|
||||
reason = result._result.get('skipped_reason', '') or \
|
||||
result._result.get('skip_reason', '')
|
||||
if len(reason) < 50:
|
||||
line += f' -- {reason}'
|
||||
print(f"{line} {'-' * (line_length - len(line))}---------")
|
||||
line += ' -- {0}'.format(reason)
|
||||
print("{0} {1}---------".format(line, '-' * (line_length - len(line))))
|
||||
else:
|
||||
print(f"{line} {'-' * (line_length - len(line))}")
|
||||
print("{0} {1}".format(line, '-' * (line_length - len(line))))
|
||||
print(self._indent_text(reason, 8))
|
||||
print(reason)
|
||||
|
||||
|
||||
@@ -5,67 +5,64 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: slack
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- prettytable (python library)
|
||||
short_description: Sends play events to a Slack channel
|
||||
description:
|
||||
- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
|
||||
options:
|
||||
http_agent:
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: slack
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- prettytable (python library)
|
||||
short_description: Sends play events to a Slack channel
|
||||
description:
|
||||
- HTTP user agent to use for requests to Slack.
|
||||
type: string
|
||||
version_added: "10.5.0"
|
||||
webhook_url:
|
||||
required: true
|
||||
description: Slack Webhook URL.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_WEBHOOK_URL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: webhook_url
|
||||
channel:
|
||||
default: "#ansible"
|
||||
description: Slack room to post in.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_CHANNEL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: channel
|
||||
username:
|
||||
description: Username to post as.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_USERNAME
|
||||
default: ansible
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: username
|
||||
validate_certs:
|
||||
description: Validate the SSL certificate of the Slack server for HTTPS URLs.
|
||||
env:
|
||||
- name: SLACK_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: validate_certs
|
||||
default: true
|
||||
type: bool
|
||||
"""
|
||||
- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
|
||||
options:
|
||||
webhook_url:
|
||||
required: true
|
||||
description: Slack Webhook URL.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_WEBHOOK_URL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: webhook_url
|
||||
channel:
|
||||
default: "#ansible"
|
||||
description: Slack room to post in.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_CHANNEL
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: channel
|
||||
username:
|
||||
description: Username to post as.
|
||||
type: str
|
||||
env:
|
||||
- name: SLACK_USERNAME
|
||||
default: ansible
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: username
|
||||
validate_certs:
|
||||
description: Validate the SSL certificate of the Slack server for HTTPS URLs.
|
||||
env:
|
||||
- name: SLACK_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_slack
|
||||
key: validate_certs
|
||||
default: true
|
||||
type: bool
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from ansible import context
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
@@ -111,7 +108,7 @@ class CallbackModule(CallbackBase):
|
||||
self.username = self.get_option('username')
|
||||
self.show_invocation = (self._display.verbosity > 1)
|
||||
self.validate_certs = self.get_option('validate_certs')
|
||||
self.http_agent = self.get_option('http_agent')
|
||||
|
||||
if self.webhook_url is None:
|
||||
self.disabled = True
|
||||
self._display.warning('Slack Webhook URL was not provided. The '
|
||||
@@ -137,22 +134,18 @@ class CallbackModule(CallbackBase):
|
||||
self._display.debug(data)
|
||||
self._display.debug(self.webhook_url)
|
||||
try:
|
||||
response = open_url(
|
||||
self.webhook_url,
|
||||
data=data,
|
||||
validate_certs=self.validate_certs,
|
||||
headers=headers,
|
||||
http_agent=self.http_agent,
|
||||
)
|
||||
response = open_url(self.webhook_url, data=data, validate_certs=self.validate_certs,
|
||||
headers=headers)
|
||||
return response.read()
|
||||
except Exception as e:
|
||||
self._display.warning(f'Could not submit message to Slack: {e}')
|
||||
self._display.warning(u'Could not submit message to Slack: %s' %
|
||||
to_text(e))
|
||||
|
||||
def v2_playbook_on_start(self, playbook):
|
||||
self.playbook_name = os.path.basename(playbook._file_name)
|
||||
|
||||
title = [
|
||||
f'*Playbook initiated* (_{self.guid}_)'
|
||||
'*Playbook initiated* (_%s_)' % self.guid
|
||||
]
|
||||
|
||||
invocation_items = []
|
||||
@@ -163,23 +156,23 @@ class CallbackModule(CallbackBase):
|
||||
subset = context.CLIARGS['subset']
|
||||
inventory = [os.path.abspath(i) for i in context.CLIARGS['inventory']]
|
||||
|
||||
invocation_items.append(f"Inventory: {', '.join(inventory)}")
|
||||
invocation_items.append('Inventory: %s' % ', '.join(inventory))
|
||||
if tags and tags != ['all']:
|
||||
invocation_items.append(f"Tags: {', '.join(tags)}")
|
||||
invocation_items.append('Tags: %s' % ', '.join(tags))
|
||||
if skip_tags:
|
||||
invocation_items.append(f"Skip Tags: {', '.join(skip_tags)}")
|
||||
invocation_items.append('Skip Tags: %s' % ', '.join(skip_tags))
|
||||
if subset:
|
||||
invocation_items.append(f'Limit: {subset}')
|
||||
invocation_items.append('Limit: %s' % subset)
|
||||
if extra_vars:
|
||||
invocation_items.append(f"Extra Vars: {' '.join(extra_vars)}")
|
||||
invocation_items.append('Extra Vars: %s' %
|
||||
' '.join(extra_vars))
|
||||
|
||||
title.append(f"by *{context.CLIARGS['remote_user']}*")
|
||||
title.append('by *%s*' % context.CLIARGS['remote_user'])
|
||||
|
||||
title.append(f'\n\n*{self.playbook_name}*')
|
||||
title.append('\n\n*%s*' % self.playbook_name)
|
||||
msg_items = [' '.join(title)]
|
||||
if invocation_items:
|
||||
_inv_item = '\n'.join(invocation_items)
|
||||
msg_items.append(f'```\n{_inv_item}\n```')
|
||||
msg_items.append('```\n%s\n```' % '\n'.join(invocation_items))
|
||||
|
||||
msg = '\n'.join(msg_items)
|
||||
|
||||
@@ -199,8 +192,8 @@ class CallbackModule(CallbackBase):
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
"""Display Play start messages"""
|
||||
|
||||
name = play.name or f'Play name not specified ({play._uuid})'
|
||||
msg = f'*Starting play* (_{self.guid}_)\n\n*{name}*'
|
||||
name = play.name or 'Play name not specified (%s)' % play._uuid
|
||||
msg = '*Starting play* (_%s_)\n\n*%s*' % (self.guid, name)
|
||||
attachments = [
|
||||
{
|
||||
'fallback': msg,
|
||||
@@ -235,7 +228,7 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
attachments = []
|
||||
msg_items = [
|
||||
f'*Playbook Complete* (_{self.guid}_)'
|
||||
'*Playbook Complete* (_%s_)' % self.guid
|
||||
]
|
||||
if failures or unreachable:
|
||||
color = 'danger'
|
||||
@@ -244,7 +237,7 @@ class CallbackModule(CallbackBase):
|
||||
color = 'good'
|
||||
msg_items.append('\n*Success!*')
|
||||
|
||||
msg_items.append(f'```\n{t}\n```')
|
||||
msg_items.append('```\n%s\n```' % t)
|
||||
|
||||
msg = '\n'.join(msg_items)
|
||||
|
||||
|
||||
@@ -3,75 +3,76 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: splunk
|
||||
type: notification
|
||||
short_description: Sends task result events to Splunk HTTP Event Collector
|
||||
author: "Stuart Hirst (!UNKNOWN) <support@convergingdata.com>"
|
||||
description:
|
||||
- This callback plugin sends task results as JSON formatted events to a Splunk HTTP collector.
|
||||
- The companion Splunk Monitoring & Diagnostics App is available here U(https://splunkbase.splunk.com/app/4023/).
|
||||
- Credit to "Ryan Currah (@ryancurrah)" for original source upon which this is based.
|
||||
requirements:
|
||||
- Whitelisting this callback plugin
|
||||
- 'Create a HTTP Event Collector in Splunk'
|
||||
- 'Define the URL and token in C(ansible.cfg)'
|
||||
options:
|
||||
url:
|
||||
description: URL to the Splunk HTTP collector source.
|
||||
type: str
|
||||
env:
|
||||
- name: SPLUNK_URL
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: url
|
||||
authtoken:
|
||||
description: Token to authenticate the connection to the Splunk HTTP collector.
|
||||
type: str
|
||||
env:
|
||||
- name: SPLUNK_AUTHTOKEN
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: authtoken
|
||||
validate_certs:
|
||||
description: Whether to validate certificates for connections to HEC. It is not recommended to set to V(false) except
|
||||
when you are sure that nobody can intercept the connection between this plugin and HEC, as setting it to V(false) allows
|
||||
man-in-the-middle attacks!
|
||||
env:
|
||||
- name: SPLUNK_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: validate_certs
|
||||
type: bool
|
||||
default: true
|
||||
version_added: '1.0.0'
|
||||
include_milliseconds:
|
||||
description: Whether to include milliseconds as part of the generated timestamp field in the event sent to the Splunk
|
||||
HTTP collector.
|
||||
env:
|
||||
- name: SPLUNK_INCLUDE_MILLISECONDS
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: include_milliseconds
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 2.0.0
|
||||
batch:
|
||||
DOCUMENTATION = '''
|
||||
name: splunk
|
||||
type: notification
|
||||
short_description: Sends task result events to Splunk HTTP Event Collector
|
||||
author: "Stuart Hirst (!UNKNOWN) <support@convergingdata.com>"
|
||||
description:
|
||||
- Correlation ID which can be set across multiple playbook executions.
|
||||
env:
|
||||
- name: SPLUNK_BATCH
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: batch
|
||||
type: str
|
||||
version_added: 3.3.0
|
||||
"""
|
||||
- This callback plugin will send task results as JSON formatted events to a Splunk HTTP collector.
|
||||
- The companion Splunk Monitoring & Diagnostics App is available here U(https://splunkbase.splunk.com/app/4023/).
|
||||
- Credit to "Ryan Currah (@ryancurrah)" for original source upon which this is based.
|
||||
requirements:
|
||||
- Whitelisting this callback plugin
|
||||
- 'Create a HTTP Event Collector in Splunk'
|
||||
- 'Define the URL and token in C(ansible.cfg)'
|
||||
options:
|
||||
url:
|
||||
description: URL to the Splunk HTTP collector source.
|
||||
type: str
|
||||
env:
|
||||
- name: SPLUNK_URL
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: url
|
||||
authtoken:
|
||||
description: Token to authenticate the connection to the Splunk HTTP collector.
|
||||
type: str
|
||||
env:
|
||||
- name: SPLUNK_AUTHTOKEN
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: authtoken
|
||||
validate_certs:
|
||||
description: Whether to validate certificates for connections to HEC. It is not recommended to set to
|
||||
V(false) except when you are sure that nobody can intercept the connection
|
||||
between this plugin and HEC, as setting it to V(false) allows man-in-the-middle attacks!
|
||||
env:
|
||||
- name: SPLUNK_VALIDATE_CERTS
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: validate_certs
|
||||
type: bool
|
||||
default: true
|
||||
version_added: '1.0.0'
|
||||
include_milliseconds:
|
||||
description: Whether to include milliseconds as part of the generated timestamp field in the event
|
||||
sent to the Splunk HTTP collector.
|
||||
env:
|
||||
- name: SPLUNK_INCLUDE_MILLISECONDS
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: include_milliseconds
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 2.0.0
|
||||
batch:
|
||||
description:
|
||||
- Correlation ID which can be set across multiple playbook executions.
|
||||
env:
|
||||
- name: SPLUNK_BATCH
|
||||
ini:
|
||||
- section: callback_splunk
|
||||
key: batch
|
||||
type: str
|
||||
version_added: 3.3.0
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: >-
|
||||
EXAMPLES = '''
|
||||
examples: >
|
||||
To enable, add this to your ansible.cfg file in the defaults block
|
||||
[defaults]
|
||||
callback_whitelist = community.general.splunk
|
||||
@@ -82,7 +83,7 @@ examples: >-
|
||||
[callback_splunk]
|
||||
url = http://mysplunkinstance.datapaas.io:8088/services/collector/event
|
||||
authtoken = f23blad6-5965-4537-bf69-5b5a545blabla88
|
||||
"""
|
||||
'''
|
||||
|
||||
import json
|
||||
import uuid
|
||||
@@ -91,7 +92,6 @@ import getpass
|
||||
|
||||
from os.path import basename
|
||||
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
@@ -105,6 +105,7 @@ class SplunkHTTPCollectorSource(object):
|
||||
def __init__(self):
|
||||
self.ansible_check_mode = False
|
||||
self.ansible_playbook = ""
|
||||
self.ansible_version = ""
|
||||
self.session = str(uuid.uuid4())
|
||||
self.host = socket.gethostname()
|
||||
self.ip_address = socket.gethostbyname(socket.gethostname())
|
||||
@@ -114,6 +115,10 @@ class SplunkHTTPCollectorSource(object):
|
||||
if result._task_fields['args'].get('_ansible_check_mode') is True:
|
||||
self.ansible_check_mode = True
|
||||
|
||||
if result._task_fields['args'].get('_ansible_version'):
|
||||
self.ansible_version = \
|
||||
result._task_fields['args'].get('_ansible_version')
|
||||
|
||||
if result._task._role:
|
||||
ansible_role = str(result._task._role)
|
||||
else:
|
||||
@@ -139,7 +144,7 @@ class SplunkHTTPCollectorSource(object):
|
||||
data['ip_address'] = self.ip_address
|
||||
data['user'] = self.user
|
||||
data['runtime'] = runtime
|
||||
data['ansible_version'] = ansible_version
|
||||
data['ansible_version'] = self.ansible_version
|
||||
data['ansible_check_mode'] = self.ansible_check_mode
|
||||
data['ansible_host'] = result._host.name
|
||||
data['ansible_playbook'] = self.ansible_playbook
|
||||
@@ -148,14 +153,15 @@ class SplunkHTTPCollectorSource(object):
|
||||
data['ansible_result'] = result._result
|
||||
|
||||
# This wraps the json payload in and outer json event needed by Splunk
|
||||
jsondata = json.dumps({"event": data}, cls=AnsibleJSONEncoder, sort_keys=True)
|
||||
jsondata = json.dumps(data, cls=AnsibleJSONEncoder, sort_keys=True)
|
||||
jsondata = '{"event":' + jsondata + "}"
|
||||
|
||||
open_url(
|
||||
url,
|
||||
jsondata,
|
||||
headers={
|
||||
'Content-type': 'application/json',
|
||||
'Authorization': f"Splunk {authtoken}"
|
||||
'Authorization': 'Splunk ' + authtoken
|
||||
},
|
||||
method='POST',
|
||||
validate_certs=validate_certs
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
DOCUMENTATION = r'''
|
||||
name: sumologic
|
||||
type: notification
|
||||
short_description: Sends task result events to Sumologic
|
||||
author: "Ryan Currah (@ryancurrah)"
|
||||
description:
|
||||
- This callback plugin sends task results as JSON formatted events to a Sumologic HTTP collector source.
|
||||
- This callback plugin will send task results as JSON formatted events to a Sumologic HTTP collector source.
|
||||
requirements:
|
||||
- Whitelisting this callback plugin
|
||||
- 'Create a HTTP collector source in Sumologic and specify a custom timestamp format of V(yyyy-MM-dd HH:mm:ss ZZZZ) and
|
||||
a custom timestamp locator of V("timestamp": "(.*\)")'
|
||||
- 'Create a HTTP collector source in Sumologic and specify a custom timestamp format of V(yyyy-MM-dd HH:mm:ss ZZZZ) and a custom timestamp locator
|
||||
of V("timestamp": "(.*\)")'
|
||||
options:
|
||||
url:
|
||||
description: URL to the Sumologic HTTP collector source.
|
||||
@@ -25,10 +26,10 @@ options:
|
||||
ini:
|
||||
- section: callback_sumologic
|
||||
key: url
|
||||
"""
|
||||
'''
|
||||
|
||||
EXAMPLES = r"""
|
||||
examples: |-
|
||||
EXAMPLES = '''
|
||||
examples: |
|
||||
To enable, add this to your ansible.cfg file in the defaults block
|
||||
[defaults]
|
||||
callback_whitelist = community.general.sumologic
|
||||
@@ -39,7 +40,7 @@ examples: |-
|
||||
Set the ansible.cfg variable in the callback_sumologic block
|
||||
[callback_sumologic]
|
||||
url = https://endpoint1.collection.us2.sumologic.com/receiver/v1/http/R8moSv1d8EW9LAUFZJ6dbxCFxwLH6kfCdcBfddlfxCbLuL-BN5twcTpMk__pYy_cDmp==
|
||||
"""
|
||||
'''
|
||||
|
||||
import json
|
||||
import uuid
|
||||
@@ -48,7 +49,6 @@ import getpass
|
||||
|
||||
from os.path import basename
|
||||
|
||||
from ansible.module_utils.ansible_release import __version__ as ansible_version
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
@@ -62,6 +62,7 @@ class SumologicHTTPCollectorSource(object):
|
||||
def __init__(self):
|
||||
self.ansible_check_mode = False
|
||||
self.ansible_playbook = ""
|
||||
self.ansible_version = ""
|
||||
self.session = str(uuid.uuid4())
|
||||
self.host = socket.gethostname()
|
||||
self.ip_address = socket.gethostbyname(socket.gethostname())
|
||||
@@ -71,6 +72,10 @@ class SumologicHTTPCollectorSource(object):
|
||||
if result._task_fields['args'].get('_ansible_check_mode') is True:
|
||||
self.ansible_check_mode = True
|
||||
|
||||
if result._task_fields['args'].get('_ansible_version'):
|
||||
self.ansible_version = \
|
||||
result._task_fields['args'].get('_ansible_version')
|
||||
|
||||
if result._task._role:
|
||||
ansible_role = str(result._task._role)
|
||||
else:
|
||||
@@ -88,7 +93,7 @@ class SumologicHTTPCollectorSource(object):
|
||||
data['ip_address'] = self.ip_address
|
||||
data['user'] = self.user
|
||||
data['runtime'] = runtime
|
||||
data['ansible_version'] = ansible_version
|
||||
data['ansible_version'] = self.ansible_version
|
||||
data['ansible_check_mode'] = self.ansible_check_mode
|
||||
data['ansible_host'] = result._host.name
|
||||
data['ansible_playbook'] = self.ansible_playbook
|
||||
|
||||
@@ -4,56 +4,57 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import annotations
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: syslog_json
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
short_description: Sends JSON events to syslog
|
||||
description:
|
||||
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format.
|
||||
options:
|
||||
server:
|
||||
description: Syslog server that receives the event.
|
||||
type: str
|
||||
env:
|
||||
- name: SYSLOG_SERVER
|
||||
default: localhost
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_server
|
||||
port:
|
||||
description: Port on which the syslog server is listening.
|
||||
type: int
|
||||
env:
|
||||
- name: SYSLOG_PORT
|
||||
default: 514
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_port
|
||||
facility:
|
||||
description: Syslog facility to log as.
|
||||
type: str
|
||||
env:
|
||||
- name: SYSLOG_FACILITY
|
||||
default: user
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_facility
|
||||
setup:
|
||||
description: Log setup tasks.
|
||||
env:
|
||||
- name: ANSIBLE_SYSLOG_SETUP
|
||||
type: bool
|
||||
default: true
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_setup
|
||||
version_added: 4.5.0
|
||||
"""
|
||||
DOCUMENTATION = '''
|
||||
author: Unknown (!UNKNOWN)
|
||||
name: syslog_json
|
||||
type: notification
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
short_description: sends JSON events to syslog
|
||||
description:
|
||||
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format.
|
||||
options:
|
||||
server:
|
||||
description: Syslog server that will receive the event.
|
||||
type: str
|
||||
env:
|
||||
- name: SYSLOG_SERVER
|
||||
default: localhost
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_server
|
||||
port:
|
||||
description: Port on which the syslog server is listening.
|
||||
type: int
|
||||
env:
|
||||
- name: SYSLOG_PORT
|
||||
default: 514
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_port
|
||||
facility:
|
||||
description: Syslog facility to log as.
|
||||
type: str
|
||||
env:
|
||||
- name: SYSLOG_FACILITY
|
||||
default: user
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_facility
|
||||
setup:
|
||||
description: Log setup tasks.
|
||||
env:
|
||||
- name: ANSIBLE_SYSLOG_SETUP
|
||||
type: bool
|
||||
default: true
|
||||
ini:
|
||||
- section: callback_syslog_json
|
||||
key: syslog_setup
|
||||
version_added: 4.5.0
|
||||
'''
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2025, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
author: Felix Fontein (@felixfontein)
|
||||
name: tasks_only
|
||||
type: stdout
|
||||
version_added: 11.1.0
|
||||
short_description: Only show tasks
|
||||
description:
|
||||
- Removes play start and stats marker from P(ansible.builtin.default#callback)'s output.
|
||||
- Can be used to generate output for documentation examples.
|
||||
For this, the O(number_of_columns) option should be set to an explicit value.
|
||||
extends_documentation_fragment:
|
||||
- default_callback
|
||||
options:
|
||||
number_of_columns:
|
||||
description:
|
||||
- Sets the number of columns for Ansible's display.
|
||||
type: int
|
||||
env:
|
||||
- name: ANSIBLE_COLLECTIONS_TASKS_ONLY_NUMBER_OF_COLUMNS
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
---
|
||||
# Enable callback in ansible.cfg:
|
||||
ansible_config: |-
|
||||
[defaults]
|
||||
stdout_callback = community.general.tasks_only
|
||||
|
||||
---
|
||||
# Enable callback with environment variables:
|
||||
environment_variable: |-
|
||||
ANSIBLE_STDOUT_CALLBACK=community.general.tasks_only
|
||||
"""
|
||||
|
||||
from ansible.plugins.callback.default import CallbackModule as Default
|
||||
|
||||
|
||||
class CallbackModule(Default):
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'stdout'
|
||||
CALLBACK_NAME = 'community.general.tasks_only'
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
pass
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
pass
|
||||
|
||||
def set_options(self, *args, **kwargs):
|
||||
result = super(CallbackModule, self).set_options(*args, **kwargs)
|
||||
self.number_of_columns = self.get_option("number_of_columns")
|
||||
if self.number_of_columns is not None:
|
||||
self._display.columns = self.number_of_columns
|
||||
return result
|
||||
@@ -5,49 +5,51 @@
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
name: timestamp
|
||||
type: stdout
|
||||
short_description: Adds simple timestamp for each header
|
||||
version_added: 9.0.0
|
||||
description:
|
||||
- This callback adds simple timestamp for each header.
|
||||
author: kurokobo (@kurokobo)
|
||||
options:
|
||||
timezone:
|
||||
description:
|
||||
- Timezone to use for the timestamp in IANA time zone format.
|
||||
- For example V(America/New_York), V(Asia/Tokyo)). Ignored on Python < 3.9.
|
||||
ini:
|
||||
- section: callback_timestamp
|
||||
key: timezone
|
||||
env:
|
||||
- name: ANSIBLE_CALLBACK_TIMESTAMP_TIMEZONE
|
||||
type: string
|
||||
format_string:
|
||||
description:
|
||||
- Format of the timestamp shown to user in 1989 C standard format.
|
||||
- Refer to L(the Python documentation,https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
|
||||
for the available format codes.
|
||||
ini:
|
||||
- section: callback_timestamp
|
||||
key: format_string
|
||||
env:
|
||||
- name: ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING
|
||||
default: "%H:%M:%S"
|
||||
type: string
|
||||
seealso:
|
||||
- plugin: ansible.posix.profile_tasks
|
||||
plugin_type: callback
|
||||
description: >-
|
||||
You can use P(ansible.posix.profile_tasks#callback) callback plugin to time individual tasks and overall execution time
|
||||
with detailed timestamps.
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.default_callback
|
||||
- ansible.builtin.result_format_callback
|
||||
name: timestamp
|
||||
type: stdout
|
||||
short_description: Adds simple timestamp for each header
|
||||
version_added: 9.0.0
|
||||
description:
|
||||
- This callback adds simple timestamp for each header.
|
||||
author: kurokobo (@kurokobo)
|
||||
options:
|
||||
timezone:
|
||||
description:
|
||||
- Timezone to use for the timestamp in IANA time zone format.
|
||||
- For example C(America/New_York), C(Asia/Tokyo)). Ignored on Python < 3.9.
|
||||
ini:
|
||||
- section: callback_timestamp
|
||||
key: timezone
|
||||
env:
|
||||
- name: ANSIBLE_CALLBACK_TIMESTAMP_TIMEZONE
|
||||
type: string
|
||||
format_string:
|
||||
description:
|
||||
- Format of the timestamp shown to user in 1989 C standard format.
|
||||
- >
|
||||
Refer to L(the Python documentation,https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
|
||||
for the available format codes.
|
||||
ini:
|
||||
- section: callback_timestamp
|
||||
key: format_string
|
||||
env:
|
||||
- name: ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING
|
||||
default: "%H:%M:%S"
|
||||
type: string
|
||||
seealso:
|
||||
- plugin: ansible.posix.profile_tasks
|
||||
plugin_type: callback
|
||||
description: >
|
||||
You can use P(ansible.posix.profile_tasks#callback) callback plugin to time individual tasks and overall execution time
|
||||
with detailed timestamps.
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.default_callback
|
||||
- ansible.builtin.result_format_callback
|
||||
"""
|
||||
|
||||
|
||||
@@ -83,7 +85,7 @@ def banner(self, msg, color=None, cows=True):
|
||||
msg = to_text(msg)
|
||||
if self.b_cowsay and cows:
|
||||
try:
|
||||
self.banner_cowsay(f"{msg} @ {timestamp}")
|
||||
self.banner_cowsay("%s @ %s" % (msg, timestamp))
|
||||
return
|
||||
except OSError:
|
||||
self.warning("somebody cleverly deleted cowsay or something during the PB run. heh.")
|
||||
@@ -96,7 +98,7 @@ def banner(self, msg, color=None, cows=True):
|
||||
if star_len <= 3:
|
||||
star_len = 3
|
||||
stars = "*" * star_len
|
||||
self.display(f"\n{msg} {stars} {timestamp}", color=color)
|
||||
self.display("\n%s %s %s" % (msg, stars, timestamp), color=color)
|
||||
|
||||
|
||||
class CallbackModule(Default):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user