mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-30 18:36:28 +00:00
Compare commits
394 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
908a9836c0 | ||
|
|
adea41d773 | ||
|
|
2dd964cab3 | ||
|
|
410101a116 | ||
|
|
ec5dd70f8b | ||
|
|
3473cb504e | ||
|
|
c329192e82 | ||
|
|
7fe5e5f02c | ||
|
|
4482b04463 | ||
|
|
05608ea658 | ||
|
|
a43132ba93 | ||
|
|
33c5e17762 | ||
|
|
42efe21600 | ||
|
|
fc02a7c74e | ||
|
|
ef16e7ea4e | ||
|
|
dce0a65ffe | ||
|
|
ab2c0992e4 | ||
|
|
2c6b2e344b | ||
|
|
d87b91e279 | ||
|
|
505cde7e6b | ||
|
|
0fcf81dd18 | ||
|
|
e62a256724 | ||
|
|
ee7ba1a691 | ||
|
|
ee428ccd64 | ||
|
|
580f8b30a8 | ||
|
|
7204798479 | ||
|
|
59b1329337 | ||
|
|
e3eaeda81a | ||
|
|
b7e68f5b2d | ||
|
|
dcb38bece0 | ||
|
|
c82c375aef | ||
|
|
d5f6889f51 | ||
|
|
ff8e67840f | ||
|
|
c1d142f543 | ||
|
|
cb9be1349a | ||
|
|
59086813c1 | ||
|
|
12319f4a82 | ||
|
|
04b29342d2 | ||
|
|
0d23fa7a49 | ||
|
|
dbec1ebf7a | ||
|
|
feefec42df | ||
|
|
a60cba237f | ||
|
|
76d011ed4b | ||
|
|
3ba00d5a07 | ||
|
|
8eb3003894 | ||
|
|
89158ce325 | ||
|
|
7f8d77b9f3 | ||
|
|
3cf90b3e36 | ||
|
|
189b737c50 | ||
|
|
c4bd7c2b7b | ||
|
|
ea427d3c82 | ||
|
|
bc011c60e6 | ||
|
|
a76537b24f | ||
|
|
327777a1da | ||
|
|
d8ee97c7e9 | ||
|
|
52ffc4eb7c | ||
|
|
be8c05bf65 | ||
|
|
bf284a01cb | ||
|
|
c1f9aeaac8 | ||
|
|
1a5ad80589 | ||
|
|
f2e2157bcc | ||
|
|
20d2488f0f | ||
|
|
7d75bb3d7e | ||
|
|
c5f8719fe9 | ||
|
|
79cfc48dd5 | ||
|
|
e0489d738a | ||
|
|
3e1cbbb617 | ||
|
|
28d7c499b5 | ||
|
|
9f218be912 | ||
|
|
363936a544 | ||
|
|
a8351fa9df | ||
|
|
4b84127ef3 | ||
|
|
82e1f24d2e | ||
|
|
9e8b6ea803 | ||
|
|
db8a6609de | ||
|
|
5be75b8e43 | ||
|
|
71b1ae0aff | ||
|
|
9b2fa2cfd7 | ||
|
|
83193ffc1f | ||
|
|
4b955a3548 | ||
|
|
c7dbda3656 | ||
|
|
e98cf96499 | ||
|
|
8d52dc3f1d | ||
|
|
5f9ea0b7ac | ||
|
|
d125f5bee6 | ||
|
|
82c5970811 | ||
|
|
f7267c7123 | ||
|
|
b6e1d04c0c | ||
|
|
2a7f9f28b9 | ||
|
|
f5c544d99c | ||
|
|
8055dd1c9b | ||
|
|
2b62826082 | ||
|
|
f7532c7d9d | ||
|
|
e556abb56b | ||
|
|
f4d8168131 | ||
|
|
6ee1f27304 | ||
|
|
714e06089e | ||
|
|
58846a6203 | ||
|
|
c7150dd818 | ||
|
|
8c7778735d | ||
|
|
e829a7384f | ||
|
|
09ae963f58 | ||
|
|
3b87f58070 | ||
|
|
bb59962061 | ||
|
|
932b657ab8 | ||
|
|
9abba2816e | ||
|
|
3ae19aa28f | ||
|
|
33133f3ba9 | ||
|
|
eb4f8d4301 | ||
|
|
cdb19ab8c1 | ||
|
|
e07f5e2f40 | ||
|
|
479e7df687 | ||
|
|
4ea40e9473 | ||
|
|
91fdc8e06a | ||
|
|
d7bb8648f3 | ||
|
|
557a4ae653 | ||
|
|
40809ed953 | ||
|
|
c7084c6c30 | ||
|
|
1bf5a44a77 | ||
|
|
8a51a3c30c | ||
|
|
85e866ccdd | ||
|
|
323a1dcff8 | ||
|
|
324c22f612 | ||
|
|
55893f27c9 | ||
|
|
0ca07b0b05 | ||
|
|
6c9713b36c | ||
|
|
160e00e5b9 | ||
|
|
cda6fc956f | ||
|
|
39895a6d38 | ||
|
|
8c069f3afb | ||
|
|
8dc5a60294 | ||
|
|
cd83b245bb | ||
|
|
92d7bd68e9 | ||
|
|
53978b7440 | ||
|
|
35b252c9ad | ||
|
|
eae7161bf0 | ||
|
|
43396efa2c | ||
|
|
b88b04593f | ||
|
|
fbebcbada5 | ||
|
|
7bf24eeb84 | ||
|
|
522fb23e06 | ||
|
|
a0d8f4de78 | ||
|
|
28b3da88a9 | ||
|
|
f4237dde1b | ||
|
|
756e8f5cca | ||
|
|
12b48aaa2d | ||
|
|
c4009deeb1 | ||
|
|
fc2e6c4b45 | ||
|
|
9489a1ea1c | ||
|
|
c3fd14e18f | ||
|
|
9a7a7a9658 | ||
|
|
721108d92e | ||
|
|
b38423c059 | ||
|
|
d0f229f5d8 | ||
|
|
4030481b36 | ||
|
|
8a9b98273d | ||
|
|
be9dcd2c85 | ||
|
|
fc530cd3f5 | ||
|
|
e7ff0acdac | ||
|
|
1beb38ceff | ||
|
|
7d97b37b21 | ||
|
|
17c7687283 | ||
|
|
6e3a56ffce | ||
|
|
bdf8852e8d | ||
|
|
bc9dca4bc8 | ||
|
|
bc1df329a2 | ||
|
|
208df2c9e6 | ||
|
|
afeeb89af6 | ||
|
|
517e2d48eb | ||
|
|
6012d2623e | ||
|
|
8fa667eeb7 | ||
|
|
568814fc3e | ||
|
|
58d89ce442 | ||
|
|
12708c3848 | ||
|
|
9021e7416d | ||
|
|
6b17ac1f30 | ||
|
|
0862511e82 | ||
|
|
a23cd6c1d5 | ||
|
|
9f1a9e306c | ||
|
|
0c03f34f54 | ||
|
|
631d215fe8 | ||
|
|
40cad3e7a9 | ||
|
|
41bd07e372 | ||
|
|
14bc13ba3c | ||
|
|
c2d3302fc4 | ||
|
|
ce6b2bc362 | ||
|
|
d6ebba1aea | ||
|
|
6303096648 | ||
|
|
f3a02b3efb | ||
|
|
8652fd9528 | ||
|
|
e7d8ef4cf9 | ||
|
|
4c8c25bc93 | ||
|
|
5fcb98cd3f | ||
|
|
7721420388 | ||
|
|
d1d9895eb6 | ||
|
|
17dce5a288 | ||
|
|
eaf3926c2c | ||
|
|
33998a5b70 | ||
|
|
aff2ebcdbc | ||
|
|
6a558734f7 | ||
|
|
55cfd27be9 | ||
|
|
bf728aadfb | ||
|
|
f9f5c45c94 | ||
|
|
eafdf87b1b | ||
|
|
5e27bbfdf6 | ||
|
|
7496466f9d | ||
|
|
a0c67a8894 | ||
|
|
5988b9acea | ||
|
|
f7176df480 | ||
|
|
fe3eec0122 | ||
|
|
e75dc74613 | ||
|
|
e7a6412ec4 | ||
|
|
a8809401ee | ||
|
|
2089769ccc | ||
|
|
d74425580b | ||
|
|
91152cb123 | ||
|
|
c6393cb2ac | ||
|
|
4fda040e9e | ||
|
|
c1f2f126cf | ||
|
|
d08924d759 | ||
|
|
478652843f | ||
|
|
4b17fd4265 | ||
|
|
c7fa11d576 | ||
|
|
d17ec06d2a | ||
|
|
fd9d948267 | ||
|
|
a942545dd2 | ||
|
|
87053e5266 | ||
|
|
3a7044e2b8 | ||
|
|
b46d5d8197 | ||
|
|
5d7899b341 | ||
|
|
00bfc3e131 | ||
|
|
e815040877 | ||
|
|
cc5e1b6fe7 | ||
|
|
17b4219b8b | ||
|
|
d7c1a814ea | ||
|
|
f9448574bd | ||
|
|
d9951cbc32 | ||
|
|
796ad3565e | ||
|
|
f6714edabb | ||
|
|
e00e82b0ca | ||
|
|
3ce4fe8dd8 | ||
|
|
0be7162848 | ||
|
|
528216fd7e | ||
|
|
8a344ea036 | ||
|
|
9abdc5a995 | ||
|
|
7020b27b0a | ||
|
|
b818afd464 | ||
|
|
c70edfa84b | ||
|
|
e0324cdc90 | ||
|
|
3a6955cbd7 | ||
|
|
ea6fb9da8f | ||
|
|
70503411ee | ||
|
|
24feb1dd10 | ||
|
|
1c4e2d51b2 | ||
|
|
e427857db7 | ||
|
|
680f3f8970 | ||
|
|
3e56da5371 | ||
|
|
4b382ed1df | ||
|
|
cc8e2d676a | ||
|
|
065ce3a134 | ||
|
|
d048ea9586 | ||
|
|
2bf85cca51 | ||
|
|
9adc82d5d1 | ||
|
|
0ae8f9d631 | ||
|
|
7609cebae9 | ||
|
|
33af903b24 | ||
|
|
43dee97e43 | ||
|
|
d074af8d09 | ||
|
|
59479b4abd | ||
|
|
7adb99855a | ||
|
|
f6ee2177a2 | ||
|
|
7748002636 | ||
|
|
704a3019b7 | ||
|
|
fc74f9a4f2 | ||
|
|
5365647ee7 | ||
|
|
dc0d00452f | ||
|
|
eb73dc464e | ||
|
|
49349fce5e | ||
|
|
e79c7e0f41 | ||
|
|
242258eb53 | ||
|
|
53c1ed184d | ||
|
|
91a681870e | ||
|
|
9d8bec14c0 | ||
|
|
601fa0df62 | ||
|
|
216962a98c | ||
|
|
99a35b1664 | ||
|
|
4d704c03df | ||
|
|
11406715f5 | ||
|
|
c34a22717e | ||
|
|
41e3f4d5fa | ||
|
|
e5dc697887 | ||
|
|
b6883492c7 | ||
|
|
92ea63e20b | ||
|
|
2d6e369d81 | ||
|
|
867704dd75 | ||
|
|
eb734d03c0 | ||
|
|
3fd4cdb119 | ||
|
|
89ad18d1a7 | ||
|
|
c4a2801f99 | ||
|
|
a53bf9d261 | ||
|
|
3571df837d | ||
|
|
70e2048d8c | ||
|
|
93f5a48b8c | ||
|
|
22efbcc627 | ||
|
|
7b404fd45d | ||
|
|
8b5dc27d23 | ||
|
|
08fa05f05f | ||
|
|
cfaeed0492 | ||
|
|
fd63f583ef | ||
|
|
f88f40086d | ||
|
|
c10fd44baf | ||
|
|
cd48e818ae | ||
|
|
806f6da16b | ||
|
|
6bff57ee6e | ||
|
|
13e9e4b196 | ||
|
|
ffb9b6ff96 | ||
|
|
068a799bba | ||
|
|
75b4539708 | ||
|
|
9642be8b19 | ||
|
|
4871140696 | ||
|
|
5d9eb8be95 | ||
|
|
24aeedbc15 | ||
|
|
fb04dc3db2 | ||
|
|
24f27a0bdf | ||
|
|
f710a10f25 | ||
|
|
e85b008036 | ||
|
|
e06a0e22f7 | ||
|
|
ccdcf70d69 | ||
|
|
3b13c30112 | ||
|
|
473e557c2f | ||
|
|
eff0cb0ed9 | ||
|
|
45eb1e3915 | ||
|
|
b271dba4bf | ||
|
|
c694abbdf9 | ||
|
|
3e0d84bdda | ||
|
|
2ed82e0318 | ||
|
|
6fc1df9b83 | ||
|
|
7ae8cc9902 | ||
|
|
8801463575 | ||
|
|
f3ecf4c7f8 | ||
|
|
bb2169340d | ||
|
|
9395df1c6f | ||
|
|
9f47cdde32 | ||
|
|
032996e005 | ||
|
|
1f6d404deb | ||
|
|
2dbe529a90 | ||
|
|
74ffb29573 | ||
|
|
2bd8469a92 | ||
|
|
42f7531f21 | ||
|
|
78c42def04 | ||
|
|
011b2f8bdc | ||
|
|
eddd1ba4f2 | ||
|
|
61b889749e | ||
|
|
994f08b37a | ||
|
|
621bedf751 | ||
|
|
7216286466 | ||
|
|
494909aba5 | ||
|
|
07a5f07eaa | ||
|
|
2cfbcb4efd | ||
|
|
58958fc417 | ||
|
|
b78d1999e1 | ||
|
|
91b692634e | ||
|
|
c4e7a943c0 | ||
|
|
36e8653cf7 | ||
|
|
16abb96bd8 | ||
|
|
f71a474726 | ||
|
|
59db302deb | ||
|
|
c9aae5e45c | ||
|
|
47f39675a9 | ||
|
|
01f21b1d46 | ||
|
|
b133aa40c6 | ||
|
|
c76af60a73 | ||
|
|
0c96d22994 | ||
|
|
57f8d84776 | ||
|
|
4c0eff02e5 | ||
|
|
486f6553f5 | ||
|
|
8586adcd51 | ||
|
|
4373f2f33b | ||
|
|
bd6cec2105 | ||
|
|
e588e675d9 | ||
|
|
64f74e1228 | ||
|
|
ee47497e97 | ||
|
|
a6dd7d789b | ||
|
|
27fe14bfc1 | ||
|
|
ab046755bb | ||
|
|
29790df583 | ||
|
|
a9fd9f8982 | ||
|
|
5f968fb075 | ||
|
|
cb1e6376db | ||
|
|
d643bd5794 | ||
|
|
08377df7a5 | ||
|
|
dca7fd4552 | ||
|
|
ddca52ee15 | ||
|
|
f833982d6f |
@@ -247,10 +247,10 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.14/{0}
|
||||
targets:
|
||||
- name: RHEL 9.0
|
||||
test: rhel/9.0
|
||||
#- name: macOS 12.0
|
||||
# test: macos/12.0
|
||||
- name: RHEL 9.0
|
||||
test: rhel/9.0
|
||||
#- name: FreeBSD 12.4
|
||||
# test: freebsd/12.4
|
||||
groups:
|
||||
|
||||
13
.github/BOTMETA.yml
vendored
13
.github/BOTMETA.yml
vendored
@@ -119,7 +119,7 @@ files:
|
||||
labels: hwc
|
||||
maintainers: $team_huawei
|
||||
$doc_fragments/nomad.py:
|
||||
maintainers: chris93111
|
||||
maintainers: chris93111 apecnascimento
|
||||
$doc_fragments/xenserver.py:
|
||||
labels: xenserver
|
||||
maintainers: bvitnik
|
||||
@@ -497,6 +497,9 @@ files:
|
||||
$modules/facter.py:
|
||||
labels: facter
|
||||
maintainers: $team_ansible_core gamethis
|
||||
$modules/facter_facts.py:
|
||||
labels: facter
|
||||
maintainers: russoz $team_ansible_core gamethis
|
||||
$modules/filesize.py:
|
||||
maintainers: quidame
|
||||
$modules/filesystem.py:
|
||||
@@ -523,6 +526,8 @@ files:
|
||||
maintainers: russoz
|
||||
$modules/git_config.py:
|
||||
maintainers: djmattyg007 mgedmin
|
||||
$modules/git_config_info.py:
|
||||
maintainers: guenhter
|
||||
$modules/github_:
|
||||
maintainers: stpierre
|
||||
$modules/github_deploy_key.py:
|
||||
@@ -544,6 +549,8 @@ files:
|
||||
ignore: dj-wasabi
|
||||
$modules/gitlab_branch.py:
|
||||
maintainers: paytroff
|
||||
$modules/gitlab_issue.py:
|
||||
maintainers: zvaraondrej
|
||||
$modules/gitlab_merge_request.py:
|
||||
maintainers: zvaraondrej
|
||||
$modules/gitlab_project_variable.py:
|
||||
@@ -867,7 +874,7 @@ files:
|
||||
$modules/nmcli.py:
|
||||
maintainers: alcamie101
|
||||
$modules/nomad_:
|
||||
maintainers: chris93111
|
||||
maintainers: chris93111 apecnascimento
|
||||
$modules/nosh.py:
|
||||
maintainers: tacatac
|
||||
$modules/npm.py:
|
||||
@@ -1396,6 +1403,8 @@ files:
|
||||
maintainers: $team_suse
|
||||
$tests/a_module.py:
|
||||
maintainers: felixfontein
|
||||
$tests/fqdn_valid.py:
|
||||
maintainers: vbotka
|
||||
#########################
|
||||
tests/:
|
||||
labels: tests
|
||||
|
||||
120
.github/workflows/ansible-test.yml
vendored
120
.github/workflows/ansible-test.yml
vendored
@@ -14,9 +14,9 @@ on:
|
||||
- main
|
||||
- stable-*
|
||||
pull_request:
|
||||
# Run EOL CI once per day (at 10:00 UTC)
|
||||
# Run EOL CI once per day (at 08:00 UTC)
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
- cron: '0 8 * * *'
|
||||
|
||||
concurrency:
|
||||
# Make sure there is at most one active run per PR, but do not cancel any non-PR runs
|
||||
@@ -29,23 +29,17 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
ansible:
|
||||
- '2.11'
|
||||
- '2.12'
|
||||
- '2.13'
|
||||
# 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: >-
|
||||
${{ contains(fromJson(
|
||||
'["2.9", "2.10", "2.11"]'
|
||||
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Perform sanity testing
|
||||
uses: felixfontein/ansible-test-gh-action@main
|
||||
with:
|
||||
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
|
||||
ansible-core-version: stable-${{ matrix.ansible }}
|
||||
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
|
||||
pull-request-change-detection: 'true'
|
||||
@@ -57,10 +51,7 @@ jobs:
|
||||
# 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: >-
|
||||
${{ contains(fromJson(
|
||||
'["2.9", "2.10", "2.11"]'
|
||||
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
name: EOL Units (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }})
|
||||
strategy:
|
||||
# As soon as the first unit test fails, cancel the others to free up the CI queue
|
||||
@@ -73,13 +64,9 @@ jobs:
|
||||
exclude:
|
||||
- ansible: ''
|
||||
include:
|
||||
- ansible: '2.11'
|
||||
- ansible: '2.13'
|
||||
python: '2.7'
|
||||
- ansible: '2.11'
|
||||
python: '3.5'
|
||||
- ansible: '2.12'
|
||||
python: '2.6'
|
||||
- ansible: '2.12'
|
||||
- ansible: '2.13'
|
||||
python: '3.8'
|
||||
- ansible: '2.13'
|
||||
python: '2.7'
|
||||
@@ -92,7 +79,6 @@ jobs:
|
||||
Ansible version ${{ matrix.ansible }}
|
||||
uses: felixfontein/ansible-test-gh-action@main
|
||||
with:
|
||||
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
|
||||
ansible-core-version: stable-${{ matrix.ansible }}
|
||||
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
|
||||
pre-test-cmd: >-
|
||||
@@ -109,10 +95,7 @@ jobs:
|
||||
# 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: >-
|
||||
${{ contains(fromJson(
|
||||
'["2.9", "2.10", "2.11"]'
|
||||
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
name: EOL I (Ⓐ${{ matrix.ansible }}+${{ matrix.docker }}+py${{ matrix.python }}:${{ matrix.target }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -128,94 +111,6 @@ jobs:
|
||||
exclude:
|
||||
- ansible: ''
|
||||
include:
|
||||
# 2.11
|
||||
- ansible: '2.11'
|
||||
docker: fedora32
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.11'
|
||||
docker: fedora32
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.11'
|
||||
docker: fedora32
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.11'
|
||||
docker: fedora33
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.11'
|
||||
docker: fedora33
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.11'
|
||||
docker: fedora33
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.11'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.11'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.11'
|
||||
docker: alpine3
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - ansible: '2.11'
|
||||
# docker: default
|
||||
# python: '2.7'
|
||||
# target: azp/generic/1/
|
||||
# - ansible: '2.11'
|
||||
# docker: default
|
||||
# python: '3.5'
|
||||
# target: azp/generic/1/
|
||||
# 2.12
|
||||
- ansible: '2.12'
|
||||
docker: centos6
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.12'
|
||||
docker: centos6
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.12'
|
||||
docker: centos6
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.12'
|
||||
docker: fedora34
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.12'
|
||||
docker: fedora34
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.12'
|
||||
docker: fedora34
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
- ansible: '2.12'
|
||||
docker: ubuntu1804
|
||||
python: ''
|
||||
target: azp/posix/1/
|
||||
- ansible: '2.12'
|
||||
docker: ubuntu1804
|
||||
python: ''
|
||||
target: azp/posix/2/
|
||||
- ansible: '2.12'
|
||||
docker: ubuntu1804
|
||||
python: ''
|
||||
target: azp/posix/3/
|
||||
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
|
||||
# - ansible: '2.12'
|
||||
# docker: default
|
||||
# python: '3.8'
|
||||
# target: azp/generic/1/
|
||||
# 2.13
|
||||
- ansible: '2.13'
|
||||
docker: fedora35
|
||||
@@ -266,7 +161,6 @@ jobs:
|
||||
under Python ${{ matrix.python }}
|
||||
uses: felixfontein/ansible-test-gh-action@main
|
||||
with:
|
||||
ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
|
||||
ansible-core-version: stable-${{ matrix.ansible }}
|
||||
coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
|
||||
docker-image: ${{ matrix.docker }}
|
||||
|
||||
921
CHANGELOG.rst
921
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
20
README.md
20
README.md
@@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Community General Collection
|
||||
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
|
||||
[](https://github.com/ansible-collections/community.general/actions)
|
||||
[](https://codecov.io/gh/ansible-collections/community.general)
|
||||
|
||||
@@ -24,9 +24,7 @@ If you encounter abusive behavior violating the [Ansible Code of Conduct](https:
|
||||
|
||||
## Tested with Ansible
|
||||
|
||||
Tested with the current ansible-core 2.11, ansible-core 2.12, ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16 releases and the current development version of ansible-core. Ansible-core versions before 2.11.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
|
||||
|
||||
Parts of this collection will not work with ansible-core 2.11 on Python 3.12+.
|
||||
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16 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
|
||||
|
||||
@@ -73,13 +71,13 @@ We are actively accepting new contributors.
|
||||
|
||||
All types of contributions are very welcome.
|
||||
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/stable-7/CONTRIBUTING.md)!
|
||||
You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md)!
|
||||
|
||||
The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/stable-7/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals.
|
||||
The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals.
|
||||
|
||||
You can find more information in the [developer guide for collections](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections), and in the [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html).
|
||||
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/stable-7/CONTRIBUTING.md).
|
||||
Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md).
|
||||
|
||||
### Running tests
|
||||
|
||||
@@ -89,7 +87,7 @@ See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collectio
|
||||
|
||||
To learn how to maintain / become a maintainer of this collection, refer to:
|
||||
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/stable-7/commit-rights.md).
|
||||
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md).
|
||||
* [Maintainer guidelines](https://github.com/ansible/community-docs/blob/main/maintaining.rst).
|
||||
|
||||
It is necessary for maintainers of this collection to be subscribed to:
|
||||
@@ -117,7 +115,7 @@ See the [Releasing guidelines](https://github.com/ansible/community-docs/blob/ma
|
||||
|
||||
## Release notes
|
||||
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-7/CHANGELOG.rst).
|
||||
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-8/CHANGELOG.rst).
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -136,8 +134,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues
|
||||
|
||||
This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later.
|
||||
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-7/COPYING) for the full text.
|
||||
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/main/COPYING) for the full text.
|
||||
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-7/LICENSES/PSF-2.0.txt).
|
||||
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/PSF-2.0.txt).
|
||||
|
||||
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
12
galaxy.yml
12
galaxy.yml
@@ -5,17 +5,17 @@
|
||||
|
||||
namespace: community
|
||||
name: general
|
||||
version: 7.5.2
|
||||
version: 8.1.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
description: null
|
||||
description: >-
|
||||
The community.general collection is a part of the Ansible package and includes many modules and
|
||||
plugins supported by Ansible community which are not part of more specialized community collections.
|
||||
license_file: COPYING
|
||||
tags: [community]
|
||||
# NOTE: No dependencies are expected to be added here
|
||||
# dependencies:
|
||||
tags:
|
||||
- community
|
||||
repository: https://github.com/ansible-collections/community.general
|
||||
documentation: https://docs.ansible.com/ansible/latest/collections/community/general/
|
||||
homepage: https://github.com/ansible-collections/community.general
|
||||
issues: https://github.com/ansible-collections/community.general/issues
|
||||
#type: flatmap
|
||||
|
||||
@@ -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.11.0'
|
||||
requires_ansible: '>=2.13.0'
|
||||
plugin_routing:
|
||||
connection:
|
||||
docker:
|
||||
|
||||
@@ -18,8 +18,6 @@ DOCUMENTATION = '''
|
||||
short_description: notify using software speech synthesizer
|
||||
description:
|
||||
- This plugin will use the C(say) or C(espeak) program to "speak" about play events.
|
||||
notes:
|
||||
- In Ansible 2.8, this callback has been renamed from C(osx_say) into M(community.general.say).
|
||||
'''
|
||||
|
||||
import platform
|
||||
|
||||
@@ -44,26 +44,17 @@ from ansible import constants as C
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
|
||||
try:
|
||||
codeCodes = C.COLOR_CODES
|
||||
except AttributeError:
|
||||
# This constant was moved to ansible.constants in
|
||||
# https://github.com/ansible/ansible/commit/1202dd000f10b0e8959019484f1c3b3f9628fc67
|
||||
# (will be included in ansible-core 2.11.0). For older Ansible/ansible-base versions,
|
||||
# we include from the original location.
|
||||
from ansible.utils.color import codeCodes
|
||||
|
||||
|
||||
DONT_COLORIZE = False
|
||||
COLORS = {
|
||||
'normal': '\033[0m',
|
||||
'ok': '\033[{0}m'.format(codeCodes[C.COLOR_OK]),
|
||||
'ok': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_OK]),
|
||||
'bold': '\033[1m',
|
||||
'not_so_bold': '\033[1m\033[34m',
|
||||
'changed': '\033[{0}m'.format(codeCodes[C.COLOR_CHANGED]),
|
||||
'failed': '\033[{0}m'.format(codeCodes[C.COLOR_ERROR]),
|
||||
'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': '\033[{0}m'.format(codeCodes[C.COLOR_SKIP]),
|
||||
'skipped': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_SKIP]),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ DOCUMENTATION = '''
|
||||
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.
|
||||
- Before Ansible 2.4 only environment variables were available for configuring this plugin.
|
||||
options:
|
||||
webhook_url:
|
||||
required: true
|
||||
|
||||
@@ -16,7 +16,6 @@ DOCUMENTATION = '''
|
||||
short_description: sends JSON events to syslog
|
||||
description:
|
||||
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format.
|
||||
- Before Ansible 2.9 only environment variables were available for configuration.
|
||||
options:
|
||||
server:
|
||||
description: Syslog server that will receive the event.
|
||||
|
||||
@@ -71,10 +71,11 @@ class Connection(ConnectionBase):
|
||||
msg = "lxc python bindings are not installed"
|
||||
raise errors.AnsibleError(msg)
|
||||
|
||||
if self.container:
|
||||
container_name = self.get_option('remote_addr')
|
||||
if self.container and self.container_name == container_name:
|
||||
return
|
||||
|
||||
self.container_name = self.get_option('remote_addr')
|
||||
self.container_name = container_name
|
||||
|
||||
self._display.vvv("THIS IS A LOCAL LXC DIR", host=self.container_name)
|
||||
self.container = _lxc.Container(self.container_name)
|
||||
|
||||
@@ -16,7 +16,9 @@ DOCUMENTATION = '''
|
||||
options:
|
||||
remote_addr:
|
||||
description:
|
||||
- Container identifier.
|
||||
- Instance (container/VM) identifier.
|
||||
- Since community.general 8.0.0, a FQDN can be provided; in that case, the first component (the part before C(.))
|
||||
is used as the instance identifier.
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: inventory_hostname
|
||||
@@ -71,30 +73,36 @@ class Connection(ConnectionBase):
|
||||
if self._play_context.remote_user is not None and self._play_context.remote_user != 'root':
|
||||
self._display.warning('lxd does not support remote_user, using container default: root')
|
||||
|
||||
def _host(self):
|
||||
""" translate remote_addr to lxd (short) hostname """
|
||||
return self.get_option("remote_addr").split(".", 1)[0]
|
||||
|
||||
def _connect(self):
|
||||
"""connect to lxd (nothing to do here) """
|
||||
super(Connection, self)._connect()
|
||||
|
||||
if not self._connected:
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self._host())
|
||||
self._connected = True
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
""" execute a command on the lxd host """
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self._host())
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
local_cmd.extend(["--project", self.get_option("project")])
|
||||
local_cmd.extend([
|
||||
"exec",
|
||||
"%s:%s" % (self.get_option("remote"), self.get_option("remote_addr")),
|
||||
"%s:%s" % (self.get_option("remote"), self._host()),
|
||||
"--",
|
||||
self.get_option("executable"), "-c", cmd
|
||||
])
|
||||
|
||||
self._display.vvvvv(u"EXEC {0}".format(local_cmd), host=self._host())
|
||||
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
in_data = to_bytes(in_data, errors='surrogate_or_strict', nonstring='passthru')
|
||||
|
||||
@@ -104,11 +112,13 @@ class Connection(ConnectionBase):
|
||||
stdout = to_text(stdout)
|
||||
stderr = to_text(stderr)
|
||||
|
||||
if stderr == "error: Container is not running.\n":
|
||||
raise AnsibleConnectionFailure("container not running: %s" % self.get_option('remote_addr'))
|
||||
self._display.vvvvv(u"EXEC lxc output: {0} {1}".format(stdout, stderr), host=self._host())
|
||||
|
||||
if stderr == "error: not found\n":
|
||||
raise AnsibleConnectionFailure("container not found: %s" % self.get_option('remote_addr'))
|
||||
if "is not running" in stderr:
|
||||
raise AnsibleConnectionFailure("instance not running: %s" % self._host())
|
||||
|
||||
if stderr.strip() == "Error: Instance not found" or stderr.strip() == "error: not found":
|
||||
raise AnsibleConnectionFailure("instance not found: %s" % self._host())
|
||||
|
||||
return process.returncode, stdout, stderr
|
||||
|
||||
@@ -116,7 +126,7 @@ class Connection(ConnectionBase):
|
||||
""" put a file from local to lxd """
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._host())
|
||||
|
||||
if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')):
|
||||
raise AnsibleFileNotFound("input path is not a file: %s" % in_path)
|
||||
@@ -127,7 +137,7 @@ class Connection(ConnectionBase):
|
||||
local_cmd.extend([
|
||||
"file", "push",
|
||||
in_path,
|
||||
"%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), out_path)
|
||||
"%s:%s/%s" % (self.get_option("remote"), self._host(), out_path)
|
||||
])
|
||||
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
@@ -139,14 +149,14 @@ class Connection(ConnectionBase):
|
||||
""" fetch a file from lxd to local """
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self._host())
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
local_cmd.extend(["--project", self.get_option("project")])
|
||||
local_cmd.extend([
|
||||
"file", "pull",
|
||||
"%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), in_path),
|
||||
"%s:%s/%s" % (self.get_option("remote"), self._host(), in_path),
|
||||
out_path
|
||||
])
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ options:
|
||||
aliases: ['assume_role']
|
||||
alicloud_assume_role_arn:
|
||||
description:
|
||||
- The Alibaba Cloud role_arn. The ARN of the role to assume. If ARN is set to an empty string,
|
||||
- The Alibaba Cloud C(role_arn). The ARN of the role to assume. If ARN is set to an empty string,
|
||||
it does not perform role switching. It supports environment variable E(ALICLOUD_ASSUME_ROLE_ARN).
|
||||
ansible will execute with provided credentials.
|
||||
aliases: ['assume_role_arn']
|
||||
@@ -61,7 +61,7 @@ options:
|
||||
type: str
|
||||
alicloud_assume_role_session_expiration:
|
||||
description:
|
||||
- The Alibaba Cloud session_expiration. The time after which the established session for assuming
|
||||
- The Alibaba Cloud C(session_expiration). The time after which the established session for assuming
|
||||
role expires. Valid value range 900-3600 seconds. Default to 3600 (in this case Alicloud use own default
|
||||
value). It supports environment variable E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
|
||||
aliases: ['assume_role_session_expiration']
|
||||
@@ -85,12 +85,12 @@ options:
|
||||
description:
|
||||
- This is the path to the shared credentials file. It can also be sourced from the E(ALICLOUD_SHARED_CREDENTIALS_FILE)
|
||||
environment variable.
|
||||
- If this is not set and a profile is specified, ~/.aliyun/config.json will be used.
|
||||
- If this is not set and a profile is specified, C(~/.aliyun/config.json) will be used.
|
||||
type: str
|
||||
author:
|
||||
- "He Guimin (@xiaozhu36)"
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "Python >= 3.6"
|
||||
notes:
|
||||
- If parameters are not set within the module, the following
|
||||
environment variables can be used in decreasing order of precedence
|
||||
@@ -103,7 +103,7 @@ notes:
|
||||
E(ALICLOUD_PROFILE),
|
||||
E(ALICLOUD_ASSUME_ROLE_ARN),
|
||||
E(ALICLOUD_ASSUME_ROLE_SESSION_NAME),
|
||||
E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION),
|
||||
E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
|
||||
- E(ALICLOUD_REGION) or E(ALICLOUD_REGION_ID) can be typically be used to specify the
|
||||
ALICLOUD region, when required, but this can also be configured in the footmark config file
|
||||
Alicloud region, when required, but this can also be configured in the footmark config file
|
||||
'''
|
||||
|
||||
@@ -14,19 +14,19 @@ class ModuleDocFragment(object):
|
||||
options:
|
||||
api_url:
|
||||
description:
|
||||
- The resolvable endpoint for the API
|
||||
- The resolvable endpoint for the API.
|
||||
type: str
|
||||
api_username:
|
||||
description:
|
||||
- The username to use for authentication against the API
|
||||
- The username to use for authentication against the API.
|
||||
type: str
|
||||
api_password:
|
||||
description:
|
||||
- The password to use for authentication against the API
|
||||
- The password to use for authentication against the API.
|
||||
type: str
|
||||
validate_certs:
|
||||
description:
|
||||
- Whether or not to validate SSL certs when supplying a https endpoint.
|
||||
- Whether or not to validate SSL certs when supplying a HTTPS endpoint.
|
||||
type: bool
|
||||
default: true
|
||||
'''
|
||||
|
||||
@@ -20,10 +20,10 @@ options:
|
||||
region:
|
||||
description:
|
||||
- The target region.
|
||||
- Regions are defined in Apache libcloud project [libcloud/common/dimensiondata.py]
|
||||
- They are also listed in U(https://libcloud.readthedocs.io/en/latest/compute/drivers/dimensiondata.html)
|
||||
- Note that the default value "na" stands for "North America".
|
||||
- The module prepends 'dd-' to the region choice.
|
||||
- Regions are defined in Apache libcloud project [libcloud/common/dimensiondata.py].
|
||||
- They are also listed in U(https://libcloud.readthedocs.io/en/latest/compute/drivers/dimensiondata.html).
|
||||
- Note that the default value C(na) stands for "North America".
|
||||
- The module prepends C(dd-) to the region choice.
|
||||
type: str
|
||||
default: na
|
||||
mcp_user:
|
||||
|
||||
@@ -34,4 +34,4 @@ options:
|
||||
- Only applicable if O(wait=true).
|
||||
type: int
|
||||
default: 2
|
||||
'''
|
||||
'''
|
||||
|
||||
@@ -39,8 +39,7 @@ options:
|
||||
default: sysadmin
|
||||
requirements:
|
||||
- An EMC VNX Storage device.
|
||||
- Ansible 2.7.
|
||||
- storops (0.5.10 or greater). Install using 'pip install storops'.
|
||||
- storops (0.5.10 or greater). Install using C(pip install storops).
|
||||
notes:
|
||||
- The modules prefixed with emc_vnx are built to support the EMC VNX storage platform.
|
||||
- The modules prefixed with C(emc_vnx) are built to support the EMC VNX storage platform.
|
||||
'''
|
||||
|
||||
@@ -29,4 +29,9 @@ options:
|
||||
- GitLab CI job token for logging in.
|
||||
type: str
|
||||
version_added: 4.2.0
|
||||
ca_path:
|
||||
description:
|
||||
- The CA certificates bundle to use to verify GitLab server certificate.
|
||||
type: str
|
||||
version_added: 8.1.0
|
||||
'''
|
||||
|
||||
@@ -19,8 +19,8 @@ options:
|
||||
required: true
|
||||
user:
|
||||
description:
|
||||
- The user name to login with (currently only user names are
|
||||
supported, and not user IDs).
|
||||
- The user name to login with.
|
||||
- Currently only user names are supported, and not user IDs.
|
||||
type: str
|
||||
required: true
|
||||
password:
|
||||
@@ -31,14 +31,13 @@ options:
|
||||
domain:
|
||||
description:
|
||||
- The name of the Domain to scope to (Identity v3).
|
||||
(currently only domain names are supported, and not domain IDs).
|
||||
- Currently only domain names are supported, and not domain IDs.
|
||||
type: str
|
||||
required: true
|
||||
project:
|
||||
description:
|
||||
- The name of the Tenant (Identity v2) or Project (Identity v3).
|
||||
(currently only project names are supported, and not
|
||||
project IDs).
|
||||
- Currently only project names are supported, and not project IDs.
|
||||
type: str
|
||||
required: true
|
||||
region:
|
||||
@@ -47,20 +46,20 @@ options:
|
||||
type: str
|
||||
id:
|
||||
description:
|
||||
- The id of resource to be managed.
|
||||
- The ID of resource to be managed.
|
||||
type: str
|
||||
notes:
|
||||
- For authentication, you can set identity_endpoint using the
|
||||
E(ANSIBLE_HWC_IDENTITY_ENDPOINT) env variable.
|
||||
E(ANSIBLE_HWC_IDENTITY_ENDPOINT) environment variable.
|
||||
- For authentication, you can set user using the
|
||||
E(ANSIBLE_HWC_USER) env variable.
|
||||
- For authentication, you can set password using the E(ANSIBLE_HWC_PASSWORD) env
|
||||
E(ANSIBLE_HWC_USER) environment variable.
|
||||
- For authentication, you can set password using the E(ANSIBLE_HWC_PASSWORD) environment
|
||||
variable.
|
||||
- For authentication, you can set domain using the E(ANSIBLE_HWC_DOMAIN) env
|
||||
- For authentication, you can set domain using the E(ANSIBLE_HWC_DOMAIN) environment
|
||||
variable.
|
||||
- For authentication, you can set project using the E(ANSIBLE_HWC_PROJECT) env
|
||||
- For authentication, you can set project using the E(ANSIBLE_HWC_PROJECT) environment
|
||||
variable.
|
||||
- For authentication, you can set region using the E(ANSIBLE_HWC_REGION) env variable.
|
||||
- For authentication, you can set region using the E(ANSIBLE_HWC_REGION) environment variable.
|
||||
- Environment variables values will only be used if the playbook values are
|
||||
not set.
|
||||
'''
|
||||
|
||||
@@ -31,8 +31,7 @@ options:
|
||||
required: true
|
||||
notes:
|
||||
- This module requires pyxcli python library.
|
||||
Use 'pip install pyxcli' in order to get pyxcli.
|
||||
Use C(pip install pyxcli) in order to get pyxcli.
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- pyxcli
|
||||
'''
|
||||
|
||||
@@ -16,32 +16,29 @@ options:
|
||||
hostname:
|
||||
description:
|
||||
- The hostname or IP address on which InfluxDB server is listening.
|
||||
- Since Ansible 2.5, defaulted to localhost.
|
||||
type: str
|
||||
default: localhost
|
||||
username:
|
||||
description:
|
||||
- Username that will be used to authenticate against InfluxDB server.
|
||||
- Alias O(login_username) added in Ansible 2.5.
|
||||
type: str
|
||||
default: root
|
||||
aliases: [ login_username ]
|
||||
password:
|
||||
description:
|
||||
- Password that will be used to authenticate against InfluxDB server.
|
||||
- Alias O(login_password) added in Ansible 2.5.
|
||||
type: str
|
||||
default: root
|
||||
aliases: [ login_password ]
|
||||
port:
|
||||
description:
|
||||
- The port on which InfluxDB server is listening
|
||||
- The port on which InfluxDB server is listening.
|
||||
type: int
|
||||
default: 8086
|
||||
path:
|
||||
description:
|
||||
- The path on which InfluxDB server is accessible
|
||||
- Only available when using python-influxdb >= 5.1.0
|
||||
- The path on which InfluxDB server is accessible.
|
||||
- Only available when using python-influxdb >= 5.1.0.
|
||||
type: str
|
||||
default: ''
|
||||
version_added: '0.2.0'
|
||||
@@ -64,7 +61,7 @@ options:
|
||||
description:
|
||||
- Number of retries client will try before aborting.
|
||||
- V(0) indicates try until success.
|
||||
- Only available when using python-influxdb >= 4.1.0
|
||||
- Only available when using python-influxdb >= 4.1.0.
|
||||
type: int
|
||||
default: 3
|
||||
use_udp:
|
||||
|
||||
@@ -18,7 +18,6 @@ options:
|
||||
- Port of FreeIPA / IPA server.
|
||||
- If the value is not specified in the task, the value of environment variable E(IPA_PORT) will be used instead.
|
||||
- If both the environment variable E(IPA_PORT) and the value are not specified in the task, then default value is set.
|
||||
- Environment variable fallback mechanism is added in Ansible 2.5.
|
||||
type: int
|
||||
default: 443
|
||||
ipa_host:
|
||||
@@ -26,9 +25,8 @@ options:
|
||||
- IP or hostname of IPA server.
|
||||
- If the value is not specified in the task, the value of environment variable E(IPA_HOST) will be used instead.
|
||||
- If both the environment variable E(IPA_HOST) and the value are not specified in the task, then DNS will be used to try to discover the FreeIPA server.
|
||||
- The relevant entry needed in FreeIPA is the 'ipa-ca' entry.
|
||||
- The relevant entry needed in FreeIPA is the C(ipa-ca) entry.
|
||||
- If neither the DNS entry, nor the environment E(IPA_HOST), nor the value are available in the task, then the default value will be used.
|
||||
- Environment variable fallback mechanism is added in Ansible 2.5.
|
||||
type: str
|
||||
default: ipa.example.com
|
||||
ipa_user:
|
||||
@@ -36,7 +34,6 @@ options:
|
||||
- Administrative account used on IPA server.
|
||||
- If the value is not specified in the task, the value of environment variable E(IPA_USER) will be used instead.
|
||||
- If both the environment variable E(IPA_USER) and the value are not specified in the task, then default value is set.
|
||||
- Environment variable fallback mechanism is added in Ansible 2.5.
|
||||
type: str
|
||||
default: admin
|
||||
ipa_pass:
|
||||
@@ -47,14 +44,12 @@ options:
|
||||
- If the environment variable E(KRB5CCNAME) is available, the module will use this kerberos credentials cache to authenticate to the FreeIPA server.
|
||||
- If the environment variable E(KRB5_CLIENT_KTNAME) is available, and E(KRB5CCNAME) is not; the module will use this kerberos keytab to authenticate.
|
||||
- If GSSAPI is not available, the usage of O(ipa_pass) is required.
|
||||
- Environment variable fallback mechanism is added in Ansible 2.5.
|
||||
type: str
|
||||
ipa_prot:
|
||||
description:
|
||||
- Protocol used by IPA server.
|
||||
- If the value is not specified in the task, the value of environment variable E(IPA_PROT) will be used instead.
|
||||
- If both the environment variable E(IPA_PROT) and the value are not specified in the task, then default value is set.
|
||||
- Environment variable fallback mechanism is added in Ansible 2.5.
|
||||
type: str
|
||||
choices: [ http, https ]
|
||||
default: https
|
||||
|
||||
@@ -69,6 +69,7 @@ options:
|
||||
type: int
|
||||
default: 10
|
||||
version_added: 4.5.0
|
||||
|
||||
http_agent:
|
||||
description:
|
||||
- Configures the HTTP User-Agent header.
|
||||
|
||||
@@ -30,7 +30,7 @@ options:
|
||||
|
||||
auth_url:
|
||||
description:
|
||||
- lxca https full web address
|
||||
- lxca HTTPS full web address.
|
||||
type: str
|
||||
required: true
|
||||
|
||||
@@ -38,7 +38,6 @@ requirements:
|
||||
- pylxca
|
||||
|
||||
notes:
|
||||
- Additional detail about pylxca can be found at U(https://github.com/lenovo/pylxca)
|
||||
- Playbooks using these modules can be found at U(https://github.com/lenovo/ansible.lenovo-lxca)
|
||||
- Check mode is not supported.
|
||||
- Additional detail about pylxca can be found at U(https://github.com/lenovo/pylxca).
|
||||
- Playbooks using these modules can be found at U(https://github.com/lenovo/ansible.lenovo-lxca).
|
||||
'''
|
||||
|
||||
@@ -18,6 +18,12 @@ options:
|
||||
- FQDN of Nomad server.
|
||||
required: true
|
||||
type: str
|
||||
port:
|
||||
description:
|
||||
- Port of Nomad server.
|
||||
type: int
|
||||
default: 4646
|
||||
version_added: 8.0.0
|
||||
use_ssl:
|
||||
description:
|
||||
- Use TLS/SSL connection.
|
||||
|
||||
75
plugins/doc_fragments/onepassword.py
Normal file
75
plugins/doc_fragments/onepassword.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023, 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
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
DOCUMENTATION = r'''
|
||||
requirements:
|
||||
- See U(https://support.1password.com/command-line/)
|
||||
options:
|
||||
master_password:
|
||||
description: The password used to unlock the specified vault.
|
||||
aliases: ['vault_password']
|
||||
type: str
|
||||
section:
|
||||
description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section.
|
||||
domain:
|
||||
description: Domain of 1Password.
|
||||
default: '1password.com'
|
||||
type: str
|
||||
subdomain:
|
||||
description: The 1Password subdomain to authenticate against.
|
||||
type: str
|
||||
account_id:
|
||||
description: The account ID to target.
|
||||
type: str
|
||||
username:
|
||||
description: The username used to sign in.
|
||||
type: str
|
||||
secret_key:
|
||||
description: The secret key used when performing an initial sign in.
|
||||
type: str
|
||||
service_account_token:
|
||||
description:
|
||||
- The access key for a service account.
|
||||
- Only works with 1Password CLI version 2 or later.
|
||||
type: str
|
||||
vault:
|
||||
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
|
||||
type: str
|
||||
connect_host:
|
||||
description: The host for 1Password Connect. Must be used in combination with O(connect_token).
|
||||
type: str
|
||||
env:
|
||||
- name: OP_CONNECT_HOST
|
||||
version_added: 8.1.0
|
||||
connect_token:
|
||||
description: The token for 1Password Connect. Must be used in combination with O(connect_host).
|
||||
type: str
|
||||
env:
|
||||
- name: OP_CONNECT_TOKEN
|
||||
version_added: 8.1.0
|
||||
'''
|
||||
|
||||
LOOKUP = r'''
|
||||
options: {}
|
||||
notes:
|
||||
- This lookup will use an existing 1Password session if one exists. If not, and you have already
|
||||
performed an initial sign in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the
|
||||
O(master_password) is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
|
||||
- This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password).
|
||||
- Can target a specific account by providing the O(account_id).
|
||||
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials
|
||||
needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength
|
||||
to the 1Password master password.
|
||||
- This lookup stores potentially sensitive data from 1Password as Ansible facts.
|
||||
Facts are subject to caching if enabled, which means this data could be stored in clear text
|
||||
on disk or in a database.
|
||||
- Tested with C(op) version 2.7.2.
|
||||
'''
|
||||
@@ -15,7 +15,7 @@ class ModuleDocFragment(object):
|
||||
options:
|
||||
config:
|
||||
description:
|
||||
- Path to a .json configuration file containing the OneView client configuration.
|
||||
- Path to a JSON configuration file containing the OneView client configuration.
|
||||
The configuration file is optional and when used should be present in the host running the ansible commands.
|
||||
If the file path is not provided, the configuration will be loaded from environment variables.
|
||||
For links to example configuration files or how to use the environment variables verify the notes section.
|
||||
@@ -42,7 +42,7 @@ options:
|
||||
type: str
|
||||
|
||||
requirements:
|
||||
- python >= 2.7.9
|
||||
- Python >= 2.7.9
|
||||
|
||||
notes:
|
||||
- "A sample configuration file for the config parameter can be found at:
|
||||
@@ -70,11 +70,11 @@ options:
|
||||
options:
|
||||
params:
|
||||
description:
|
||||
- List of params to delimit, filter and sort the list of resources.
|
||||
- "params allowed:
|
||||
- C(start): The first item to return, using 0-based indexing.
|
||||
- C(count): The number of resources to return.
|
||||
- C(filter): A general filter/query string to narrow the list of items returned.
|
||||
- C(sort): The sort order of the returned data set."
|
||||
- List of parameters to delimit, filter and sort the list of resources.
|
||||
- "Parameter keys allowed are:"
|
||||
- "C(start): The first item to return, using 0-based indexing."
|
||||
- "C(count): The number of resources to return."
|
||||
- "C(filter): A general filter/query string to narrow the list of items returned."
|
||||
- "C(sort): The sort order of the returned data set."
|
||||
type: dict
|
||||
'''
|
||||
|
||||
@@ -20,7 +20,7 @@ options:
|
||||
aliases: [ oauth_token ]
|
||||
api_url:
|
||||
description:
|
||||
- Online API URL
|
||||
- Online API URL.
|
||||
type: str
|
||||
default: 'https://api.online.net'
|
||||
aliases: [ base_url ]
|
||||
@@ -36,7 +36,7 @@ options:
|
||||
type: bool
|
||||
default: true
|
||||
notes:
|
||||
- Also see the API documentation on U(https://console.online.net/en/api/)
|
||||
- Also see the API documentation on U(https://console.online.net/en/api/).
|
||||
- If O(api_token) is not set within the module, the following
|
||||
environment variables can be used in decreasing order of precedence
|
||||
E(ONLINE_TOKEN), E(ONLINE_API_KEY), E(ONLINE_OAUTH_TOKEN), E(ONLINE_API_TOKEN).
|
||||
|
||||
@@ -64,7 +64,7 @@ options:
|
||||
description:
|
||||
- Configures the transport connection to use when connecting to the
|
||||
remote device. The transport argument supports connectivity to the
|
||||
device over ssh, cli or REST.
|
||||
device over SSH (V(ssh)), CLI (V(cli)), or REST (V(rest)).
|
||||
required: true
|
||||
type: str
|
||||
choices: [ cli, rest, ssh ]
|
||||
|
||||
@@ -10,22 +10,21 @@ __metaclass__ = type
|
||||
class ModuleDocFragment(object):
|
||||
DOCUMENTATION = """
|
||||
requirements:
|
||||
- "python >= 2.7"
|
||||
- Python SDK for Oracle Cloud Infrastructure U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io)
|
||||
- Python SDK for Oracle Cloud Infrastructure U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io)
|
||||
notes:
|
||||
- For OCI python sdk configuration, please refer to
|
||||
U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/configuration.html)
|
||||
- For OCI Python SDK configuration, please refer to
|
||||
U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/configuration.html).
|
||||
options:
|
||||
config_file_location:
|
||||
description:
|
||||
- Path to configuration file. If not set then the value of the E(OCI_CONFIG_FILE) environment variable,
|
||||
if any, is used. Otherwise, defaults to ~/.oci/config.
|
||||
if any, is used. Otherwise, defaults to C(~/.oci/config).
|
||||
type: str
|
||||
config_profile_name:
|
||||
description:
|
||||
- The profile to load from the config file referenced by O(config_file_location). If not set, then the
|
||||
value of the E(OCI_CONFIG_PROFILE) environment variable, if any, is used. Otherwise, defaults to the
|
||||
"DEFAULT" profile in O(config_file_location).
|
||||
C(DEFAULT) profile in O(config_file_location).
|
||||
default: "DEFAULT"
|
||||
type: str
|
||||
api_user:
|
||||
@@ -70,8 +69,8 @@ class ModuleDocFragment(object):
|
||||
description:
|
||||
- OCID of your tenancy. If not set, then the value of the OCI_TENANCY variable, if any, is
|
||||
used. This option is required if the tenancy OCID is not specified through a configuration file
|
||||
(See O(config_file_location)). To get the tenancy OCID, please refer
|
||||
U(https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm)
|
||||
(See O(config_file_location)). To get the tenancy OCID, please refer to
|
||||
U(https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm).
|
||||
type: str
|
||||
region:
|
||||
description:
|
||||
|
||||
@@ -21,7 +21,7 @@ class ModuleDocFragment(object):
|
||||
wait_until:
|
||||
description: The lifecycle state to wait for the resource to transition into when O(wait=true). By default,
|
||||
when O(wait=true), we wait for the resource to get into ACTIVE/ATTACHED/AVAILABLE/PROVISIONED/
|
||||
RUNNING applicable lifecycle state during create operation & to get into DELETED/DETACHED/
|
||||
RUNNING applicable lifecycle state during create operation and to get into DELETED/DETACHED/
|
||||
TERMINATED lifecycle state during delete operation.
|
||||
type: str
|
||||
"""
|
||||
|
||||
@@ -32,11 +32,10 @@ options:
|
||||
- FlashBlade API token for admin privileged user.
|
||||
type: str
|
||||
notes:
|
||||
- This module requires the C(purity_fb) Python library
|
||||
- This module requires the C(purity_fb) Python library.
|
||||
- You must set E(PUREFB_URL) and E(PUREFB_API) environment variables
|
||||
if O(fb_url) and O(api_token) arguments are not passed to the module directly
|
||||
if O(fb_url) and O(api_token) arguments are not passed to the module directly.
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- purity_fb >= 1.1
|
||||
'''
|
||||
|
||||
@@ -54,10 +53,9 @@ options:
|
||||
type: str
|
||||
required: true
|
||||
notes:
|
||||
- This module requires the C(purestorage) Python library
|
||||
- This module requires the C(purestorage) Python library.
|
||||
- You must set E(PUREFA_URL) and E(PUREFA_API) environment variables
|
||||
if O(fa_url) and O(api_token) arguments are not passed to the module directly
|
||||
if O(fa_url) and O(api_token) arguments are not passed to the module directly.
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- purestorage
|
||||
'''
|
||||
|
||||
@@ -43,15 +43,14 @@ options:
|
||||
type: bool
|
||||
aliases: [ verify_ssl ]
|
||||
requirements:
|
||||
- python >= 2.6
|
||||
- pyrax
|
||||
notes:
|
||||
- The following environment variables can be used, E(RAX_USERNAME),
|
||||
E(RAX_API_KEY), E(RAX_CREDS_FILE), E(RAX_CREDENTIALS), E(RAX_REGION).
|
||||
- E(RAX_CREDENTIALS) and E(RAX_CREDS_FILE) point to a credentials file
|
||||
appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating)
|
||||
- E(RAX_USERNAME) and E(RAX_API_KEY) obviate the use of a credentials file
|
||||
- E(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...)
|
||||
appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating).
|
||||
- E(RAX_USERNAME) and E(RAX_API_KEY) obviate the use of a credentials file.
|
||||
- E(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...).
|
||||
'''
|
||||
|
||||
# Documentation fragment including attributes to enable communication
|
||||
@@ -67,7 +66,7 @@ options:
|
||||
type: str
|
||||
description:
|
||||
- The URI of the authentication service.
|
||||
- If not specified will be set to U(https://identity.api.rackspacecloud.com/v2.0/)
|
||||
- If not specified will be set to U(https://identity.api.rackspacecloud.com/v2.0/).
|
||||
credentials:
|
||||
type: path
|
||||
description:
|
||||
@@ -110,13 +109,12 @@ deprecated:
|
||||
why: This module relies on the deprecated package pyrax.
|
||||
alternative: Use the Openstack modules instead.
|
||||
requirements:
|
||||
- python >= 2.6
|
||||
- pyrax
|
||||
notes:
|
||||
- The following environment variables can be used, E(RAX_USERNAME),
|
||||
E(RAX_API_KEY), E(RAX_CREDS_FILE), E(RAX_CREDENTIALS), E(RAX_REGION).
|
||||
- E(RAX_CREDENTIALS) and E(RAX_CREDS_FILE) points to a credentials file
|
||||
appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating)
|
||||
- E(RAX_USERNAME) and E(RAX_API_KEY) obviate the use of a credentials file
|
||||
- E(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...)
|
||||
appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating).
|
||||
- E(RAX_USERNAME) and E(RAX_API_KEY) obviate the use of a credentials file.
|
||||
- E(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...).
|
||||
'''
|
||||
|
||||
@@ -42,7 +42,7 @@ options:
|
||||
type: bool
|
||||
default: true
|
||||
notes:
|
||||
- Also see the API documentation on U(https://developer.scaleway.com/)
|
||||
- Also see the API documentation on U(https://developer.scaleway.com/).
|
||||
- If O(api_token) is not set within the module, the following
|
||||
environment variables can be used in decreasing order of precedence
|
||||
E(SCW_TOKEN), E(SCW_API_KEY), E(SCW_OAUTH_TOKEN) or E(SCW_API_TOKEN).
|
||||
|
||||
@@ -14,7 +14,7 @@ options:
|
||||
headers:
|
||||
description:
|
||||
- A dictionary of additional headers to be sent to POST and PUT requests.
|
||||
- Is needed for some modules
|
||||
- Is needed for some modules.
|
||||
type: dict
|
||||
required: false
|
||||
default: {}
|
||||
@@ -30,8 +30,9 @@ options:
|
||||
default: 4444
|
||||
utm_token:
|
||||
description:
|
||||
- "The token used to identify at the REST-API. See U(https://www.sophos.com/en-us/medialibrary/\
|
||||
PDFs/documentation/UTMonAWS/Sophos-UTM-RESTful-API.pdf?la=en), Chapter 2.4.2."
|
||||
- "The token used to identify at the REST-API. See
|
||||
U(https://www.sophos.com/en-us/medialibrary/PDFs/documentation/UTMonAWS/Sophos-UTM-RESTful-API.pdf?la=en),
|
||||
Chapter 2.4.2."
|
||||
type: str
|
||||
required: true
|
||||
utm_protocol:
|
||||
@@ -48,8 +49,8 @@ options:
|
||||
state:
|
||||
description:
|
||||
- The desired state of the object.
|
||||
- V(present) will create or update an object
|
||||
- V(absent) will delete an object if it was present
|
||||
- V(present) will create or update an object.
|
||||
- V(absent) will delete an object if it was present.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
|
||||
@@ -30,11 +30,13 @@ options:
|
||||
user:
|
||||
description:
|
||||
- Vexata API user with administrative privileges.
|
||||
- Uses the E(VEXATA_USER) environment variable as a fallback.
|
||||
required: false
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Vexata API user password.
|
||||
- Uses the E(VEXATA_PASSWORD) environment variable as a fallback.
|
||||
required: false
|
||||
type: str
|
||||
validate_certs:
|
||||
@@ -48,7 +50,6 @@ options:
|
||||
requirements:
|
||||
- Vexata VX100 storage array with VXOS >= v3.5.0 on storage array
|
||||
- vexatapi >= 0.0.1
|
||||
- python >= 2.7
|
||||
- VEXATA_USER and VEXATA_PASSWORD environment variables must be set if
|
||||
- E(VEXATA_USER) and E(VEXATA_PASSWORD) environment variables must be set if
|
||||
user and password arguments are not passed to the module directly.
|
||||
'''
|
||||
|
||||
@@ -14,7 +14,6 @@ DOCUMENTATION = '''
|
||||
- Stefan Heitmüller (@morph027) <stefan.heitmueller@gmx.com>
|
||||
short_description: Ansible dynamic inventory plugin for GitLab runners.
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab > 1.8.0
|
||||
extends_documentation_fragment:
|
||||
- constructed
|
||||
|
||||
@@ -72,7 +72,7 @@ url: http://localhost:5665
|
||||
user: ansible
|
||||
password: secure
|
||||
host_filter: \"linux-servers\" in host.groups
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when connecting to localhost!
|
||||
inventory_attr: name
|
||||
groups:
|
||||
# simple name matching
|
||||
|
||||
@@ -12,7 +12,6 @@ DOCUMENTATION = r'''
|
||||
- Luke Murphy (@decentral1se)
|
||||
short_description: Ansible dynamic inventory plugin for Linode.
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- linode_api4 >= 2.0.0
|
||||
description:
|
||||
- Reads inventories from the Linode API v4.
|
||||
|
||||
@@ -41,6 +41,20 @@ DOCUMENTATION = r'''
|
||||
aliases: [ cert_file ]
|
||||
default: $HOME/.config/lxc/client.crt
|
||||
type: path
|
||||
server_cert:
|
||||
description:
|
||||
- The server certificate file path.
|
||||
type: path
|
||||
version_added: 8.0.0
|
||||
server_check_hostname:
|
||||
description:
|
||||
- This option controls if the server's hostname is checked as part of the HTTPS connection verification.
|
||||
This can be useful to disable, if for example, the server certificate provided (see O(server_cert) option)
|
||||
does not cover a name matching the one used to communicate with the server. Such mismatch is common as LXD
|
||||
generates self-signed server certificates by default.
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 8.0.0
|
||||
trust_password:
|
||||
description:
|
||||
- The client trusted password.
|
||||
@@ -286,7 +300,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
urls = (url for url in url_list if self.validate_url(url))
|
||||
for url in urls:
|
||||
try:
|
||||
socket_connection = LXDClient(url, self.client_key, self.client_cert, self.debug)
|
||||
socket_connection = LXDClient(url, self.client_key, self.client_cert, self.debug, self.server_cert, self.server_check_hostname)
|
||||
return socket_connection
|
||||
except LXDClientException as err:
|
||||
error_storage[url] = err
|
||||
@@ -1078,6 +1092,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
try:
|
||||
self.client_key = self.get_option('client_key')
|
||||
self.client_cert = self.get_option('client_cert')
|
||||
self.server_cert = self.get_option('server_cert')
|
||||
self.server_check_hostname = self.get_option('server_check_hostname')
|
||||
self.project = self.get_option('project')
|
||||
self.debug = self.DEBUG
|
||||
self.data = {} # store for inventory-data
|
||||
|
||||
@@ -116,6 +116,11 @@ DOCUMENTATION = '''
|
||||
- The default of this option changed from V(true) to V(false) in community.general 6.0.0.
|
||||
type: bool
|
||||
default: false
|
||||
exclude_nodes:
|
||||
description: Exclude proxmox nodes and the nodes-group from the inventory output.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 8.1.0
|
||||
filters:
|
||||
version_added: 4.6.0
|
||||
description: A list of Jinja templates that allow filtering hosts.
|
||||
@@ -166,7 +171,6 @@ plugin: community.general.proxmox
|
||||
url: http://pve.domain.com:8006
|
||||
user: ansible@pve
|
||||
password: secure
|
||||
validate_certs: false
|
||||
want_facts: true
|
||||
keyed_groups:
|
||||
# proxmox_tags_parsed is an example of a fact only returned when 'want_facts=true'
|
||||
@@ -187,10 +191,10 @@ want_proxmox_nodes_ansible_host: true
|
||||
# Note: my_inv_var demonstrates how to add a string variable to every host used by the inventory.
|
||||
# my.proxmox.yml
|
||||
plugin: community.general.proxmox
|
||||
url: http://pve.domain.com:8006
|
||||
url: http://192.168.1.2:8006
|
||||
user: ansible@pve
|
||||
password: secure
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
want_facts: true
|
||||
want_proxmox_nodes_ansible_host: false
|
||||
compose:
|
||||
@@ -565,9 +569,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
for group in default_groups:
|
||||
self.inventory.add_group(self._group('all_%s' % (group)))
|
||||
|
||||
nodes_group = self._group('nodes')
|
||||
self.inventory.add_group(nodes_group)
|
||||
if not self.exclude_nodes:
|
||||
self.inventory.add_group(nodes_group)
|
||||
|
||||
want_proxmox_nodes_ansible_host = self.get_option("want_proxmox_nodes_ansible_host")
|
||||
|
||||
@@ -577,22 +581,23 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
for node in self._get_nodes():
|
||||
if not node.get('node'):
|
||||
continue
|
||||
|
||||
self.inventory.add_host(node['node'])
|
||||
if node['type'] == 'node':
|
||||
if not self.exclude_nodes:
|
||||
self.inventory.add_host(node['node'])
|
||||
if node['type'] == 'node' and not self.exclude_nodes:
|
||||
self.inventory.add_child(nodes_group, node['node'])
|
||||
|
||||
if node['status'] == 'offline':
|
||||
continue
|
||||
|
||||
# get node IP address
|
||||
if want_proxmox_nodes_ansible_host:
|
||||
if want_proxmox_nodes_ansible_host and not self.exclude_nodes:
|
||||
ip = self._get_node_ip(node['node'])
|
||||
self.inventory.set_variable(node['node'], 'ansible_host', ip)
|
||||
|
||||
# Setting composite variables
|
||||
variables = self.inventory.get_host(node['node']).get_vars()
|
||||
self._set_composite_vars(self.get_option('compose'), variables, node['node'], strict=self.strict)
|
||||
if not self.exclude_nodes:
|
||||
variables = self.inventory.get_host(node['node']).get_vars()
|
||||
self._set_composite_vars(self.get_option('compose'), variables, node['node'], strict=self.strict)
|
||||
|
||||
# add LXC/Qemu groups for the node
|
||||
for ittype in ('lxc', 'qemu'):
|
||||
@@ -635,8 +640,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
if self.get_option('qemu_extended_statuses') and not self.get_option('want_facts'):
|
||||
raise AnsibleError('You must set want_facts to True if you want to use qemu_extended_statuses.')
|
||||
|
||||
# read rest of options
|
||||
self.exclude_nodes = self.get_option('exclude_nodes')
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
self.host_filters = self.get_option('filters')
|
||||
|
||||
@@ -104,6 +104,8 @@ class Bitwarden(object):
|
||||
out, err = p.communicate(to_bytes(stdin))
|
||||
rc = p.wait()
|
||||
if rc != expected_rc:
|
||||
if len(args) > 2 and args[0] == 'get' and args[1] == 'item' and b'Not found.' in err:
|
||||
return 'null', ''
|
||||
raise BitwardenException(err)
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict')
|
||||
|
||||
@@ -112,7 +114,10 @@ class Bitwarden(object):
|
||||
"""
|
||||
|
||||
# Prepare set of params for Bitwarden CLI
|
||||
params = ['list', 'items', '--search', search_value]
|
||||
if search_field == 'id':
|
||||
params = ['get', 'item', search_value]
|
||||
else:
|
||||
params = ['list', 'items', '--search', search_value]
|
||||
|
||||
if collection_id:
|
||||
params.extend(['--collectionid', collection_id])
|
||||
@@ -121,7 +126,11 @@ class Bitwarden(object):
|
||||
|
||||
# This includes things that matched in different fields.
|
||||
initial_matches = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
|
||||
if search_field == 'id':
|
||||
if initial_matches is None:
|
||||
initial_matches = []
|
||||
else:
|
||||
initial_matches = [initial_matches]
|
||||
# Filter to only include results from the right field.
|
||||
return [item for item in initial_matches if item[search_field] == search_value]
|
||||
|
||||
|
||||
@@ -98,15 +98,10 @@ def load_collection_meta(collection_pkg, no_version='*'):
|
||||
if os.path.exists(manifest_path):
|
||||
return load_collection_meta_manifest(manifest_path)
|
||||
|
||||
# Try to load galaxy.y(a)ml
|
||||
# Try to load galaxy.yml
|
||||
galaxy_path = os.path.join(path, 'galaxy.yml')
|
||||
galaxy_alt_path = os.path.join(path, 'galaxy.yaml')
|
||||
# galaxy.yaml was only supported in ansible-base 2.10 and ansible-core 2.11. Support was removed
|
||||
# in https://github.com/ansible/ansible/commit/595413d11346b6f26bb3d9df2d8e05f2747508a3 for
|
||||
# ansible-core 2.12.
|
||||
for path in (galaxy_path, galaxy_alt_path):
|
||||
if os.path.exists(path):
|
||||
return load_collection_meta_galaxy(path, no_version=no_version)
|
||||
if os.path.exists(galaxy_path):
|
||||
return load_collection_meta_galaxy(galaxy_path, no_version=no_version)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ EXAMPLES = '''
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.etcd', 'foo', 'bar', 'baz') }}"
|
||||
|
||||
- name: "since Ansible 2.5 you can set server options inline"
|
||||
- name: "you can set server options inline"
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.etcd', 'foo', version='v2', url='http://192.168.0.27:4001') }}"
|
||||
'''
|
||||
@@ -62,7 +62,7 @@ EXAMPLES = '''
|
||||
RETURN = '''
|
||||
_raw:
|
||||
description:
|
||||
- list of values associated with input keys
|
||||
- List of values associated with input keys.
|
||||
type: list
|
||||
elements: string
|
||||
'''
|
||||
|
||||
@@ -14,59 +14,28 @@ DOCUMENTATION = '''
|
||||
- Scott Buchanan (@scottsb)
|
||||
- Andrew Zenk (@azenk)
|
||||
- Sam Doran (@samdoran)
|
||||
requirements:
|
||||
- C(op) 1Password command line utility. See U(https://support.1password.com/command-line/)
|
||||
short_description: fetch field values from 1Password
|
||||
short_description: Fetch field values from 1Password
|
||||
description:
|
||||
- P(community.general.onepassword#lookup) wraps the C(op) command line utility to fetch specific field values from 1Password.
|
||||
requirements:
|
||||
- C(op) 1Password command line utility
|
||||
options:
|
||||
_terms:
|
||||
description: identifier(s) (UUID, name, or subdomain; case-insensitive) of item(s) to retrieve.
|
||||
description: Identifier(s) (case-insensitive UUID or name) of item(s) to retrieve.
|
||||
required: true
|
||||
field:
|
||||
description: field to return from each matching item (case-insensitive).
|
||||
default: 'password'
|
||||
master_password:
|
||||
description: The password used to unlock the specified vault.
|
||||
aliases: ['vault_password']
|
||||
section:
|
||||
description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section.
|
||||
domain:
|
||||
description: Domain of 1Password.
|
||||
version_added: 3.2.0
|
||||
default: '1password.com'
|
||||
type: str
|
||||
subdomain:
|
||||
description: The 1Password subdomain to authenticate against.
|
||||
account_id:
|
||||
description: The account ID to target.
|
||||
type: str
|
||||
version_added: 7.5.0
|
||||
username:
|
||||
description: The username used to sign in.
|
||||
secret_key:
|
||||
description: The secret key used when performing an initial sign in.
|
||||
service_account_token:
|
||||
description:
|
||||
- The access key for a service account.
|
||||
- Only works with 1Password CLI version 2 or later.
|
||||
domain:
|
||||
version_added: 3.2.0
|
||||
field:
|
||||
description: Field to return from each matching item (case-insensitive).
|
||||
default: 'password'
|
||||
type: str
|
||||
service_account_token:
|
||||
version_added: 7.1.0
|
||||
vault:
|
||||
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
|
||||
notes:
|
||||
- This lookup will use an existing 1Password session if one exists. If not, and you have already
|
||||
performed an initial sign in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the
|
||||
C(master_password) is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
|
||||
- This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password).
|
||||
- Can target a specific account by providing the O(account_id).
|
||||
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials
|
||||
needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength
|
||||
to the 1Password master password.
|
||||
- This lookup stores potentially sensitive data from 1Password as Ansible facts.
|
||||
Facts are subject to caching if enabled, which means this data could be stored in clear text
|
||||
on disk or in a database.
|
||||
- Tested with C(op) version 2.7.2
|
||||
extends_documentation_fragment:
|
||||
- community.general.onepassword
|
||||
- community.general.onepassword.lookup
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
@@ -108,7 +77,7 @@ EXAMPLES = """
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: field data requested
|
||||
description: Field data requested.
|
||||
type: list
|
||||
elements: str
|
||||
"""
|
||||
@@ -119,7 +88,7 @@ import json
|
||||
import subprocess
|
||||
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.errors import AnsibleLookupError, AnsibleOptionsError
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
from ansible.module_utils.six import with_metaclass
|
||||
@@ -147,6 +116,8 @@ class OnePassCLIBase(with_metaclass(abc.ABCMeta, object)):
|
||||
master_password=None,
|
||||
service_account_token=None,
|
||||
account_id=None,
|
||||
connect_host=None,
|
||||
connect_token=None,
|
||||
):
|
||||
self.subdomain = subdomain
|
||||
self.domain = domain
|
||||
@@ -155,6 +126,8 @@ class OnePassCLIBase(with_metaclass(abc.ABCMeta, object)):
|
||||
self.secret_key = secret_key
|
||||
self.service_account_token = service_account_token
|
||||
self.account_id = account_id
|
||||
self.connect_host = connect_host
|
||||
self.connect_token = connect_token
|
||||
|
||||
self._path = None
|
||||
self._version = None
|
||||
@@ -333,6 +306,10 @@ class OnePassCLIv1(OnePassCLIBase):
|
||||
return not bool(rc)
|
||||
|
||||
def full_signin(self):
|
||||
if self.connect_host or self.connect_token:
|
||||
raise AnsibleLookupError(
|
||||
"1Password Connect is not available with 1Password CLI version 1. Please use version 2 or later.")
|
||||
|
||||
if self.service_account_token:
|
||||
raise AnsibleLookupError(
|
||||
"1Password CLI version 1 does not support Service Accounts. Please use version 2 or later.")
|
||||
@@ -521,6 +498,9 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
return ""
|
||||
|
||||
def assert_logged_in(self):
|
||||
if self.connect_host and self.connect_token:
|
||||
return True
|
||||
|
||||
if self.service_account_token:
|
||||
args = ["whoami"]
|
||||
environment_update = {"OP_SERVICE_ACCOUNT_TOKEN": self.service_account_token}
|
||||
@@ -580,6 +560,15 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
if vault is not None:
|
||||
args += ["--vault={0}".format(vault)]
|
||||
|
||||
if self.connect_host and self.connect_token:
|
||||
if vault is None:
|
||||
raise AnsibleLookupError("'vault' is required with 1Password Connect")
|
||||
environment_update = {
|
||||
"OP_CONNECT_HOST": self.connect_host,
|
||||
"OP_CONNECT_TOKEN": self.connect_token,
|
||||
}
|
||||
return self._run(args, environment_update=environment_update)
|
||||
|
||||
if self.service_account_token:
|
||||
if vault is None:
|
||||
raise AnsibleLookupError("'vault' is required with 'service_account_token'")
|
||||
@@ -603,7 +592,7 @@ class OnePassCLIv2(OnePassCLIBase):
|
||||
|
||||
class OnePass(object):
|
||||
def __init__(self, subdomain=None, domain="1password.com", username=None, secret_key=None, master_password=None,
|
||||
service_account_token=None, account_id=None):
|
||||
service_account_token=None, account_id=None, connect_host=None, connect_token=None, cli_class=None):
|
||||
self.subdomain = subdomain
|
||||
self.domain = domain
|
||||
self.username = username
|
||||
@@ -611,19 +600,28 @@ class OnePass(object):
|
||||
self.master_password = master_password
|
||||
self.service_account_token = service_account_token
|
||||
self.account_id = account_id
|
||||
self.connect_host = connect_host
|
||||
self.connect_token = connect_token
|
||||
|
||||
self.logged_in = False
|
||||
self.token = None
|
||||
|
||||
self._config = OnePasswordConfig()
|
||||
self._cli = self._get_cli_class()
|
||||
self._cli = self._get_cli_class(cli_class)
|
||||
|
||||
if (self.connect_host or self.connect_token) and None in (self.connect_host, self.connect_token):
|
||||
raise AnsibleOptionsError("connect_host and connect_token are required together")
|
||||
|
||||
def _get_cli_class(self, cli_class=None):
|
||||
if cli_class is not None:
|
||||
return cli_class(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token)
|
||||
|
||||
def _get_cli_class(self):
|
||||
version = OnePassCLIBase.get_current_version()
|
||||
for cls in OnePassCLIBase.__subclasses__():
|
||||
if cls.supports_version == version.split(".")[0]:
|
||||
try:
|
||||
return cls(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token, self.account_id)
|
||||
return cls(self.subdomain, self.domain, self.username, self.secret_key, self.master_password, self.service_account_token,
|
||||
self.account_id, self.connect_host, self.connect_token)
|
||||
except TypeError as e:
|
||||
raise AnsibleLookupError(e)
|
||||
|
||||
@@ -688,8 +686,20 @@ class LookupModule(LookupBase):
|
||||
master_password = self.get_option("master_password")
|
||||
service_account_token = self.get_option("service_account_token")
|
||||
account_id = self.get_option("account_id")
|
||||
connect_host = self.get_option("connect_host")
|
||||
connect_token = self.get_option("connect_token")
|
||||
|
||||
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id)
|
||||
op = OnePass(
|
||||
subdomain=subdomain,
|
||||
domain=domain,
|
||||
username=username,
|
||||
secret_key=secret_key,
|
||||
master_password=master_password,
|
||||
service_account_token=service_account_token,
|
||||
account_id=account_id,
|
||||
connect_host=connect_host,
|
||||
connect_token=connect_token,
|
||||
)
|
||||
op.assert_logged_in()
|
||||
|
||||
values = []
|
||||
|
||||
104
plugins/lookup/onepassword_doc.py
Normal file
104
plugins/lookup/onepassword_doc.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2023, 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 = '''
|
||||
name: onepassword_doc
|
||||
author:
|
||||
- Sam Doran (@samdoran)
|
||||
requirements:
|
||||
- C(op) 1Password command line utility version 2 or later.
|
||||
short_description: Fetch documents stored in 1Password
|
||||
version_added: "8.1.0"
|
||||
description:
|
||||
- P(community.general.onepassword_doc#lookup) wraps C(op) command line utility to fetch one or more documents from 1Password.
|
||||
notes:
|
||||
- The document contents are a string exactly as stored in 1Password.
|
||||
- This plugin requires C(op) version 2 or later.
|
||||
|
||||
options:
|
||||
_terms:
|
||||
description: Identifier(s) (case-insensitive UUID or name) of item(s) to retrieve.
|
||||
required: true
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.onepassword
|
||||
- community.general.onepassword.lookup
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Retrieve a private key from 1Password
|
||||
ansible.builtin.debug:
|
||||
var: lookup('community.general.onepassword_doc', 'Private key')
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: Requested document
|
||||
type: list
|
||||
elements: string
|
||||
"""
|
||||
|
||||
from ansible_collections.community.general.plugins.lookup.onepassword import OnePass, OnePassCLIv2
|
||||
from ansible.errors import AnsibleLookupError
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class OnePassCLIv2Doc(OnePassCLIv2):
|
||||
def get_raw(self, item_id, vault=None, token=None):
|
||||
args = ["document", "get", item_id]
|
||||
if vault is not None:
|
||||
args = [*args, "--vault={0}".format(vault)]
|
||||
|
||||
if self.service_account_token:
|
||||
if vault is None:
|
||||
raise AnsibleLookupError("'vault' is required with 'service_account_token'")
|
||||
|
||||
environment_update = {"OP_SERVICE_ACCOUNT_TOKEN": self.service_account_token}
|
||||
return self._run(args, environment_update=environment_update)
|
||||
|
||||
if token is not None:
|
||||
args = [*args, to_bytes("--session=") + token]
|
||||
|
||||
return self._run(args)
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
vault = self.get_option("vault")
|
||||
subdomain = self.get_option("subdomain")
|
||||
domain = self.get_option("domain", "1password.com")
|
||||
username = self.get_option("username")
|
||||
secret_key = self.get_option("secret_key")
|
||||
master_password = self.get_option("master_password")
|
||||
service_account_token = self.get_option("service_account_token")
|
||||
account_id = self.get_option("account_id")
|
||||
connect_host = self.get_option("connect_host")
|
||||
connect_token = self.get_option("connect_token")
|
||||
|
||||
op = OnePass(
|
||||
subdomain=subdomain,
|
||||
domain=domain,
|
||||
username=username,
|
||||
secret_key=secret_key,
|
||||
master_password=master_password,
|
||||
service_account_token=service_account_token,
|
||||
account_id=account_id,
|
||||
connect_host=connect_host,
|
||||
connect_token=connect_token,
|
||||
cli_class=OnePassCLIv2Doc,
|
||||
)
|
||||
op.assert_logged_in()
|
||||
|
||||
values = []
|
||||
for term in terms:
|
||||
values.append(op.get_raw(term, vault))
|
||||
|
||||
return values
|
||||
@@ -15,55 +15,23 @@ DOCUMENTATION = '''
|
||||
- Andrew Zenk (@azenk)
|
||||
- Sam Doran (@samdoran)
|
||||
requirements:
|
||||
- C(op) 1Password command line utility. See U(https://support.1password.com/command-line/)
|
||||
short_description: fetch an entire item from 1Password
|
||||
- C(op) 1Password command line utility
|
||||
short_description: Fetch an entire item from 1Password
|
||||
description:
|
||||
- P(community.general.onepassword_raw#lookup) wraps C(op) command line utility to fetch an entire item from 1Password.
|
||||
options:
|
||||
_terms:
|
||||
description: identifier(s) (UUID, name, or domain; case-insensitive) of item(s) to retrieve.
|
||||
description: Identifier(s) (case-insensitive UUID or name) of item(s) to retrieve.
|
||||
required: true
|
||||
master_password:
|
||||
description: The password used to unlock the specified vault.
|
||||
aliases: ['vault_password']
|
||||
section:
|
||||
description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section.
|
||||
subdomain:
|
||||
description: The 1Password subdomain to authenticate against.
|
||||
domain:
|
||||
description: Domain of 1Password.
|
||||
version_added: 6.0.0
|
||||
default: '1password.com'
|
||||
type: str
|
||||
account_id:
|
||||
description: The account ID to target.
|
||||
type: str
|
||||
version_added: 7.5.0
|
||||
username:
|
||||
description: The username used to sign in.
|
||||
secret_key:
|
||||
description: The secret key used when performing an initial sign in.
|
||||
domain:
|
||||
version_added: 6.0.0
|
||||
service_account_token:
|
||||
description:
|
||||
- The access key for a service account.
|
||||
- Only works with 1Password CLI version 2 or later.
|
||||
type: string
|
||||
version_added: 7.1.0
|
||||
vault:
|
||||
description: Vault containing the item to retrieve (case-insensitive). If absent will search all vaults.
|
||||
notes:
|
||||
- This lookup will use an existing 1Password session if one exists. If not, and you have already
|
||||
performed an initial sign in (meaning C(~/.op/config exists)), then only the O(master_password) is required.
|
||||
You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
|
||||
- This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password).
|
||||
- Can target a specific account by providing the O(account_id).
|
||||
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials
|
||||
needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength
|
||||
to the 1Password master password.
|
||||
- This lookup stores potentially sensitive data from 1Password as Ansible facts.
|
||||
Facts are subject to caching if enabled, which means this data could be stored in clear text
|
||||
on disk or in a database.
|
||||
- Tested with C(op) version 2.7.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.onepassword
|
||||
- community.general.onepassword.lookup
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
@@ -78,7 +46,7 @@ EXAMPLES = """
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: field data requested
|
||||
description: Entire item requested.
|
||||
type: list
|
||||
elements: dict
|
||||
"""
|
||||
@@ -102,8 +70,20 @@ class LookupModule(LookupBase):
|
||||
master_password = self.get_option("master_password")
|
||||
service_account_token = self.get_option("service_account_token")
|
||||
account_id = self.get_option("account_id")
|
||||
connect_host = self.get_option("connect_host")
|
||||
connect_token = self.get_option("connect_token")
|
||||
|
||||
op = OnePass(subdomain, domain, username, secret_key, master_password, service_account_token, account_id)
|
||||
op = OnePass(
|
||||
subdomain=subdomain,
|
||||
domain=domain,
|
||||
username=username,
|
||||
secret_key=secret_key,
|
||||
master_password=master_password,
|
||||
service_account_token=service_account_token,
|
||||
account_id=account_id,
|
||||
connect_host=connect_host,
|
||||
connect_token=connect_token,
|
||||
)
|
||||
op.assert_logged_in()
|
||||
|
||||
values = []
|
||||
|
||||
@@ -129,6 +129,16 @@ DOCUMENTATION = '''
|
||||
- pass
|
||||
- gopass
|
||||
version_added: 5.2.0
|
||||
timestamp:
|
||||
description: Add the password generation information to the end of the file.
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 8.1.0
|
||||
preserve:
|
||||
description: Include the old (edited) password inside the pass file.
|
||||
type: bool
|
||||
default: true
|
||||
version_added: 8.1.0
|
||||
notes:
|
||||
- The lookup supports passing all options as lookup parameters since community.general 6.0.0.
|
||||
'''
|
||||
@@ -386,11 +396,13 @@ class LookupModule(LookupBase):
|
||||
# generate new password, insert old lines from current result and return new password
|
||||
newpass = self.get_newpass()
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass + '\n'
|
||||
if self.passoutput[1:]:
|
||||
msg += '\n'.join(self.passoutput[1:]) + '\n'
|
||||
if self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
msg = newpass
|
||||
if self.paramvals['preserve'] or self.paramvals['timestamp']:
|
||||
msg += '\n'
|
||||
if self.paramvals['preserve'] and self.passoutput[1:]:
|
||||
msg += '\n'.join(self.passoutput[1:]) + '\n'
|
||||
if self.paramvals['timestamp'] and self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
@@ -402,7 +414,9 @@ class LookupModule(LookupBase):
|
||||
# use pwgen to generate the password and insert values with pass -m
|
||||
newpass = self.get_newpass()
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass + '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
|
||||
msg = newpass
|
||||
if self.paramvals['timestamp']:
|
||||
msg += '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
|
||||
try:
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
@@ -465,6 +479,8 @@ class LookupModule(LookupBase):
|
||||
'backup': self.get_option('backup'),
|
||||
'missing': self.get_option('missing'),
|
||||
'umask': self.get_option('umask'),
|
||||
'timestamp': self.get_option('timestamp'),
|
||||
'preserve': self.get_option('preserve'),
|
||||
}
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
|
||||
@@ -34,6 +34,7 @@ except Exception:
|
||||
|
||||
def auth_argument_spec(spec=None):
|
||||
arg_spec = (dict(
|
||||
ca_path=dict(type='str'),
|
||||
api_token=dict(type='str', no_log=True),
|
||||
api_oauth_token=dict(type='str', no_log=True),
|
||||
api_job_token=dict(type='str', no_log=True),
|
||||
@@ -74,33 +75,36 @@ def ensure_gitlab_package(module):
|
||||
|
||||
|
||||
def gitlab_authentication(module):
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
gitlab_url = module.params['api_url']
|
||||
validate_certs = module.params['validate_certs']
|
||||
ca_path = module.params['ca_path']
|
||||
gitlab_user = module.params['api_username']
|
||||
gitlab_password = module.params['api_password']
|
||||
gitlab_token = module.params['api_token']
|
||||
gitlab_oauth_token = module.params['api_oauth_token']
|
||||
gitlab_job_token = module.params['api_job_token']
|
||||
|
||||
ensure_gitlab_package(module)
|
||||
verify = ca_path if validate_certs and ca_path else validate_certs
|
||||
|
||||
try:
|
||||
# python-gitlab library remove support for username/password authentication since 1.13.0
|
||||
# Changelog : https://github.com/python-gitlab/python-gitlab/releases/tag/v1.13.0
|
||||
# This condition allow to still support older version of the python-gitlab library
|
||||
if LooseVersion(gitlab.__version__) < LooseVersion("1.13.0"):
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, email=gitlab_user, password=gitlab_password,
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=verify, email=gitlab_user, password=gitlab_password,
|
||||
private_token=gitlab_token, api_version=4)
|
||||
else:
|
||||
# We can create an oauth_token using a username and password
|
||||
# https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
|
||||
if gitlab_user:
|
||||
data = {'grant_type': 'password', 'username': gitlab_user, 'password': gitlab_password}
|
||||
resp = requests.post(urljoin(gitlab_url, "oauth/token"), data=data, verify=validate_certs)
|
||||
resp = requests.post(urljoin(gitlab_url, "oauth/token"), data=data, verify=verify)
|
||||
resp_data = resp.json()
|
||||
gitlab_oauth_token = resp_data["access_token"]
|
||||
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, private_token=gitlab_token,
|
||||
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=verify, private_token=gitlab_token,
|
||||
oauth_token=gitlab_oauth_token, job_token=gitlab_job_token, api_version=4)
|
||||
|
||||
gitlab_instance.auth()
|
||||
|
||||
@@ -292,8 +292,8 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
self.fail_open_url(e, msg='Could not obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
@@ -317,8 +317,8 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
self.fail_open_url(e, msg='Could not obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
@@ -338,8 +338,8 @@ class KeycloakAPI(object):
|
||||
return open_url(realm_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
self.fail_open_url(e, msg='Could not update realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
|
||||
def create_realm(self, realmrep):
|
||||
""" Create a realm in keycloak
|
||||
@@ -352,8 +352,8 @@ class KeycloakAPI(object):
|
||||
return open_url(realm_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create realm %s: %s' % (realmrep['id'], str(e)),
|
||||
exception=traceback.format_exc())
|
||||
self.fail_open_url(e, msg='Could not create realm %s: %s' % (realmrep['id'], str(e)),
|
||||
exception=traceback.format_exc())
|
||||
|
||||
def delete_realm(self, realm="master"):
|
||||
""" Delete a realm from Keycloak
|
||||
@@ -367,8 +367,8 @@ class KeycloakAPI(object):
|
||||
return open_url(realm_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
self.fail_open_url(e, msg='Could not delete realm %s: %s' % (realm, str(e)),
|
||||
exception=traceback.format_exc())
|
||||
|
||||
def get_clients(self, realm='master', filter=None):
|
||||
""" Obtains client representations for clients in a realm
|
||||
@@ -389,7 +389,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of clients for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of clients for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of clients for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_client_by_clientid(self, client_id, realm='master'):
|
||||
@@ -422,7 +422,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not obtain client %s for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain client %s for realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client %s for realm %s: %s'
|
||||
@@ -457,7 +457,7 @@ class KeycloakAPI(object):
|
||||
return open_url(client_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update client %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def create_client(self, clientrep, realm="master"):
|
||||
@@ -472,7 +472,7 @@ class KeycloakAPI(object):
|
||||
return open_url(client_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create client %s in realm %s: %s'
|
||||
% (clientrep['clientId'], realm, str(e)))
|
||||
|
||||
def delete_client(self, id, realm="master"):
|
||||
@@ -488,7 +488,7 @@ class KeycloakAPI(object):
|
||||
return open_url(client_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not delete client %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def get_client_roles_by_id(self, cid, realm="master"):
|
||||
@@ -504,7 +504,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
||||
% (cid, realm, str(e)))
|
||||
|
||||
def get_client_role_id_by_name(self, cid, name, realm="master"):
|
||||
@@ -539,7 +539,7 @@ class KeycloakAPI(object):
|
||||
if rid == role['id']:
|
||||
return role
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s in group %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch rolemappings for client %s in group %s, realm %s: %s"
|
||||
% (cid, gid, realm, str(e)))
|
||||
return None
|
||||
|
||||
@@ -557,7 +557,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
% (cid, gid, realm, str(e)))
|
||||
|
||||
def get_client_group_composite_rolemappings(self, gid, cid, realm="master"):
|
||||
@@ -574,7 +574,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
% (cid, gid, realm, str(e)))
|
||||
|
||||
def get_role_by_id(self, rid, realm="master"):
|
||||
@@ -590,7 +590,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch role for id %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch role for id %s in realm %s: %s"
|
||||
% (rid, realm, str(e)))
|
||||
|
||||
def get_client_roles_by_id_composite_rolemappings(self, rid, cid, realm="master"):
|
||||
@@ -607,7 +607,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch role for id %s and cid %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch role for id %s and cid %s in realm %s: %s"
|
||||
% (rid, cid, realm, str(e)))
|
||||
|
||||
def add_client_roles_by_id_composite_rolemapping(self, rid, roles_rep, realm="master"):
|
||||
@@ -623,7 +623,7 @@ class KeycloakAPI(object):
|
||||
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(roles_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not assign roles to composite role %s and realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not assign roles to composite role %s and realm %s: %s"
|
||||
% (rid, realm, str(e)))
|
||||
|
||||
def add_group_rolemapping(self, gid, cid, role_rep, realm="master"):
|
||||
@@ -640,7 +640,7 @@ class KeycloakAPI(object):
|
||||
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
% (cid, gid, realm, str(e)))
|
||||
|
||||
def delete_group_rolemapping(self, gid, cid, role_rep, realm="master"):
|
||||
@@ -657,7 +657,7 @@ class KeycloakAPI(object):
|
||||
open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
||||
% (cid, gid, realm, str(e)))
|
||||
|
||||
def get_client_user_rolemapping_by_id(self, uid, cid, rid, realm='master'):
|
||||
@@ -678,7 +678,7 @@ class KeycloakAPI(object):
|
||||
if rid == role['id']:
|
||||
return role
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s and user %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch rolemappings for client %s and user %s, realm %s: %s"
|
||||
% (cid, uid, realm, str(e)))
|
||||
return None
|
||||
|
||||
@@ -696,7 +696,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch effective rolemappings for client %s and user %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch effective rolemappings for client %s and user %s, realm %s: %s"
|
||||
% (cid, uid, realm, str(e)))
|
||||
|
||||
def get_client_user_composite_rolemappings(self, uid, cid, realm="master"):
|
||||
@@ -713,7 +713,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||
% (uid, realm, str(e)))
|
||||
|
||||
def get_realm_user_rolemapping_by_id(self, uid, rid, realm='master'):
|
||||
@@ -733,7 +733,7 @@ class KeycloakAPI(object):
|
||||
if rid == role['id']:
|
||||
return role
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for user %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch rolemappings for user %s, realm %s: %s"
|
||||
% (uid, realm, str(e)))
|
||||
return None
|
||||
|
||||
@@ -750,7 +750,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||
% (uid, realm, str(e)))
|
||||
|
||||
def get_realm_user_composite_rolemappings(self, uid, realm="master"):
|
||||
@@ -766,7 +766,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch effective rolemappings for user %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch effective rolemappings for user %s, realm %s: %s"
|
||||
% (uid, realm, str(e)))
|
||||
|
||||
def get_user_by_username(self, username, realm="master"):
|
||||
@@ -793,7 +793,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the user for realm %s and username %s: %s'
|
||||
% (realm, username, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain the user for realm %s and username %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain the user for realm %s and username %s: %s'
|
||||
% (realm, username, str(e)))
|
||||
|
||||
def get_service_account_user_by_client_id(self, client_id, realm="master"):
|
||||
@@ -814,7 +814,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the service-account-user for realm %s and client_id %s: %s'
|
||||
% (realm, client_id, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain the service-account-user for realm %s and client_id %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain the service-account-user for realm %s and client_id %s: %s'
|
||||
% (realm, client_id, str(e)))
|
||||
|
||||
def add_user_rolemapping(self, uid, cid, role_rep, realm="master"):
|
||||
@@ -832,7 +832,7 @@ class KeycloakAPI(object):
|
||||
open_url(user_realm_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not map roles to userId %s for realm %s and roles %s: %s"
|
||||
self.fail_open_url(e, msg="Could not map roles to userId %s for realm %s and roles %s: %s"
|
||||
% (uid, realm, json.dumps(role_rep), str(e)))
|
||||
else:
|
||||
user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||
@@ -840,7 +840,7 @@ class KeycloakAPI(object):
|
||||
open_url(user_client_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not map roles to userId %s for client %s, realm %s and roles %s: %s"
|
||||
self.fail_open_url(e, msg="Could not map roles to userId %s for client %s, realm %s and roles %s: %s"
|
||||
% (cid, uid, realm, json.dumps(role_rep), str(e)))
|
||||
|
||||
def delete_user_rolemapping(self, uid, cid, role_rep, realm="master"):
|
||||
@@ -858,7 +858,7 @@ class KeycloakAPI(object):
|
||||
open_url(user_realm_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not remove roles %s from userId %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not remove roles %s from userId %s, realm %s: %s"
|
||||
% (json.dumps(role_rep), uid, realm, str(e)))
|
||||
else:
|
||||
user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||
@@ -866,7 +866,7 @@ class KeycloakAPI(object):
|
||||
open_url(user_client_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not remove roles %s for client %s from userId %s, realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not remove roles %s for client %s from userId %s, realm %s: %s"
|
||||
% (json.dumps(role_rep), cid, uid, realm, str(e)))
|
||||
|
||||
def get_client_templates(self, realm='master'):
|
||||
@@ -884,7 +884,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of client templates for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of client templates for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of client templates for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_client_template_by_id(self, id, realm='master'):
|
||||
@@ -903,7 +903,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client templates %s for realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain client template %s for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain client template %s for realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def get_client_template_by_name(self, name, realm='master'):
|
||||
@@ -946,7 +946,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client template %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update client template %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def create_client_template(self, clienttrep, realm="master"):
|
||||
@@ -961,7 +961,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client template %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create client template %s in realm %s: %s'
|
||||
% (clienttrep['clientId'], realm, str(e)))
|
||||
|
||||
def delete_client_template(self, id, realm="master"):
|
||||
@@ -977,7 +977,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client template %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not delete client template %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def get_clientscopes(self, realm="master"):
|
||||
@@ -995,7 +995,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of clientscopes in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch list of clientscopes in realm %s: %s"
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_clientscope_by_clientscopeid(self, cid, realm="master"):
|
||||
@@ -1017,7 +1017,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg="Could not fetch clientscope %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch clientscope %s in realm %s: %s"
|
||||
% (cid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not clientscope group %s in realm %s: %s"
|
||||
@@ -1058,7 +1058,7 @@ class KeycloakAPI(object):
|
||||
return open_url(clientscopes_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create clientscope %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not create clientscope %s in realm %s: %s"
|
||||
% (clientscoperep['name'], realm, str(e)))
|
||||
|
||||
def update_clientscope(self, clientscoperep, realm="master"):
|
||||
@@ -1074,7 +1074,7 @@ class KeycloakAPI(object):
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update clientscope %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update clientscope %s in realm %s: %s'
|
||||
% (clientscoperep['name'], realm, str(e)))
|
||||
|
||||
def delete_clientscope(self, name=None, cid=None, realm="master"):
|
||||
@@ -1112,7 +1112,7 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to delete clientscope %s: %s" % (cid, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to delete clientscope %s: %s" % (cid, str(e)))
|
||||
|
||||
def get_clientscope_protocolmappers(self, cid, realm="master"):
|
||||
""" Fetch the name and ID of all clientscopes on the Keycloak server.
|
||||
@@ -1130,7 +1130,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of protocolmappers in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch list of protocolmappers in realm %s: %s"
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_clientscope_protocolmapper_by_protocolmapperid(self, pid, cid, realm="master"):
|
||||
@@ -1154,7 +1154,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg="Could not fetch protocolmapper %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch protocolmapper %s in realm %s: %s"
|
||||
% (pid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch protocolmapper %s in realm %s: %s"
|
||||
@@ -1197,7 +1197,7 @@ class KeycloakAPI(object):
|
||||
return open_url(protocolmappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create protocolmapper %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not create protocolmapper %s in realm %s: %s"
|
||||
% (mapper_rep['name'], realm, str(e)))
|
||||
|
||||
def update_clientscope_protocolmappers(self, cid, mapper_rep, realm="master"):
|
||||
@@ -1214,7 +1214,7 @@ class KeycloakAPI(object):
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update protocolmappers for clientscope %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update protocolmappers for clientscope %s in realm %s: %s'
|
||||
% (mapper_rep, realm, str(e)))
|
||||
|
||||
def get_default_clientscopes(self, realm, client_id=None):
|
||||
@@ -1261,7 +1261,7 @@ class KeycloakAPI(object):
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of %s clientscopes in realm %s: %s" % (scope_type, realm, str(e)))
|
||||
self.fail_open_url(e, msg="Could not fetch list of %s clientscopes in realm %s: %s" % (scope_type, realm, str(e)))
|
||||
else:
|
||||
cid = self.get_client_id(client_id=client_id, realm=realm)
|
||||
clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid)
|
||||
@@ -1269,7 +1269,7 @@ class KeycloakAPI(object):
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of %s clientscopes in client %s: %s" % (scope_type, client_id, clientscopes_url))
|
||||
self.fail_open_url(e, msg="Could not fetch list of %s clientscopes in client %s: %s" % (scope_type, client_id, clientscopes_url))
|
||||
|
||||
def _decide_url_type_clientscope(self, client_id=None, scope_type="default"):
|
||||
"""Decides which url to use.
|
||||
@@ -1340,7 +1340,7 @@ class KeycloakAPI(object):
|
||||
|
||||
except Exception as e:
|
||||
place = 'realm' if client_id is None else 'client ' + client_id
|
||||
self.module.fail_json(msg="Unable to %s %s clientscope %s @ %s : %s" % (action, scope_type, id, place, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to %s %s clientscope %s @ %s : %s" % (action, scope_type, id, place, str(e)))
|
||||
|
||||
def create_clientsecret(self, id, realm="master"):
|
||||
""" Generate a new client secret by id
|
||||
@@ -1360,7 +1360,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
@@ -1384,7 +1384,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s'
|
||||
@@ -1404,7 +1404,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of groups in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch list of groups in realm %s: %s"
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_group_by_groupid(self, gid, realm="master"):
|
||||
@@ -1425,7 +1425,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg="Could not fetch group %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not fetch group %s in realm %s: %s"
|
||||
% (gid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch group %s in realm %s: %s"
|
||||
@@ -1572,7 +1572,7 @@ class KeycloakAPI(object):
|
||||
return open_url(groups_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create group %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not create group %s in realm %s: %s"
|
||||
% (grouprep['name'], realm, str(e)))
|
||||
|
||||
def create_subgroup(self, parents, grouprep, realm="master"):
|
||||
@@ -1600,7 +1600,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create subgroup %s for parent group %s in realm %s: %s"
|
||||
self.fail_open_url(e, msg="Could not create subgroup %s for parent group %s in realm %s: %s"
|
||||
% (grouprep['name'], parent_id, realm, str(e)))
|
||||
|
||||
def update_group(self, grouprep, realm="master"):
|
||||
@@ -1615,7 +1615,7 @@ class KeycloakAPI(object):
|
||||
return open_url(group_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update group %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update group %s in realm %s: %s'
|
||||
% (grouprep['name'], realm, str(e)))
|
||||
|
||||
def delete_group(self, name=None, groupid=None, realm="master"):
|
||||
@@ -1652,7 +1652,7 @@ class KeycloakAPI(object):
|
||||
return open_url(group_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to delete group %s: %s" % (groupid, str(e)))
|
||||
|
||||
def get_realm_roles(self, realm='master'):
|
||||
""" Obtains role representations for roles in a realm
|
||||
@@ -1669,7 +1669,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of roles for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of roles for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_realm_role(self, name, realm='master'):
|
||||
@@ -1687,7 +1687,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not fetch role %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not fetch role %s in realm %s: %s'
|
||||
% (name, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not fetch role %s in realm %s: %s'
|
||||
@@ -1707,7 +1707,7 @@ class KeycloakAPI(object):
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create role %s in realm %s: %s'
|
||||
% (rolerep['name'], realm, str(e)))
|
||||
|
||||
def update_realm_role(self, rolerep, realm='master'):
|
||||
@@ -1728,7 +1728,7 @@ class KeycloakAPI(object):
|
||||
self.update_role_composites(rolerep=rolerep, composites=composites, realm=realm)
|
||||
return role_response
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update role %s in realm %s: %s'
|
||||
% (rolerep['name'], realm, str(e)))
|
||||
|
||||
def get_role_composites(self, rolerep, clientid=None, realm='master'):
|
||||
@@ -1749,7 +1749,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not get role %s composites in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not get role %s composites in realm %s: %s'
|
||||
% (rolerep['name'], realm, str(e)))
|
||||
|
||||
def create_role_composites(self, rolerep, composites, clientid=None, realm='master'):
|
||||
@@ -1766,7 +1766,7 @@ class KeycloakAPI(object):
|
||||
return open_url(composite_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(composites), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s composites in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create role %s composites in realm %s: %s'
|
||||
% (rolerep['name'], realm, str(e)))
|
||||
|
||||
def delete_role_composites(self, rolerep, composites, clientid=None, realm='master'):
|
||||
@@ -1783,7 +1783,7 @@ class KeycloakAPI(object):
|
||||
return open_url(composite_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(composites), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s composites in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create role %s composites in realm %s: %s'
|
||||
% (rolerep['name'], realm, str(e)))
|
||||
|
||||
def update_role_composites(self, rolerep, composites, clientid=None, realm='master'):
|
||||
@@ -1847,7 +1847,7 @@ class KeycloakAPI(object):
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Unable to delete role %s in realm %s: %s'
|
||||
% (name, realm, str(e)))
|
||||
|
||||
def get_client_roles(self, clientid, realm='master'):
|
||||
@@ -1870,7 +1870,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of roles for client %s in realm %s: %s'
|
||||
% (clientid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of roles for client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of roles for client %s in realm %s: %s'
|
||||
% (clientid, realm, str(e)))
|
||||
|
||||
def get_client_role(self, name, clientid, realm='master'):
|
||||
@@ -1894,7 +1894,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not fetch role %s in client %s of realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not fetch role %s in client %s of realm %s: %s'
|
||||
% (name, clientid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not fetch role %s for client %s in realm %s: %s'
|
||||
@@ -1920,7 +1920,7 @@ class KeycloakAPI(object):
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s for client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create role %s for client %s in realm %s: %s'
|
||||
% (rolerep['name'], clientid, realm, str(e)))
|
||||
|
||||
def convert_role_composites(self, composites):
|
||||
@@ -1962,7 +1962,7 @@ class KeycloakAPI(object):
|
||||
self.update_role_composites(rolerep=rolerep, clientid=clientid, composites=composites, realm=realm)
|
||||
return update_role_response
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s for client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update role %s for client %s in realm %s: %s'
|
||||
% (rolerep['name'], clientid, realm, str(e)))
|
||||
|
||||
def delete_client_role(self, name, clientid, realm="master"):
|
||||
@@ -1981,7 +1981,7 @@ class KeycloakAPI(object):
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s for client %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Unable to delete role %s for client %s in realm %s: %s'
|
||||
% (name, clientid, realm, str(e)))
|
||||
|
||||
def get_authentication_flow_by_alias(self, alias, realm='master'):
|
||||
@@ -2003,7 +2003,7 @@ class KeycloakAPI(object):
|
||||
break
|
||||
return authentication_flow
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable get authentication flow %s: %s" % (alias, str(e)))
|
||||
self.fail_open_url(e, msg="Unable get authentication flow %s: %s" % (alias, str(e)))
|
||||
|
||||
def delete_authentication_flow_by_id(self, id, realm='master'):
|
||||
"""
|
||||
@@ -2018,8 +2018,8 @@ class KeycloakAPI(object):
|
||||
return open_url(flow_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete authentication flow %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not delete authentication flow %s in realm %s: %s'
|
||||
% (id, realm, str(e)))
|
||||
|
||||
def copy_auth_flow(self, config, realm='master'):
|
||||
"""
|
||||
@@ -2055,8 +2055,8 @@ class KeycloakAPI(object):
|
||||
return flow
|
||||
return None
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not copy authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not copy authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
||||
def create_empty_auth_flow(self, config, realm='master'):
|
||||
"""
|
||||
@@ -2095,8 +2095,8 @@ class KeycloakAPI(object):
|
||||
return flow
|
||||
return None
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create empty authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create empty authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
||||
def update_authentication_executions(self, flowAlias, updatedExec, realm='master'):
|
||||
""" Update authentication executions
|
||||
@@ -2117,8 +2117,8 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except HTTPError as e:
|
||||
self.module.fail_json(msg="Unable to update execution '%s': %s: %s %s" %
|
||||
(flowAlias, repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(updatedExec)))
|
||||
self.fail_open_url(e, msg="Unable to update execution '%s': %s: %s %s"
|
||||
% (flowAlias, repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(updatedExec)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
|
||||
|
||||
@@ -2141,7 +2141,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
|
||||
def create_subflow(self, subflowName, flowAlias, realm='master', flowType='basic-flow'):
|
||||
""" Create new sublow on the flow
|
||||
@@ -2166,7 +2166,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new subflow %s: %s" % (subflowName, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to create new subflow %s: %s" % (subflowName, str(e)))
|
||||
|
||||
def create_execution(self, execution, flowAlias, realm='master'):
|
||||
""" Create new execution on the flow
|
||||
@@ -2190,8 +2190,8 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except HTTPError as e:
|
||||
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s: %s %s" %
|
||||
(flowAlias, execution["providerId"], repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(newExec)))
|
||||
self.fail_open_url(e, msg="Unable to create new execution '%s' %s: %s: %s %s"
|
||||
% (flowAlias, execution["providerId"], repr(e), ";".join([e.url, e.msg, str(e.code), str(e.hdrs)]), str(newExec)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new execution '%s' %s: %s" % (flowAlias, execution["providerId"], repr(e)))
|
||||
|
||||
@@ -2227,7 +2227,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to change execution priority %s: %s" % (executionId, str(e)))
|
||||
self.fail_open_url(e, msg="Unable to change execution priority %s: %s" % (executionId, str(e)))
|
||||
|
||||
def get_executions_representation(self, config, realm='master'):
|
||||
"""
|
||||
@@ -2264,8 +2264,8 @@ class KeycloakAPI(object):
|
||||
execution["authenticationConfig"] = execConfig
|
||||
return executions
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not get executions for authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not get executions for authentication flow %s in realm %s: %s'
|
||||
% (config["alias"], realm, str(e)))
|
||||
|
||||
def get_required_actions(self, realm='master'):
|
||||
"""
|
||||
@@ -2318,7 +2318,8 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs
|
||||
)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
self.fail_open_url(
|
||||
e,
|
||||
msg='Unable to register required action %s in realm %s: %s'
|
||||
% (rep["name"], realm, str(e))
|
||||
)
|
||||
@@ -2346,7 +2347,8 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs
|
||||
)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
self.fail_open_url(
|
||||
e,
|
||||
msg='Unable to update required action %s in realm %s: %s'
|
||||
% (alias, realm, str(e))
|
||||
)
|
||||
@@ -2372,7 +2374,8 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs
|
||||
)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
self.fail_open_url(
|
||||
e,
|
||||
msg='Unable to delete required action %s in realm %s: %s'
|
||||
% (alias, realm, str(e))
|
||||
)
|
||||
@@ -2390,7 +2393,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity providers for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of identity providers for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of identity providers for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_identity_provider(self, alias, realm='master'):
|
||||
@@ -2407,7 +2410,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not fetch identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not fetch identity provider %s in realm %s: %s'
|
||||
% (alias, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not fetch identity provider %s in realm %s: %s'
|
||||
@@ -2424,7 +2427,7 @@ class KeycloakAPI(object):
|
||||
return open_url(idps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create identity provider %s in realm %s: %s'
|
||||
% (idprep['alias'], realm, str(e)))
|
||||
|
||||
def update_identity_provider(self, idprep, realm='master'):
|
||||
@@ -2438,7 +2441,7 @@ class KeycloakAPI(object):
|
||||
return open_url(idp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update identity provider %s in realm %s: %s'
|
||||
% (idprep['alias'], realm, str(e)))
|
||||
|
||||
def delete_identity_provider(self, alias, realm='master'):
|
||||
@@ -2451,7 +2454,7 @@ class KeycloakAPI(object):
|
||||
return open_url(idp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Unable to delete identity provider %s in realm %s: %s'
|
||||
% (alias, realm, str(e)))
|
||||
|
||||
def get_identity_provider_mappers(self, alias, realm='master'):
|
||||
@@ -2469,7 +2472,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of identity provider mappers for idp %s in realm %s: %s'
|
||||
% (alias, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of identity provider mappers for idp %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of identity provider mappers for idp %s in realm %s: %s'
|
||||
% (alias, realm, str(e)))
|
||||
|
||||
def get_identity_provider_mapper(self, mid, alias, realm='master'):
|
||||
@@ -2488,7 +2491,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not fetch mapper %s for identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not fetch mapper %s for identity provider %s in realm %s: %s'
|
||||
% (mid, alias, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not fetch mapper %s for identity provider %s in realm %s: %s'
|
||||
@@ -2506,7 +2509,7 @@ class KeycloakAPI(object):
|
||||
return open_url(mappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider mapper %s for idp %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create identity provider mapper %s for idp %s in realm %s: %s'
|
||||
% (mapper['name'], alias, realm, str(e)))
|
||||
|
||||
def update_identity_provider_mapper(self, mapper, alias, realm='master'):
|
||||
@@ -2521,7 +2524,7 @@ class KeycloakAPI(object):
|
||||
return open_url(mapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update mapper %s for identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update mapper %s for identity provider %s in realm %s: %s'
|
||||
% (mapper['id'], alias, realm, str(e)))
|
||||
|
||||
def delete_identity_provider_mapper(self, mid, alias, realm='master'):
|
||||
@@ -2535,7 +2538,7 @@ class KeycloakAPI(object):
|
||||
return open_url(mapper_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete mapper %s for identity provider %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Unable to delete mapper %s for identity provider %s in realm %s: %s'
|
||||
% (mid, alias, realm, str(e)))
|
||||
|
||||
def get_components(self, filter=None, realm='master'):
|
||||
@@ -2555,7 +2558,7 @@ class KeycloakAPI(object):
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of components for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not obtain list of components for realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not obtain list of components for realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def get_component(self, cid, realm='master'):
|
||||
@@ -2572,7 +2575,7 @@ class KeycloakAPI(object):
|
||||
if e.code == 404:
|
||||
return None
|
||||
else:
|
||||
self.module.fail_json(msg='Could not fetch component %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not fetch component %s in realm %s: %s'
|
||||
% (cid, realm, str(e)))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not fetch component %s in realm %s: %s'
|
||||
@@ -2595,7 +2598,7 @@ class KeycloakAPI(object):
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create component in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create component in realm %s: %s'
|
||||
% (realm, str(e)))
|
||||
|
||||
def update_component(self, comprep, realm='master'):
|
||||
@@ -2612,7 +2615,7 @@ class KeycloakAPI(object):
|
||||
return open_url(comp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(comprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update component %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update component %s in realm %s: %s'
|
||||
% (cid, realm, str(e)))
|
||||
|
||||
def delete_component(self, cid, realm='master'):
|
||||
@@ -2625,7 +2628,7 @@ class KeycloakAPI(object):
|
||||
return open_url(comp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Unable to delete component %s in realm %s: %s'
|
||||
% (cid, realm, str(e)))
|
||||
|
||||
def get_authz_authorization_scope_by_name(self, name, client_id, realm):
|
||||
@@ -2647,7 +2650,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create authorization scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create authorization scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def update_authz_authorization_scope(self, payload, id, client_id, realm):
|
||||
"""Update an authorization scope for a Keycloak client"""
|
||||
@@ -2657,7 +2660,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create update scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create update scope %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def remove_authz_authorization_scope(self, id, client_id, realm):
|
||||
"""Remove an authorization scope from a Keycloak client"""
|
||||
@@ -2667,7 +2670,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete scope %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not delete scope %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
|
||||
def get_user_by_id(self, user_id, realm='master'):
|
||||
"""
|
||||
@@ -2690,7 +2693,7 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs))
|
||||
return userrep
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not get user %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not get user %s in realm %s: %s'
|
||||
% (user_id, realm, str(e)))
|
||||
|
||||
def create_user(self, userrep, realm='master'):
|
||||
@@ -2718,7 +2721,7 @@ class KeycloakAPI(object):
|
||||
realm=realm)
|
||||
return created_user
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create user %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not create user %s in realm %s: %s'
|
||||
% (userrep['username'], realm, str(e)))
|
||||
|
||||
def convert_user_attributes_to_keycloak_dict(self, attributes):
|
||||
@@ -2764,7 +2767,7 @@ class KeycloakAPI(object):
|
||||
realm=realm)
|
||||
return updated_user
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update user %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not update user %s in realm %s: %s'
|
||||
% (userrep['username'], realm, str(e)))
|
||||
|
||||
def delete_user(self, user_id, realm='master'):
|
||||
@@ -2786,7 +2789,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete user %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not delete user %s in realm %s: %s'
|
||||
% (user_id, realm, str(e)))
|
||||
|
||||
def get_user_groups(self, user_id, realm='master'):
|
||||
@@ -2813,7 +2816,7 @@ class KeycloakAPI(object):
|
||||
groups.append(user_group["name"])
|
||||
return groups
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not get groups for user %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not get groups for user %s in realm %s: %s'
|
||||
% (user_id, realm, str(e)))
|
||||
|
||||
def add_user_in_group(self, user_id, group_id, realm='master'):
|
||||
@@ -2837,7 +2840,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not add user %s in group %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not add user %s in group %s in realm %s: %s'
|
||||
% (user_id, group_id, realm, str(e)))
|
||||
|
||||
def remove_user_from_group(self, user_id, group_id, realm='master'):
|
||||
@@ -2861,7 +2864,7 @@ class KeycloakAPI(object):
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not remove user %s from group %s in realm %s: %s'
|
||||
self.fail_open_url(e, msg='Could not remove user %s from group %s in realm %s: %s'
|
||||
% (user_id, group_id, realm, str(e)))
|
||||
|
||||
def update_user_groups_membership(self, userrep, groups, realm='master'):
|
||||
@@ -2933,7 +2936,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def remove_authz_custom_policy(self, policy_id, client_id, realm):
|
||||
"""Remove a custom policy from a Keycloak client"""
|
||||
@@ -2944,7 +2947,7 @@ class KeycloakAPI(object):
|
||||
return open_url(delete_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete custom policy %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not delete custom policy %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
|
||||
def get_authz_permission_by_name(self, name, client_id, realm):
|
||||
"""Get authorization permission by name"""
|
||||
@@ -2966,7 +2969,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def remove_authz_permission(self, id, client_id, realm):
|
||||
"""Create an authorization permission for a Keycloak client"""
|
||||
@@ -2976,7 +2979,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete permission %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not delete permission %s for client %s in realm %s: %s' % (id, client_id, realm, str(e)))
|
||||
|
||||
def update_authz_permission(self, payload, permission_type, id, client_id, realm):
|
||||
"""Update a permission for a Keycloak client"""
|
||||
@@ -2986,7 +2989,7 @@ class KeycloakAPI(object):
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(payload), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create update permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
self.fail_open_url(e, msg='Could not create update permission %s for client %s in realm %s: %s' % (payload['name'], client_id, realm, str(e)))
|
||||
|
||||
def get_authz_resource_by_name(self, name, client_id, realm):
|
||||
"""Get authorization resource by name"""
|
||||
@@ -3011,3 +3014,11 @@ class KeycloakAPI(object):
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def fail_open_url(self, e, msg, **kwargs):
|
||||
try:
|
||||
if isinstance(e, HTTPError):
|
||||
msg = "%s: %s" % (msg, to_native(e.read()))
|
||||
except Exception as ingore:
|
||||
pass
|
||||
self.module.fail_json(msg, **kwargs)
|
||||
|
||||
@@ -41,7 +41,7 @@ class LXDClientException(Exception):
|
||||
|
||||
|
||||
class LXDClient(object):
|
||||
def __init__(self, url, key_file=None, cert_file=None, debug=False):
|
||||
def __init__(self, url, key_file=None, cert_file=None, debug=False, server_cert_file=None, server_check_hostname=True):
|
||||
"""LXD Client.
|
||||
|
||||
:param url: The URL of the LXD server. (e.g. unix:/var/lib/lxd/unix.socket or https://127.0.0.1)
|
||||
@@ -52,6 +52,10 @@ class LXDClient(object):
|
||||
:type cert_file: ``str``
|
||||
:param debug: The debug flag. The request and response are stored in logs when debug is true.
|
||||
:type debug: ``bool``
|
||||
:param server_cert_file: The path of the server certificate file.
|
||||
:type server_cert_file: ``str``
|
||||
:param server_check_hostname: Whether to check the server's hostname as part of TLS verification.
|
||||
:type debug: ``bool``
|
||||
"""
|
||||
self.url = url
|
||||
self.debug = debug
|
||||
@@ -61,6 +65,10 @@ class LXDClient(object):
|
||||
self.key_file = key_file
|
||||
parts = generic_urlparse(urlparse(self.url))
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
||||
if server_cert_file:
|
||||
# Check that the received cert is signed by the provided server_cert_file
|
||||
ctx.load_verify_locations(cafile=server_cert_file)
|
||||
ctx.check_hostname = server_check_hostname
|
||||
ctx.load_cert_chain(cert_file, keyfile=key_file)
|
||||
self.connection = HTTPSConnection(parts.get('netloc'), context=ctx)
|
||||
elif url.startswith('unix:'):
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from functools import partial
|
||||
|
||||
|
||||
class ArgFormat(object):
|
||||
"""
|
||||
Argument formatter for use as a command line parameter. Used in CmdMixin.
|
||||
"""
|
||||
BOOLEAN = 0
|
||||
PRINTF = 1
|
||||
FORMAT = 2
|
||||
BOOLEAN_NOT = 3
|
||||
|
||||
@staticmethod
|
||||
def stars_deco(num):
|
||||
if num == 1:
|
||||
def deco(f):
|
||||
return lambda v: f(*v)
|
||||
return deco
|
||||
elif num == 2:
|
||||
def deco(f):
|
||||
return lambda v: f(**v)
|
||||
return deco
|
||||
|
||||
return lambda f: f
|
||||
|
||||
def __init__(self, name, fmt=None, style=FORMAT, stars=0):
|
||||
"""
|
||||
THIS CLASS IS BEING DEPRECATED.
|
||||
It was never meant to be used outside the scope of CmdMixin, and CmdMixin is being deprecated.
|
||||
See the deprecation notice in ``CmdMixin.__init__()`` below.
|
||||
|
||||
Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for
|
||||
the CLI command execution.
|
||||
:param name: Name of the argument to be formatted
|
||||
:param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that
|
||||
:param style: Whether arg_format (as str) should use printf-style formatting.
|
||||
Ignored if arg_format is None or not a str (should be callable).
|
||||
:param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value
|
||||
"""
|
||||
def printf_fmt(_fmt, v):
|
||||
try:
|
||||
return [_fmt % v]
|
||||
except TypeError as e:
|
||||
if e.args[0] != 'not all arguments converted during string formatting':
|
||||
raise
|
||||
return [_fmt]
|
||||
|
||||
_fmts = {
|
||||
ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []),
|
||||
ArgFormat.BOOLEAN_NOT: lambda _fmt, v: ([] if bool(v) else [_fmt]),
|
||||
ArgFormat.PRINTF: printf_fmt,
|
||||
ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)],
|
||||
}
|
||||
|
||||
self.name = name
|
||||
self.stars = stars
|
||||
self.style = style
|
||||
|
||||
if fmt is None:
|
||||
fmt = "{0}"
|
||||
style = ArgFormat.FORMAT
|
||||
|
||||
if isinstance(fmt, str):
|
||||
func = _fmts[style]
|
||||
self.arg_format = partial(func, fmt)
|
||||
elif isinstance(fmt, list) or isinstance(fmt, tuple):
|
||||
self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt]
|
||||
elif hasattr(fmt, '__call__'):
|
||||
self.arg_format = fmt
|
||||
else:
|
||||
raise TypeError('Parameter fmt must be either: a string, a list/tuple of '
|
||||
'strings or a function: type={0}, value={1}'.format(type(fmt), fmt))
|
||||
|
||||
if stars:
|
||||
self.arg_format = (self.stars_deco(stars))(self.arg_format)
|
||||
|
||||
def to_text(self, value):
|
||||
if value is None and self.style != ArgFormat.BOOLEAN_NOT:
|
||||
return []
|
||||
func = self.arg_format
|
||||
return [str(p) for p in func(value)]
|
||||
|
||||
|
||||
class CmdMixin(object):
|
||||
"""
|
||||
THIS CLASS IS BEING DEPRECATED.
|
||||
See the deprecation notice in ``CmdMixin.__init__()`` below.
|
||||
|
||||
Mixin for mapping module options to running a CLI command with its arguments.
|
||||
"""
|
||||
command = None
|
||||
command_args_formats = {}
|
||||
run_command_fixed_options = {}
|
||||
check_rc = False
|
||||
force_lang = "C"
|
||||
|
||||
@property
|
||||
def module_formats(self):
|
||||
result = {}
|
||||
for param in self.module.params.keys():
|
||||
result[param] = ArgFormat(param)
|
||||
return result
|
||||
|
||||
@property
|
||||
def custom_formats(self):
|
||||
result = {}
|
||||
for param, fmt_spec in self.command_args_formats.items():
|
||||
result[param] = ArgFormat(param, **fmt_spec)
|
||||
return result
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CmdMixin, self).__init__(*args, **kwargs)
|
||||
self.module.deprecate(
|
||||
'The CmdMixin used in classes CmdModuleHelper and CmdStateModuleHelper is being deprecated. '
|
||||
'Modules should use community.general.plugins.module_utils.cmd_runner.CmdRunner instead.',
|
||||
version='8.0.0',
|
||||
collection_name='community.general',
|
||||
)
|
||||
|
||||
def _calculate_args(self, extra_params=None, params=None):
|
||||
def add_arg_formatted_param(_cmd_args, arg_format, _value):
|
||||
args = list(arg_format.to_text(_value))
|
||||
return _cmd_args + args
|
||||
|
||||
def find_format(_param):
|
||||
return self.custom_formats.get(_param, self.module_formats.get(_param))
|
||||
|
||||
extra_params = extra_params or dict()
|
||||
cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)
|
||||
try:
|
||||
cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)
|
||||
except ValueError:
|
||||
pass
|
||||
param_list = params if params else self.vars.keys()
|
||||
|
||||
for param in param_list:
|
||||
if isinstance(param, dict):
|
||||
if len(param) != 1:
|
||||
self.do_raise("run_command parameter as a dict must contain only one key: {0}".format(param))
|
||||
_param = list(param.keys())[0]
|
||||
fmt = find_format(_param)
|
||||
value = param[_param]
|
||||
elif isinstance(param, str):
|
||||
if param in self.vars.keys():
|
||||
fmt = find_format(param)
|
||||
value = self.vars[param]
|
||||
elif param in extra_params:
|
||||
fmt = find_format(param)
|
||||
value = extra_params[param]
|
||||
else:
|
||||
self.do_raise('Cannot determine value for parameter: {0}'.format(param))
|
||||
else:
|
||||
self.do_raise("run_command parameter must be either a str or a dict: {0}".format(param))
|
||||
cmd_args = add_arg_formatted_param(cmd_args, fmt, value)
|
||||
|
||||
return cmd_args
|
||||
|
||||
def process_command_output(self, rc, out, err):
|
||||
return rc, out, err
|
||||
|
||||
def run_command(self,
|
||||
extra_params=None,
|
||||
params=None,
|
||||
process_output=None,
|
||||
publish_rc=True,
|
||||
publish_out=True,
|
||||
publish_err=True,
|
||||
publish_cmd=True,
|
||||
*args, **kwargs):
|
||||
cmd_args = self._calculate_args(extra_params, params)
|
||||
options = dict(self.run_command_fixed_options)
|
||||
options['check_rc'] = options.get('check_rc', self.check_rc)
|
||||
options.update(kwargs)
|
||||
env_update = dict(options.get('environ_update', {}))
|
||||
if self.force_lang:
|
||||
env_update.update({
|
||||
'LANGUAGE': self.force_lang,
|
||||
'LC_ALL': self.force_lang,
|
||||
})
|
||||
self.update_output(force_lang=self.force_lang)
|
||||
options['environ_update'] = env_update
|
||||
rc, out, err = self.module.run_command(cmd_args, *args, **options)
|
||||
if publish_rc:
|
||||
self.update_output(rc=rc)
|
||||
if publish_out:
|
||||
self.update_output(stdout=out)
|
||||
if publish_err:
|
||||
self.update_output(stderr=err)
|
||||
if publish_cmd:
|
||||
self.update_output(cmd_args=cmd_args)
|
||||
if process_output is None:
|
||||
_process = self.process_command_output
|
||||
else:
|
||||
_process = process_output
|
||||
|
||||
return _process(rc, out, err)
|
||||
@@ -12,7 +12,6 @@ from ansible.module_utils.common.dict_transformations import dict_merge
|
||||
|
||||
# (TODO: remove AnsibleModule!) pylint: disable-next=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.base import ModuleHelperBase, AnsibleModule # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyMixin
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarsMixin
|
||||
@@ -66,19 +65,3 @@ class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelper
|
||||
|
||||
class StateModuleHelper(StateMixin, ModuleHelper):
|
||||
pass
|
||||
|
||||
|
||||
class CmdModuleHelper(CmdMixin, ModuleHelper):
|
||||
"""
|
||||
THIS CLASS IS BEING DEPRECATED.
|
||||
See the deprecation notice in ``CmdMixin.__init__()``.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class CmdStateModuleHelper(CmdMixin, StateMixin, ModuleHelper):
|
||||
"""
|
||||
THIS CLASS IS BEING DEPRECATED.
|
||||
See the deprecation notice in ``CmdMixin.__init__()``.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -11,9 +11,8 @@ __metaclass__ = type
|
||||
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.module_helper import (
|
||||
ModuleHelper, StateModuleHelper, CmdModuleHelper, CmdStateModuleHelper, AnsibleModule
|
||||
ModuleHelper, StateModuleHelper, AnsibleModule
|
||||
)
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr, DependencyMixin # noqa: F401
|
||||
from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException # noqa: F401
|
||||
|
||||
@@ -7,12 +7,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
# (TODO: remove next line!)
|
||||
import atexit # noqa: F401, pylint: disable=unused-import
|
||||
# (TODO: remove next line!)
|
||||
import time # noqa: F401, pylint: disable=unused-import
|
||||
# (TODO: remove next line!)
|
||||
import re # noqa: F401, pylint: disable=unused-import
|
||||
import traceback
|
||||
|
||||
PROXMOXER_IMP_ERR = None
|
||||
@@ -26,8 +20,6 @@ except ImportError:
|
||||
|
||||
|
||||
from ansible.module_utils.basic import env_fallback, missing_required_lib
|
||||
# (TODO: remove next line!)
|
||||
from ansible.module_utils.common.text.converters import to_native # noqa: F401, pylint: disable=unused-import
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
|
||||
|
||||
@@ -2955,7 +2955,7 @@ class RedfishUtils(object):
|
||||
result = {}
|
||||
inventory = {}
|
||||
# Get these entries, but does not fail if not found
|
||||
properties = ['Status', 'HostName', 'PowerState', 'Model', 'Manufacturer',
|
||||
properties = ['Status', 'HostName', 'PowerState', 'BootProgress', 'Model', 'Manufacturer',
|
||||
'PartNumber', 'SystemType', 'AssetTag', 'ServiceTag',
|
||||
'SerialNumber', 'SKU', 'BiosVersion', 'MemorySummary',
|
||||
'ProcessorSummary', 'TrustedModules', 'Name', 'Id']
|
||||
|
||||
@@ -66,6 +66,19 @@ class _Variable(object):
|
||||
if verbosity is not None:
|
||||
self.verbosity = verbosity
|
||||
|
||||
def as_dict(self, meta_only=False):
|
||||
d = {
|
||||
"diff": self.diff,
|
||||
"change": self.change,
|
||||
"output": self.output,
|
||||
"fact": self.fact,
|
||||
"verbosity": self.verbosity,
|
||||
}
|
||||
if not meta_only:
|
||||
d["initial_value"] = copy.deepcopy(self.initial_value)
|
||||
d["value"] = self.value
|
||||
return d
|
||||
|
||||
def set_value(self, value):
|
||||
if not self.init:
|
||||
self.initial_value = copy.deepcopy(value)
|
||||
@@ -93,7 +106,7 @@ class _Variable(object):
|
||||
|
||||
|
||||
class VarDict(object):
|
||||
reserved_names = ('__vars__', 'var', 'set_meta', 'set', 'output', 'diff', 'facts', 'has_changed')
|
||||
reserved_names = ('__vars__', '_var', 'var', 'set_meta', 'get_meta', 'set', 'output', 'diff', 'facts', 'has_changed', 'as_dict')
|
||||
|
||||
def __init__(self):
|
||||
self.__vars__ = dict()
|
||||
@@ -119,6 +132,9 @@ class VarDict(object):
|
||||
def _var(self, name):
|
||||
return self.__vars__[name]
|
||||
|
||||
def var(self, name):
|
||||
return self._var(name).as_dict()
|
||||
|
||||
def set_meta(self, name, **kwargs):
|
||||
"""Set the metadata for the variable
|
||||
|
||||
@@ -133,6 +149,9 @@ class VarDict(object):
|
||||
"""
|
||||
self._var(name).set_meta(**kwargs)
|
||||
|
||||
def get_meta(self, name):
|
||||
return self._var(name).as_dict(meta_only=True)
|
||||
|
||||
def set(self, name, value, **kwargs):
|
||||
"""Set the value and optionally metadata for a variable. The variable is not required to exist prior to calling `set`.
|
||||
|
||||
@@ -172,7 +191,7 @@ class VarDict(object):
|
||||
|
||||
@property
|
||||
def has_changed(self):
|
||||
return any(True for var in self.__vars__.values() if var.has_changed)
|
||||
return any(var.has_changed for var in self.__vars__.values())
|
||||
|
||||
def as_dict(self):
|
||||
return dict((name, var.value) for name, var in self.__vars__.items())
|
||||
|
||||
@@ -10,13 +10,4 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.six import raise_from
|
||||
|
||||
try:
|
||||
from ansible.module_utils.compat.version import LooseVersion # noqa: F401, pylint: disable=unused-import
|
||||
except ImportError:
|
||||
try:
|
||||
from distutils.version import LooseVersion # noqa: F401, pylint: disable=unused-import
|
||||
except ImportError as exc:
|
||||
msg = 'To use this plugin or module with ansible-core 2.11, you need to use Python < 3.12 with distutils.version present'
|
||||
raise_from(ImportError(msg), exc)
|
||||
from ansible.module_utils.compat.version import LooseVersion # noqa: F401, pylint: disable=unused-import
|
||||
|
||||
@@ -253,7 +253,7 @@ options:
|
||||
author:
|
||||
- "He Guimin (@xiaozhu36)"
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "Python >= 3.6"
|
||||
- "footmark >= 1.19.0"
|
||||
extends_documentation_fragment:
|
||||
- community.general.alicloud
|
||||
|
||||
@@ -31,7 +31,6 @@ short_description: Gather information on instances of Alibaba Cloud ECS
|
||||
description:
|
||||
- This module fetches data from the Open API in Alicloud.
|
||||
The module must be called from within the ECS instance itself.
|
||||
- This module was called C(ali_instance_facts) before Ansible 2.9. The usage did not change.
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
@@ -61,7 +60,7 @@ options:
|
||||
author:
|
||||
- "He Guimin (@xiaozhu36)"
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "Python >= 3.6"
|
||||
- "footmark >= 1.13.0"
|
||||
extends_documentation_fragment:
|
||||
- community.general.alicloud
|
||||
|
||||
@@ -17,15 +17,13 @@ version_added: 3.5.0
|
||||
description:
|
||||
- This module allows the installation of Ansible collections or roles using C(ansible-galaxy).
|
||||
notes:
|
||||
- >
|
||||
B(Ansible 2.9/2.10): The C(ansible-galaxy) command changed significantly between Ansible 2.9 and
|
||||
ansible-base 2.10 (later ansible-core 2.11). See comments in the parameters.
|
||||
- Support for B(Ansible 2.9/2.10) was removed in community.general 8.0.0.
|
||||
- >
|
||||
The module will try and run using the C(C.UTF-8) locale.
|
||||
If that fails, it will try C(en_US.UTF-8).
|
||||
If that one also fails, the module will fail.
|
||||
requirements:
|
||||
- Ansible 2.9, ansible-base 2.10, or ansible-core 2.11 or newer
|
||||
- ansible-core 2.11 or newer
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
@@ -39,7 +37,6 @@ options:
|
||||
- The type of installation performed by C(ansible-galaxy).
|
||||
- If O(type=both), then O(requirements_file) must be passed and it may contain both roles and collections.
|
||||
- "Note however that the opposite is not true: if using a O(requirements_file), then O(type) can be any of the three choices."
|
||||
- "B(Ansible 2.9): The option V(both) will have the same effect as V(role)."
|
||||
type: str
|
||||
choices: [collection, role, both]
|
||||
required: true
|
||||
@@ -56,7 +53,6 @@ options:
|
||||
- Path to a file containing a list of requirements to be installed.
|
||||
- It works for O(type) equals to V(collection) and V(role).
|
||||
- O(name) and O(requirements_file) are mutually exclusive.
|
||||
- "B(Ansible 2.9): It can only be used to install either O(type=role) or O(type=collection), but not both at the same run."
|
||||
type: path
|
||||
dest:
|
||||
description:
|
||||
@@ -75,24 +71,16 @@ options:
|
||||
description:
|
||||
- Force overwriting an existing role or collection.
|
||||
- Using O(force=true) is mandatory when downgrading.
|
||||
- "B(Ansible 2.9 and 2.10): Must be V(true) to upgrade roles and collections."
|
||||
type: bool
|
||||
default: false
|
||||
ack_ansible29:
|
||||
description:
|
||||
- Acknowledge using Ansible 2.9 with its limitations, and prevents the module from generating warnings about them.
|
||||
- This option is completely ignored if using a version of Ansible greater than C(2.9.x).
|
||||
- Note that this option will be removed without any further deprecation warning once support
|
||||
for Ansible 2.9 is removed from this module.
|
||||
- This option has no longer any effect and will be removed in community.general 9.0.0.
|
||||
type: bool
|
||||
default: false
|
||||
ack_min_ansiblecore211:
|
||||
description:
|
||||
- Acknowledge the module is deprecating support for Ansible 2.9 and ansible-base 2.10.
|
||||
- Support for those versions will be removed in community.general 8.0.0.
|
||||
At the same time, this option will be removed without any deprecation warning!
|
||||
- This option is completely ignored if using a version of ansible-core/ansible-base/Ansible greater than C(2.11).
|
||||
- For the sake of conciseness, setting this parameter to V(true) implies O(ack_ansible29=true).
|
||||
- This option has no longer any effect and will be removed in community.general 9.0.0.
|
||||
type: bool
|
||||
default: false
|
||||
"""
|
||||
@@ -147,7 +135,6 @@ RETURN = """
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the roles installed per path.
|
||||
- If O(name) is specified, returns that role name and the version installed per path.
|
||||
- "B(Ansible 2.9): Returns empty because C(ansible-galaxy) has no C(list) subcommand."
|
||||
type: dict
|
||||
returned: always when installing roles
|
||||
contains:
|
||||
@@ -164,7 +151,6 @@ RETURN = """
|
||||
description:
|
||||
- If O(requirements_file) is specified instead, returns dictionary with all the collections installed per path.
|
||||
- If O(name) is specified, returns that collection name and the version installed per path.
|
||||
- "B(Ansible 2.9): Returns empty because C(ansible-galaxy) has no C(list) subcommand."
|
||||
type: dict
|
||||
returned: always when installing collections
|
||||
contains:
|
||||
@@ -206,7 +192,6 @@ class AnsibleGalaxyInstall(ModuleHelper):
|
||||
_RE_LIST_ROLE = re.compile(r'^- (?P<elem>\w+\.\w+),\s+(?P<version>[\d\.]+)\s*$')
|
||||
_RE_INSTALL_OUTPUT = None # Set after determining ansible version, see __init_module__()
|
||||
ansible_version = None
|
||||
is_ansible29 = None
|
||||
|
||||
output_params = ('type', 'name', 'dest', 'requirements_file', 'force', 'no_deps')
|
||||
module = dict(
|
||||
@@ -217,8 +202,18 @@ class AnsibleGalaxyInstall(ModuleHelper):
|
||||
dest=dict(type='path'),
|
||||
force=dict(type='bool', default=False),
|
||||
no_deps=dict(type='bool', default=False),
|
||||
ack_ansible29=dict(type='bool', default=False),
|
||||
ack_min_ansiblecore211=dict(type='bool', default=False),
|
||||
ack_ansible29=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
removed_in_version='9.0.0',
|
||||
removed_from_collection='community.general',
|
||||
),
|
||||
ack_min_ansiblecore211=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
removed_in_version='9.0.0',
|
||||
removed_from_collection='community.general',
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[('name', 'requirements_file')],
|
||||
required_one_of=[('name', 'requirements_file')],
|
||||
@@ -268,26 +263,22 @@ class AnsibleGalaxyInstall(ModuleHelper):
|
||||
def __init_module__(self):
|
||||
# self.runner = CmdRunner(self.module, command=self.command, arg_formats=self.command_args_formats, force_lang=self.force_lang)
|
||||
self.runner, self.ansible_version = self._get_ansible_galaxy_version()
|
||||
if self.ansible_version < (2, 11) and not self.vars.ack_min_ansiblecore211:
|
||||
self.module.deprecate(
|
||||
"Support for Ansible 2.9 and ansible-base 2.10 is being deprecated. "
|
||||
"At the same time support for them is ended, also the ack_ansible29 option will be removed. "
|
||||
"Upgrading is strongly recommended, or set 'ack_min_ansiblecore211' to suppress this message.",
|
||||
version="8.0.0",
|
||||
collection_name="community.general",
|
||||
if self.ansible_version < (2, 11):
|
||||
self.module.fail_json(
|
||||
msg="Support for Ansible 2.9 and ansible-base 2.10 has ben removed."
|
||||
)
|
||||
self.is_ansible29 = self.ansible_version < (2, 10)
|
||||
if self.is_ansible29:
|
||||
self._RE_INSTALL_OUTPUT = re.compile(r"^(?:.*Installing '(?P<collection>\w+\.\w+):(?P<cversion>[\d\.]+)'.*"
|
||||
r'|- (?P<role>\w+\.\w+) \((?P<rversion>[\d\.]+)\)'
|
||||
r' was installed successfully)$')
|
||||
else:
|
||||
# Collection install output changed:
|
||||
# ansible-base 2.10: "coll.name (x.y.z)"
|
||||
# ansible-core 2.11+: "coll.name:x.y.z"
|
||||
self._RE_INSTALL_OUTPUT = re.compile(r'^(?:(?P<collection>\w+\.\w+)(?: \(|:)(?P<cversion>[\d\.]+)\)?'
|
||||
r'|- (?P<role>\w+\.\w+) \((?P<rversion>[\d\.]+)\))'
|
||||
r' was installed successfully$')
|
||||
# Collection install output changed:
|
||||
# ansible-base 2.10: "coll.name (x.y.z)"
|
||||
# ansible-core 2.11+: "coll.name:x.y.z"
|
||||
self._RE_INSTALL_OUTPUT = re.compile(r'^(?:(?P<collection>\w+\.\w+)(?: \(|:)(?P<cversion>[\d\.]+)\)?'
|
||||
r'|- (?P<role>\w+\.\w+) \((?P<rversion>[\d\.]+)\))'
|
||||
r' was installed successfully$')
|
||||
self.vars.set("new_collections", {}, change=True)
|
||||
self.vars.set("new_roles", {}, change=True)
|
||||
if self.vars.type != "collection":
|
||||
self.vars.installed_roles = self._list_roles()
|
||||
if self.vars.type != "roles":
|
||||
self.vars.installed_collections = self._list_collections()
|
||||
|
||||
def _list_element(self, _type, path_re, elem_re):
|
||||
def process(rc, out, err):
|
||||
@@ -322,24 +313,8 @@ class AnsibleGalaxyInstall(ModuleHelper):
|
||||
def _list_roles(self):
|
||||
return self._list_element('role', self._RE_LIST_PATH, self._RE_LIST_ROLE)
|
||||
|
||||
def _setup29(self):
|
||||
self.vars.set("new_collections", {})
|
||||
self.vars.set("new_roles", {})
|
||||
self.vars.set("ansible29_change", False, change=True, output=False)
|
||||
if not (self.vars.ack_ansible29 or self.vars.ack_min_ansiblecore211):
|
||||
self.warn("Ansible 2.9 or older: unable to retrieve lists of roles and collections already installed")
|
||||
if self.vars.requirements_file is not None and self.vars.type == 'both':
|
||||
self.warn("Ansible 2.9 or older: will install only roles from requirement files")
|
||||
|
||||
def _setup210plus(self):
|
||||
self.vars.set("new_collections", {}, change=True)
|
||||
self.vars.set("new_roles", {}, change=True)
|
||||
if self.vars.type != "collection":
|
||||
self.vars.installed_roles = self._list_roles()
|
||||
if self.vars.type != "roles":
|
||||
self.vars.installed_collections = self._list_collections()
|
||||
|
||||
def __run__(self):
|
||||
|
||||
def process(rc, out, err):
|
||||
for line in out.splitlines():
|
||||
match = self._RE_INSTALL_OUTPUT.match(line)
|
||||
@@ -347,19 +322,9 @@ class AnsibleGalaxyInstall(ModuleHelper):
|
||||
continue
|
||||
if match.group("collection"):
|
||||
self.vars.new_collections[match.group("collection")] = match.group("cversion")
|
||||
if self.is_ansible29:
|
||||
self.vars.ansible29_change = True
|
||||
elif match.group("role"):
|
||||
self.vars.new_roles[match.group("role")] = match.group("rversion")
|
||||
if self.is_ansible29:
|
||||
self.vars.ansible29_change = True
|
||||
|
||||
if self.is_ansible29:
|
||||
if self.vars.type == 'both':
|
||||
raise ValueError("Type 'both' not supported in Ansible 2.9")
|
||||
self._setup29()
|
||||
else:
|
||||
self._setup210plus()
|
||||
with self.runner("type galaxy_cmd force no_deps dest requirements_file name", output_process=process) as ctx:
|
||||
ctx.run(galaxy_cmd="install")
|
||||
if self.verbosity > 2:
|
||||
|
||||
@@ -28,6 +28,9 @@ options:
|
||||
package:
|
||||
description:
|
||||
- List of packages to install, upgrade, or remove.
|
||||
- Since community.general 8.0.0, may include paths to local C(.rpm) files
|
||||
if O(state=installed) or O(state=present), requires C(rpm) python
|
||||
module.
|
||||
aliases: [ name, pkg ]
|
||||
type: list
|
||||
elements: str
|
||||
@@ -63,6 +66,9 @@ options:
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 6.5.0
|
||||
requirements:
|
||||
- C(rpm) python package (rpm bindings), optional. Required if O(package)
|
||||
option includes local files.
|
||||
author:
|
||||
- Evgenii Terechkov (@evgkrsk)
|
||||
'''
|
||||
@@ -109,15 +115,48 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.basic import (
|
||||
AnsibleModule,
|
||||
missing_required_lib,
|
||||
)
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
try:
|
||||
import rpm
|
||||
except ImportError:
|
||||
HAS_RPM_PYTHON = False
|
||||
RPM_PYTHON_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_RPM_PYTHON = True
|
||||
RPM_PYTHON_IMPORT_ERROR = None
|
||||
|
||||
APT_CACHE = "/usr/bin/apt-cache"
|
||||
APT_PATH = "/usr/bin/apt-get"
|
||||
RPM_PATH = "/usr/bin/rpm"
|
||||
APT_GET_ZERO = "\n0 upgraded, 0 newly installed"
|
||||
UPDATE_KERNEL_ZERO = "\nTry to install new kernel "
|
||||
|
||||
|
||||
def local_rpm_package_name(path):
|
||||
"""return package name of a local rpm passed in.
|
||||
Inspired by ansible.builtin.yum"""
|
||||
|
||||
ts = rpm.TransactionSet()
|
||||
ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
|
||||
fd = os.open(path, os.O_RDONLY)
|
||||
try:
|
||||
header = ts.hdrFromFdno(fd)
|
||||
except rpm.error as e:
|
||||
return None
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
return to_native(header[rpm.RPMTAG_NAME])
|
||||
|
||||
|
||||
def query_package(module, name):
|
||||
# rpm -q returns 0 if the package is installed,
|
||||
# 1 if it is not installed
|
||||
@@ -128,11 +167,38 @@ def query_package(module, name):
|
||||
return False
|
||||
|
||||
|
||||
def check_package_version(module, name):
|
||||
# compare installed and candidate version
|
||||
# if newest version already installed return True
|
||||
# otherwise return False
|
||||
|
||||
rc, out, err = module.run_command([APT_CACHE, "policy", name], environ_update={"LANG": "C"})
|
||||
installed = re.split("\n |: ", out)[2]
|
||||
candidate = re.split("\n |: ", out)[4]
|
||||
if installed >= candidate:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def query_package_provides(module, name):
|
||||
# rpm -q returns 0 if the package is installed,
|
||||
# 1 if it is not installed
|
||||
if name.endswith('.rpm'):
|
||||
# Likely a local RPM file
|
||||
if not HAS_RPM_PYTHON:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib('rpm'),
|
||||
exception=RPM_PYTHON_IMPORT_ERROR,
|
||||
)
|
||||
|
||||
name = local_rpm_package_name(name)
|
||||
|
||||
rc, out, err = module.run_command("%s -q --provides %s" % (RPM_PATH, name))
|
||||
return rc == 0
|
||||
if rc == 0:
|
||||
if check_package_version(module, name):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def update_package_db(module):
|
||||
|
||||
@@ -36,7 +36,6 @@ options:
|
||||
format:
|
||||
description:
|
||||
- The type of compression to use.
|
||||
- Support for xz was added in Ansible 2.5.
|
||||
type: str
|
||||
choices: [ bz2, gz, tar, xz, zip ]
|
||||
default: gz
|
||||
|
||||
@@ -21,7 +21,6 @@ notes:
|
||||
- Host should support C(atomic) command
|
||||
requirements:
|
||||
- atomic
|
||||
- "python >= 2.6"
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
|
||||
@@ -21,7 +21,6 @@ notes:
|
||||
- Host should be an atomic platform (verified by existence of '/run/ostree-booted' file).
|
||||
requirements:
|
||||
- atomic
|
||||
- python >= 2.6
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
|
||||
@@ -21,7 +21,6 @@ notes:
|
||||
- Host should support C(atomic) command.
|
||||
requirements:
|
||||
- atomic
|
||||
- python >= 2.6
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
|
||||
@@ -45,8 +45,7 @@ options:
|
||||
force:
|
||||
description:
|
||||
- If V(true), any modified files in the working
|
||||
tree will be discarded. Before Ansible 1.9 the default
|
||||
value was V(true).
|
||||
tree will be discarded.
|
||||
type: bool
|
||||
default: false
|
||||
executable:
|
||||
|
||||
@@ -13,8 +13,6 @@ DOCUMENTATION = r'''
|
||||
module: cloudflare_dns
|
||||
author:
|
||||
- Michael Gruener (@mgruener)
|
||||
requirements:
|
||||
- python >= 2.6
|
||||
short_description: Manage Cloudflare DNS records
|
||||
description:
|
||||
- "Manages dns records via the Cloudflare API, see the docs: U(https://api.cloudflare.com/)."
|
||||
@@ -59,6 +57,20 @@ options:
|
||||
- Required for O(type=TLSA) when O(state=present).
|
||||
type: int
|
||||
choices: [ 0, 1, 2, 3 ]
|
||||
flag:
|
||||
description:
|
||||
- Issuer Critical Flag.
|
||||
- Required for O(type=CAA) when O(state=present).
|
||||
type: int
|
||||
choices: [ 0, 1 ]
|
||||
version_added: 8.0.0
|
||||
tag:
|
||||
description:
|
||||
- CAA issue restriction.
|
||||
- Required for O(type=CAA) when O(state=present).
|
||||
type: str
|
||||
choices: [ issue, issuewild, iodef ]
|
||||
version_added: 8.0.0
|
||||
hash_type:
|
||||
description:
|
||||
- Hash type number.
|
||||
@@ -85,7 +97,6 @@ options:
|
||||
description:
|
||||
- Service protocol. Required for O(type=SRV) and O(type=TLSA).
|
||||
- Common values are TCP and UDP.
|
||||
- Before Ansible 2.6 only TCP and UDP were available.
|
||||
type: str
|
||||
proxied:
|
||||
description:
|
||||
@@ -137,9 +148,8 @@ options:
|
||||
type:
|
||||
description:
|
||||
- The type of DNS record to create. Required if O(state=present).
|
||||
- O(type=DS), O(type=SSHFP), and O(type=TLSA) were added in Ansible 2.7.
|
||||
type: str
|
||||
choices: [ A, AAAA, CNAME, DS, MX, NS, SPF, SRV, SSHFP, TLSA, TXT ]
|
||||
choices: [ A, AAAA, CNAME, DS, MX, NS, SPF, SRV, SSHFP, TLSA, CAA, TXT ]
|
||||
value:
|
||||
description:
|
||||
- The record value.
|
||||
@@ -262,6 +272,15 @@ EXAMPLES = r'''
|
||||
hash_type: 1
|
||||
value: 6b76d034492b493e15a7376fccd08e63befdad0edab8e442562f532338364bf3
|
||||
|
||||
- name: Create a CAA record subdomain.example.com
|
||||
community.general.cloudflare_dns:
|
||||
zone: example.com
|
||||
record: subdomain
|
||||
type: CAA
|
||||
flag: 0
|
||||
tag: issue
|
||||
value: ca.example.com
|
||||
|
||||
- name: Create a DS record for subdomain.example.com
|
||||
community.general.cloudflare_dns:
|
||||
zone: example.com
|
||||
@@ -291,7 +310,7 @@ record:
|
||||
sample: "2016-03-25T19:09:42.516553Z"
|
||||
data:
|
||||
description: Additional record data.
|
||||
returned: success, if type is SRV, DS, SSHFP or TLSA
|
||||
returned: success, if type is SRV, DS, SSHFP TLSA or CAA
|
||||
type: dict
|
||||
sample: {
|
||||
name: "jabber",
|
||||
@@ -391,6 +410,8 @@ class CloudflareAPI(object):
|
||||
self.algorithm = module.params['algorithm']
|
||||
self.cert_usage = module.params['cert_usage']
|
||||
self.hash_type = module.params['hash_type']
|
||||
self.flag = module.params['flag']
|
||||
self.tag = module.params['tag']
|
||||
self.key_tag = module.params['key_tag']
|
||||
self.port = module.params['port']
|
||||
self.priority = module.params['priority']
|
||||
@@ -595,7 +616,7 @@ class CloudflareAPI(object):
|
||||
def delete_dns_records(self, **kwargs):
|
||||
params = {}
|
||||
for param in ['port', 'proto', 'service', 'solo', 'type', 'record', 'value', 'weight', 'zone',
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag']:
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag', 'flag', 'tag']:
|
||||
if param in kwargs:
|
||||
params[param] = kwargs[param]
|
||||
else:
|
||||
@@ -640,7 +661,7 @@ class CloudflareAPI(object):
|
||||
def ensure_dns_record(self, **kwargs):
|
||||
params = {}
|
||||
for param in ['port', 'priority', 'proto', 'proxied', 'service', 'ttl', 'type', 'record', 'value', 'weight', 'zone',
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag']:
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag', 'flag', 'tag']:
|
||||
if param in kwargs:
|
||||
params[param] = kwargs[param]
|
||||
else:
|
||||
@@ -757,12 +778,36 @@ class CloudflareAPI(object):
|
||||
}
|
||||
search_value = str(params['cert_usage']) + '\t' + str(params['selector']) + '\t' + str(params['hash_type']) + '\t' + params['value']
|
||||
|
||||
if params['type'] == 'CAA':
|
||||
for attr in [params['flag'], params['tag'], params['value']]:
|
||||
if (attr is None) or (attr == ''):
|
||||
self.module.fail_json(msg="You must provide flag, tag and a value to create this record type")
|
||||
caa_data = {
|
||||
"flags": params['flag'],
|
||||
"tag": params['tag'],
|
||||
"value": params['value'],
|
||||
}
|
||||
new_record = {
|
||||
"type": params['type'],
|
||||
"name": params['record'],
|
||||
'data': caa_data,
|
||||
"ttl": params['ttl'],
|
||||
}
|
||||
search_value = None
|
||||
|
||||
zone_id = self._get_zone_id(params['zone'])
|
||||
records = self.get_dns_records(params['zone'], params['type'], search_record, search_value)
|
||||
# in theory this should be impossible as cloudflare does not allow
|
||||
# the creation of duplicate records but lets cover it anyways
|
||||
if len(records) > 1:
|
||||
self.module.fail_json(msg="More than one record already exists for the given attributes. That should be impossible, please open an issue!")
|
||||
# As Cloudflare API cannot filter record containing quotes
|
||||
# CAA records must be compared locally
|
||||
if params['type'] == 'CAA':
|
||||
for rr in records:
|
||||
if rr['data']['flags'] == caa_data['flags'] and rr['data']['tag'] == caa_data['tag'] and rr['data']['value'] == caa_data['value']:
|
||||
return rr, self.changed
|
||||
else:
|
||||
self.module.fail_json(msg="More than one record already exists for the given attributes. That should be impossible, please open an issue!")
|
||||
# record already exists, check if it must be updated
|
||||
if len(records) == 1:
|
||||
cur_record = records[0]
|
||||
@@ -811,6 +856,8 @@ def main():
|
||||
hash_type=dict(type='int', choices=[1, 2]),
|
||||
key_tag=dict(type='int', no_log=False),
|
||||
port=dict(type='int'),
|
||||
flag=dict(type='int', choices=[0, 1]),
|
||||
tag=dict(type='str', choices=['issue', 'issuewild', 'iodef']),
|
||||
priority=dict(type='int', default=1),
|
||||
proto=dict(type='str'),
|
||||
proxied=dict(type='bool', default=False),
|
||||
@@ -821,7 +868,7 @@ def main():
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
timeout=dict(type='int', default=30),
|
||||
ttl=dict(type='int', default=1),
|
||||
type=dict(type='str', choices=['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'TXT']),
|
||||
type=dict(type='str', choices=['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'CAA', 'TXT']),
|
||||
value=dict(type='str', aliases=['content']),
|
||||
weight=dict(type='int', default=1),
|
||||
zone=dict(type='str', required=True, aliases=['domain']),
|
||||
@@ -832,6 +879,7 @@ def main():
|
||||
('state', 'absent', ['record']),
|
||||
('type', 'SRV', ['proto', 'service']),
|
||||
('type', 'TLSA', ['proto', 'port']),
|
||||
('type', 'CAA', ['flag', 'tag']),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -858,6 +906,13 @@ def main():
|
||||
and (module.params['value'] is None or module.params['value'] == ''))):
|
||||
module.fail_json(msg="For TLSA records the params cert_usage, selector, hash_type and value all need to be defined, or not at all.")
|
||||
|
||||
if module.params['type'] == 'CAA':
|
||||
if not ((module.params['flag'] is not None and module.params['tag'] is not None
|
||||
and not (module.params['value'] is None or module.params['value'] == ''))
|
||||
or (module.params['flag'] is None and module.params['tag'] is None
|
||||
and (module.params['value'] is None or module.params['value'] == ''))):
|
||||
module.fail_json(msg="For CAA records the params flag, tag and value all need to be defined, or not at all.")
|
||||
|
||||
if module.params['type'] == 'DS':
|
||||
if not ((module.params['key_tag'] is not None and module.params['algorithm'] is not None and module.params['hash_type'] is not None
|
||||
and not (module.params['value'] is None or module.params['value'] == ''))
|
||||
|
||||
@@ -104,6 +104,7 @@ options:
|
||||
description:
|
||||
- The script/command that will be run periodically to check the health of the service.
|
||||
- Requires O(interval) to be provided.
|
||||
- Mutually exclusive with O(ttl), O(tcp) and O(http).
|
||||
interval:
|
||||
type: str
|
||||
description:
|
||||
@@ -131,6 +132,7 @@ options:
|
||||
Similar to the interval this is a number with a V(s) or V(m) suffix to
|
||||
signify the units of seconds or minutes, for example V(15s) or V(1m).
|
||||
If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
|
||||
- Mutually exclusive with O(script), O(tcp) and O(http).
|
||||
tcp:
|
||||
type: str
|
||||
description:
|
||||
@@ -138,6 +140,7 @@ options:
|
||||
will check if the connection attempt to that port is successful (that is, the port is currently accepting connections).
|
||||
The format is V(host:port), for example V(localhost:80).
|
||||
- Requires O(interval) to be provided.
|
||||
- Mutually exclusive with O(script), O(ttl) and O(http).
|
||||
version_added: '1.3.0'
|
||||
http:
|
||||
type: str
|
||||
@@ -145,6 +148,7 @@ options:
|
||||
- Checks can be registered with an HTTP endpoint. This means that consul
|
||||
will check that the http endpoint returns a successful HTTP status.
|
||||
- Requires O(interval) to be provided.
|
||||
- Mutually exclusive with O(script), O(ttl) and O(tcp).
|
||||
timeout:
|
||||
type: str
|
||||
description:
|
||||
@@ -159,7 +163,7 @@ options:
|
||||
ack_params_state_absent:
|
||||
type: bool
|
||||
description:
|
||||
- Disable deprecation warning when using parameters incompatible with O(state=absent).
|
||||
- This parameter has no more effect and is deprecated. It will be removed in community.general 10.0.0.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -377,13 +381,7 @@ def get_service_by_id_or_name(consul_api, service_id_or_name):
|
||||
|
||||
|
||||
def parse_check(module):
|
||||
_checks = [module.params[p] for p in ('script', 'ttl', 'tcp', 'http') if module.params[p]]
|
||||
|
||||
if len(_checks) > 1:
|
||||
module.fail_json(
|
||||
msg='checks are either script, tcp, http or ttl driven, supplying more than one does not make sense')
|
||||
|
||||
if module.params['check_id'] or _checks:
|
||||
if module.params['check_id'] or any(module.params[p] is not None for p in ('script', 'ttl', 'tcp', 'http')):
|
||||
return ConsulCheck(
|
||||
module.params['check_id'],
|
||||
module.params['check_name'],
|
||||
@@ -501,15 +499,9 @@ class ConsulCheck(object):
|
||||
self.check = consul.Check.ttl(self.ttl)
|
||||
|
||||
if http:
|
||||
if interval is None:
|
||||
raise Exception('http check must specify interval')
|
||||
|
||||
self.check = consul.Check.http(http, self.interval, self.timeout)
|
||||
|
||||
if tcp:
|
||||
if interval is None:
|
||||
raise Exception('tcp check must specify interval')
|
||||
|
||||
regex = r"(?P<host>.*):(?P<port>(?:[0-9]+))$"
|
||||
match = re.match(regex, tcp)
|
||||
|
||||
@@ -596,30 +588,33 @@ def main():
|
||||
timeout=dict(type='str'),
|
||||
tags=dict(type='list', elements='str'),
|
||||
token=dict(no_log=True),
|
||||
ack_params_state_absent=dict(type='bool'),
|
||||
ack_params_state_absent=dict(
|
||||
type='bool',
|
||||
removed_in_version='10.0.0',
|
||||
removed_from_collection='community.general',
|
||||
),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
('script', 'ttl', 'tcp', 'http'),
|
||||
],
|
||||
required_if=[
|
||||
('state', 'present', ['service_name']),
|
||||
('state', 'absent', ['service_id', 'service_name', 'check_id', 'check_name'], True),
|
||||
],
|
||||
required_by={
|
||||
'script': 'interval',
|
||||
'http': 'interval',
|
||||
'tcp': 'interval',
|
||||
},
|
||||
supports_check_mode=False,
|
||||
)
|
||||
p = module.params
|
||||
|
||||
test_dependencies(module)
|
||||
if p['state'] == 'absent' and any(p[x] for x in ['script', 'ttl', 'tcp', 'http', 'interval']) and not p['ack_params_state_absent']:
|
||||
module.deprecate(
|
||||
"The use of parameters 'script', 'ttl', 'tcp', 'http', 'interval' along with 'state=absent' is deprecated. "
|
||||
"In community.general 8.0.0 their use will become an error. "
|
||||
"To suppress this deprecation notice, set parameter ack_params_state_absent=true.",
|
||||
version="8.0.0",
|
||||
collection_name="community.general",
|
||||
if p['state'] == 'absent' and any(p[x] for x in ['script', 'ttl', 'tcp', 'http', 'interval']):
|
||||
module.fail_json(
|
||||
msg="The use of parameters 'script', 'ttl', 'tcp', 'http', 'interval' along with 'state=absent' is no longer allowed."
|
||||
)
|
||||
# When reaching c.g 8.0.0:
|
||||
# - Replace the deprecation with a fail_json(), remove the "ack_params_state_absent" condition from the "if"
|
||||
# - Add mutually_exclusive for ('script', 'ttl', 'tcp', 'http'), then remove that validation from parse_check()
|
||||
# - Add required_by {'script': 'interval', 'http': 'interval', 'tcp': 'interval'}, then remove checks for 'interval' in ConsulCheck.__init__()
|
||||
# - Deprecate the parameter ack_params_state_absent
|
||||
|
||||
try:
|
||||
register_with_consul(module)
|
||||
|
||||
@@ -69,7 +69,6 @@ options:
|
||||
plugin_bin:
|
||||
description:
|
||||
- Location of the plugin binary. If this file is not found, the default plugin binaries will be used.
|
||||
- The default changed in Ansible 2.4 to None.
|
||||
type: path
|
||||
plugin_dir:
|
||||
description:
|
||||
|
||||
90
plugins/modules/facter_facts.py
Normal file
90
plugins/modules/facter_facts.py
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023, Alexei Znamensky
|
||||
# Copyright (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: facter_facts
|
||||
short_description: Runs the discovery program C(facter) on the remote system and return Ansible facts
|
||||
version_added: 8.0.0
|
||||
description:
|
||||
- Runs the C(facter) discovery program
|
||||
(U(https://github.com/puppetlabs/facter)) on the remote system, returning Ansible facts from the
|
||||
JSON data that can be useful for inventory purposes.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.facts
|
||||
- community.general.attributes.facts_module
|
||||
options:
|
||||
arguments:
|
||||
description:
|
||||
- Specifies arguments for facter.
|
||||
type: list
|
||||
elements: str
|
||||
requirements:
|
||||
- facter
|
||||
- ruby-json
|
||||
author:
|
||||
- Ansible Core Team
|
||||
- Michael DeHaan
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Execute facter no arguments
|
||||
community.general.facter_facts:
|
||||
|
||||
- name: Execute facter with arguments
|
||||
community.general.facter_facts:
|
||||
arguments:
|
||||
- -p
|
||||
- system_uptime
|
||||
- timezone
|
||||
- is_virtual
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
ansible_facts:
|
||||
description: Dictionary with one key C(facter).
|
||||
returned: always
|
||||
type: dict
|
||||
contains:
|
||||
facter:
|
||||
description: Dictionary containing facts discovered in the remote system.
|
||||
returned: always
|
||||
type: dict
|
||||
'''
|
||||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
arguments=dict(type='list', elements='str'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
facter_path = module.get_bin_path(
|
||||
'facter',
|
||||
opt_dirs=['/opt/puppetlabs/bin'])
|
||||
|
||||
cmd = [facter_path, "--json"]
|
||||
if module.params['arguments']:
|
||||
cmd += module.params['arguments']
|
||||
|
||||
rc, out, err = module.run_command(cmd, check_rc=True)
|
||||
module.exit_json(ansible_facts=dict(facter=json.loads(out)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -47,9 +47,8 @@ options:
|
||||
type: str
|
||||
description:
|
||||
- The action to take upon the key/value.
|
||||
- State V(get) is deprecated and will be removed in community.general 8.0.0. Please use the module M(community.general.gconftool2_info) instead.
|
||||
required: true
|
||||
choices: [ absent, get, present ]
|
||||
choices: [ absent, present ]
|
||||
config_source:
|
||||
type: str
|
||||
description:
|
||||
@@ -114,7 +113,7 @@ class GConftool(StateModuleHelper):
|
||||
key=dict(type='str', required=True, no_log=False),
|
||||
value_type=dict(type='str', choices=['bool', 'float', 'int', 'string']),
|
||||
value=dict(type='str'),
|
||||
state=dict(type='str', required=True, choices=['absent', 'get', 'present']),
|
||||
state=dict(type='str', required=True, choices=['absent', 'present']),
|
||||
direct=dict(type='bool', default=False),
|
||||
config_source=dict(type='str'),
|
||||
),
|
||||
@@ -149,12 +148,6 @@ class GConftool(StateModuleHelper):
|
||||
def _get(self):
|
||||
return self.runner("state key", output_process=self._make_process(False)).run(state="get")
|
||||
|
||||
def state_get(self):
|
||||
self.deprecate(
|
||||
msg="State 'get' is deprecated. Please use the module community.general.gconftool2_info instead",
|
||||
version="8.0.0", collection_name="community.general"
|
||||
)
|
||||
|
||||
def state_absent(self):
|
||||
with self.runner("state key", output_process=self._make_process(False)) as ctx:
|
||||
ctx.run()
|
||||
|
||||
@@ -20,7 +20,7 @@ author:
|
||||
requirements: ['git']
|
||||
short_description: Read and write git configuration
|
||||
description:
|
||||
- The M(community.general.git_config) module changes git configuration by invoking 'git config'.
|
||||
- The M(community.general.git_config) module changes git configuration by invoking C(git config).
|
||||
This is needed if you do not want to use M(ansible.builtin.template) for the entire git
|
||||
config file (for example because you need to change just C(user.email) in
|
||||
/etc/.git/config). Solutions involving M(ansible.builtin.command) are cumbersome or
|
||||
@@ -75,6 +75,16 @@ options:
|
||||
- When specifying the name of a single setting, supply a value to
|
||||
set that setting to the given value.
|
||||
type: str
|
||||
add_mode:
|
||||
description:
|
||||
- Specify if a value should replace the existing value(s) or if the new
|
||||
value should be added alongside other values with the same name.
|
||||
- This option is only relevant when adding/replacing values. If O(state=absent) or
|
||||
values are just read out, this option is not considered.
|
||||
choices: [ "add", "replace-all" ]
|
||||
type: str
|
||||
default: "replace-all"
|
||||
version_added: 8.1.0
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -118,6 +128,15 @@ EXAMPLES = '''
|
||||
name: color.ui
|
||||
value: auto
|
||||
|
||||
- name: Add several options for the same name
|
||||
community.general.git_config:
|
||||
name: push.pushoption
|
||||
value: "{{ item }}"
|
||||
add_mode: add
|
||||
loop:
|
||||
- merge_request.create
|
||||
- merge_request.draft
|
||||
|
||||
- name: Make etckeeper not complaining when it is invoked by cron
|
||||
community.general.git_config:
|
||||
name: user.email
|
||||
@@ -178,6 +197,7 @@ def main():
|
||||
name=dict(type='str'),
|
||||
repo=dict(type='path'),
|
||||
file=dict(type='path'),
|
||||
add_mode=dict(required=False, type='str', default='replace-all', choices=['add', 'replace-all']),
|
||||
scope=dict(required=False, type='str', choices=['file', 'local', 'global', 'system']),
|
||||
state=dict(required=False, type='str', default='present', choices=['present', 'absent']),
|
||||
value=dict(required=False),
|
||||
@@ -197,94 +217,118 @@ def main():
|
||||
# Set the locale to C to ensure consistent messages.
|
||||
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
|
||||
|
||||
if params['name']:
|
||||
name = params['name']
|
||||
else:
|
||||
name = None
|
||||
name = params['name'] or ''
|
||||
unset = params['state'] == 'absent'
|
||||
new_value = params['value'] or ''
|
||||
add_mode = params['add_mode']
|
||||
|
||||
if params['scope']:
|
||||
scope = params['scope']
|
||||
elif params['list_all']:
|
||||
scope = None
|
||||
else:
|
||||
scope = 'system'
|
||||
scope = determine_scope(params)
|
||||
cwd = determine_cwd(scope, params)
|
||||
|
||||
if params['state'] == 'absent':
|
||||
unset = 'unset'
|
||||
params['value'] = None
|
||||
else:
|
||||
unset = None
|
||||
base_args = [git_path, "config", "--includes"]
|
||||
|
||||
if params['value']:
|
||||
new_value = params['value']
|
||||
else:
|
||||
new_value = None
|
||||
|
||||
args = [git_path, "config", "--includes"]
|
||||
if params['list_all']:
|
||||
args.append('-l')
|
||||
if scope == 'file':
|
||||
args.append('-f')
|
||||
args.append(params['file'])
|
||||
base_args.append('-f')
|
||||
base_args.append(params['file'])
|
||||
elif scope:
|
||||
args.append("--" + scope)
|
||||
base_args.append("--" + scope)
|
||||
|
||||
list_args = list(base_args)
|
||||
|
||||
if params['list_all']:
|
||||
list_args.append('-l')
|
||||
|
||||
if name:
|
||||
args.append(name)
|
||||
list_args.append("--get-all")
|
||||
list_args.append(name)
|
||||
|
||||
if scope == 'local':
|
||||
dir = params['repo']
|
||||
elif params['list_all'] and params['repo']:
|
||||
# Include local settings from a specific repo when listing all available settings
|
||||
dir = params['repo']
|
||||
else:
|
||||
# Run from root directory to avoid accidentally picking up any local config settings
|
||||
dir = "/"
|
||||
(rc, out, err) = module.run_command(list_args, cwd=cwd, expand_user_and_vars=False)
|
||||
|
||||
(rc, out, err) = module.run_command(args, cwd=dir, expand_user_and_vars=False)
|
||||
if params['list_all'] and scope and rc == 128 and 'unable to read config file' in err:
|
||||
# This just means nothing has been set at the given scope
|
||||
module.exit_json(changed=False, msg='', config_values={})
|
||||
elif rc >= 2:
|
||||
# If the return code is 1, it just means the option hasn't been set yet, which is fine.
|
||||
module.fail_json(rc=rc, msg=err, cmd=' '.join(args))
|
||||
module.fail_json(rc=rc, msg=err, cmd=' '.join(list_args))
|
||||
|
||||
old_values = out.rstrip().splitlines()
|
||||
|
||||
if params['list_all']:
|
||||
values = out.rstrip().splitlines()
|
||||
config_values = {}
|
||||
for value in values:
|
||||
for value in old_values:
|
||||
k, v = value.split('=', 1)
|
||||
config_values[k] = v
|
||||
module.exit_json(changed=False, msg='', config_values=config_values)
|
||||
elif not new_value and not unset:
|
||||
module.exit_json(changed=False, msg='', config_value=out.rstrip())
|
||||
module.exit_json(changed=False, msg='', config_value=old_values[0] if old_values else '')
|
||||
elif unset and not out:
|
||||
module.exit_json(changed=False, msg='no setting to unset')
|
||||
elif new_value in old_values and (len(old_values) == 1 or add_mode == "add"):
|
||||
module.exit_json(changed=False, msg="")
|
||||
|
||||
# Until this point, the git config was just read and in case no change is needed, the module has already exited.
|
||||
|
||||
set_args = list(base_args)
|
||||
if unset:
|
||||
set_args.append("--unset-all")
|
||||
set_args.append(name)
|
||||
else:
|
||||
old_value = out.rstrip()
|
||||
if old_value == new_value:
|
||||
module.exit_json(changed=False, msg="")
|
||||
set_args.append("--" + add_mode)
|
||||
set_args.append(name)
|
||||
set_args.append(new_value)
|
||||
|
||||
if not module.check_mode:
|
||||
if unset:
|
||||
args.insert(len(args) - 1, "--" + unset)
|
||||
cmd = args
|
||||
else:
|
||||
cmd = args + [new_value]
|
||||
(rc, out, err) = module.run_command(cmd, cwd=dir, ignore_invalid_cwd=False, expand_user_and_vars=False)
|
||||
(rc, out, err) = module.run_command(set_args, cwd=cwd, ignore_invalid_cwd=False, expand_user_and_vars=False)
|
||||
if err:
|
||||
module.fail_json(rc=rc, msg=err, cmd=cmd)
|
||||
module.fail_json(rc=rc, msg=err, cmd=set_args)
|
||||
|
||||
if unset:
|
||||
after_values = []
|
||||
elif add_mode == "add":
|
||||
after_values = old_values + [new_value]
|
||||
else:
|
||||
after_values = [new_value]
|
||||
|
||||
module.exit_json(
|
||||
msg='setting changed',
|
||||
diff=dict(
|
||||
before_header=' '.join(args),
|
||||
before=old_value + "\n",
|
||||
after_header=' '.join(args),
|
||||
after=(new_value or '') + "\n"
|
||||
before_header=' '.join(set_args),
|
||||
before=build_diff_value(old_values),
|
||||
after_header=' '.join(set_args),
|
||||
after=build_diff_value(after_values),
|
||||
),
|
||||
changed=True
|
||||
)
|
||||
|
||||
|
||||
def determine_scope(params):
|
||||
if params['scope']:
|
||||
return params['scope']
|
||||
elif params['list_all']:
|
||||
return ""
|
||||
else:
|
||||
return 'system'
|
||||
|
||||
|
||||
def build_diff_value(value):
|
||||
if not value:
|
||||
return "\n"
|
||||
elif len(value) == 1:
|
||||
return value[0] + "\n"
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def determine_cwd(scope, params):
|
||||
if scope == 'local':
|
||||
return params['repo']
|
||||
elif params['list_all'] and params['repo']:
|
||||
# Include local settings from a specific repo when listing all available settings
|
||||
return params['repo']
|
||||
else:
|
||||
# Run from root directory to avoid accidentally picking up any local config settings
|
||||
return "/"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
187
plugins/modules/git_config_info.py
Normal file
187
plugins/modules/git_config_info.py
Normal file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023, Guenther Grill <grill.guenther@gmail.com>
|
||||
#
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: git_config_info
|
||||
author:
|
||||
- Guenther Grill (@guenhter)
|
||||
version_added: 8.1.0
|
||||
requirements: ['git']
|
||||
short_description: Read git configuration
|
||||
description:
|
||||
- The M(community.general.git_config_info) module reads the git configuration
|
||||
by invoking C(git config).
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the setting to read.
|
||||
- If not provided, all settings will be returned as RV(config_values).
|
||||
type: str
|
||||
path:
|
||||
description:
|
||||
- Path to a git repository or file for reading values from a specific repo.
|
||||
- If O(scope) is V(local), this must point to a repository to read from.
|
||||
- If O(scope) is V(file), this must point to specific git config file to read from.
|
||||
- Otherwise O(path) is ignored if set.
|
||||
type: path
|
||||
scope:
|
||||
description:
|
||||
- Specify which scope to read values from.
|
||||
- If set to V(global), the global git config is used. O(path) is ignored.
|
||||
- If set to V(system), the system git config is used. O(path) is ignored.
|
||||
- If set to V(local), O(path) must be set to the repo to read from.
|
||||
- If set to V(file), O(path) must be set to the config file to read from.
|
||||
choices: [ "global", "system", "local", "file" ]
|
||||
default: "system"
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Read a system wide config
|
||||
community.general.git_config_info:
|
||||
name: core.editor
|
||||
register: result
|
||||
|
||||
- name: Show value of core.editor
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ result.config_value | default('(not set)', true) }}"
|
||||
|
||||
- name: Read a global config from ~/.gitconfig
|
||||
community.general.git_config_info:
|
||||
name: alias.remotev
|
||||
scope: global
|
||||
|
||||
- name: Read a project specific config
|
||||
community.general.git_config_info:
|
||||
name: color.ui
|
||||
scope: local
|
||||
path: /etc
|
||||
|
||||
- name: Read all global values
|
||||
community.general.git_config_info:
|
||||
scope: global
|
||||
|
||||
- name: Read all system wide values
|
||||
community.general.git_config_info:
|
||||
|
||||
- name: Read all values of a specific file
|
||||
community.general.git_config_info:
|
||||
scope: file
|
||||
path: /etc/gitconfig
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
---
|
||||
config_value:
|
||||
description: >
|
||||
When O(name) is set, a string containing the value of the setting in name. If O(name) is not set, empty.
|
||||
If a config key such as V(push.pushoption) has more then one entry, just the first one is returned here.
|
||||
returned: success if O(name) is set
|
||||
type: str
|
||||
sample: "vim"
|
||||
|
||||
config_values:
|
||||
description:
|
||||
- This is a dictionary mapping a git configuration setting to a list of its values.
|
||||
- When O(name) is not set, all configuration settings are returned here.
|
||||
- When O(name) is set, only the setting specified in O(name) is returned here.
|
||||
If that setting is not set, the key will still be present, and its value will be an empty list.
|
||||
returned: success
|
||||
type: dict
|
||||
sample:
|
||||
core.editor: ["vim"]
|
||||
color.ui: ["auto"]
|
||||
push.pushoption: ["merge_request.create", "merge_request.draft"]
|
||||
alias.remotev: ["remote -v"]
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(type="str"),
|
||||
path=dict(type="path"),
|
||||
scope=dict(required=False, type="str", default="system", choices=["global", "system", "local", "file"]),
|
||||
),
|
||||
required_if=[
|
||||
("scope", "local", ["path"]),
|
||||
("scope", "file", ["path"]),
|
||||
],
|
||||
required_one_of=[],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
# We check error message for a pattern, so we need to make sure the messages appear in the form we're expecting.
|
||||
# Set the locale to C to ensure consistent messages.
|
||||
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
|
||||
|
||||
name = module.params["name"]
|
||||
path = module.params["path"]
|
||||
scope = module.params["scope"]
|
||||
|
||||
run_cwd = path if scope == "local" else "/"
|
||||
args = build_args(module, name, path, scope)
|
||||
|
||||
(rc, out, err) = module.run_command(args, cwd=run_cwd, expand_user_and_vars=False)
|
||||
|
||||
if rc == 128 and "unable to read config file" in err:
|
||||
# This just means nothing has been set at the given scope
|
||||
pass
|
||||
elif rc >= 2:
|
||||
# If the return code is 1, it just means the option hasn't been set yet, which is fine.
|
||||
module.fail_json(rc=rc, msg=err, cmd=" ".join(args))
|
||||
|
||||
output_lines = out.strip("\0").split("\0") if out else []
|
||||
|
||||
if name:
|
||||
first_value = output_lines[0] if output_lines else ""
|
||||
config_values = {name: output_lines}
|
||||
module.exit_json(changed=False, msg="", config_value=first_value, config_values=config_values)
|
||||
else:
|
||||
config_values = text_to_dict(output_lines)
|
||||
module.exit_json(changed=False, msg="", config_value="", config_values=config_values)
|
||||
|
||||
|
||||
def build_args(module, name, path, scope):
|
||||
git_path = module.get_bin_path("git", True)
|
||||
args = [git_path, "config", "--includes", "--null", "--" + scope]
|
||||
|
||||
if scope == "file":
|
||||
args.append(path)
|
||||
|
||||
if name:
|
||||
args.extend(["--get-all", name])
|
||||
else:
|
||||
args.append("--list")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def text_to_dict(text_lines):
|
||||
config_values = {}
|
||||
for value in text_lines:
|
||||
k, v = value.split("\n", 1)
|
||||
if k in config_values:
|
||||
config_values[k].append(v)
|
||||
else:
|
||||
config_values[k] = [v]
|
||||
return config_values
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -94,7 +94,7 @@ EXAMPLES = '''
|
||||
repo: testrepo
|
||||
action: latest_release
|
||||
|
||||
- name: Get latest release of test repo using username and password. Ansible 2.4.
|
||||
- name: Get latest release of test repo using username and password
|
||||
community.general.github_release:
|
||||
user: testuser
|
||||
password: secret123
|
||||
|
||||
@@ -14,7 +14,6 @@ module: github_webhook_info
|
||||
short_description: Query information about GitHub webhooks
|
||||
description:
|
||||
- "Query information about GitHub webhooks"
|
||||
- This module was called C(github_webhook_facts) before Ansible 2.9. The usage did not change.
|
||||
requirements:
|
||||
- "PyGithub >= 1.3.5"
|
||||
extends_documentation_fragment:
|
||||
|
||||
@@ -16,7 +16,6 @@ description:
|
||||
author:
|
||||
- paytroff (@paytroff)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab >= 2.3.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -84,7 +83,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -144,7 +143,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=False
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
project = module.params['project']
|
||||
branch = module.params['branch']
|
||||
@@ -156,7 +157,6 @@ def main():
|
||||
module.fail_json(msg="community.general.gitlab_proteched_branch requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
|
||||
" Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version)
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
this_gitlab = GitlabBranch(module=module, project=project, gitlab_instance=gitlab_instance)
|
||||
|
||||
this_branch = this_gitlab.get_branch(branch)
|
||||
|
||||
@@ -20,7 +20,6 @@ author:
|
||||
- Marcus Watkins (@marwatk)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -121,7 +120,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_project, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, find_project, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -261,7 +260,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
state = module.params['state']
|
||||
project_identifier = module.params['project']
|
||||
@@ -269,8 +270,6 @@ def main():
|
||||
key_keyfile = module.params['key']
|
||||
key_can_push = module.params['can_push']
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
gitlab_deploy_key = GitLabDeployKey(module, gitlab_instance)
|
||||
|
||||
project = find_project(gitlab_instance, project_identifier)
|
||||
|
||||
@@ -20,7 +20,6 @@ author:
|
||||
- Werner Dijkerman (@dj-wasabi)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -108,7 +107,6 @@ EXAMPLES = '''
|
||||
community.general.gitlab_group:
|
||||
api_url: https://gitlab.example.com/
|
||||
api_token: "{{ access_token }}"
|
||||
validate_certs: false
|
||||
name: my_first_group
|
||||
state: absent
|
||||
|
||||
@@ -178,7 +176,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_group, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, find_group, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -355,7 +353,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
group_name = module.params['name']
|
||||
group_path = module.params['path']
|
||||
@@ -370,8 +370,6 @@ def main():
|
||||
avatar_path = module.params['avatar_path']
|
||||
force_delete = module.params['force_delete']
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
# Define default group_path based on group_name
|
||||
if group_path is None:
|
||||
group_path = group_name.replace(" ", "_")
|
||||
|
||||
@@ -160,7 +160,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -273,7 +273,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
access_level_int = {
|
||||
'guest': gitlab.const.GUEST_ACCESS,
|
||||
@@ -291,9 +293,6 @@ def main():
|
||||
if purge_users:
|
||||
purge_users = [access_level_int[level] for level in purge_users]
|
||||
|
||||
# connect to gitlab server
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
group = GitLabGroup(module, gl)
|
||||
|
||||
gitlab_group_id = group.get_group_id(gitlab_group)
|
||||
|
||||
@@ -21,7 +21,6 @@ description:
|
||||
author:
|
||||
- Florent Madiot (@scodeman)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -207,7 +206,7 @@ group_variable:
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables, vars_to_variables
|
||||
auth_argument_spec, gitlab_authentication, filter_returned_variables, vars_to_variables
|
||||
)
|
||||
|
||||
|
||||
@@ -413,7 +412,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
purge = module.params['purge']
|
||||
var_list = module.params['vars']
|
||||
@@ -428,8 +429,6 @@ def main():
|
||||
if any(x['value'] is None for x in variables):
|
||||
module.fail_json(msg='value parameter is required in state present')
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
this_gitlab = GitlabGroupVariables(module=module, gitlab_instance=gitlab_instance)
|
||||
|
||||
changed, raw_return_value, before, after = native_python_main(this_gitlab, purge, variables, state, module)
|
||||
|
||||
@@ -21,7 +21,6 @@ author:
|
||||
- Marcus Watkins (@marwatk)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -123,7 +122,6 @@ EXAMPLES = '''
|
||||
state: present
|
||||
push_events: true
|
||||
tag_push_events: true
|
||||
hook_validate_certs: false
|
||||
token: "my-super-secret-token-that-my-ci-server-will-check"
|
||||
|
||||
- name: "Delete the previous hook"
|
||||
@@ -171,7 +169,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_project, gitlab_authentication, ensure_gitlab_package
|
||||
auth_argument_spec, find_project, gitlab_authentication
|
||||
)
|
||||
|
||||
|
||||
@@ -325,7 +323,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
state = module.params['state']
|
||||
project_identifier = module.params['project']
|
||||
@@ -342,8 +342,6 @@ def main():
|
||||
enable_ssl_verification = module.params['hook_validate_certs']
|
||||
hook_token = module.params['token']
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
gitlab_hook = GitLabHook(module, gitlab_instance)
|
||||
|
||||
project = find_project(gitlab_instance, project_identifier)
|
||||
|
||||
@@ -23,7 +23,6 @@ description:
|
||||
author:
|
||||
- Benedikt Braunger (@benibr)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -139,7 +138,7 @@ instance_variable:
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables
|
||||
auth_argument_spec, gitlab_authentication, filter_returned_variables
|
||||
)
|
||||
|
||||
|
||||
@@ -326,7 +325,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
purge = module.params['purge']
|
||||
state = module.params['state']
|
||||
@@ -337,8 +338,6 @@ def main():
|
||||
if any(x['value'] is None for x in variables):
|
||||
module.fail_json(msg='value parameter is required in state present')
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
this_gitlab = GitlabInstanceVariables(module=module, gitlab_instance=gitlab_instance)
|
||||
|
||||
changed, raw_return_value, before, after = native_python_main(this_gitlab, purge, variables, state, module)
|
||||
|
||||
408
plugins/modules/gitlab_issue.py
Normal file
408
plugins/modules/gitlab_issue.py
Normal file
@@ -0,0 +1,408 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023, Ondrej Zvara (ozvara1@gmail.com)
|
||||
# Based on code:
|
||||
# Copyright (c) 2021, Lennert Mertens (lennert@nubera.be)
|
||||
# Copyright (c) 2021, Werner Dijkerman (ikben@werner-dijkerman.nl)
|
||||
# Copyright (c) 2015, Werner Dijkerman (ikben@werner-dijkerman.nl)
|
||||
# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr)
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: gitlab_issue
|
||||
short_description: Create, update, or delete GitLab issues
|
||||
version_added: '8.1.0'
|
||||
description:
|
||||
- Creates an issue if it does not exist.
|
||||
- When an issue does exist, it will be updated if the provided parameters are different.
|
||||
- When an issue does exist and O(state=absent), the issue will be deleted.
|
||||
- When multiple issues are detected, the task fails.
|
||||
- Existing issues are matched based on O(title) and O(state_filter) filters.
|
||||
author:
|
||||
- zvaraondrej (@zvaraondrej)
|
||||
requirements:
|
||||
- python-gitlab >= 2.3.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
- community.general.gitlab
|
||||
- community.general.attributes
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
|
||||
options:
|
||||
assignee_ids:
|
||||
description:
|
||||
- A list of assignee usernames omitting V(@) character.
|
||||
- Set to an empty array to unassign all assignees.
|
||||
type: list
|
||||
elements: str
|
||||
description:
|
||||
description:
|
||||
- A description of the issue.
|
||||
- Gets overridden by a content of file specified at O(description_path), if found.
|
||||
type: str
|
||||
description_path:
|
||||
description:
|
||||
- A path of file containing issue's description.
|
||||
- Accepts MarkDown formatted files.
|
||||
type: path
|
||||
issue_type:
|
||||
description:
|
||||
- Type of the issue.
|
||||
default: issue
|
||||
type: str
|
||||
choices: ["issue", "incident", "test_case"]
|
||||
labels:
|
||||
description:
|
||||
- A list of label names.
|
||||
- Set to an empty array to remove all labels.
|
||||
type: list
|
||||
elements: str
|
||||
milestone_search:
|
||||
description:
|
||||
- The name of the milestone.
|
||||
- Set to empty string to unassign milestone.
|
||||
type: str
|
||||
milestone_group_id:
|
||||
description:
|
||||
- The path or numeric ID of the group hosting desired milestone.
|
||||
type: str
|
||||
project:
|
||||
description:
|
||||
- The path or name of the project.
|
||||
required: true
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Create or delete issue.
|
||||
default: present
|
||||
type: str
|
||||
choices: ["present", "absent"]
|
||||
state_filter:
|
||||
description:
|
||||
- Filter specifying state of issues while searching.
|
||||
type: str
|
||||
choices: ["opened", "closed"]
|
||||
default: opened
|
||||
title:
|
||||
description:
|
||||
- A title for the issue. The title is used as a unique identifier to ensure idempotency.
|
||||
type: str
|
||||
required: true
|
||||
'''
|
||||
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create Issue
|
||||
community.general.gitlab_issue:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
project: "group1/project1"
|
||||
title: "Ansible demo Issue"
|
||||
description: "Demo Issue description"
|
||||
labels:
|
||||
- Ansible
|
||||
- Demo
|
||||
assignee_ids:
|
||||
- testassignee
|
||||
state_filter: "opened"
|
||||
state: present
|
||||
|
||||
- name: Delete Issue
|
||||
community.general.gitlab_issue:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
project: "group1/project1"
|
||||
title: "Ansible demo Issue"
|
||||
state_filter: "opened"
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
msg:
|
||||
description: Success or failure message.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "Success"
|
||||
|
||||
issue:
|
||||
description: API object.
|
||||
returned: success
|
||||
type: dict
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, find_project, find_group
|
||||
)
|
||||
|
||||
|
||||
class GitlabIssue(object):
|
||||
|
||||
def __init__(self, module, project, gitlab_instance):
|
||||
self._gitlab = gitlab_instance
|
||||
self._module = module
|
||||
self.project = project
|
||||
|
||||
'''
|
||||
@param milestone_id Title of the milestone
|
||||
'''
|
||||
def get_milestone(self, milestone_id, group):
|
||||
milestones = []
|
||||
try:
|
||||
milestones = group.milestones.list(search=milestone_id)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
self._module.fail_json(msg="Failed to list the Milestones: %s" % to_native(e))
|
||||
|
||||
if len(milestones) > 1:
|
||||
self._module.fail_json(msg="Multiple Milestones matched search criteria.")
|
||||
if len(milestones) < 1:
|
||||
self._module.fail_json(msg="No Milestones matched search criteria.")
|
||||
if len(milestones) == 1:
|
||||
try:
|
||||
return group.milestones.get(id=milestones[0].id)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
self._module.fail_json(msg="Failed to get the Milestones: %s" % to_native(e))
|
||||
|
||||
'''
|
||||
@param title Title of the Issue
|
||||
@param state_filter Issue's state to filter on
|
||||
'''
|
||||
def get_issue(self, title, state_filter):
|
||||
issues = []
|
||||
try:
|
||||
issues = self.project.issues.list(title=title, state=state_filter)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
self._module.fail_json(msg="Failed to list the Issues: %s" % to_native(e))
|
||||
|
||||
if len(issues) > 1:
|
||||
self._module.fail_json(msg="Multiple Issues matched search criteria.")
|
||||
if len(issues) == 1:
|
||||
try:
|
||||
return self.project.issues.get(id=issues[0].iid)
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
self._module.fail_json(msg="Failed to get the Issue: %s" % to_native(e))
|
||||
|
||||
'''
|
||||
@param username Name of the user
|
||||
'''
|
||||
def get_user(self, username):
|
||||
users = []
|
||||
try:
|
||||
users = [user for user in self.project.users.list(username=username, all=True) if user.username == username]
|
||||
except gitlab.exceptions.GitlabGetError as e:
|
||||
self._module.fail_json(msg="Failed to list the users: %s" % to_native(e))
|
||||
|
||||
if len(users) > 1:
|
||||
self._module.fail_json(msg="Multiple Users matched search criteria.")
|
||||
elif len(users) < 1:
|
||||
self._module.fail_json(msg="No User matched search criteria.")
|
||||
else:
|
||||
return users[0]
|
||||
|
||||
'''
|
||||
@param users List of usernames
|
||||
'''
|
||||
def get_user_ids(self, users):
|
||||
return [self.get_user(user).id for user in users]
|
||||
|
||||
'''
|
||||
@param options Options of the Issue
|
||||
'''
|
||||
def create_issue(self, options):
|
||||
if self._module.check_mode:
|
||||
self._module.exit_json(changed=True, msg="Successfully created Issue '%s'." % options["title"])
|
||||
|
||||
try:
|
||||
return self.project.issues.create(options)
|
||||
except gitlab.exceptions.GitlabCreateError as e:
|
||||
self._module.fail_json(msg="Failed to create Issue: %s " % to_native(e))
|
||||
|
||||
'''
|
||||
@param issue Issue object to delete
|
||||
'''
|
||||
def delete_issue(self, issue):
|
||||
if self._module.check_mode:
|
||||
self._module.exit_json(changed=True, msg="Successfully deleted Issue '%s'." % issue["title"])
|
||||
|
||||
try:
|
||||
return issue.delete()
|
||||
except gitlab.exceptions.GitlabDeleteError as e:
|
||||
self._module.fail_json(msg="Failed to delete Issue: '%s'." % to_native(e))
|
||||
|
||||
'''
|
||||
@param issue Issue object to update
|
||||
@param options Options of the Issue
|
||||
'''
|
||||
def update_issue(self, issue, options):
|
||||
if self._module.check_mode:
|
||||
self._module.exit_json(changed=True, msg="Successfully updated Issue '%s'." % issue["title"])
|
||||
|
||||
try:
|
||||
return self.project.issues.update(issue.iid, options)
|
||||
except gitlab.exceptions.GitlabUpdateError as e:
|
||||
self._module.fail_json(msg="Failed to update Issue %s." % to_native(e))
|
||||
|
||||
'''
|
||||
@param issue Issue object to evaluate
|
||||
@param options New options to update Issue with
|
||||
'''
|
||||
def issue_has_changed(self, issue, options):
|
||||
for key, value in options.items():
|
||||
if value is not None:
|
||||
|
||||
if key == 'milestone_id':
|
||||
old_milestone = getattr(issue, 'milestone')['id'] if getattr(issue, 'milestone') else ""
|
||||
if options[key] != old_milestone:
|
||||
return True
|
||||
elif key == 'assignee_ids':
|
||||
if options[key] != sorted([user["id"] for user in getattr(issue, 'assignees')]):
|
||||
return True
|
||||
|
||||
elif key == 'labels':
|
||||
if options[key] != sorted(getattr(issue, key)):
|
||||
return True
|
||||
|
||||
elif getattr(issue, key) != value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = basic_auth_argument_spec()
|
||||
argument_spec.update(auth_argument_spec())
|
||||
argument_spec.update(
|
||||
assignee_ids=dict(type='list', elements='str', required=False),
|
||||
description=dict(type='str', required=False),
|
||||
description_path=dict(type='path', required=False),
|
||||
issue_type=dict(type='str', default='issue', choices=["issue", "incident", "test_case"], required=False),
|
||||
labels=dict(type='list', elements='str', required=False),
|
||||
milestone_search=dict(type='str', required=False),
|
||||
milestone_group_id=dict(type='str', required=False),
|
||||
project=dict(type='str', required=True),
|
||||
state=dict(type='str', default="present", choices=["absent", "present"]),
|
||||
state_filter=dict(type='str', default="opened", choices=["opened", "closed"]),
|
||||
title=dict(type='str', required=True),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
['api_username', 'api_token'],
|
||||
['api_username', 'api_oauth_token'],
|
||||
['api_username', 'api_job_token'],
|
||||
['api_token', 'api_oauth_token'],
|
||||
['api_token', 'api_job_token'],
|
||||
['description', 'description_path'],
|
||||
],
|
||||
required_together=[
|
||||
['api_username', 'api_password'],
|
||||
['milestone_search', 'milestone_group_id'],
|
||||
],
|
||||
required_one_of=[
|
||||
['api_username', 'api_token', 'api_oauth_token', 'api_job_token']
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
assignee_ids = module.params['assignee_ids']
|
||||
description = module.params['description']
|
||||
description_path = module.params['description_path']
|
||||
issue_type = module.params['issue_type']
|
||||
labels = module.params['labels']
|
||||
milestone_id = module.params['milestone_search']
|
||||
milestone_group_id = module.params['milestone_group_id']
|
||||
project = module.params['project']
|
||||
state = module.params['state']
|
||||
state_filter = module.params['state_filter']
|
||||
title = module.params['title']
|
||||
|
||||
gitlab_version = gitlab.__version__
|
||||
if LooseVersion(gitlab_version) < LooseVersion('2.3.0'):
|
||||
module.fail_json(msg="community.general.gitlab_issue requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
|
||||
" Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
this_project = find_project(gitlab_instance, project)
|
||||
if this_project is None:
|
||||
module.fail_json(msg="Failed to get the project: %s" % project)
|
||||
|
||||
this_gitlab = GitlabIssue(module=module, project=this_project, gitlab_instance=gitlab_instance)
|
||||
|
||||
if milestone_id and milestone_group_id:
|
||||
this_group = find_group(gitlab_instance, milestone_group_id)
|
||||
if this_group is None:
|
||||
module.fail_json(msg="Failed to get the group: %s" % milestone_group_id)
|
||||
|
||||
milestone_id = this_gitlab.get_milestone(milestone_id, this_group).id
|
||||
|
||||
this_issue = this_gitlab.get_issue(title, state_filter)
|
||||
|
||||
if state == "present":
|
||||
if description_path:
|
||||
try:
|
||||
with open(description_path, 'rb') as f:
|
||||
description = to_text(f.read(), errors='surrogate_or_strict')
|
||||
except IOError as e:
|
||||
module.fail_json(msg='Cannot open {0}: {1}'.format(description_path, e))
|
||||
|
||||
# sorting necessary in order to properly detect changes, as we don't want to get false positive
|
||||
# results due to differences in ids ordering;
|
||||
assignee_ids = sorted(this_gitlab.get_user_ids(assignee_ids)) if assignee_ids else assignee_ids
|
||||
labels = sorted(labels) if labels else labels
|
||||
|
||||
options = {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"labels": labels,
|
||||
"issue_type": issue_type,
|
||||
"milestone_id": milestone_id,
|
||||
"assignee_ids": assignee_ids,
|
||||
}
|
||||
|
||||
if not this_issue:
|
||||
issue = this_gitlab.create_issue(options)
|
||||
module.exit_json(
|
||||
changed=True, msg="Created Issue '{t}'.".format(t=title),
|
||||
issue=issue.asdict()
|
||||
)
|
||||
else:
|
||||
if this_gitlab.issue_has_changed(this_issue, options):
|
||||
issue = this_gitlab.update_issue(this_issue, options)
|
||||
module.exit_json(
|
||||
changed=True, msg="Updated Issue '{t}'.".format(t=title),
|
||||
issue=issue
|
||||
)
|
||||
else:
|
||||
module.exit_json(
|
||||
changed=False, msg="Issue '{t}' already exists".format(t=title),
|
||||
issue=this_issue.asdict()
|
||||
)
|
||||
elif state == "absent":
|
||||
if not this_issue:
|
||||
module.exit_json(changed=False, msg="Issue '{t}' does not exist or has already been deleted.".format(t=title))
|
||||
else:
|
||||
issue = this_gitlab.delete_issue(this_issue)
|
||||
module.exit_json(
|
||||
changed=True, msg="Issue '{t}' deleted.".format(t=title),
|
||||
issue=issue
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -26,7 +26,6 @@ description:
|
||||
author:
|
||||
- zvaraondrej (@zvaraondrej)
|
||||
requirements:
|
||||
- Python >= 2.7
|
||||
- python-gitlab >= 2.3.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -152,7 +151,7 @@ from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package, find_project
|
||||
auth_argument_spec, gitlab_authentication, gitlab, find_project
|
||||
)
|
||||
|
||||
|
||||
@@ -321,7 +320,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
project = module.params['project']
|
||||
source_branch = module.params['source_branch']
|
||||
@@ -341,8 +342,6 @@ def main():
|
||||
module.fail_json(msg="community.general.gitlab_merge_request requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
|
||||
" Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version)
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
this_project = find_project(gitlab_instance, project)
|
||||
if this_project is None:
|
||||
module.fail_json(msg="Failed to get the project: %s" % project)
|
||||
|
||||
@@ -21,7 +21,6 @@ author:
|
||||
- Werner Dijkerman (@dj-wasabi)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -175,8 +174,10 @@ options:
|
||||
version_added: "4.2.0"
|
||||
default_branch:
|
||||
description:
|
||||
- Default branch name for a new project.
|
||||
- This option is only used on creation, not for updates. This is also only used if O(initialize_with_readme=true).
|
||||
- The default branch name for this project.
|
||||
- For project creation, this option requires O(initialize_with_readme=true).
|
||||
- For project update, the branch must exist.
|
||||
- Supports project's default branch update since community.general 8.0.0.
|
||||
type: str
|
||||
version_added: "4.2.0"
|
||||
builds_access_level:
|
||||
@@ -272,7 +273,6 @@ EXAMPLES = r'''
|
||||
community.general.gitlab_project:
|
||||
api_url: https://gitlab.example.com/
|
||||
api_token: "{{ access_token }}"
|
||||
validate_certs: false
|
||||
name: my_first_project
|
||||
state: absent
|
||||
delegate_to: localhost
|
||||
@@ -338,7 +338,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_group, find_project, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, find_group, find_project, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
@@ -355,7 +355,7 @@ class GitLabProject(object):
|
||||
@param namespace Namespace Object (User or Group)
|
||||
@param options Options of the project
|
||||
'''
|
||||
def create_or_update_project(self, project_name, namespace, options):
|
||||
def create_or_update_project(self, module, project_name, namespace, options):
|
||||
changed = False
|
||||
project_options = {
|
||||
'name': project_name,
|
||||
@@ -395,6 +395,8 @@ class GitLabProject(object):
|
||||
|
||||
# Because we have already call userExists in main()
|
||||
if self.project_object is None:
|
||||
if options['default_branch'] and not options['initialize_with_readme']:
|
||||
module.fail_json(msg="Param default_branch need param initialize_with_readme set to true")
|
||||
project_options.update({
|
||||
'path': options['path'],
|
||||
'import_url': options['import_url'],
|
||||
@@ -416,6 +418,8 @@ class GitLabProject(object):
|
||||
|
||||
changed = True
|
||||
else:
|
||||
if options['default_branch']:
|
||||
project_options['default_branch'] = options['default_branch']
|
||||
changed, project = self.update_project(self.project_object, project_options)
|
||||
|
||||
self.project_object = project
|
||||
@@ -552,7 +556,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
group_identifier = module.params['group']
|
||||
project_name = module.params['name']
|
||||
@@ -590,11 +596,6 @@ def main():
|
||||
security_and_compliance_access_level = module.params['security_and_compliance_access_level']
|
||||
topics = module.params['topics']
|
||||
|
||||
if default_branch and not initialize_with_readme:
|
||||
module.fail_json(msg="Param default_branch need param initialize_with_readme set to true")
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
# Set project_path to project_name if it is empty.
|
||||
if project_path is None:
|
||||
project_path = project_name.replace(" ", "_")
|
||||
@@ -636,7 +637,7 @@ def main():
|
||||
|
||||
if state == 'present':
|
||||
|
||||
if gitlab_project.create_or_update_project(project_name, namespace, {
|
||||
if gitlab_project.create_or_update_project(module, project_name, namespace, {
|
||||
"path": project_path,
|
||||
"description": project_description,
|
||||
"initialize_with_readme": initialize_with_readme,
|
||||
|
||||
@@ -97,7 +97,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, find_project, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, find_project
|
||||
)
|
||||
|
||||
|
||||
@@ -159,13 +159,12 @@ state_strategy = {
|
||||
|
||||
|
||||
def core(module):
|
||||
ensure_gitlab_package(module)
|
||||
# check prerequisites and connect to gitlab server
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
gitlab_project = module.params['project']
|
||||
state = module.params['state']
|
||||
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
project = find_project(gl, gitlab_project)
|
||||
# project doesn't exist
|
||||
if not project:
|
||||
|
||||
@@ -106,7 +106,6 @@ EXAMPLES = r'''
|
||||
community.general.gitlab_project_members:
|
||||
api_url: 'https://gitlab.example.com'
|
||||
api_token: 'Your-Private-Token'
|
||||
validate_certs: false
|
||||
project: projectname
|
||||
gitlab_user: username
|
||||
state: absent
|
||||
@@ -163,7 +162,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -279,7 +278,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
access_level_int = {
|
||||
'guest': gitlab.const.GUEST_ACCESS,
|
||||
@@ -296,9 +297,6 @@ def main():
|
||||
if purge_users:
|
||||
purge_users = [access_level_int[level] for level in purge_users]
|
||||
|
||||
# connect to gitlab server
|
||||
gl = gitlab_authentication(module)
|
||||
|
||||
project = GitLabProjectMembers(module, gl)
|
||||
|
||||
gitlab_project_id = project.get_project(gitlab_project)
|
||||
|
||||
@@ -18,7 +18,6 @@ description:
|
||||
author:
|
||||
- "Markus Bergholz (@markuman)"
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -221,13 +220,12 @@ project_variable:
|
||||
sample: ['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY']
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.api import basic_auth_argument_spec
|
||||
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables, vars_to_variables,
|
||||
HAS_GITLAB_PACKAGE, GITLAB_IMP_ERR
|
||||
auth_argument_spec, gitlab_authentication, filter_returned_variables, vars_to_variables
|
||||
)
|
||||
|
||||
|
||||
@@ -436,10 +434,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
if not HAS_GITLAB_PACKAGE:
|
||||
module.fail_json(msg=missing_required_lib("python-gitlab"), exception=GITLAB_IMP_ERR)
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
purge = module.params['purge']
|
||||
var_list = module.params['vars']
|
||||
@@ -454,8 +451,6 @@ def main():
|
||||
if any(x['value'] is None for x in variables):
|
||||
module.fail_json(msg='value parameter is required for all variables in state present')
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
this_gitlab = GitlabProjectVariables(module=module, gitlab_instance=gitlab_instance)
|
||||
|
||||
change, raw_return_value, before, after = native_python_main(this_gitlab, purge, variables, state, module)
|
||||
|
||||
@@ -16,7 +16,6 @@ description:
|
||||
author:
|
||||
- "Werner Dijkerman (@dj-wasabi)"
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab >= 2.3.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -83,7 +82,7 @@ from ansible.module_utils.api import basic_auth_argument_spec
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -164,7 +163,9 @@ def main():
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
project = module.params['project']
|
||||
name = module.params['name']
|
||||
@@ -177,7 +178,6 @@ def main():
|
||||
module.fail_json(msg="community.general.gitlab_proteched_branch requires python-gitlab Python module >= 2.3.0 (installed version: [%s])."
|
||||
" Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version)
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
this_gitlab = GitlabProtectedBranch(module=module, project=project, gitlab_instance=gitlab_instance)
|
||||
|
||||
p_branch = this_gitlab.protected_branch_exist(name=name)
|
||||
|
||||
@@ -30,7 +30,6 @@ author:
|
||||
- Samy Coenen (@SamyCoenen)
|
||||
- Guillaume Martinez (@Lunik)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab >= 1.5.0
|
||||
extends_documentation_fragment:
|
||||
- community.general.auth_basic
|
||||
@@ -48,6 +47,7 @@ options:
|
||||
description:
|
||||
- ID or full path of the group in the form group/subgroup.
|
||||
- Mutually exclusive with O(owned) and O(project).
|
||||
- Must be group's numeric ID if O(registration_token) is not set and O(state=present).
|
||||
type: str
|
||||
version_added: '6.5.0'
|
||||
project:
|
||||
@@ -55,6 +55,7 @@ options:
|
||||
- ID or full path of the project in the form of group/name.
|
||||
- Mutually exclusive with O(owned) since community.general 4.5.0.
|
||||
- Mutually exclusive with O(group).
|
||||
- Must be project's numeric ID if O(registration_token) is not set and O(state=present).
|
||||
type: str
|
||||
version_added: '3.7.0'
|
||||
description:
|
||||
@@ -73,8 +74,11 @@ options:
|
||||
type: str
|
||||
registration_token:
|
||||
description:
|
||||
- The registration token is used to register new runners.
|
||||
- Required if O(state=present).
|
||||
- The registration token is used to register new runners before GitLab 16.0.
|
||||
- Required if O(state=present) for GitLab < 16.0.
|
||||
- If set, the runner will be created using the old runner creation workflow.
|
||||
- If not set, the runner will be created using the new runner creation workflow, introduced in GitLab 16.0.
|
||||
- If not set, requires python-gitlab >= 4.0.0.
|
||||
type: str
|
||||
owned:
|
||||
description:
|
||||
@@ -87,9 +91,18 @@ options:
|
||||
active:
|
||||
description:
|
||||
- Define if the runners is immediately active after creation.
|
||||
- Mutually exclusive with O(paused).
|
||||
required: false
|
||||
default: true
|
||||
type: bool
|
||||
paused:
|
||||
description:
|
||||
- Define if the runners is active or paused after creation.
|
||||
- Mutually exclusive with O(active).
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 8.1.0
|
||||
locked:
|
||||
description:
|
||||
- Determines if the runner is locked or not.
|
||||
@@ -103,9 +116,9 @@ options:
|
||||
is only applied on updates.
|
||||
- If set to V(not_protected), runner can pick up jobs from both protected and unprotected branches.
|
||||
- If set to V(ref_protected), runner can pick up jobs only from protected branches.
|
||||
- The current default is V(ref_protected). This will change to no default in community.general 8.0.0.
|
||||
From that version on, if this option is not specified explicitly, GitLab will use V(not_protected)
|
||||
on creation, and the value set will not be changed on any updates.
|
||||
- Before community.general 8.0.0 the default was V(ref_protected). This was changed to no default in community.general 8.0.0.
|
||||
If this option is not specified explicitly, GitLab will use V(not_protected) on creation, and the value set
|
||||
will not be changed on any updates.
|
||||
required: false
|
||||
choices: ["not_protected", "ref_protected"]
|
||||
type: str
|
||||
@@ -206,10 +219,13 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
|
||||
class GitLabRunner(object):
|
||||
def __init__(self, module, gitlab_instance, group=None, project=None):
|
||||
self._module = module
|
||||
@@ -232,18 +248,32 @@ class GitLabRunner(object):
|
||||
changed = False
|
||||
|
||||
arguments = {
|
||||
'active': options['active'],
|
||||
'locked': options['locked'],
|
||||
'run_untagged': options['run_untagged'],
|
||||
'maximum_timeout': options['maximum_timeout'],
|
||||
'tag_list': options['tag_list'],
|
||||
}
|
||||
|
||||
if options.get('paused') is not None:
|
||||
arguments['paused'] = options['paused']
|
||||
else:
|
||||
arguments['active'] = options['active']
|
||||
|
||||
if options.get('access_level') is not None:
|
||||
arguments['access_level'] = options['access_level']
|
||||
# Because we have already call userExists in main()
|
||||
if self.runner_object is None:
|
||||
arguments['description'] = description
|
||||
arguments['token'] = options['registration_token']
|
||||
if options.get('registration_token') is not None:
|
||||
arguments['token'] = options['registration_token']
|
||||
elif options.get('group') is not None:
|
||||
arguments['runner_type'] = 'group_type'
|
||||
arguments['group_id'] = options['group']
|
||||
elif options.get('project') is not None:
|
||||
arguments['runner_type'] = 'project_type'
|
||||
arguments['project_id'] = options['project']
|
||||
else:
|
||||
arguments['runner_type'] = 'instance_type'
|
||||
|
||||
access_level_on_creation = self._module.params['access_level_on_creation']
|
||||
if not access_level_on_creation:
|
||||
@@ -253,19 +283,17 @@ class GitLabRunner(object):
|
||||
changed = True
|
||||
else:
|
||||
changed, runner = self.update_runner(self.runner_object, arguments)
|
||||
if changed:
|
||||
if self._module.check_mode:
|
||||
self._module.exit_json(changed=True, msg="Successfully updated the runner %s" % description)
|
||||
|
||||
try:
|
||||
runner.save()
|
||||
except Exception as e:
|
||||
self._module.fail_json(msg="Failed to update runner: %s " % to_native(e))
|
||||
|
||||
self.runner_object = runner
|
||||
if changed:
|
||||
if self._module.check_mode:
|
||||
self._module.exit_json(changed=True, msg="Successfully created or updated the runner %s" % description)
|
||||
|
||||
try:
|
||||
runner.save()
|
||||
except Exception as e:
|
||||
self._module.fail_json(msg="Failed to update runner: %s " % to_native(e))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return changed
|
||||
|
||||
'''
|
||||
@param arguments Attributes of the runner
|
||||
@@ -275,7 +303,12 @@ class GitLabRunner(object):
|
||||
return True
|
||||
|
||||
try:
|
||||
runner = self._gitlab.runners.create(arguments)
|
||||
if arguments.get('token') is not None:
|
||||
runner = self._gitlab.runners.create(arguments)
|
||||
elif LooseVersion(gitlab.__version__) < LooseVersion('4.0.0'):
|
||||
self._module.fail_json(msg="New runner creation workflow requires python-gitlab 4.0.0 or higher")
|
||||
else:
|
||||
runner = self._gitlab.user.runners.create(arguments)
|
||||
except (gitlab.exceptions.GitlabCreateError) as e:
|
||||
self._module.fail_json(msg="Failed to create runner: %s " % to_native(e))
|
||||
|
||||
@@ -348,6 +381,7 @@ def main():
|
||||
argument_spec.update(dict(
|
||||
description=dict(type='str', required=True, aliases=["name"]),
|
||||
active=dict(type='bool', default=True),
|
||||
paused=dict(type='bool', default=False),
|
||||
owned=dict(type='bool', default=False),
|
||||
tag_list=dict(type='list', elements='str', default=[]),
|
||||
run_untagged=dict(type='bool', default=True),
|
||||
@@ -372,6 +406,7 @@ def main():
|
||||
['project', 'owned'],
|
||||
['group', 'owned'],
|
||||
['project', 'group'],
|
||||
['active', 'paused'],
|
||||
],
|
||||
required_together=[
|
||||
['api_username', 'api_password'],
|
||||
@@ -379,12 +414,11 @@ def main():
|
||||
required_one_of=[
|
||||
['api_username', 'api_token', 'api_oauth_token', 'api_job_token'],
|
||||
],
|
||||
required_if=[
|
||||
('state', 'present', ['registration_token']),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
state = module.params['state']
|
||||
runner_description = module.params['description']
|
||||
@@ -398,16 +432,6 @@ def main():
|
||||
project = module.params['project']
|
||||
group = module.params['group']
|
||||
|
||||
if access_level is None:
|
||||
message = "The option 'access_level' is unspecified, so 'ref_protected' is assumed. "\
|
||||
"In order to align the module with GitLab's runner API, this option will lose "\
|
||||
"its default value in community.general 8.0.0. From that version on, you must set "\
|
||||
"this option to 'ref_protected' explicitly, if you want to have a protected runner, "\
|
||||
"otherwise GitLab's default access level gets applied, which is 'not_protected'"
|
||||
module.deprecate(message, version='8.0.0', collection_name='community.general')
|
||||
access_level = 'ref_protected'
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
gitlab_project = None
|
||||
gitlab_group = None
|
||||
|
||||
@@ -441,6 +465,8 @@ def main():
|
||||
"access_level": access_level,
|
||||
"maximum_timeout": maximum_timeout,
|
||||
"registration_token": registration_token,
|
||||
"group": group,
|
||||
"project": project,
|
||||
}):
|
||||
module.exit_json(changed=True, runner=gitlab_runner.runner_object._attrs,
|
||||
msg="Successfully created or updated the runner %s" % runner_description)
|
||||
|
||||
@@ -27,7 +27,6 @@ author:
|
||||
- Lennert Mertens (@LennertMertens)
|
||||
- Stef Graces (@stgrace)
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- python-gitlab python module
|
||||
- administrator rights on the GitLab server
|
||||
extends_documentation_fragment:
|
||||
@@ -151,7 +150,6 @@ EXAMPLES = '''
|
||||
community.general.gitlab_user:
|
||||
api_url: https://gitlab.example.com/
|
||||
api_token: "{{ access_token }}"
|
||||
validate_certs: false
|
||||
username: myusername
|
||||
state: absent
|
||||
|
||||
@@ -191,7 +189,6 @@ EXAMPLES = '''
|
||||
community.general.gitlab_user:
|
||||
api_url: https://gitlab.example.com/
|
||||
api_token: "{{ access_token }}"
|
||||
validate_certs: false
|
||||
username: myusername
|
||||
state: blocked
|
||||
|
||||
@@ -199,7 +196,6 @@ EXAMPLES = '''
|
||||
community.general.gitlab_user:
|
||||
api_url: https://gitlab.example.com/
|
||||
api_token: "{{ access_token }}"
|
||||
validate_certs: false
|
||||
username: myusername
|
||||
state: unblocked
|
||||
'''
|
||||
@@ -234,7 +230,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.gitlab import (
|
||||
auth_argument_spec, find_group, gitlab_authentication, gitlab, ensure_gitlab_package
|
||||
auth_argument_spec, find_group, gitlab_authentication, gitlab
|
||||
)
|
||||
|
||||
|
||||
@@ -616,7 +612,9 @@ def main():
|
||||
('state', 'present', ['name', 'email']),
|
||||
)
|
||||
)
|
||||
ensure_gitlab_package(module)
|
||||
|
||||
# check prerequisites and connect to gitlab server
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
user_name = module.params['name']
|
||||
state = module.params['state']
|
||||
@@ -635,8 +633,6 @@ def main():
|
||||
user_identities = module.params['identities']
|
||||
overwrite_identities = module.params['overwrite_identities']
|
||||
|
||||
gitlab_instance = gitlab_authentication(module)
|
||||
|
||||
gitlab_user = GitLabUser(module, gitlab_instance)
|
||||
user_exists = gitlab_user.exists_user(user_username)
|
||||
if user_exists:
|
||||
|
||||
@@ -43,8 +43,7 @@ options:
|
||||
type: str
|
||||
force:
|
||||
description:
|
||||
- Discards uncommitted changes. Runs C(hg update -C). Prior to
|
||||
Ansible 1.9, the default was V(true).
|
||||
- Discards uncommitted changes. Runs C(hg update -C).
|
||||
type: bool
|
||||
default: false
|
||||
purge:
|
||||
|
||||
@@ -19,8 +19,6 @@ description:
|
||||
These information includes hardware and network related information useful
|
||||
for provisioning (e.g. macaddress, uuid).
|
||||
- This module requires the C(hpilo) python module.
|
||||
- This module was called C(hpilo_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(community.general.hpilo_info) module no longer returns C(ansible_facts)!
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
|
||||
@@ -106,22 +106,16 @@ EXAMPLES = """
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import traceback
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils import deps
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
PASSLIB_IMP_ERR = None
|
||||
try:
|
||||
with deps.declare("passlib"):
|
||||
from passlib.apache import HtpasswdFile, htpasswd_context
|
||||
from passlib.context import CryptContext
|
||||
import passlib
|
||||
except ImportError:
|
||||
PASSLIB_IMP_ERR = traceback.format_exc()
|
||||
passlib_installed = False
|
||||
else:
|
||||
passlib_installed = True
|
||||
|
||||
|
||||
apache_hashes = ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"]
|
||||
|
||||
@@ -146,36 +140,20 @@ def present(dest, username, password, hash_scheme, create, check_mode):
|
||||
if check_mode:
|
||||
return ("Create %s" % dest, True)
|
||||
create_missing_directories(dest)
|
||||
if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
|
||||
ht = HtpasswdFile(dest, new=True, default_scheme=hash_scheme, context=context)
|
||||
else:
|
||||
ht = HtpasswdFile(dest, autoload=False, default=hash_scheme, context=context)
|
||||
if getattr(ht, 'set_password', None):
|
||||
ht.set_password(username, password)
|
||||
else:
|
||||
ht.update(username, password)
|
||||
ht = HtpasswdFile(dest, new=True, default_scheme=hash_scheme, context=context)
|
||||
ht.set_password(username, password)
|
||||
ht.save()
|
||||
return ("Created %s and added %s" % (dest, username), True)
|
||||
else:
|
||||
if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
|
||||
ht = HtpasswdFile(dest, new=False, default_scheme=hash_scheme, context=context)
|
||||
else:
|
||||
ht = HtpasswdFile(dest, default=hash_scheme, context=context)
|
||||
ht = HtpasswdFile(dest, new=False, default_scheme=hash_scheme, context=context)
|
||||
|
||||
found = None
|
||||
if getattr(ht, 'check_password', None):
|
||||
found = ht.check_password(username, password)
|
||||
else:
|
||||
found = ht.verify(username, password)
|
||||
found = ht.check_password(username, password)
|
||||
|
||||
if found:
|
||||
return ("%s already present" % username, False)
|
||||
else:
|
||||
if not check_mode:
|
||||
if getattr(ht, 'set_password', None):
|
||||
ht.set_password(username, password)
|
||||
else:
|
||||
ht.update(username, password)
|
||||
ht.set_password(username, password)
|
||||
ht.save()
|
||||
return ("Add/update %s" % username, True)
|
||||
|
||||
@@ -184,10 +162,7 @@ def absent(dest, username, check_mode):
|
||||
""" Ensures user is absent
|
||||
|
||||
Returns (msg, changed) """
|
||||
if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
|
||||
ht = HtpasswdFile(dest, new=False)
|
||||
else:
|
||||
ht = HtpasswdFile(dest)
|
||||
ht = HtpasswdFile(dest, new=False)
|
||||
|
||||
if username not in ht.users():
|
||||
return ("%s not present" % username, False)
|
||||
@@ -233,20 +208,13 @@ def main():
|
||||
create = module.params['create']
|
||||
check_mode = module.check_mode
|
||||
|
||||
if not passlib_installed:
|
||||
module.fail_json(msg=missing_required_lib("passlib"), exception=PASSLIB_IMP_ERR)
|
||||
deps.validate(module)
|
||||
|
||||
# TODO double check if this hack below is still needed.
|
||||
# Check file for blank lines in effort to avoid "need more than 1 value to unpack" error.
|
||||
try:
|
||||
f = open(path, "r")
|
||||
except IOError:
|
||||
# No preexisting file to remove blank lines from
|
||||
f = None
|
||||
else:
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
lines = f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
# If the file gets edited, it returns true, so only edit the file if it has blank lines
|
||||
strip = False
|
||||
@@ -260,11 +228,12 @@ def main():
|
||||
if check_mode:
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
path = temp.name
|
||||
f = open(path, "w")
|
||||
try:
|
||||
[f.write(line) for line in lines if line.strip()]
|
||||
finally:
|
||||
f.close()
|
||||
with open(path, "w") as f:
|
||||
f.writelines(line for line in lines if line.strip())
|
||||
|
||||
except IOError:
|
||||
# No preexisting file to remove blank lines from
|
||||
pass
|
||||
|
||||
try:
|
||||
if state == 'present':
|
||||
|
||||
@@ -106,7 +106,7 @@ options:
|
||||
type: str
|
||||
description:
|
||||
- The IP address of the host.
|
||||
required: true
|
||||
- This is no longer required since community.general 8.0.0.
|
||||
variables:
|
||||
type: dict
|
||||
description:
|
||||
@@ -243,7 +243,7 @@ def main():
|
||||
template=dict(default=None),
|
||||
check_command=dict(default="hostalive"),
|
||||
display_name=dict(default=None),
|
||||
ip=dict(required=True),
|
||||
ip=dict(),
|
||||
variables=dict(type='dict', default=None),
|
||||
)
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ description:
|
||||
- Builds Redfish URIs locally and sends them to remote iDRAC controllers to
|
||||
get information back.
|
||||
- For use with Dell EMC iDRAC operations that require Redfish OEM extensions.
|
||||
- This module was called C(idrac_redfish_facts) before Ansible 2.9, returning C(ansible_facts).
|
||||
Note that the M(community.general.idrac_redfish_info) module no longer returns C(ansible_facts)!
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
|
||||
@@ -100,7 +100,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_hostname }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
content: |
|
||||
<configConfMo><inConfig>
|
||||
<computeRackUnit dn="sys/rack-unit-1" adminPower="down"/>
|
||||
@@ -112,7 +112,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_hostname }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
timeout: 120
|
||||
content: |
|
||||
<!-- Configure Serial-on-LAN -->
|
||||
@@ -137,7 +137,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_hostname }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
content: |
|
||||
<!-- Configure PXE boot -->
|
||||
<configConfMo><inConfig>
|
||||
@@ -155,7 +155,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_host }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
content: |
|
||||
<configConfMo><inConfig>
|
||||
<lsbootStorage dn="sys/rack-unit-1/boot-policy/storage-read-write" access="read-write" order="1" type="storage"/>
|
||||
@@ -167,7 +167,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_host }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
content: |
|
||||
<configConfMo><inConfig>
|
||||
<computeRackUnit dn="sys/rack-unit-1" usrLbl="Customer Lab - POD{{ pod_id }} - {{ inventory_hostname_short }}"/>
|
||||
@@ -179,7 +179,7 @@ EXAMPLES = r'''
|
||||
hostname: '{{ imc_host }}'
|
||||
username: '{{ imc_username }}'
|
||||
password: '{{ imc_password }}'
|
||||
validate_certs: false
|
||||
validate_certs: false # only do this when you trust the network!
|
||||
timeout: 120
|
||||
content: |
|
||||
<configConfMo><inConfig>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user