mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-27 05:43:05 +00:00
Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
680cd4c6ee | ||
|
|
401b911171 | ||
|
|
7f61e72a2c | ||
|
|
3c3396a7b8 | ||
|
|
45f583b1ed | ||
|
|
2de1dccbf5 | ||
|
|
a44515c701 | ||
|
|
8cf2e7ef7b | ||
|
|
ec198d0e09 | ||
|
|
b162122630 | ||
|
|
b89d2b1316 | ||
|
|
1d3eab804d | ||
|
|
d3b8f54d7d | ||
|
|
b7d1a2789b | ||
|
|
6bfcfcdc81 | ||
|
|
ebe5671dff | ||
|
|
2266756968 | ||
|
|
3a0a1a7529 | ||
|
|
65015e63e9 | ||
|
|
dead467982 | ||
|
|
ae286f5226 | ||
|
|
ea53e34537 | ||
|
|
48b0a13a54 | ||
|
|
04a8299be6 | ||
|
|
b0252fb57a | ||
|
|
78091e2238 | ||
|
|
25afcc3491 | ||
|
|
6b4fd03bc6 | ||
|
|
095e6a4155 | ||
|
|
2cb11d44ec | ||
|
|
9499a3ed9f | ||
|
|
7632f90edb | ||
|
|
07e9d87e92 | ||
|
|
0cebb3e2a2 | ||
|
|
d2bcaa3b81 | ||
|
|
37ba14f164 | ||
|
|
9b88207100 | ||
|
|
9d6a83dce7 | ||
|
|
b489e2b8a8 | ||
|
|
1d18063497 | ||
|
|
7548c5afd1 | ||
|
|
27348d8f26 | ||
|
|
7ba6ae348b | ||
|
|
a025e476ea | ||
|
|
a44ffbf3dd | ||
|
|
846fdc0698 | ||
|
|
faace4f376 | ||
|
|
bde3eb8294 | ||
|
|
971fcc917a | ||
|
|
13d7d714d7 | ||
|
|
8a93627079 | ||
|
|
c24ff079d6 | ||
|
|
4f1a01b85b | ||
|
|
886abee4e2 | ||
|
|
ce8487e394 | ||
|
|
bf5555271d | ||
|
|
752fa1087d | ||
|
|
fe836b538d | ||
|
|
746e4c0ffa | ||
|
|
8fa29a9522 | ||
|
|
de8d724663 | ||
|
|
b401ba0354 | ||
|
|
dd700d956b | ||
|
|
3ca9982c73 | ||
|
|
6a1f61931d | ||
|
|
e1e8ff5916 | ||
|
|
3b08edda50 | ||
|
|
0d47429000 | ||
|
|
870dfec9df | ||
|
|
7e62ebd7b4 | ||
|
|
081d0f658d | ||
|
|
d708fc4734 | ||
|
|
4a4700191e | ||
|
|
8c88413ac1 | ||
|
|
30c4748fe2 | ||
|
|
68f775842d | ||
|
|
cf7fc949fe | ||
|
|
40e00a6234 | ||
|
|
c24e5710da | ||
|
|
43a525139b | ||
|
|
e0bdfdfe32 | ||
|
|
65937ed8c3 | ||
|
|
ec2c0c4b59 | ||
|
|
753a8b0bd1 | ||
|
|
a1f882ac52 | ||
|
|
8d99ce4207 | ||
|
|
e3e7d71474 | ||
|
|
285fb6b8e0 | ||
|
|
d2648b142a | ||
|
|
67e192242c | ||
|
|
9eefc1ae7c | ||
|
|
b9d0b35e83 | ||
|
|
85006d611f | ||
|
|
82412ef761 | ||
|
|
2e178e5a38 | ||
|
|
f47d134335 | ||
|
|
541c514aa9 | ||
|
|
85257b9d03 | ||
|
|
e7f902ca48 | ||
|
|
a1bfa608e2 | ||
|
|
e15c716906 | ||
|
|
4167982208 | ||
|
|
eefd94d13f | ||
|
|
667177db07 | ||
|
|
f301ad55aa | ||
|
|
fd9ecc246d | ||
|
|
b15552540c | ||
|
|
25b41b7eca | ||
|
|
b53f2a08d6 | ||
|
|
b140f04a9d | ||
|
|
f2b3e88d5a | ||
|
|
e001ecb1de | ||
|
|
44b3c3003d | ||
|
|
c7a3b26ec4 | ||
|
|
214b6bba7e | ||
|
|
9252284504 | ||
|
|
9665769cfc | ||
|
|
7d02d4d409 | ||
|
|
700d2b7335 | ||
|
|
fb5f59db23 | ||
|
|
90e8098bf4 | ||
|
|
d615d3d4ed | ||
|
|
8ba665213f | ||
|
|
036891d09a | ||
|
|
93a441494d | ||
|
|
77b72af1a2 | ||
|
|
4bfde9b61a | ||
|
|
f113e7071e | ||
|
|
ef9f7fcb84 | ||
|
|
5c5b05a74f | ||
|
|
fa94b14e91 | ||
|
|
cd3646ad67 | ||
|
|
2e8c2f881f | ||
|
|
b88cab07ff | ||
|
|
f406de14e8 | ||
|
|
a86970efe1 | ||
|
|
0af208d271 | ||
|
|
10b16a3bbf | ||
|
|
5c871242a7 | ||
|
|
2f9791f6c5 | ||
|
|
f41104520e | ||
|
|
d81994475e | ||
|
|
612b60766e | ||
|
|
d6eaf91225 | ||
|
|
cb95248ef5 | ||
|
|
ca0aed54b0 | ||
|
|
17bba27abf | ||
|
|
22f31d02f2 | ||
|
|
5e9a2e8c2e | ||
|
|
0757bfee0a | ||
|
|
1eff04a09a | ||
|
|
f43831407b | ||
|
|
17dd8e4ec6 | ||
|
|
0d57d69a99 | ||
|
|
d1c3ecc95d | ||
|
|
c92e9a5ca1 | ||
|
|
be78368eb0 | ||
|
|
7ee6fc3238 |
17
.github/workflows/ansible-test.yml
vendored
Normal file
17
.github/workflows/ansible-test.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: ansible-test sanity
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
ansible_test:
|
||||
name: Verify ansible-test sanity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install virtualenv using pip
|
||||
run: pip install virtualenv
|
||||
- name: Run ansible-test
|
||||
run: bash tests/sanity/sanity.sh
|
||||
28
.github/workflows/docs.yml
vendored
28
.github/workflows/docs.yml
vendored
@@ -12,9 +12,11 @@ jobs:
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Run ansible-doc-test
|
||||
- name: Install Ansible 2.9
|
||||
run: |
|
||||
python -m pip install "ansible < 2.10"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_2_11:
|
||||
@@ -25,9 +27,27 @@ jobs:
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Run ansible-doc-test
|
||||
- name: Install Ansible 2.11
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.11,<2.12"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_2_12:
|
||||
name: Check Ansible Documentation with ansible-core 2.12.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.12
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.12,<2.13"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.12,<2.13"
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_latest:
|
||||
@@ -38,7 +58,9 @@ jobs:
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Run ansible-doc-test
|
||||
- name: Install Ansible-latest
|
||||
run: |
|
||||
python -m pip install ansible
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
22
.github/workflows/lint.yml
vendored
22
.github/workflows/lint.yml
vendored
@@ -13,15 +13,9 @@ jobs:
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Run ansible-lint
|
||||
uses: ansible/ansible-lint-action@master
|
||||
with:
|
||||
targets: |
|
||||
tests/*.yml
|
||||
tests/*/*.yml
|
||||
tests/*/*/*.yml
|
||||
playbooks/*.yml
|
||||
playbooks/*/*.yml
|
||||
roles/*/*/*.yml
|
||||
run: |
|
||||
pip install ansible-core==2.11.6 ansible-lint
|
||||
find playbooks roles tests -name '*.yml' ! -name "env_*" ! -name "tasks_*" -exec ansible-lint --force-color {} \+
|
||||
env:
|
||||
ANSIBLE_MODULE_UTILS: plugins/module_utils
|
||||
ANSIBLE_LIBRARY: plugins/modules
|
||||
@@ -75,4 +69,12 @@ jobs:
|
||||
- name: Run pylint
|
||||
run: |
|
||||
pip install pylint==2.10.2
|
||||
pylint plugins --disable=import-error
|
||||
pylint plugins roles --disable=import-error
|
||||
|
||||
shellcheck:
|
||||
name: Shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@1.1.0
|
||||
|
||||
16
.github/workflows/readme.yml
vendored
Normal file
16
.github/workflows/readme.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: readme test
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
ansible_test:
|
||||
name: Verify readme
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run readme test
|
||||
run: |
|
||||
error=0
|
||||
for i in roles/ipa*/README.md README-*.md; do grep -q $i README.md && echo "OK: $i" || { echo -e "\033[31;1mERROR: ${i} missing\033[0m"; error=1; } done
|
||||
exit $error
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/ansible/ansible-lint.git
|
||||
rev: v5.1.2
|
||||
rev: v5.3.2
|
||||
hooks:
|
||||
- id: ansible-lint
|
||||
always_run: false
|
||||
@@ -38,3 +38,10 @@ repos:
|
||||
entry: utils/ansible-doc-test
|
||||
# args: ['-v', 'roles', 'plugins']
|
||||
files: ^.*.py$
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
name: ShellCheck
|
||||
language: system
|
||||
entry: shellcheck
|
||||
files: \.sh$
|
||||
|
||||
@@ -104,20 +104,164 @@ Example playbook to add an inclusive condition to an existing rule
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "My domain hosts"
|
||||
description: "my automember condition"
|
||||
automember_tye: hostgroup
|
||||
automember_type: hostgroup
|
||||
action: member
|
||||
inclusive:
|
||||
- key: fqdn
|
||||
expression: ".*.mydomain.com"
|
||||
```
|
||||
|
||||
Example playbook to ensure group membership for all users has been rebuilt
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure group membership for all users has been rebuilt
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: rebuilt
|
||||
```
|
||||
|
||||
Example playbook to ensure group membership for given users has been rebuilt
|
||||
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure group membership for given users has been rebuilt
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
users:
|
||||
- user1
|
||||
- user2
|
||||
state: rebuilt
|
||||
```
|
||||
|
||||
Example playbook to ensure hostgroup membership for all hosts has been rebuilt
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure hostgroup membership for all hosts has been rebuilt
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: rebuilt
|
||||
```
|
||||
|
||||
Example playbook to ensure hostgroup membership for given hosts has been rebuilt
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure hostgroup membership for given hosts has been rebuilt
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- host1.mydomain.com
|
||||
- host2.mydomain.com
|
||||
state: rebuilt
|
||||
```
|
||||
|
||||
Example playbook to ensure default group fallback_group for all unmatched group entries is set
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure default group fallback_group for all unmatched group entries is set
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: fallback_group
|
||||
```
|
||||
|
||||
Example playbook to ensure default group for all unmatched group entries is not set
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure default group for all unmatched group entries is not set
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
default_group: ""
|
||||
automember_type: group
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: fallback_hostgroup
|
||||
```
|
||||
|
||||
Example playbook to ensure default hostgroup for all unmatched group entries is not set
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure default hostgroup for all unmatched group entries is not set
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: ""
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to ensure all orphan automember group rules are removed:
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure all orphan automember group rules are removed
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: orphans_removed
|
||||
```
|
||||
|
||||
Example playbook to ensure all orphan automember hostgroup rules are removed:
|
||||
|
||||
```yaml
|
||||
- name: Playbook to ensure all orphan automember hostgroup rules are removed
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: orphans_removed
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaautomember
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
@@ -129,11 +273,16 @@ Variable | Description | Required
|
||||
`automember_type` | Grouping to which the rule applies. It can be one of `group`, `hostgroup`. | yes
|
||||
`inclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': inclusive_regex}` | no
|
||||
`exclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': exclusive_regex}` | no
|
||||
`users` | Users to rebuild membership for. | no
|
||||
`hosts` | Hosts to rebuild membership for. | no
|
||||
`no_wait` | Don't wait for rebuilding membership. | no
|
||||
`default_group` | Default (fallback) group for all unmatched entries. Use the empty string "" for ensuring the default group is not set. | no
|
||||
`action` | Work on automember or member level. It can be one of `member` or `automember` and defaults to `automember`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, 'rebuilt'. 'orphans_removed' default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Mark Hahl
|
||||
Thomas Woerner
|
||||
|
||||
112
README-automountkey.md
Normal file
112
README-automountkey.md
Normal file
@@ -0,0 +1,112 @@
|
||||
Automountkey module
|
||||
=====================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The automountkey module allows management of keys within an automount map.
|
||||
|
||||
It is desgined to follow the IPA api as closely as possible while ensuring ease of use.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* Automount key management
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaautomountkey module.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to ensure presence of an automount key:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage automount key
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: ensure automount key TestKey is present
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
info: 192.168.122.1:/exports
|
||||
state: present
|
||||
```
|
||||
|
||||
Example playbook to rename an automount map:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to add an automount map
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: ensure aumount key TestKey is renamed to NewKeyName
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: password01
|
||||
automountlocationcn: TestLocation
|
||||
automountmapname: TestMap
|
||||
automountkey: TestKey
|
||||
newname: NewKeyName
|
||||
state: renamed
|
||||
```
|
||||
|
||||
Example playbook to ensure an automount key is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage an automount key
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: ensure automount key TestKey is absent
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`location` \| `automountlocationcn` \| `automountlocation` | Location name. | yes
|
||||
`mapname` \| `map` \| `automountmapname` \| `automountmap` | Map the key belongs to | yes
|
||||
`key` \| `name` \| `automountkey` | Automount key to manage | yes
|
||||
`rename` \| `new_name` \| `newautomountkey` | the name to change the key to if state is `renamed` | yes when state is `renamed`
|
||||
`info` \| `information` \| `automountinformation` | Mount information for the key | yes when state is `present`
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | no
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Chris Procter
|
||||
@@ -97,9 +97,6 @@ Example playbook to ensure absence of an automount location:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipaautomountlocation
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
96
README-automountmap.md
Normal file
96
README-automountmap.md
Normal file
@@ -0,0 +1,96 @@
|
||||
Automountmap module
|
||||
=====================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The automountmap module allows the addition and removal of maps within automount locations.
|
||||
|
||||
It is desgined to follow the IPA api as closely as possible while ensuring ease of use.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* Automount map management
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaautomountmap module.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
Example playbook to ensure presence of an automount map:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to add an automount map
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: ensure map named auto.DMZ in location DMZ is created
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.DMZ
|
||||
location: DMZ
|
||||
desc: "this is a map for servers in the DMZ"
|
||||
```
|
||||
|
||||
Example playbook to ensure auto.DMZi is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to remove an automount map
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: ensure map auto.DMZ has been removed
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.DMZ
|
||||
location: DMZ
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `mapname` \| `map` \| `automountmapname` | Name of the map to manage | yes
|
||||
`location` \| `automountlocation` \| `automountlocationcn` | Location name. | yes
|
||||
`desc` \| `description` | Description of the map | yes
|
||||
`state` | The state to ensure. It can be one of `present`, or `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Creation of indirect mount points are not supported.
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Chris Procter
|
||||
@@ -56,12 +56,12 @@ Example playbook to read config options:
|
||||
register: result
|
||||
- name: display default login shell
|
||||
debug:
|
||||
msg: '{{ result.config.defaultlogin }}'
|
||||
msg: '{{ result.config.defaultshell }}'
|
||||
|
||||
- name: ensure defaultloginshell and maxusernamelength are set as required
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
defaultlogin: /bin/bash
|
||||
defaultshell: /bin/bash
|
||||
maxusername: 64
|
||||
```
|
||||
|
||||
@@ -82,9 +82,6 @@ Example playbook to read config options:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipauser
|
||||
-------
|
||||
|
||||
**General Variables:**
|
||||
|
||||
Variable | Description | Required
|
||||
|
||||
@@ -135,9 +135,6 @@ Example playbook to make sure delegation "basic manager attributes" is absent:
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipadelegation
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -71,6 +71,7 @@ Example playbook to ensure a global forwarder, with a custom port, is absent:
|
||||
forwarders:
|
||||
- ip_address: 2001:4860:4860::8888
|
||||
port: 53
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
@@ -119,9 +120,6 @@ Example playbook to disallow synchronization of forward (A, AAAA) and reverse (P
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipadnsconfig
|
||||
------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
@@ -133,7 +131,8 @@ Variable | Description | Required
|
||||
| `port` - The custom port that should be used on this server. | no
|
||||
`forward_policy` | The global forwarding policy. It can be one of `only`, `first`, or `none`. | no
|
||||
`allow_sync_ptr` | Allow synchronization of forward (A, AAAA) and reverse (PTR) records (bool). | yes
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
||||
`action` | Work on dnsconfig or member level. It can be one of `member` or `dnsconfig` and defaults to `dnsconfig`. Only `forwarders` can be managed with `action: member`. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. `absent` can only be used with `action: member` and `forwarders`. | yes
|
||||
|
||||
|
||||
Authors
|
||||
|
||||
@@ -100,9 +100,6 @@ Example playbook to ensure presence of a forwardzone to ipa DNS:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipagroup
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -242,9 +242,6 @@ Example playbook to ensure multiple DNS records are absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipadnsrecord
|
||||
------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -195,9 +195,6 @@ Example playbook to create a zone for reverse DNS lookup, from an IP address, gi
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipadnszone
|
||||
----------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
@@ -233,9 +230,6 @@ Variable | Description | Required
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipadnszone
|
||||
----------
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`dnszone` | DNS Zone dict with zone name infered from `name_from_ip`. <br>Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created.
|
||||
|
||||
@@ -147,9 +147,6 @@ Example playbook to remove groups:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipagroup
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -129,9 +129,6 @@ Example playbook to make sure HBAC Rule login is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipahbacrule
|
||||
---------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -91,9 +91,6 @@ Example playbook to make sure HBAC Services for http and tftp are absent
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipahbacsvc
|
||||
----------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -129,9 +129,6 @@ Example playbook to make sure HBAC Service Group login is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipahbacsvcgroup
|
||||
---------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -313,9 +313,6 @@ Example playbook to ensure a host is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipahost
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
@@ -370,9 +367,6 @@ Variable | Description | Required
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipahost
|
||||
-------
|
||||
|
||||
There are only return values if one or more random passwords have been generated.
|
||||
|
||||
Variable | Description | Returned When
|
||||
|
||||
@@ -143,9 +143,6 @@ Example playbook to make sure host-group databases is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipahostgroup
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -74,9 +74,6 @@ Example playbook to make sure location "my_location1" is absent:
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipalocation
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -154,9 +154,6 @@ Example playbook to make sure permission "MyPermission" is renamed to "MyNewPerm
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipapermission
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -126,9 +126,6 @@ Example playbook to make sure privilege "DNS Special Privilege" is absent:
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaprivilege
|
||||
------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin`. | no
|
||||
|
||||
@@ -81,7 +81,7 @@ Example playbook to ensure maxlife is set to 49 in global policy:
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Ensure absence of pwpolicies for group ops
|
||||
# Ensure maxlife is set to 49 in global policy
|
||||
- ipapwpolicy:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxlife: 49
|
||||
@@ -91,9 +91,6 @@ Example playbook to ensure maxlife is set to 49 in global policy:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipapwpolicy
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -238,9 +238,6 @@ Example playbook to ensure that different members are not associated with a role
|
||||
Variables
|
||||
---------
|
||||
|
||||
iparole
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -131,9 +131,6 @@ Example playbook to make sure selfservice "Users can manage their own name detai
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaselfservice
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -242,9 +242,6 @@ This task will always report a change.
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaserver
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -285,8 +285,6 @@ Example playbook to allow users, groups, hosts or hostgroups to retrieve a keyta
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaservice
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -76,9 +76,6 @@ Example playbook to make sure sudocmd is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipasudocmd
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -116,9 +116,6 @@ Example playbook to make sure sudocmdgroup is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipasudocmdgroup
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -113,9 +113,6 @@ Example playbook to make sure Sudo Rule is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipasudorule
|
||||
---------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -94,9 +94,6 @@ This will only delete the ipa-side of the trust and it does NOT delete the id-ra
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipatrust
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
|
||||
@@ -356,9 +356,6 @@ Example playbook to ensure users are absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipauser
|
||||
-------
|
||||
|
||||
**General Variables:**
|
||||
|
||||
Variable | Description | Required
|
||||
@@ -432,9 +429,6 @@ Variable | Description | Required
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipauser
|
||||
-------
|
||||
|
||||
There are only return values if one or more random passwords have been generated.
|
||||
|
||||
Variable | Description | Returned When
|
||||
|
||||
@@ -210,9 +210,6 @@ Example playbook to make sure vault is absent:
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipavault
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
@@ -250,9 +247,6 @@ Variable | Description | Required
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipavault
|
||||
--------
|
||||
|
||||
There is only a return value if `state` is `retrieved`.
|
||||
|
||||
Variable | Description | Returned When
|
||||
|
||||
20
README.md
20
README.md
@@ -13,6 +13,9 @@ Features
|
||||
* Repair mode for clients
|
||||
* Backup and restore, also to and from controller
|
||||
* Modules for automembership rule management
|
||||
* Modules for automount key management
|
||||
* Modules for automount location management
|
||||
* Modules for automount map management
|
||||
* Modules for config management
|
||||
* Modules for delegation management
|
||||
* Modules for dns config management
|
||||
@@ -62,7 +65,6 @@ Requirements
|
||||
**Controller**
|
||||
* Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection)
|
||||
* /usr/bin/kinit is required on the controller if a one time password (OTP) is used
|
||||
* python3-gssapi is required on the controller if a one time password (OTP) is used with keytab to install the client.
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
@@ -282,7 +284,8 @@ ipaserver_domain=test.local
|
||||
ipaserver_realm=TEST.LOCAL
|
||||
```
|
||||
|
||||
For enhanced security it is possible to use a auto-generated one-time-password (OTP). This will be generated on the controller using the (first) server. It is needed to have the python-gssapi bindings installed on the controller for this.
|
||||
For enhanced security it is possible to use a auto-generated one-time-password (OTP). This will be generated on the controller using the (first) server.
|
||||
|
||||
To enable the generation of the one-time-password:
|
||||
```yaml
|
||||
[ipaclients:vars]
|
||||
@@ -424,6 +427,9 @@ Modules in plugin/modules
|
||||
=========================
|
||||
|
||||
* [ipaautomember](README-automember.md)
|
||||
* [ipaautomountkey](README-automountkey.md)
|
||||
* [ipaautomountlocation](README-automountlocation.md)
|
||||
* [ipaautomountmap](README-automountmap.md)
|
||||
* [ipaconfig](README-config.md)
|
||||
* [ipadelegation](README-delegation.md)
|
||||
* [ipadnsconfig](README-dnsconfig.md)
|
||||
@@ -433,15 +439,15 @@ Modules in plugin/modules
|
||||
* [ipagroup](README-group.md)
|
||||
* [ipahbacrule](README-hbacrule.md)
|
||||
* [ipahbacsvc](README-hbacsvc.md)
|
||||
* [ipahbacsvcgroup](README-hbacsvc.md)
|
||||
* [ipahbacsvcgroup](README-hbacsvcgroup.md)
|
||||
* [ipahost](README-host.md)
|
||||
* [ipahostgroup](README-hostgroup.md)
|
||||
* [ipalocation](README-ipalocation.md)
|
||||
* [ipapermission](README-ipapermission.md)
|
||||
* [ipaprivilege](README-ipaprivilege.md)
|
||||
* [ipalocation](README-location.md)
|
||||
* [ipapermission](README-permission.md)
|
||||
* [ipaprivilege](README-privilege.md)
|
||||
* [ipapwpolicy](README-pwpolicy.md)
|
||||
* [iparole](README-role.md)
|
||||
* [ipaselfservice](README-ipaselfservice.md)
|
||||
* [ipaselfservice](README-selfservice.md)
|
||||
* [ipaserver](README-server.md)
|
||||
* [ipaservice](README-service.md)
|
||||
* [ipasudocmd](README-sudocmd.md)
|
||||
|
||||
@@ -16,6 +16,7 @@ readme: "README.md"
|
||||
license: "GPL-3.0-or-later"
|
||||
|
||||
tags:
|
||||
- "linux"
|
||||
- "system"
|
||||
- "identity"
|
||||
- "ipa"
|
||||
|
||||
30
molecule/centos-9-build/Dockerfile
Normal file
30
molecule/centos-9-build/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
FROM quay.io/centos/centos:stream9
|
||||
ENV container=docker
|
||||
|
||||
RUN rm -fv /var/cache/dnf/metadata_lock.pid; \
|
||||
dnf makecache; \
|
||||
dnf --assumeyes install \
|
||||
/usr/bin/python3 \
|
||||
/usr/bin/python3-config \
|
||||
/usr/bin/dnf-3 \
|
||||
sudo \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
iproute && \
|
||||
dnf clean all; \
|
||||
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
|
||||
rm -f /lib/systemd/system/multi-user.target.wants/*;\
|
||||
rm -f /etc/systemd/system/*.wants/*;\
|
||||
rm -f /lib/systemd/system/local-fs.target.wants/*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
|
||||
rm -f /lib/systemd/system/basic.target.wants/*;\
|
||||
rm -f /lib/systemd/system/anaconda.target.wants/*; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
VOLUME ["/sys/fs/cgroup"]
|
||||
|
||||
CMD ["/usr/sbin/init"]
|
||||
18
molecule/centos-9-build/molecule.yml
Normal file
18
molecule/centos-9-build/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-9-build
|
||||
image: "quay.io/centos/centos:stream9"
|
||||
dockerfile: Dockerfile
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare-build.yml
|
||||
18
molecule/centos-9/molecule.yml
Normal file
18
molecule/centos-9/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-9
|
||||
image: quay.io/ansible-freeipa/upstream-tests:centos-9
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 127.0.0.1
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare.yml
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# IPA depends on IPv6 and without it dirsrv service won't start.
|
||||
- name: Ensure IPv6 is ENABLED
|
||||
sysctl:
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.name }}"
|
||||
value: "{{ item.value }}"
|
||||
sysctl_set: yes
|
||||
@@ -19,14 +19,14 @@
|
||||
# This is needed in some IPA versions in order to get KRA enabled.
|
||||
# See https://pagure.io/freeipa/issue/7906 for more information.
|
||||
- name: stat protected_regular
|
||||
stat:
|
||||
ansible.builtin.stat:
|
||||
path: /proc/sys/fs/protected_regular
|
||||
register: result
|
||||
|
||||
- name: Ensure fs.protected_regular is disabled
|
||||
sysctl:
|
||||
ansible.posix.sysctl:
|
||||
name: fs.protected_regular
|
||||
value: '0'
|
||||
value: 0
|
||||
sysctl_set: yes
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# To avoid this problem we create the directories before starting IPA.
|
||||
- name: Ensure lock dirs for DS exists
|
||||
file:
|
||||
ansible.builtin.file:
|
||||
state: directory
|
||||
owner: dirsrv
|
||||
group: dirsrv
|
||||
@@ -22,6 +22,27 @@
|
||||
- /var/lock/dirsrv/slapd-TEST-LOCAL/
|
||||
|
||||
- name: Ensure IPA server is up an running
|
||||
service:
|
||||
ansible.builtin.service:
|
||||
name: ipa
|
||||
state: started
|
||||
|
||||
- name: Wait for krb5dkc to be running
|
||||
ansible.builtin.service_facts:
|
||||
no_log: True
|
||||
register: result
|
||||
until: "'krb5kdc.service' in result.ansible_facts.services and \
|
||||
result.ansible_facts.services['krb5kdc.service'].state == 'running'"
|
||||
retries: 30
|
||||
delay: 5
|
||||
|
||||
- name: Check if TGT is available for admin.
|
||||
ansible.builtin.shell:
|
||||
cmd: echo SomeADMINpassword | kinit -c ansible_freeipa_cache admin
|
||||
register: result
|
||||
until: not result.failed
|
||||
retries: 30
|
||||
delay: 5
|
||||
|
||||
- name: Cleanup TGT.
|
||||
ansible.builtin.shell:
|
||||
cmd: kdestroy -c ansible_freeipa_cache -A
|
||||
|
||||
10
playbooks/automember/automember-default-group-not-set.yml
Normal file
10
playbooks/automember/automember-default-group-not-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default group not set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default group is not set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: ""
|
||||
10
playbooks/automember/automember-default-group-set.yml
Normal file
10
playbooks/automember/automember-default-group-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default group set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default group is set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: fallback_group
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default hostgroup not set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default hostgroup is not set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: ""
|
||||
10
playbooks/automember/automember-default-hostgroup-set.yml
Normal file
10
playbooks/automember/automember-default-hostgroup-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default hostgroup set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default hostgroup is set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: fallback_hostgroup
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember group membership for all users rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure group automember rule admins is present
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automember group membership for given users rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure group membership for given users has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
users:
|
||||
- user1
|
||||
- user2
|
||||
state: rebuilt
|
||||
10
playbooks/automember/automember-group-orphans-removed.yml
Normal file
10
playbooks/automember/automember-group-orphans-removed.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember orphan group rules are removed example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure orphan group rules are removed
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: orphans_removed
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember hostgroup membership for all hosts rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure hostgroup membership for all hosts has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automember hostgroup membership for given hosts rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure hostgroup membership for given hosts has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- host1.mydomain.com
|
||||
- host2.mydomain.com
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember orphan hostgroup rules are removed example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure orphan hostgroup rules are removed
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: orphans_removed
|
||||
BIN
playbooks/automount/.automount-map-present.yml.swp
Normal file
BIN
playbooks/automount/.automount-map-present.yml.swp
Normal file
Binary file not shown.
12
playbooks/automount/automount-map-absent.yaml
Normal file
12
playbooks/automount/automount-map-absent.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automount map absent example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: ensure map TestMap is absent
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: TestMap
|
||||
location: TestLocation
|
||||
state: absent
|
||||
12
playbooks/automount/automount-map-present.yaml
Normal file
12
playbooks/automount/automount-map-present.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automount map present example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: ensure map TestMap is present
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: TestMap
|
||||
location: TestLocation
|
||||
desc: "this is a test map"
|
||||
13
playbooks/automount/automountkey-present.yml
Normal file
13
playbooks/automount/automountkey-present.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to manage an automout key
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: Ensure autmount key is present
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
info: 192.168.122.1:/exports
|
||||
state: present
|
||||
13
playbooks/automount/automountkey-renamed.yml
Normal file
13
playbooks/automount/automountkey-renamed.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to manage an automount key
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: Ensure aumount key TestKey is renamed to NewKeyName
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: password01
|
||||
automountlocationcn: TestLocation
|
||||
automountmapname: TestMap
|
||||
automountkey: TestKey
|
||||
newname: NewKeyName
|
||||
state: renamed
|
||||
12
playbooks/automount/automoutkey-absent.yml
Normal file
12
playbooks/automount/automoutkey-absent.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Playbook to manage an automount key
|
||||
hosts: ipaserver
|
||||
|
||||
tasks:
|
||||
- name: Ensure autmount key is present
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
state: absent
|
||||
@@ -4,10 +4,11 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Set dnsconfig.
|
||||
- name: Set dnsconfig forwarders.
|
||||
ipadnsconfig:
|
||||
forwarders:
|
||||
- ip_address: 8.8.4.4
|
||||
- ip_address: 2001:4860:4860::8888
|
||||
port: 53
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
13
playbooks/dnsconfig/forwarders-present.yml
Normal file
13
playbooks/dnsconfig/forwarders-present.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to handle global DNS configuration
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Set dnsconfig forwarders.
|
||||
ipadnsconfig:
|
||||
forwarders:
|
||||
- ip_address: 8.8.4.4
|
||||
- ip_address: 2001:4860:4860::8888
|
||||
port: 53
|
||||
action: member
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object): # pylint: disable=R0205,R0903
|
||||
DOCUMENTATION = r"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -22,6 +21,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
__all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
|
||||
"DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT",
|
||||
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
|
||||
@@ -100,7 +103,7 @@ else:
|
||||
|
||||
import socket
|
||||
import base64
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
|
||||
try:
|
||||
from collections.abc import Mapping # noqa
|
||||
@@ -397,6 +400,14 @@ else:
|
||||
def module_params_get(module, name):
|
||||
return _afm_convert(module.params.get(name))
|
||||
|
||||
def module_params_get_lowercase(module, name):
|
||||
value = _afm_convert(module.params.get(name))
|
||||
if isinstance(value, list):
|
||||
value = [v.lower() for v in value]
|
||||
if isinstance(value, (str, unicode)):
|
||||
value = value.lower()
|
||||
return value
|
||||
|
||||
def api_get_domain():
|
||||
return api.env.domain
|
||||
|
||||
@@ -408,6 +419,9 @@ else:
|
||||
def api_get_realm():
|
||||
return api.env.realm
|
||||
|
||||
def api_get_basedn():
|
||||
return api.env.basedn
|
||||
|
||||
def gen_add_del_lists(user_list, res_list):
|
||||
"""
|
||||
Generate the lists for the addition and removal of members.
|
||||
@@ -555,10 +569,76 @@ else:
|
||||
print(jsonify(kwargs))
|
||||
sys.exit(0)
|
||||
|
||||
class AnsibleFreeIPAParams(Mapping):
|
||||
def __init__(self, ansible_module):
|
||||
class IPAParamMapping(Mapping):
|
||||
"""
|
||||
Provides IPA API mapping to playbook parameters or computed values.
|
||||
|
||||
It can be used to define a mapping of playbook parameters
|
||||
or methods that provide computed values to IPA API arguments.
|
||||
|
||||
Playbook parameters can be retrieved as properties,
|
||||
and the set of IPA arguments for a command can be
|
||||
retrived with ``get_ipa_command_args()``. The keys for
|
||||
``param_mapping`` are also the keys of the argument set.
|
||||
|
||||
The values of ``param_mapping`` can be either:
|
||||
* a str representing a key of ``AnsibleModule.params``.
|
||||
* a callable.
|
||||
|
||||
In case of an ``AnsibleModule.param`` the value of the playbook
|
||||
param will be used for that argument. If it is a ``callable``,
|
||||
the value returned by the execution of it will be used.
|
||||
|
||||
Example:
|
||||
-------
|
||||
def check_params(ipa_params):
|
||||
# Module parameters can be accessed as properties.
|
||||
if len(ipa_params.name) == 0:
|
||||
ipa_params.ansible_module.fail_json(msg="No given name.")
|
||||
|
||||
|
||||
def define_ipa_commands(self):
|
||||
# Create the argument dict from the defined mapping.
|
||||
args = self.get_ipa_command_args()
|
||||
|
||||
_commands = [("obj-name", "some_ipa_command", args)]
|
||||
return _commands
|
||||
|
||||
|
||||
def a_method_for_a_computed_param():
|
||||
return "Some computed value"
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = SomeIPAModule(argument_spec=dict(
|
||||
name=dict(type="list", aliases=["cn"], required=True),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
module_param=(type="str", required=False),
|
||||
)
|
||||
)
|
||||
|
||||
# Define the playbook to IPA API mapping
|
||||
ipa_param_mapping = {
|
||||
"arg_to_be_passed_to_ipa_command": "module_param",
|
||||
"another_arg": a_method_for_a_computed_param,
|
||||
}
|
||||
ipa_params = IPAParamMapping(
|
||||
ansible_module,
|
||||
param_mapping=ipa_param_mapping
|
||||
)
|
||||
|
||||
check_params(ipa_params)
|
||||
comands = define_ipa_commands(ipa_params)
|
||||
|
||||
ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ansible_module, param_mapping=None):
|
||||
self.mapping = ansible_module.params
|
||||
self.ansible_module = ansible_module
|
||||
self.param_mapping = param_mapping or {}
|
||||
|
||||
def __getitem__(self, key):
|
||||
param = self.mapping[key]
|
||||
@@ -579,6 +659,36 @@ else:
|
||||
def __getattr__(self, name):
|
||||
return self.get(name)
|
||||
|
||||
def get_ipa_command_args(self, **kwargs):
|
||||
"""Return a dict to be passed to an IPA command."""
|
||||
args = {}
|
||||
for ipa_param_name, param_name in self.param_mapping.items():
|
||||
|
||||
# Check if param_name is actually a param
|
||||
if param_name in self.ansible_module.params:
|
||||
value = self.ansible_module.params_get(param_name)
|
||||
if isinstance(value, bool):
|
||||
value = "TRUE" if value else "FALSE"
|
||||
|
||||
# Since param wasn't a param check if it's a method name
|
||||
elif callable(param_name):
|
||||
value = param_name(**kwargs)
|
||||
|
||||
# We don't have a way to guess the value so fail.
|
||||
else:
|
||||
self.ansible_module.fail_json(
|
||||
msg=(
|
||||
"Couldn't get a value for '%s'. Option '%s' is "
|
||||
"not a module argument neither a defined method."
|
||||
)
|
||||
% (ipa_param_name, param_name)
|
||||
)
|
||||
|
||||
if value is not None:
|
||||
args[ipa_param_name] = value
|
||||
|
||||
return args
|
||||
|
||||
class IPAAnsibleModule(AnsibleModule):
|
||||
"""
|
||||
IPA Ansible Module.
|
||||
@@ -699,6 +809,18 @@ else:
|
||||
"""
|
||||
return module_params_get(self, name)
|
||||
|
||||
def params_get_lowercase(self, name):
|
||||
"""
|
||||
Retrieve value set for module parameter as lowercase, if not None.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: string
|
||||
The name of the parameter to retrieve.
|
||||
|
||||
"""
|
||||
return module_params_get_lowercase(self, name)
|
||||
|
||||
def params_fail_used_invalid(self, invalid_params, state, action=None):
|
||||
"""
|
||||
Fail module execution if one of the invalid parameters is not None.
|
||||
@@ -753,16 +875,22 @@ else:
|
||||
"""
|
||||
return api_command_no_name(self, command, args)
|
||||
|
||||
@staticmethod
|
||||
def ipa_get_domain():
|
||||
def ipa_get_domain(self):
|
||||
"""Retrieve IPA API domain."""
|
||||
return api_get_domain()
|
||||
if not hasattr(self, "__ipa_api_domain"):
|
||||
setattr(self, "__ipa_api_domain", api_get_domain())
|
||||
return getattr(self, "__ipa_api_domain")
|
||||
|
||||
@staticmethod
|
||||
def ipa_get_realm():
|
||||
"""Retrieve IPA API realm."""
|
||||
return api_get_realm()
|
||||
|
||||
@staticmethod
|
||||
def ipa_get_basedn():
|
||||
"""Retrieve IPA API basedn."""
|
||||
return api_get_basedn()
|
||||
|
||||
@staticmethod
|
||||
def ipa_command_exists(command):
|
||||
"""
|
||||
@@ -1011,6 +1139,11 @@ else:
|
||||
# pylint: disable=super-with-arguments
|
||||
super(FreeIPABaseModule, self).__init__(*args, **kwargs)
|
||||
|
||||
self.deprecate(
|
||||
msg="FreeIPABaseModule is deprecated. Use IPAAnsibleModule.",
|
||||
version="1.5.0"
|
||||
)
|
||||
|
||||
# Status of an execution. Will be changed to True
|
||||
# if something is actually peformed.
|
||||
self.changed = False
|
||||
@@ -1026,11 +1159,6 @@ else:
|
||||
# Module exit arguments.
|
||||
self.exit_args = {}
|
||||
|
||||
# Wrapper around the AnsibleModule.params.
|
||||
# Return the actual params but performing transformations
|
||||
# when needed.
|
||||
self.ipa_params = AnsibleFreeIPAParams(self)
|
||||
|
||||
def get_ipa_command_args(self, **kwargs):
|
||||
"""
|
||||
Return a dict to be passed to an IPA command.
|
||||
@@ -1051,97 +1179,77 @@ else:
|
||||
server).
|
||||
|
||||
"""
|
||||
args = {}
|
||||
for ipa_param_name, param_name in self.ipa_param_mapping.items():
|
||||
|
||||
# Check if param_name is actually a param
|
||||
if param_name in self.ipa_params:
|
||||
value = self.ipa_params.get(param_name)
|
||||
if isinstance(value, bool):
|
||||
value = "TRUE" if value else "FALSE"
|
||||
|
||||
# Since param wasn't a param check if it's a method name
|
||||
elif hasattr(self, param_name):
|
||||
method = getattr(self, param_name)
|
||||
if callable(method):
|
||||
value = method(**kwargs)
|
||||
|
||||
# We don't have a way to guess the value so fail.
|
||||
else:
|
||||
self.fail_json(
|
||||
msg=(
|
||||
"Couldn't get a value for '%s'. Option '%s' is "
|
||||
"not a module argument neither a defined method."
|
||||
)
|
||||
% (ipa_param_name, param_name)
|
||||
)
|
||||
|
||||
if value is not None:
|
||||
args[ipa_param_name] = value
|
||||
|
||||
return args
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
|
||||
"Use 'AnsibleFreeIPAParams.get_ipa_command_args()', "
|
||||
"Instantiate it using the class 'ipa_params_mapping'."
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
mapping = IPAParamMapping(self, self.ipa_param_mapping)
|
||||
return mapping.get_ipa_command_args(**kwargs)
|
||||
|
||||
def check_ipa_params(self):
|
||||
"""Validate ipa_params before command is called."""
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def define_ipa_commands(self):
|
||||
"""Define commands that will be run in IPA server."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_command_errors(self, command, result):
|
||||
"""Look for erros into command results."""
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
errors = []
|
||||
for item in result.get("failed", tuple()):
|
||||
failed_item = result["failed"][item]
|
||||
for member_type in failed_item:
|
||||
for member, failure in failed_item[member_type]:
|
||||
if (
|
||||
"already a member" in failure
|
||||
or "not a member" in failure
|
||||
):
|
||||
continue
|
||||
errors.append(
|
||||
"%s: %s %s: %s"
|
||||
% (command, member_type, member, failure)
|
||||
)
|
||||
|
||||
if len(errors) > 0:
|
||||
self.fail_json(", ".join("errors")) # pylint: disable=E1121
|
||||
|
||||
def add_ipa_command(self, command, name=None, args=None):
|
||||
"""Add a command to the list of commands to be executed."""
|
||||
self.ipa_commands.append((name, command, args or {}))
|
||||
|
||||
def _run_ipa_commands(self):
|
||||
"""Execute commands in self.ipa_commands."""
|
||||
if self.check_mode:
|
||||
self.changed = len(self.ipa_commands) > 0
|
||||
return
|
||||
self.changed = self.execute_ipa_commands(
|
||||
self.ipa_commands,
|
||||
result_handler=self.process_results.__func__,
|
||||
exit_args=self.exit_args
|
||||
)
|
||||
|
||||
result = None
|
||||
def process_results(
|
||||
self, result, command, name, args, exit_args
|
||||
): # pylint: disable=unused-argument
|
||||
"""
|
||||
Process an API command result.
|
||||
|
||||
for name, command, args in self.ipa_commands:
|
||||
try:
|
||||
result = self.ipa_command(command, name, args)
|
||||
except Exception as excpt:
|
||||
self.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(excpt)))
|
||||
else:
|
||||
self.process_command_result(name, command, args, result)
|
||||
self.get_command_errors(command, result)
|
||||
This method must be overriden in subclasses if 'exit_args'
|
||||
is to be modified.
|
||||
"""
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
self.process_command_result(name, command, args, result)
|
||||
|
||||
def process_command_result(self, _name, _command, _args, result):
|
||||
"""
|
||||
Process an API command result.
|
||||
|
||||
This method can be overriden in subclasses, and
|
||||
change self.exit_values
|
||||
to return data in the result for the controller.
|
||||
change self.exit_values to return data in the
|
||||
result for the controller.
|
||||
"""
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
|
||||
"To aid in porting to IPAAnsibleModule, change to "
|
||||
"'FreeIPABaseModule.process_results'."
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
self.changed = True
|
||||
@@ -1155,12 +1263,26 @@ else:
|
||||
Returns True in case current IPA object attributes differ from
|
||||
args passed to the module.
|
||||
"""
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
|
||||
"FreeIPABaseModule require_ipa_attrs_change() is "
|
||||
"deprecated. Use ansible_freeipa_module.compare_args()."
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
equal = compare_args_ipa(self, command_args, ipa_attrs)
|
||||
return not equal
|
||||
|
||||
def ipa_run(self):
|
||||
"""Execute module actions."""
|
||||
ipaapi_context = self.ipa_params.get("ipaapi_context")
|
||||
self.deprecate(
|
||||
msg=(
|
||||
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule."
|
||||
),
|
||||
version="1.5.0"
|
||||
)
|
||||
ipaapi_context = self.params_get("ipaapi_context")
|
||||
with self.ipa_connect(context=ipaapi_context):
|
||||
self.check_ipa_params()
|
||||
self.define_ipa_commands()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Writing a new Ansible FreeIPA module
|
||||
|
||||
## Minimum requirements
|
||||
A ansible-freeipa module should have:
|
||||
|
||||
* Code:
|
||||
@@ -13,68 +12,4 @@ A ansible-freeipa module should have:
|
||||
* Tests:
|
||||
* Test cases (also playbooks) defined in `tests/<module_name>/test_<something>.yml`. It's ok to have multiple files in this directory.
|
||||
|
||||
## Code
|
||||
|
||||
The module file have to start with the python shebang line, license header and definition of the constants `ANSIBLE_METADATA`, `DOCUMENTATION`, `EXAMPLES` and `RETURNS`. Those constants need to be defined before the code (even imports). See https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#starting-a-new-module for more information.
|
||||
|
||||
|
||||
Although it's use is not yet required, ansible-freeipa provides `FreeIPABaseModule` as a helper class for the implementation of new modules. See the example bellow:
|
||||
|
||||
```python
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import FreeIPABaseModule
|
||||
|
||||
|
||||
class SomeIPAModule(FreeIPABaseModule):
|
||||
ipa_param_mapping = {
|
||||
"arg_to_be_passed_to_ipa_command": "module_param",
|
||||
"another_arg": "get_another_module_param",
|
||||
}
|
||||
|
||||
def get_another_module_param(self):
|
||||
another_module_param = self.ipa_params.another_module_param
|
||||
|
||||
# Validate or modify another_module_param ...
|
||||
|
||||
return another_module_param
|
||||
|
||||
def check_ipa_params(self):
|
||||
|
||||
# Validate your params here ...
|
||||
|
||||
# Example:
|
||||
if not self.ipa_params.module_param in VALID_OPTIONS:
|
||||
self.fail_json(msg="Invalid value for argument module_param")
|
||||
|
||||
def define_ipa_commands(self):
|
||||
args = self.get_ipa_command_args()
|
||||
|
||||
self.add_ipa_command("some_ipa_command", name="obj-name", args=args)
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = SomeIPAModule(argument_spec=dict(
|
||||
module_param=dict(type="str", default=None, required=False),
|
||||
another_module_param=dict(type="str", default=None, required=False),
|
||||
))
|
||||
ipa_module.ipa_run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In the example above, the module will call the command `some_ipa_command`, using "obj-name" as name and, `arg_to_be_passed_to_ipa_command` and `another_arg` as arguments.
|
||||
|
||||
The values of the arguments will be determined by the class attribute `ipa_param_mapping`.
|
||||
|
||||
In the case of `arg_to_be_passed_to_ipa_command` the key (`module_param`) is defined in the module `argument_specs` so the value of the argument is actually used.
|
||||
|
||||
On the other hand, `another_arg` as mapped to something else: a callable method. In this case the method will be called and it's result used as value for `another_arg`.
|
||||
|
||||
**NOTE**: Keep mind that to take advantage of the parameters mapping defined in `ipa_param_mapping` you will have to call `args = self.get_ipa_command_args()` and use `args` in your command. There is no implicit call of this method.
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
The `FreeIPABaseModule` is new and might not be suitable to all cases and every module yet. In case you need to extend it's functionality for a new module please open an issue or PR and we'll be happy to discuss it.
|
||||
Use the script `utils/new_module` to create the stub files for a new module.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -21,6 +20,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
@@ -77,6 +79,20 @@ options:
|
||||
description: The expression of the regex
|
||||
type: str
|
||||
required: true
|
||||
users:
|
||||
description: Users to rebuild membership for.
|
||||
type: list
|
||||
required: false
|
||||
hosts:
|
||||
description: Hosts to rebuild membership for.
|
||||
type: list
|
||||
required: false
|
||||
no_wait:
|
||||
description: Don't wait for rebuilding membership.
|
||||
type: bool
|
||||
default_group:
|
||||
description: Default (fallback) group for all unmatched entries.
|
||||
type: str
|
||||
action:
|
||||
description: Work on automember or member level
|
||||
default: automember
|
||||
@@ -84,10 +100,11 @@ options:
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "rebuilt", "orphans_removed"]
|
||||
author:
|
||||
- Mark Hahl
|
||||
- Jake Reynolds
|
||||
- Thomas Woerner
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
@@ -100,7 +117,7 @@ EXAMPLES = """
|
||||
state: present
|
||||
inclusive:
|
||||
- key: "mail"
|
||||
expression: "example.com$
|
||||
expression: "example.com"
|
||||
|
||||
# Delete an automember rule
|
||||
- ipaautomember:
|
||||
@@ -114,12 +131,78 @@ EXAMPLES = """
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "My domain hosts"
|
||||
automember_tye: hostgroup
|
||||
automember_type: hostgroup
|
||||
action: member
|
||||
inclusive:
|
||||
- key: fqdn
|
||||
expression: ".*.mydomain.com"
|
||||
|
||||
# Ensure group membership for all users has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: rebuilt
|
||||
|
||||
# Ensure group membership for given users has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
users:
|
||||
- user1
|
||||
- user2
|
||||
state: rebuilt
|
||||
|
||||
# Ensure hostgroup membership for all hosts has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: rebuilt
|
||||
|
||||
# Ensure hostgroup membership for given hosts has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- host1.mydomain.com
|
||||
- host2.mydomain.com
|
||||
state: rebuilt
|
||||
|
||||
# Ensure default group fallback_group for all unmatched group entries is set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: fallback_group
|
||||
|
||||
# Ensure default group for all unmatched group entries is not set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
default_group: ""
|
||||
automember_type: group
|
||||
state: absent
|
||||
|
||||
# Ensure default hostgroup fallback_hostgroup for all unmatched group entries
|
||||
# is set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: fallback_hostgroup
|
||||
|
||||
# Ensure default hostgroup for all unmatched group entries is not set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: ""
|
||||
state: absent
|
||||
|
||||
# Example playbook to ensure all orphan automember group rules are removed:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: orphans_removed
|
||||
|
||||
# Example playbook to ensure all orphan automember hostgroup rules are removed:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: orphans_removed
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -127,14 +210,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors, DN
|
||||
)
|
||||
|
||||
|
||||
def find_automember(module, name, grouping):
|
||||
def find_automember(module, name, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": grouping
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -144,13 +227,40 @@ def find_automember(module, name, grouping):
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_condition_args(grouping,
|
||||
def find_automember_orphans(module, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
_result = module.ipa_command_no_name("automember_find_orphans", _args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return _result
|
||||
|
||||
|
||||
def find_automember_default_group(module, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
_result = module.ipa_command_no_name("automember_default_group_show",
|
||||
_args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_condition_args(automember_type,
|
||||
key,
|
||||
inclusiveregex=None,
|
||||
exclusiveregex=None):
|
||||
_args = {}
|
||||
if grouping is not None:
|
||||
_args['type'] = grouping
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
if key is not None:
|
||||
_args['key'] = key
|
||||
if inclusiveregex is not None:
|
||||
@@ -161,13 +271,23 @@ def gen_condition_args(grouping,
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args(description, grouping):
|
||||
def gen_rebuild_args(automember_type, rebuild_users, rebuild_hosts, no_wait):
|
||||
_args = {"no_wait": no_wait}
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
if rebuild_users is not None:
|
||||
_args["users"] = rebuild_users
|
||||
if rebuild_hosts is not None:
|
||||
_args["hosts"] = rebuild_hosts
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args(description, automember_type):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if grouping is not None:
|
||||
_args['type'] = grouping
|
||||
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
return _args
|
||||
|
||||
|
||||
@@ -192,28 +312,35 @@ def main():
|
||||
argument_spec=dict(
|
||||
# general
|
||||
inclusive=dict(type="list",
|
||||
aliases=["automemberinclusiveregex"], default=None,
|
||||
aliases=["automemberinclusiveregex"],
|
||||
default=None,
|
||||
options=dict(
|
||||
key=dict(type="str", required=True),
|
||||
expression=dict(type="str", required=True)
|
||||
),
|
||||
elements="dict", required=False),
|
||||
exclusive=dict(type="list", aliases=[
|
||||
"automemberexclusiveregex"], default=None,
|
||||
elements="dict",
|
||||
required=False),
|
||||
exclusive=dict(type="list",
|
||||
aliases=["automemberexclusiveregex"],
|
||||
default=None,
|
||||
options=dict(
|
||||
key=dict(type="str", required=True),
|
||||
expression=dict(type="str", required=True)
|
||||
),
|
||||
elements="dict", required=False),
|
||||
elements="dict",
|
||||
required=False),
|
||||
name=dict(type="list", aliases=["cn"],
|
||||
default=None, required=True),
|
||||
default=None, required=False),
|
||||
description=dict(type="str", default=None),
|
||||
automember_type=dict(type='str', required=False,
|
||||
choices=['group', 'hostgroup']),
|
||||
no_wait=dict(type="bool", default=None),
|
||||
default_group=dict(type="str", default=None),
|
||||
action=dict(type="str", default="automember",
|
||||
choices=["member", "automember"]),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "rebuild"]),
|
||||
choices=["present", "absent", "rebuilt",
|
||||
"orphans_removed"]),
|
||||
users=dict(type="list", default=None),
|
||||
hosts=dict(type="list", default=None),
|
||||
),
|
||||
@@ -226,6 +353,8 @@ def main():
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
if names is None:
|
||||
names = []
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
@@ -234,6 +363,12 @@ def main():
|
||||
inclusive = ansible_module.params_get("inclusive")
|
||||
exclusive = ansible_module.params_get("exclusive")
|
||||
|
||||
# no_wait for rebuilt
|
||||
no_wait = ansible_module.params_get("no_wait")
|
||||
|
||||
# default_group
|
||||
default_group = ansible_module.params_get("default_group")
|
||||
|
||||
# action
|
||||
action = ansible_module.params_get("action")
|
||||
# state
|
||||
@@ -248,12 +383,51 @@ def main():
|
||||
# Check parameters
|
||||
invalid = []
|
||||
|
||||
if state != "rebuild":
|
||||
invalid = ["rebuild_hosts", "rebuild_users"]
|
||||
if state in ["rebuilt", "orphans_removed"]:
|
||||
invalid = ["name", "description", "exclusive", "inclusive",
|
||||
"default_group"]
|
||||
|
||||
if not automember_type and state != "rebuild":
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required unless state: rebuild")
|
||||
msg="'action=member' is not usable with state '%s'" % state)
|
||||
|
||||
if state == "rebuilt":
|
||||
if automember_type == "group" and rebuild_hosts is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="state %s: hosts can not be set when type is '%s'" %
|
||||
(state, automember_type))
|
||||
if automember_type == "hostgroup" and rebuild_users is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="state %s: users can not be set when type is '%s'" %
|
||||
(state, automember_type))
|
||||
|
||||
elif state == "orphans_removed":
|
||||
invalid.extend(["users", "hosts"])
|
||||
|
||||
if not automember_type:
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required unless state: rebuilt")
|
||||
|
||||
else:
|
||||
if default_group is not None:
|
||||
for param in ["name", "exclusive", "inclusive", "users", "hosts"
|
||||
"no_wait"]:
|
||||
if ansible_module.params.get(param) is not None:
|
||||
msg = "Cannot use {0} together with default_group"
|
||||
ansible_module.fail_json(msg=msg.format(param))
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot use default_group with action:member")
|
||||
if state == "absent":
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot use default_group with state:absent")
|
||||
|
||||
else:
|
||||
invalid = ["users", "hosts", "no_wait"]
|
||||
|
||||
if not automember_type:
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required.")
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
@@ -307,15 +481,21 @@ def main():
|
||||
commands.append([name, 'automember_add', args])
|
||||
res_find = {}
|
||||
|
||||
inclusive_add, inclusive_del = gen_add_del_lists(
|
||||
transform_conditions(inclusive or []),
|
||||
res_find.get("automemberinclusiveregex", [])
|
||||
)
|
||||
if inclusive is not None:
|
||||
inclusive_add, inclusive_del = gen_add_del_lists(
|
||||
transform_conditions(inclusive),
|
||||
res_find.get("automemberinclusiveregex", [])
|
||||
)
|
||||
else:
|
||||
inclusive_add, inclusive_del = [], []
|
||||
|
||||
exclusive_add, exclusive_del = gen_add_del_lists(
|
||||
transform_conditions(exclusive or []),
|
||||
res_find.get("automemberexclusiveregex", [])
|
||||
)
|
||||
if exclusive is not None:
|
||||
exclusive_add, exclusive_del = gen_add_del_lists(
|
||||
transform_conditions(exclusive),
|
||||
res_find.get("automemberexclusiveregex", [])
|
||||
)
|
||||
else:
|
||||
exclusive_add, exclusive_del = [], []
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -384,16 +564,45 @@ def main():
|
||||
'automember_remove_condition',
|
||||
condition_args])
|
||||
|
||||
elif state == "rebuild":
|
||||
if automember_type:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"type": automember_type}])
|
||||
if rebuild_users:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"users": rebuild_users}])
|
||||
if rebuild_hosts:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"hosts": rebuild_hosts}])
|
||||
if len(names) == 0:
|
||||
if state == "rebuilt":
|
||||
args = gen_rebuild_args(automember_type, rebuild_users,
|
||||
rebuild_hosts, no_wait)
|
||||
commands.append([None, 'automember_rebuild', args])
|
||||
|
||||
elif state == "orphans_removed":
|
||||
res_find = find_automember_orphans(ansible_module,
|
||||
automember_type)
|
||||
if res_find["count"] > 0:
|
||||
commands.append([None, 'automember_find_orphans',
|
||||
{'type': automember_type,
|
||||
'remove': True}])
|
||||
|
||||
elif default_group is not None and state == "present":
|
||||
res_find = find_automember_default_group(ansible_module,
|
||||
automember_type)
|
||||
|
||||
if default_group == "":
|
||||
if isinstance(res_find["automemberdefaultgroup"], list):
|
||||
commands.append([None,
|
||||
'automember_default_group_remove',
|
||||
{'type': automember_type}])
|
||||
ansible_module.warn("commands: %s" % repr(commands))
|
||||
|
||||
else:
|
||||
dn_default_group = [DN(('cn', default_group),
|
||||
('cn', '%ss' % automember_type),
|
||||
('cn', 'accounts'),
|
||||
ansible_module.ipa_get_basedn())]
|
||||
if repr(res_find["automemberdefaultgroup"]) != \
|
||||
repr(dn_default_group):
|
||||
commands.append(
|
||||
[None, 'automember_default_group_set',
|
||||
{'type': automember_type,
|
||||
'automemberdefaultgroup': default_group}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Invalid operation")
|
||||
|
||||
# Execute commands
|
||||
|
||||
|
||||
235
plugins/modules/ipaautomountkey.py
Normal file
235
plugins/modules/ipaautomountkey.py
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Chris Procter <cprocter@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2021 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ipaautomountkey
|
||||
author: chris procter
|
||||
short_description: Manage FreeIPA autommount map
|
||||
description:
|
||||
- Add, delete, and modify an IPA automount map
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: False
|
||||
location:
|
||||
description: automount location map is in
|
||||
required: True
|
||||
choices: ["automountlocationcn", "automountlocation"]
|
||||
mapname:
|
||||
description: automount map to be managed
|
||||
choices: ["map", "automountmapname", "automountmap"]
|
||||
required: True
|
||||
key:
|
||||
description: automount key to be managed
|
||||
required: True
|
||||
choices: ["name", "automountkey"]
|
||||
newkey:
|
||||
description: key to change to if state is 'renamed'
|
||||
required: True
|
||||
choices: ["newname", "newautomountkey"]
|
||||
info:
|
||||
description: Mount information for the key
|
||||
required: True
|
||||
choices: ["information", "automountinformation"]
|
||||
state:
|
||||
description: State to ensure
|
||||
required: False
|
||||
default: present
|
||||
choices: ["present", "absent", "renamed"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: create key TestKey
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
locationcn: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
info: 192.168.122.1:/exports
|
||||
state: present
|
||||
|
||||
- name: ensure key TestKey is absent
|
||||
ipaautomountkey:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestLocation
|
||||
mapname: TestMap
|
||||
key: TestKey
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
IPAAnsibleModule, ipalib_errors
|
||||
)
|
||||
|
||||
|
||||
class AutomountKey(IPAAnsibleModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(AutomountKey, self).__init__(*args, **kwargs)
|
||||
self.commands = []
|
||||
|
||||
def get_key(self, location, mapname, key):
|
||||
try:
|
||||
args = {
|
||||
"automountmapautomountmapname": mapname,
|
||||
"automountkey": key,
|
||||
"all": True,
|
||||
}
|
||||
resp = self.ipa_command("automountkey_show", location, args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
else:
|
||||
return resp.get("result")
|
||||
|
||||
def check_ipa_params(self):
|
||||
invalid = []
|
||||
state = self.params_get("state")
|
||||
if state == "present":
|
||||
invalid = ["rename"]
|
||||
if not self.params_get("info"):
|
||||
self.fail_json(msg="Value required for argument 'info'")
|
||||
|
||||
if state == "rename":
|
||||
invalid = ["info"]
|
||||
if not self.params_get("rename"):
|
||||
self.fail_json(msg="Value required for argument 'renamed'")
|
||||
|
||||
if state == "absent":
|
||||
invalid = ["info", "rename"]
|
||||
|
||||
self.params_fail_used_invalid(invalid, state)
|
||||
|
||||
@staticmethod
|
||||
def get_args(mapname, key, info, rename):
|
||||
_args = {}
|
||||
if mapname:
|
||||
_args["automountmapautomountmapname"] = mapname
|
||||
if key:
|
||||
_args["automountkey"] = key
|
||||
if info:
|
||||
_args["automountinformation"] = info
|
||||
if rename:
|
||||
_args["rename"] = rename
|
||||
return _args
|
||||
|
||||
def define_ipa_commands(self):
|
||||
state = self.params_get("state")
|
||||
location = self.params_get("location")
|
||||
mapname = self.params_get("mapname")
|
||||
key = self.params_get("key")
|
||||
info = self.params_get("info")
|
||||
rename = self.params_get("rename")
|
||||
|
||||
args = self.get_args(mapname, key, info, rename)
|
||||
|
||||
res_find = self.get_key(location, mapname, key)
|
||||
|
||||
if state == "present":
|
||||
if res_find is None:
|
||||
# does not exist and is wanted
|
||||
self.commands.append([location, "automountkey_add", args])
|
||||
else:
|
||||
# exists and is wanted, check for changes
|
||||
if info not in res_find.get("automountinformation"):
|
||||
self.commands.append([location, "automountkey_mod", args])
|
||||
|
||||
if state == "renamed":
|
||||
if res_find is None:
|
||||
self.fail_json(
|
||||
msg=(
|
||||
"Cannot rename inexistent key: '%s', '%s', '%s'"
|
||||
% (location, mapname, key)
|
||||
)
|
||||
)
|
||||
self.commands.append([location, "automountkey_mod", args])
|
||||
|
||||
if state == "absent":
|
||||
# if key exists and self.ipa_params.state == "absent":
|
||||
if res_find is not None:
|
||||
self.commands.append([location, "automountkey_del", args])
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = AutomountKey(
|
||||
argument_spec=dict(
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['present', 'absent', 'renamed'],
|
||||
required=None,
|
||||
default='present',
|
||||
),
|
||||
location=dict(
|
||||
type="str",
|
||||
aliases=["automountlocationcn", "automountlocation"],
|
||||
required=True,
|
||||
),
|
||||
rename=dict(
|
||||
type="str",
|
||||
aliases=["new_name", "newautomountkey"],
|
||||
required=False,
|
||||
),
|
||||
mapname=dict(
|
||||
type="str",
|
||||
aliases=["map", "automountmapname", "automountmap"],
|
||||
required=True,
|
||||
),
|
||||
key=dict(
|
||||
type="str",
|
||||
aliases=["name", "automountkey"],
|
||||
required=True,
|
||||
),
|
||||
info=dict(
|
||||
type="str",
|
||||
aliases=["information", "automountinformation"],
|
||||
required=False,
|
||||
),
|
||||
),
|
||||
)
|
||||
ipaapi_context = ipa_module.params_get("ipaapi_context")
|
||||
with ipa_module.ipa_connect(context=ipaapi_context):
|
||||
ipa_module.check_ipa_params()
|
||||
ipa_module.define_ipa_commands()
|
||||
changed = ipa_module.execute_ipa_commands(ipa_module.commands)
|
||||
ipa_module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Authors:
|
||||
# Chris Procter <cprocter@redhat.com>
|
||||
@@ -19,6 +18,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -65,13 +68,16 @@ RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
FreeIPABaseModule, ipalib_errors
|
||||
IPAAnsibleModule, ipalib_errors
|
||||
)
|
||||
|
||||
|
||||
class AutomountLocation(FreeIPABaseModule):
|
||||
class AutomountLocation(IPAAnsibleModule):
|
||||
|
||||
ipa_param_mapping = {}
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(AutomountLocation, self).__init__(*args, **kwargs)
|
||||
self.commands = []
|
||||
|
||||
def get_location(self, location):
|
||||
try:
|
||||
@@ -84,40 +90,28 @@ class AutomountLocation(FreeIPABaseModule):
|
||||
return response.get("result", None)
|
||||
|
||||
def check_ipa_params(self):
|
||||
if len(self.ipa_params.name) == 0:
|
||||
if len(self.params_get("name")) == 0:
|
||||
self.fail_json(msg="At least one location must be provided.")
|
||||
|
||||
def define_ipa_commands(self):
|
||||
state = self.params_get("state")
|
||||
|
||||
for location_name in self.ipa_params.name:
|
||||
for location_name in self.params_get("name"):
|
||||
location = self.get_location(location_name)
|
||||
|
||||
if not location and self.ipa_params.state == "present":
|
||||
if not location and state == "present":
|
||||
# does not exist and is wanted
|
||||
self.add_ipa_command(
|
||||
"automountlocation_add",
|
||||
name=location_name,
|
||||
args=None,
|
||||
)
|
||||
elif location and self.ipa_params.state == "absent":
|
||||
self.commands.append(
|
||||
(location_name, "automountlocation_add", {}))
|
||||
elif location and state == "absent":
|
||||
# exists and is not wanted
|
||||
self.add_ipa_command(
|
||||
"automountlocation_del",
|
||||
name=location_name,
|
||||
args=None,
|
||||
)
|
||||
self.commands.append(
|
||||
(location_name, "automountlocation_del", {}))
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = AutomountLocation(
|
||||
argument_spec=dict(
|
||||
ipaadmin_principal=dict(type="str",
|
||||
default="admin"
|
||||
),
|
||||
ipaadmin_password=dict(type="str",
|
||||
required=False,
|
||||
no_log=True
|
||||
),
|
||||
state=dict(type='str',
|
||||
default='present',
|
||||
choices=['present', 'absent']
|
||||
@@ -129,7 +123,12 @@ def main():
|
||||
),
|
||||
),
|
||||
)
|
||||
ipa_module.ipa_run()
|
||||
ipaapi_context = ipa_module.params_get("ipaapi_context")
|
||||
with ipa_module.ipa_connect(context=ipaapi_context):
|
||||
ipa_module.check_ipa_params()
|
||||
ipa_module.define_ipa_commands()
|
||||
changed = ipa_module.execute_ipa_commands(ipa_module.commands)
|
||||
ipa_module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
195
plugins/modules/ipaautomountmap.py
Normal file
195
plugins/modules/ipaautomountmap.py
Normal file
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Authors:
|
||||
# Chris Procter <cprocter@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2021 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ipaautomountmap
|
||||
author: Chris Procter
|
||||
short_description: Manage FreeIPA autommount map
|
||||
description:
|
||||
- Add, delete, and modify an IPA automount map
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password.
|
||||
required: false
|
||||
automountlocation:
|
||||
description: automount location map is anchored to
|
||||
choices: ["location", "automountlocationcn"]
|
||||
required: True
|
||||
name:
|
||||
description: automount map to be managed.
|
||||
choices: ["mapname", "map", "automountmapname"]
|
||||
required: True
|
||||
desc:
|
||||
description: description of automount map.
|
||||
choices: ["description"]
|
||||
required: false
|
||||
state:
|
||||
description: State to ensure
|
||||
required: false
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: ensure map named auto.DMZ in location DMZ is present
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.DMZ
|
||||
location: DMZ
|
||||
desc: "this is a map for servers in the DMZ"
|
||||
|
||||
- name: remove a map named auto.DMZ in location DMZ if it exists
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: auto.DMZ
|
||||
location: DMZ
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
)
|
||||
|
||||
|
||||
class AutomountMap(IPAAnsibleModule):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(AutomountMap, self).__init__(*args, **kwargs)
|
||||
self.commands = []
|
||||
|
||||
def get_automountmap(self, location, name):
|
||||
try:
|
||||
response = self.ipa_command(
|
||||
"automountmap_show",
|
||||
location,
|
||||
{"automountmapname": name, "all": True}
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
else:
|
||||
return response["result"]
|
||||
|
||||
def check_ipa_params(self):
|
||||
invalid = []
|
||||
name = self.params_get("name")
|
||||
state = self.params_get("state")
|
||||
if state == "present":
|
||||
if len(name) != 1:
|
||||
self.fail_json(msg="Exactly one name must be provided \
|
||||
for state=present.")
|
||||
if state == "absent":
|
||||
if len(name) == 0:
|
||||
self.fail_json(msg="Argument 'map_type' can not be used with "
|
||||
"state 'absent'")
|
||||
invalid = ["desc"]
|
||||
|
||||
self.params_fail_used_invalid(invalid, state)
|
||||
|
||||
def get_args(self, mapname, desc): # pylint: disable=no-self-use
|
||||
_args = {}
|
||||
if mapname:
|
||||
_args["automountmapname"] = mapname
|
||||
if desc:
|
||||
_args["description"] = desc
|
||||
return _args
|
||||
|
||||
def define_ipa_commands(self):
|
||||
name = self.params_get("name")
|
||||
state = self.params_get("state")
|
||||
location = self.params_get("location")
|
||||
desc = self.params_get("desc")
|
||||
|
||||
for mapname in name:
|
||||
automountmap = self.get_automountmap(location, mapname)
|
||||
|
||||
if state == "present":
|
||||
args = self.get_args(mapname, desc)
|
||||
if automountmap is None:
|
||||
self.commands.append([location, "automountmap_add", args])
|
||||
else:
|
||||
if not compare_args_ipa(self, args, automountmap):
|
||||
self.commands.append(
|
||||
[location, "automountmap_mod", args]
|
||||
)
|
||||
|
||||
if state == "absent":
|
||||
if automountmap is not None:
|
||||
self.commands.append([
|
||||
location,
|
||||
"automountmap_del",
|
||||
{"automountmapname": [mapname]}
|
||||
])
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = AutomountMap(
|
||||
argument_spec=dict(
|
||||
state=dict(type='str',
|
||||
default='present',
|
||||
choices=['present', 'absent']
|
||||
),
|
||||
location=dict(type="str",
|
||||
aliases=["automountlocation", "automountlocationcn"],
|
||||
default=None,
|
||||
required=True
|
||||
),
|
||||
name=dict(type="list",
|
||||
aliases=["mapname", "map", "automountmapname"],
|
||||
default=None,
|
||||
required=True
|
||||
),
|
||||
desc=dict(type="str",
|
||||
aliases=["description"],
|
||||
required=False,
|
||||
default=None
|
||||
),
|
||||
),
|
||||
)
|
||||
changed = False
|
||||
ipaapi_context = ipa_module.params_get("ipaapi_context")
|
||||
with ipa_module.ipa_connect(context=ipaapi_context):
|
||||
ipa_module.check_ipa_params()
|
||||
ipa_module.define_ipa_commands()
|
||||
changed = ipa_module.execute_ipa_commands(ipa_module.commands)
|
||||
ipa_module.exit_json(changed=changed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -296,7 +299,7 @@ def main():
|
||||
"KDC:Disable Last Success",
|
||||
"KDC:Disable Lockout",
|
||||
"KDC:Disable Default Preauth for SPNs",
|
||||
""]), # noqa E128
|
||||
""]), # noqa E128
|
||||
selinuxusermaporder=dict(type="list", required=False,
|
||||
aliases=['ipaselinuxusermaporder']),
|
||||
selinuxusermapdefault=dict(type="str", required=False,
|
||||
@@ -362,15 +365,15 @@ def main():
|
||||
|
||||
if params.get("ipadomainresolutionorder", None):
|
||||
params["ipadomainresolutionorder"] = \
|
||||
":".join(params["ipadomainresolutionorder"])
|
||||
":".join(params["ipadomainresolutionorder"])
|
||||
|
||||
if params.get("ipausersearchfields", None):
|
||||
params["ipausersearchfields"] = \
|
||||
",".join(params["ipausersearchfields"])
|
||||
",".join(params["ipausersearchfields"])
|
||||
|
||||
if params.get("ipagroupsearchfields", None):
|
||||
params["ipagroupsearchfields"] = \
|
||||
",".join(params["ipagroupsearchfields"])
|
||||
",".join(params["ipagroupsearchfields"])
|
||||
|
||||
# verify limits on INT values.
|
||||
args_with_limits = [
|
||||
@@ -415,12 +418,12 @@ def main():
|
||||
if ansible_module.argument_spec.get(k):
|
||||
arg_type = ansible_module.argument_spec[k]['type']
|
||||
if k in (
|
||||
'ipaselinuxusermaporder', 'domain_resolution_order'
|
||||
):
|
||||
'ipaselinuxusermaporder', 'domain_resolution_order'
|
||||
):
|
||||
exit_args[k] = result.get(key)[0].split('$')
|
||||
elif k in (
|
||||
'usersearch', 'groupsearch'
|
||||
):
|
||||
'usersearch', 'groupsearch'
|
||||
):
|
||||
exit_args[k] = result.get(key)[0].split(',')
|
||||
elif isinstance(value, str) and arg_type == "list":
|
||||
exit_args[k] = [value]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -21,6 +20,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -56,8 +59,16 @@ options:
|
||||
Allow synchronization of forward (A, AAAA) and reverse (PTR) records.
|
||||
required: false
|
||||
type: bool
|
||||
action:
|
||||
description: |
|
||||
Work on dnsconfig or member level. It can be one of `member` or
|
||||
`dnsconfig`. Only `forwarders` can be managed with `action: member`.
|
||||
default: "dnsconfig"
|
||||
choices: ["member", "dnsconfig"]
|
||||
state:
|
||||
description: State to ensure
|
||||
description: |
|
||||
The state to ensure. It can be one of `present` or `absent`.
|
||||
`absent` can only be used with `action: member` and `forwarders`.
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
"""
|
||||
@@ -80,6 +91,7 @@ EXAMPLES = """
|
||||
- ip_address: 2001:4860:4860::8888
|
||||
port: 53
|
||||
state: absent
|
||||
action: member
|
||||
|
||||
# Disable PTR record synchronization.
|
||||
- ipadnsconfig:
|
||||
@@ -115,7 +127,7 @@ def find_dnsconfig(module):
|
||||
return None
|
||||
|
||||
|
||||
def gen_args(module, state, dnsconfig, forwarders, forward_policy,
|
||||
def gen_args(module, state, action, dnsconfig, forwarders, forward_policy,
|
||||
allow_sync_ptr):
|
||||
_args = {}
|
||||
|
||||
@@ -134,15 +146,20 @@ def gen_args(module, state, dnsconfig, forwarders, forward_policy,
|
||||
|
||||
global_forwarders = dnsconfig.get('idnsforwarders', [])
|
||||
if state == 'absent':
|
||||
_args['idnsforwarders'] = [
|
||||
fwd for fwd in global_forwarders if fwd not in _forwarders]
|
||||
# When all forwarders should be excluded, use an empty string ('').
|
||||
if not _args['idnsforwarders']:
|
||||
_args['idnsforwarders'] = ['']
|
||||
if action == "member":
|
||||
_args['idnsforwarders'] = [
|
||||
fwd for fwd in global_forwarders if fwd not in _forwarders]
|
||||
# When all forwarders should be excluded,
|
||||
# use an empty string ('').
|
||||
if not _args['idnsforwarders']:
|
||||
_args['idnsforwarders'] = ['']
|
||||
|
||||
elif state == 'present':
|
||||
_args['idnsforwarders'] = [
|
||||
fwd for fwd in _forwarders if fwd not in global_forwarders]
|
||||
if action == "member":
|
||||
_args['idnsforwarders'] = \
|
||||
list(set(list(_forwarders) + list(global_forwarders)))
|
||||
else:
|
||||
_args['idnsforwarders'] = _forwarders
|
||||
# If no forwarders should be added, remove argument.
|
||||
if not _args['idnsforwarders']:
|
||||
del _args['idnsforwarders']
|
||||
@@ -162,24 +179,25 @@ def gen_args(module, state, dnsconfig, forwarders, forward_policy,
|
||||
|
||||
def main():
|
||||
forwarder_spec = dict(
|
||||
ip_address=dict(type=str, required=True),
|
||||
port=dict(type=int, required=False, default=None)
|
||||
ip_address=dict(type=str, required=True),
|
||||
port=dict(type=int, required=False, default=None)
|
||||
)
|
||||
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# dnsconfig
|
||||
forwarders=dict(type='list', default=None, required=False,
|
||||
options=dict(**forwarder_spec)),
|
||||
forward_policy=dict(type='str', required=False, default=None,
|
||||
choices=['only', 'first', 'none']),
|
||||
allow_sync_ptr=dict(type='bool', required=False, default=None),
|
||||
argument_spec=dict(
|
||||
# dnsconfig
|
||||
forwarders=dict(type='list', default=None, required=False,
|
||||
options=dict(**forwarder_spec)),
|
||||
forward_policy=dict(type='str', required=False, default=None,
|
||||
choices=['only', 'first', 'none']),
|
||||
allow_sync_ptr=dict(type='bool', required=False, default=None),
|
||||
|
||||
# general
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
|
||||
)
|
||||
# general
|
||||
action=dict(type="str", default="dnsconfig",
|
||||
choices=["member", "dnsconfig"]),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
)
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
@@ -189,11 +207,17 @@ def main():
|
||||
forward_policy = ansible_module.params_get('forward_policy')
|
||||
allow_sync_ptr = ansible_module.params_get('allow_sync_ptr')
|
||||
|
||||
action = ansible_module.params_get('action')
|
||||
state = ansible_module.params_get('state')
|
||||
|
||||
# Check parameters.
|
||||
invalid = []
|
||||
if state == "present" and action == "member":
|
||||
invalid = ['forward_policy', 'allow_sync_ptr']
|
||||
if state == 'absent':
|
||||
if action != "member":
|
||||
ansible_module.fail_json(
|
||||
msg="State 'absent' is only valid with action 'member'.")
|
||||
invalid = ['forward_policy', 'allow_sync_ptr']
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state)
|
||||
@@ -206,7 +230,7 @@ def main():
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
res_find = find_dnsconfig(ansible_module)
|
||||
args = gen_args(ansible_module, state, res_find, forwarders,
|
||||
args = gen_args(ansible_module, state, action, res_find, forwarders,
|
||||
forward_policy, allow_sync_ptr)
|
||||
|
||||
# Execute command only if configuration changes.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
"""DNS Record ansible-freeipa module."""
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
@@ -866,8 +869,7 @@ from ansible.module_utils.ansible_freeipa_module import \
|
||||
import dns.reversename
|
||||
import dns.resolver
|
||||
|
||||
import six
|
||||
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -1106,12 +1108,13 @@ def configure_module():
|
||||
name=dict(type="list", aliases=["record_name"], default=None,
|
||||
required=False),
|
||||
|
||||
records=dict(type="list", default=None,
|
||||
records=dict(type="list",
|
||||
default=None,
|
||||
options=dict(
|
||||
# Here name is a simple string
|
||||
name=dict(type='str', required=True,
|
||||
aliases=['record_name']),
|
||||
**record_spec),
|
||||
# Here name is a simple string
|
||||
name=dict(type='str', required=True,
|
||||
aliases=['record_name']),
|
||||
**record_spec),
|
||||
),
|
||||
|
||||
# general
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -200,45 +203,55 @@ dnszone:
|
||||
|
||||
from ipapython.dnsutil import DNSName # noqa: E402
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
FreeIPABaseModule,
|
||||
IPAAnsibleModule,
|
||||
is_ip_address,
|
||||
is_ip_network_address,
|
||||
is_valid_port,
|
||||
ipalib_errors
|
||||
ipalib_errors,
|
||||
compare_args_ipa,
|
||||
IPAParamMapping,
|
||||
) # noqa: E402
|
||||
import netaddr
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
class DNSZoneModule(FreeIPABaseModule):
|
||||
class DNSZoneModule(IPAAnsibleModule):
|
||||
|
||||
ipa_param_mapping = {
|
||||
# Direct Mapping
|
||||
"idnsforwardpolicy": "forward_policy",
|
||||
"idnssoarefresh": "refresh",
|
||||
"idnssoaretry": "retry",
|
||||
"idnssoaexpire": "expire",
|
||||
"idnssoaminimum": "minimum",
|
||||
"dnsttl": "ttl",
|
||||
"dnsdefaultttl": "default_ttl",
|
||||
"idnsallowsyncptr": "allow_sync_ptr",
|
||||
"idnsallowdynupdate": "dynamic_update",
|
||||
"idnssecinlinesigning": "dnssec",
|
||||
"idnsupdatepolicy": "update_policy",
|
||||
# Mapping by method
|
||||
"idnsforwarders": "get_ipa_idnsforwarders",
|
||||
"idnsallowtransfer": "get_ipa_idnsallowtransfer",
|
||||
"idnsallowquery": "get_ipa_idnsallowquery",
|
||||
"idnssoamname": "get_ipa_idnssoamname",
|
||||
"idnssoarname": "get_ipa_idnssoarname",
|
||||
"skip_nameserver_check": "get_ipa_skip_nameserver_check",
|
||||
"skip_overlap_check": "get_ipa_skip_overlap_check",
|
||||
"nsec3paramrecord": "get_ipa_nsec3paramrecord",
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(DNSZoneModule, self).__init__(*args, **kwargs)
|
||||
|
||||
ipa_param_mapping = {
|
||||
# Direct Mapping
|
||||
"idnsforwardpolicy": "forward_policy",
|
||||
"idnssoarefresh": "refresh",
|
||||
"idnssoaretry": "retry",
|
||||
"idnssoaexpire": "expire",
|
||||
"idnssoaminimum": "minimum",
|
||||
"dnsttl": "ttl",
|
||||
"dnsdefaultttl": "default_ttl",
|
||||
"idnsallowsyncptr": "allow_sync_ptr",
|
||||
"idnsallowdynupdate": "dynamic_update",
|
||||
"idnssecinlinesigning": "dnssec",
|
||||
"idnsupdatepolicy": "update_policy",
|
||||
# Mapping by method
|
||||
"idnsforwarders": self.get_ipa_idnsforwarders,
|
||||
"idnsallowtransfer": self.get_ipa_idnsallowtransfer,
|
||||
"idnsallowquery": self.get_ipa_idnsallowquery,
|
||||
"idnssoamname": self.get_ipa_idnssoamname,
|
||||
"idnssoarname": self.get_ipa_idnssoarname,
|
||||
"skip_nameserver_check": self.get_ipa_skip_nameserver_check,
|
||||
"skip_overlap_check": self.get_ipa_skip_overlap_check,
|
||||
"nsec3paramrecord": self.get_ipa_nsec3paramrecord,
|
||||
}
|
||||
|
||||
self.commands = []
|
||||
self.ipa_params = IPAParamMapping(self, ipa_param_mapping)
|
||||
self.exit_args = {}
|
||||
|
||||
def validate_ips(self, ips, error_msg):
|
||||
invalid_ips = [
|
||||
@@ -438,39 +451,34 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
for zone_name in self.get_zone_names():
|
||||
# Look for existing zone in IPA
|
||||
zone, is_zone_active = self.get_zone(zone_name)
|
||||
args = self.get_ipa_command_args(zone=zone)
|
||||
args = self.ipa_params.get_ipa_command_args(zone=zone)
|
||||
|
||||
if self.ipa_params.state in ["present", "enabled", "disabled"]:
|
||||
if not zone:
|
||||
# Since the zone doesn't exist we just create it
|
||||
# with given args
|
||||
self.add_ipa_command("dnszone_add", zone_name, args)
|
||||
self.commands.append((zone_name, "dnszone_add", args))
|
||||
is_zone_active = True
|
||||
# just_added = True
|
||||
|
||||
else:
|
||||
# Zone already exist so we need to verify if given args
|
||||
# matches the current config. If not we updated it.
|
||||
if self.require_ipa_attrs_change(args, zone):
|
||||
self.add_ipa_command("dnszone_mod", zone_name, args)
|
||||
if not compare_args_ipa(self, args, zone):
|
||||
self.commands.append((zone_name, "dnszone_mod", args))
|
||||
|
||||
if self.ipa_params.state == "enabled" and not is_zone_active:
|
||||
self.add_ipa_command("dnszone_enable", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_enable", {}))
|
||||
|
||||
if self.ipa_params.state == "disabled" and is_zone_active:
|
||||
self.add_ipa_command("dnszone_disable", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_disable", {}))
|
||||
|
||||
if self.ipa_params.state == "absent" and zone is not None:
|
||||
self.add_ipa_command("dnszone_del", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_del", {}))
|
||||
|
||||
def process_command_result(self, name, command, args, result):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(DNSZoneModule, self).process_command_result(
|
||||
name, command, args, result
|
||||
)
|
||||
def process_results(self, _result, command, name, _args, exit_args):
|
||||
if command == "dnszone_add" and self.ipa_params.name_from_ip:
|
||||
dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
|
||||
dnszone_exit_args['name'] = name
|
||||
exit_args.setdefault('dnszone', {})["name"] = name
|
||||
|
||||
|
||||
def get_argument_spec():
|
||||
@@ -529,12 +537,24 @@ def get_argument_spec():
|
||||
|
||||
|
||||
def main():
|
||||
DNSZoneModule(
|
||||
ansible_module = DNSZoneModule(
|
||||
argument_spec=get_argument_spec(),
|
||||
mutually_exclusive=[["name", "name_from_ip"]],
|
||||
required_one_of=[["name", "name_from_ip"]],
|
||||
supports_check_mode=True,
|
||||
).ipa_run()
|
||||
)
|
||||
|
||||
exit_args = {}
|
||||
ipaapi_context = ansible_module.params_get("ipaapi_context")
|
||||
with ansible_module.ipa_connect(context=ipaapi_context):
|
||||
ansible_module.check_ipa_params()
|
||||
ansible_module.define_ipa_commands()
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
ansible_module.commands,
|
||||
result_handler=DNSZoneModule.process_results,
|
||||
exit_args=exit_args
|
||||
)
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -178,6 +181,7 @@ EXAMPLES = """
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
|
||||
gen_add_list, gen_intersection_list
|
||||
@@ -195,7 +199,14 @@ def find_group(module, name):
|
||||
module.fail_json(
|
||||
msg="There is more than one group '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
return _result["result"][0]
|
||||
_res = _result["result"][0]
|
||||
# The returned services are of type ipapython.kerberos.Principal,
|
||||
# also services are not case sensitive. Therefore services are
|
||||
# converted to lowercase strings to be able to do the comparison.
|
||||
if "member_service" in _res:
|
||||
_res["member_service"] = \
|
||||
[to_text(svc).lower() for svc in _res["member_service"]]
|
||||
return _res
|
||||
|
||||
return None
|
||||
|
||||
@@ -305,7 +316,8 @@ def main():
|
||||
nomembers = ansible_module.params_get("nomembers")
|
||||
user = ansible_module.params_get("user")
|
||||
group = ansible_module.params_get("group")
|
||||
service = ansible_module.params_get("service")
|
||||
# Services are not case sensitive
|
||||
service = ansible_module.params_get_lowercase("service")
|
||||
membermanager_user = ansible_module.params_get("membermanager_user")
|
||||
membermanager_group = ansible_module.params_get("membermanager_group")
|
||||
externalmember = ansible_module.params_get("externalmember")
|
||||
@@ -373,6 +385,13 @@ def main():
|
||||
# Make sure group exists
|
||||
res_find = find_group(ansible_module, name)
|
||||
|
||||
user_add, user_del = [], []
|
||||
group_add, group_del = [], []
|
||||
service_add, service_del = [], []
|
||||
externalmember_add, externalmember_del = [], []
|
||||
membermanager_user_add, membermanager_user_del = [], []
|
||||
membermanager_group_add, membermanager_group_del = [], []
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Can't change an existing posix group
|
||||
@@ -390,13 +409,13 @@ def main():
|
||||
# If yes: modify
|
||||
# Also if it is a modification from nonposix to posix
|
||||
# or nonposix to external.
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find) or \
|
||||
(
|
||||
not is_posix_group(res_find) and
|
||||
not is_external_group(res_find) and
|
||||
(posix or external)
|
||||
):
|
||||
if not compare_args_ipa(
|
||||
ansible_module, args, res_find
|
||||
) or (
|
||||
not is_posix_group(res_find) and
|
||||
not is_external_group(res_find) and
|
||||
(posix or external)
|
||||
):
|
||||
if posix:
|
||||
args['posix'] = True
|
||||
if external:
|
||||
@@ -437,45 +456,6 @@ def main():
|
||||
externalmember_del) = gen_add_del_lists(
|
||||
externalmember, res_find.get("member_external"))
|
||||
|
||||
# setup member args for add/remove members.
|
||||
add_member_args = {
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
}
|
||||
del_member_args = {
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}
|
||||
if has_add_member_service:
|
||||
add_member_args["service"] = service_add
|
||||
del_member_args["service"] = service_del
|
||||
|
||||
if is_external_group(res_find):
|
||||
add_member_args["ipaexternalmember"] = \
|
||||
externalmember_add
|
||||
del_member_args["ipaexternalmember"] = \
|
||||
externalmember_del
|
||||
elif externalmember or external:
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot add external members to a "
|
||||
"non-external group."
|
||||
)
|
||||
|
||||
# Add members
|
||||
add_members = any([user_add, group_add,
|
||||
service_add, externalmember_add])
|
||||
if add_members:
|
||||
commands.append(
|
||||
[name, "group_add_member", add_member_args]
|
||||
)
|
||||
# Remove members
|
||||
remove_members = any([user_del, group_del,
|
||||
service_del, externalmember_del])
|
||||
if remove_members:
|
||||
commands.append(
|
||||
[name, "group_remove_member", del_member_args]
|
||||
)
|
||||
|
||||
membermanager_user_add, membermanager_user_del = \
|
||||
gen_add_del_lists(
|
||||
membermanager_user,
|
||||
@@ -488,93 +468,30 @@ def main():
|
||||
res_find.get("membermanager_group")
|
||||
)
|
||||
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if len(membermanager_user_add) > 0 or \
|
||||
len(membermanager_group_add) > 0:
|
||||
commands.append(
|
||||
[name, "group_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user_add,
|
||||
"group": membermanager_group_add,
|
||||
}]
|
||||
)
|
||||
# Remove member manager
|
||||
if len(membermanager_user_del) > 0 or \
|
||||
len(membermanager_group_del) > 0:
|
||||
commands.append(
|
||||
[name, "group_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user_del,
|
||||
"group": membermanager_group_del,
|
||||
}]
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No group '%s'" % name)
|
||||
|
||||
add_member_args = {
|
||||
"user": user,
|
||||
"group": group,
|
||||
}
|
||||
if has_add_member_service:
|
||||
add_member_args["service"] = service
|
||||
if is_external_group(res_find):
|
||||
add_member_args["ipaexternalmember"] = externalmember
|
||||
elif externalmember:
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot add external members to a "
|
||||
"non-external group."
|
||||
)
|
||||
|
||||
# Reduce add lists for member_user, member_group,
|
||||
# member_service and member_external to new entries
|
||||
# only that are not in res_find.
|
||||
if user is not None and "member_user" in res_find:
|
||||
user = gen_add_list(
|
||||
user, res_find["member_user"])
|
||||
if group is not None and "member_group" in res_find:
|
||||
group = gen_add_list(
|
||||
group, res_find["member_group"])
|
||||
if service is not None and "member_service" in res_find:
|
||||
service = gen_add_list(
|
||||
service, res_find["member_service"])
|
||||
if externalmember is not None \
|
||||
and "member_external" in res_find:
|
||||
externalmember = gen_add_list(
|
||||
externalmember, res_find["member_external"])
|
||||
user_add = gen_add_list(
|
||||
user, res_find.get("member_user"))
|
||||
group_add = gen_add_list(
|
||||
group, res_find.get("member_group"))
|
||||
service_add = gen_add_list(
|
||||
service, res_find.get("member_service"))
|
||||
externalmember_add = gen_add_list(
|
||||
externalmember, res_find.get("member_external"))
|
||||
|
||||
if any([user, group, service, externalmember]):
|
||||
commands.append(
|
||||
[name, "group_add_member", add_member_args]
|
||||
)
|
||||
|
||||
if has_add_membermanager:
|
||||
# Reduce add list for membermanager_user and
|
||||
# membermanager_group to new entries only that are
|
||||
# not in res_find.
|
||||
if membermanager_user is not None \
|
||||
and "membermanager_user" in res_find:
|
||||
membermanager_user = gen_add_list(
|
||||
membermanager_user,
|
||||
res_find["membermanager_user"])
|
||||
if membermanager_group is not None \
|
||||
and "membermanager_group" in res_find:
|
||||
membermanager_group = gen_add_list(
|
||||
membermanager_group,
|
||||
res_find["membermanager_group"])
|
||||
|
||||
# Add membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "group_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
membermanager_user_add = gen_add_list(
|
||||
membermanager_user,
|
||||
res_find.get("membermanager_user")
|
||||
)
|
||||
membermanager_group_add = gen_add_list(
|
||||
membermanager_group,
|
||||
res_find.get("membermanager_group")
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
if action == "group":
|
||||
@@ -585,70 +502,91 @@ def main():
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No group '%s'" % name)
|
||||
|
||||
del_member_args = {
|
||||
"user": user,
|
||||
"group": group,
|
||||
}
|
||||
if has_add_member_service:
|
||||
del_member_args["service"] = service
|
||||
if is_external_group(res_find):
|
||||
del_member_args["ipaexternalmember"] = externalmember
|
||||
elif externalmember:
|
||||
if not is_external_group(res_find) and externalmember:
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot add external members to a "
|
||||
"non-external group."
|
||||
)
|
||||
|
||||
# Reduce del lists of member_user, member_group,
|
||||
# member_service and member_external to the entries only
|
||||
# that are in res_find.
|
||||
if user is not None:
|
||||
user = gen_intersection_list(
|
||||
user, res_find.get("member_user"))
|
||||
if group is not None:
|
||||
group = gen_intersection_list(
|
||||
group, res_find.get("member_group"))
|
||||
if service is not None:
|
||||
service = gen_intersection_list(
|
||||
service, res_find.get("member_service"))
|
||||
if externalmember is not None:
|
||||
externalmember = gen_intersection_list(
|
||||
externalmember, res_find.get("member_external"))
|
||||
|
||||
if any([user, group, service, externalmember]):
|
||||
commands.append(
|
||||
[name, "group_remove_member", del_member_args]
|
||||
)
|
||||
|
||||
if has_add_membermanager:
|
||||
# Reduce del lists of membermanager_user and
|
||||
# membermanager_group to the entries only that are
|
||||
# in res_find.
|
||||
if membermanager_user is not None:
|
||||
membermanager_user = gen_intersection_list(
|
||||
membermanager_user,
|
||||
res_find.get("membermanager_user"))
|
||||
if membermanager_group is not None:
|
||||
membermanager_group = gen_intersection_list(
|
||||
membermanager_group,
|
||||
res_find.get("membermanager_group"))
|
||||
|
||||
# Remove membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "group_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
user_del = gen_intersection_list(
|
||||
user, res_find.get("member_user"))
|
||||
group_del = gen_intersection_list(
|
||||
group, res_find.get("member_group"))
|
||||
service_del = gen_intersection_list(
|
||||
service, res_find.get("member_service"))
|
||||
externalmember_del = gen_intersection_list(
|
||||
externalmember, res_find.get("member_external"))
|
||||
|
||||
membermanager_user_del = gen_intersection_list(
|
||||
membermanager_user, res_find.get("membermanager_user"))
|
||||
membermanager_group_del = gen_intersection_list(
|
||||
membermanager_group,
|
||||
res_find.get("membermanager_group")
|
||||
)
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
# manage members
|
||||
# setup member args for add/remove members.
|
||||
add_member_args = {
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
}
|
||||
del_member_args = {
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}
|
||||
if has_add_member_service:
|
||||
add_member_args["service"] = service_add
|
||||
del_member_args["service"] = service_del
|
||||
|
||||
if is_external_group(res_find):
|
||||
add_member_args["ipaexternalmember"] = \
|
||||
externalmember_add
|
||||
del_member_args["ipaexternalmember"] = \
|
||||
externalmember_del
|
||||
elif externalmember or external:
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot add external members to a "
|
||||
"non-external group."
|
||||
)
|
||||
# Add members
|
||||
add_members = any([user_add, group_add,
|
||||
service_add, externalmember_add])
|
||||
if add_members:
|
||||
commands.append(
|
||||
[name, "group_add_member", add_member_args]
|
||||
)
|
||||
# Remove members
|
||||
remove_members = any([user_del, group_del,
|
||||
service_del, externalmember_del])
|
||||
if remove_members:
|
||||
commands.append(
|
||||
[name, "group_remove_member", del_member_args]
|
||||
)
|
||||
|
||||
# manage membermanager members
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if any([membermanager_user_add, membermanager_group_add]):
|
||||
commands.append(
|
||||
[name, "group_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user_add,
|
||||
"group": membermanager_group_add,
|
||||
}]
|
||||
)
|
||||
# Remove member manager
|
||||
if any([membermanager_user_del, membermanager_group_del]):
|
||||
commands.append(
|
||||
[name, "group_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user_del,
|
||||
"group": membermanager_group_del,
|
||||
}]
|
||||
)
|
||||
|
||||
# Execute commands
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -235,12 +238,12 @@ def main():
|
||||
hostcategory = ansible_module.params_get("hostcategory")
|
||||
servicecategory = ansible_module.params_get("servicecategory")
|
||||
nomembers = ansible_module.params_get("nomembers")
|
||||
host = ansible_module.params_get("host")
|
||||
hostgroup = ansible_module.params_get("hostgroup")
|
||||
hbacsvc = ansible_module.params_get("hbacsvc")
|
||||
hbacsvcgroup = ansible_module.params_get("hbacsvcgroup")
|
||||
user = ansible_module.params_get("user")
|
||||
group = ansible_module.params_get("group")
|
||||
host = ansible_module.params_get_lowercase("host")
|
||||
hostgroup = ansible_module.params_get_lowercase("hostgroup")
|
||||
hbacsvc = ansible_module.params_get_lowercase("hbacsvc")
|
||||
hbacsvcgroup = ansible_module.params_get_lowercase("hbacsvcgroup")
|
||||
user = ansible_module.params_get_lowercase("user")
|
||||
group = ansible_module.params_get_lowercase("group")
|
||||
action = ansible_module.params_get("action")
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
@@ -304,7 +307,7 @@ def main():
|
||||
|
||||
# Ensure fqdn host names, use default domain for simple names
|
||||
if host is not None:
|
||||
_host = [ensure_fqdn(x, default_domain) for x in host]
|
||||
_host = [ensure_fqdn(x, default_domain).lower() for x in host]
|
||||
host = _host
|
||||
|
||||
commands = []
|
||||
@@ -313,6 +316,13 @@ def main():
|
||||
# Make sure hbacrule exists
|
||||
res_find = find_hbacrule(ansible_module, name)
|
||||
|
||||
host_add, host_del = [], []
|
||||
hostgroup_add, hostgroup_del = [], []
|
||||
hbacsvc_add, hbacsvc_del = [], []
|
||||
hbacsvcgroup_add, hbacsvcgroup_del = [], []
|
||||
user_add, user_del = [], []
|
||||
group_add, group_del = [], []
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Generate args
|
||||
@@ -350,69 +360,30 @@ def main():
|
||||
res_find = {}
|
||||
|
||||
# Generate addition and removal lists
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get("memberhost_host"))
|
||||
if host:
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get("memberhost_host"))
|
||||
|
||||
hostgroup_add, hostgroup_del = gen_add_del_lists(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
if hostgroup:
|
||||
hostgroup_add, hostgroup_del = gen_add_del_lists(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("memberservice_hbacsvc"))
|
||||
if hbacsvc:
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("memberservice_hbacsvc"))
|
||||
|
||||
hbacsvcgroup_add, hbacsvcgroup_del = gen_add_del_lists(
|
||||
hbacsvcgroup,
|
||||
res_find.get("memberservice_hbacsvcgroup"))
|
||||
if hbacsvcgroup:
|
||||
hbacsvcgroup_add, hbacsvcgroup_del = gen_add_del_lists(
|
||||
hbacsvcgroup,
|
||||
res_find.get("memberservice_hbacsvcgroup"))
|
||||
|
||||
user_add, user_del = gen_add_del_lists(
|
||||
user, res_find.get("memberuser_user"))
|
||||
if user:
|
||||
user_add, user_del = gen_add_del_lists(
|
||||
user, res_find.get("memberuser_user"))
|
||||
|
||||
group_add, group_del = gen_add_del_lists(
|
||||
group, res_find.get("memberuser_group"))
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if len(host_add) > 0 or len(hostgroup_add) > 0:
|
||||
commands.append([name, "hbacrule_add_host",
|
||||
{
|
||||
"host": host_add,
|
||||
"hostgroup": hostgroup_add,
|
||||
}])
|
||||
# Remove hosts and hostgroups
|
||||
if len(host_del) > 0 or len(hostgroup_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_host",
|
||||
{
|
||||
"host": host_del,
|
||||
"hostgroup": hostgroup_del,
|
||||
}])
|
||||
|
||||
# Add hbacsvcs and hbacsvcgroups
|
||||
if len(hbacsvc_add) > 0 or len(hbacsvcgroup_add) > 0:
|
||||
commands.append([name, "hbacrule_add_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc_add,
|
||||
"hbacsvcgroup": hbacsvcgroup_add,
|
||||
}])
|
||||
# Remove hbacsvcs and hbacsvcgroups
|
||||
if len(hbacsvc_del) > 0 or len(hbacsvcgroup_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc_del,
|
||||
"hbacsvcgroup": hbacsvcgroup_del,
|
||||
}])
|
||||
|
||||
# Add users and groups
|
||||
if len(user_add) > 0 or len(group_add) > 0:
|
||||
commands.append([name, "hbacrule_add_user",
|
||||
{
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
}])
|
||||
# Remove users and groups
|
||||
if len(user_del) > 0 or len(group_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_user",
|
||||
{
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}])
|
||||
if group:
|
||||
group_add, group_del = gen_add_del_lists(
|
||||
group, res_find.get("memberuser_group"))
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -421,63 +392,33 @@ def main():
|
||||
# Generate add lists for host, hostgroup and
|
||||
# res_find to only try to add hosts and hostgroups
|
||||
# that not in hbacrule already
|
||||
if host is not None and \
|
||||
"memberhost_host" in res_find:
|
||||
host = gen_add_list(
|
||||
host, res_find["memberhost_host"])
|
||||
if hostgroup is not None and \
|
||||
"memberhost_hostgroup" in res_find:
|
||||
hostgroup = gen_add_list(
|
||||
hostgroup, res_find["memberhost_hostgroup"])
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if host is not None or hostgroup is not None:
|
||||
commands.append([name, "hbacrule_add_host",
|
||||
{
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
if host:
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get("memberhost_host"))
|
||||
if hostgroup is not None:
|
||||
hostgroup_add = gen_add_list(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
|
||||
# Generate add lists for hbacsvc, hbacsvcgroup and
|
||||
# res_find to only try to add hbacsvcs and hbacsvcgroups
|
||||
# that not in hbacrule already
|
||||
if hbacsvc is not None and \
|
||||
"memberservice_hbacsvc" in res_find:
|
||||
hbacsvc = gen_add_list(
|
||||
hbacsvc, res_find["memberservice_hbacsvc"])
|
||||
if hbacsvcgroup is not None and \
|
||||
"memberservice_hbacsvcgroup" in res_find:
|
||||
hbacsvcgroup = gen_add_list(
|
||||
if hbacsvc:
|
||||
hbacsvc_add = gen_add_list(
|
||||
hbacsvc, res_find.get("memberservice_hbacsvc"))
|
||||
if hbacsvcgroup:
|
||||
hbacsvcgroup_add = gen_add_list(
|
||||
hbacsvcgroup,
|
||||
res_find["memberservice_hbacsvcgroup"])
|
||||
|
||||
# Add hbacsvcs and hbacsvcgroups
|
||||
if hbacsvc is not None or hbacsvcgroup is not None:
|
||||
commands.append([name, "hbacrule_add_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc,
|
||||
"hbacsvcgroup": hbacsvcgroup,
|
||||
}])
|
||||
res_find.get("memberservice_hbacsvcgroup"))
|
||||
|
||||
# Generate add lists for user, group and
|
||||
# res_find to only try to add users and groups
|
||||
# that not in hbacrule already
|
||||
if user is not None and \
|
||||
"memberuser_user" in res_find:
|
||||
user = gen_add_list(
|
||||
user, res_find["memberuser_user"])
|
||||
if group is not None and \
|
||||
"memberuser_group" in res_find:
|
||||
group = gen_add_list(
|
||||
group, res_find["memberuser_group"])
|
||||
|
||||
# Add users and groups
|
||||
if user is not None or group is not None:
|
||||
commands.append([name, "hbacrule_add_user",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
}])
|
||||
if user:
|
||||
user_add = gen_add_list(
|
||||
user, res_find.get("memberuser_user"))
|
||||
if group:
|
||||
group_add = gen_add_list(
|
||||
group, res_find.get("memberuser_group"))
|
||||
|
||||
elif state == "absent":
|
||||
if action == "hbacrule":
|
||||
@@ -491,75 +432,39 @@ def main():
|
||||
# Generate intersection lists for host, hostgroup and
|
||||
# res_find to only try to remove hosts and hostgroups
|
||||
# that are in hbacrule
|
||||
if host is not None:
|
||||
if host:
|
||||
if "memberhost_host" in res_find:
|
||||
host = gen_intersection_list(
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find["memberhost_host"])
|
||||
else:
|
||||
host = None
|
||||
if hostgroup is not None:
|
||||
if hostgroup:
|
||||
if "memberhost_hostgroup" in res_find:
|
||||
hostgroup = gen_intersection_list(
|
||||
hostgroup_del = gen_intersection_list(
|
||||
hostgroup, res_find["memberhost_hostgroup"])
|
||||
else:
|
||||
hostgroup = None
|
||||
|
||||
# Remove hosts and hostgroups
|
||||
if host is not None or hostgroup is not None:
|
||||
commands.append([name, "hbacrule_remove_host",
|
||||
{
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
|
||||
# Generate intersection lists for hbacsvc, hbacsvcgroup
|
||||
# and res_find to only try to remove hbacsvcs and
|
||||
# hbacsvcgroups that are in hbacrule
|
||||
if hbacsvc is not None:
|
||||
if hbacsvc:
|
||||
if "memberservice_hbacsvc" in res_find:
|
||||
hbacsvc = gen_intersection_list(
|
||||
hbacsvc_del = gen_intersection_list(
|
||||
hbacsvc, res_find["memberservice_hbacsvc"])
|
||||
else:
|
||||
hbacsvc = None
|
||||
if hbacsvcgroup is not None:
|
||||
if hbacsvcgroup:
|
||||
if "memberservice_hbacsvcgroup" in res_find:
|
||||
hbacsvcgroup = gen_intersection_list(
|
||||
hbacsvcgroup_del = gen_intersection_list(
|
||||
hbacsvcgroup,
|
||||
res_find["memberservice_hbacsvcgroup"])
|
||||
else:
|
||||
hbacsvcgroup = None
|
||||
|
||||
# Remove hbacsvcs and hbacsvcgroups
|
||||
if hbacsvc is not None or hbacsvcgroup is not None:
|
||||
commands.append([name, "hbacrule_remove_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc,
|
||||
"hbacsvcgroup": hbacsvcgroup,
|
||||
}])
|
||||
|
||||
# Generate intersection lists for user, group and
|
||||
# res_find to only try to remove users and groups
|
||||
# that are in hbacrule
|
||||
if user is not None:
|
||||
if user:
|
||||
if "memberuser_user" in res_find:
|
||||
user = gen_intersection_list(
|
||||
user_del = gen_intersection_list(
|
||||
user, res_find["memberuser_user"])
|
||||
else:
|
||||
user = None
|
||||
if group is not None:
|
||||
if group:
|
||||
if "memberuser_group" in res_find:
|
||||
group = gen_intersection_list(
|
||||
group_del = gen_intersection_list(
|
||||
group, res_find["memberuser_group"])
|
||||
else:
|
||||
group = None
|
||||
|
||||
# Remove users and groups
|
||||
if user is not None or group is not None:
|
||||
commands.append([name, "hbacrule_remove_user",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
}])
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is None:
|
||||
@@ -584,6 +489,53 @@ def main():
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Manage HBAC rule members.
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if len(host_add) > 0 or len(hostgroup_add) > 0:
|
||||
commands.append([name, "hbacrule_add_host",
|
||||
{
|
||||
"host": host_add,
|
||||
"hostgroup": hostgroup_add,
|
||||
}])
|
||||
# Remove hosts and hostgroups
|
||||
if len(host_del) > 0 or len(hostgroup_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_host",
|
||||
{
|
||||
"host": host_del,
|
||||
"hostgroup": hostgroup_del,
|
||||
}])
|
||||
|
||||
# Add hbacsvcs and hbacsvcgroups
|
||||
if len(hbacsvc_add) > 0 or len(hbacsvcgroup_add) > 0:
|
||||
commands.append([name, "hbacrule_add_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc_add,
|
||||
"hbacsvcgroup": hbacsvcgroup_add,
|
||||
}])
|
||||
# Remove hbacsvcs and hbacsvcgroups
|
||||
if len(hbacsvc_del) > 0 or len(hbacsvcgroup_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_service",
|
||||
{
|
||||
"hbacsvc": hbacsvc_del,
|
||||
"hbacsvcgroup": hbacsvcgroup_del,
|
||||
}])
|
||||
|
||||
# Add users and groups
|
||||
if len(user_add) > 0 or len(group_add) > 0:
|
||||
commands.append([name, "hbacrule_add_user",
|
||||
{
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
}])
|
||||
# Remove users and groups
|
||||
if len(user_del) > 0 or len(group_del) > 0:
|
||||
commands.append([name, "hbacrule_remove_user",
|
||||
{
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}])
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -98,7 +101,8 @@ RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
|
||||
|
||||
def find_hbacsvcgroup(module, name):
|
||||
@@ -180,7 +184,7 @@ def main():
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
nomembers = ansible_module.params_get("nomembers")
|
||||
hbacsvc = ansible_module.params_get("hbacsvc")
|
||||
hbacsvc = ansible_module.params_get_lowercase("hbacsvc")
|
||||
action = ansible_module.params_get("action")
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
@@ -220,6 +224,8 @@ def main():
|
||||
# Make sure hbacsvcgroup exists
|
||||
res_find = find_hbacsvcgroup(ansible_module, name)
|
||||
|
||||
hbacsvc_add, hbacsvc_del = [], []
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Generate args
|
||||
@@ -243,32 +249,20 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("member_hbacsvc"))
|
||||
if hbacsvc is not None:
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("member_hbacsvc"))
|
||||
|
||||
# Add members
|
||||
if len(hbacsvc_add) > 0:
|
||||
commands.append([name, "hbacsvcgroup_add_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc_add
|
||||
}])
|
||||
# Remove members
|
||||
if len(hbacsvc_del) > 0:
|
||||
commands.append([name,
|
||||
"hbacsvcgroup_remove_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc_del
|
||||
}])
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No hbacsvcgroup '%s'" % name)
|
||||
|
||||
# Ensure members are present
|
||||
commands.append([name, "hbacsvcgroup_add_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc
|
||||
}])
|
||||
if hbacsvc:
|
||||
hbacsvc_add = gen_add_list(
|
||||
hbacsvc, res_find.get("member_hbacsvc"))
|
||||
|
||||
elif state == "absent":
|
||||
if action == "hbacsvcgroup":
|
||||
if res_find is not None:
|
||||
@@ -280,15 +274,28 @@ def main():
|
||||
msg="No hbacsvcgroup '%s'" % name)
|
||||
|
||||
# Ensure members are absent
|
||||
commands.append([name, "hbacsvcgroup_remove_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc
|
||||
}])
|
||||
if hbacsvc:
|
||||
hbacsvc_del = gen_intersection_list(
|
||||
hbacsvc, res_find.get("member_hbacsvc"))
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
# Manage members
|
||||
if len(hbacsvc_add) > 0:
|
||||
commands.append([name, "hbacsvcgroup_add_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc_add
|
||||
}])
|
||||
# Remove members
|
||||
if len(hbacsvc_del) > 0:
|
||||
commands.append([name,
|
||||
"hbacsvcgroup_remove_member",
|
||||
{
|
||||
"hbacsvc": hbacsvc_del
|
||||
}])
|
||||
|
||||
# Execute commands
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
|
||||
# Done
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -403,7 +406,7 @@ host:
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
|
||||
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
@@ -436,7 +439,7 @@ def find_dnsrecord(module, name):
|
||||
This function may raise ipalib_errors.NotFound in some cases,
|
||||
and it should be handled by the caller.
|
||||
"""
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
_args = {
|
||||
@@ -1228,7 +1231,7 @@ def main():
|
||||
}])
|
||||
|
||||
if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
_args = {"idnsname": host_name}
|
||||
@@ -1245,7 +1248,7 @@ def main():
|
||||
"dnsrecord_add", _args])
|
||||
|
||||
if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
# There seems to be an issue with dnsrecord_del (not
|
||||
@@ -1361,7 +1364,7 @@ def main():
|
||||
|
||||
if "arecord" in dnsrecord_args or \
|
||||
"aaaarecord" in dnsrecord_args:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
dnsrecord_args["idnsname"] = host_name
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -136,7 +139,7 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
gen_intersection_list, ensure_fqdn
|
||||
|
||||
|
||||
def find_hostgroup(module, name):
|
||||
@@ -278,6 +281,15 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Renaming hostgroups is not supported by your IPA version")
|
||||
|
||||
# If hosts are given, ensure that the hosts are FQDN and also
|
||||
# lowercase to be able to do a proper comparison to exising hosts
|
||||
# in the hostgroup.
|
||||
# Fixes #666 (ipahostgroup not idempotent and with error)
|
||||
if host is not None:
|
||||
default_domain = ansible_module.ipa_get_domain()
|
||||
host = [ensure_fqdn(_host, default_domain).lower()
|
||||
for _host in host]
|
||||
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
"""ansible-freeipa module to manage FreeIPA privileges."""
|
||||
|
||||
|
||||
@@ -108,8 +111,9 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
|
||||
import six
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -126,22 +130,6 @@ def find_privilege(module, name):
|
||||
return _result["result"]
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
for failed_item in result.get("failed", []):
|
||||
failed = result["failed"][failed_item]
|
||||
for member_type in failed:
|
||||
for member, failure in failed[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
@@ -230,47 +218,31 @@ def main():
|
||||
if action == "privilege":
|
||||
# Found the privilege
|
||||
if res_find is not None:
|
||||
res_cmp = {
|
||||
k: v for k, v in res_find.items()
|
||||
if k not in [
|
||||
"objectclass", "cn", "dn",
|
||||
"memberof_permisssion"
|
||||
]
|
||||
}
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if args and not compare_args_ipa(ansible_module, args,
|
||||
res_cmp):
|
||||
cmp = {"description": res_find.get("description")}
|
||||
if not compare_args_ipa(ansible_module, args, cmp):
|
||||
commands.append([name, "privilege_mod", args])
|
||||
else:
|
||||
commands.append([name, "privilege_add", args])
|
||||
res_find = {}
|
||||
|
||||
member_args = {}
|
||||
if permission:
|
||||
member_args['permission'] = permission
|
||||
# Generate addition and removal lists
|
||||
permission_add, permission_del = gen_add_del_lists(
|
||||
permission, res_find.get("memberof_permission")
|
||||
)
|
||||
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
|
||||
# Generate addition and removal lists
|
||||
permission_add, permission_del = gen_add_del_lists(
|
||||
permission, res_find.get("member_permission"))
|
||||
|
||||
# Add members
|
||||
if len(permission_add) > 0:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{
|
||||
"permission": permission_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(permission_del) > 0:
|
||||
commands.append([
|
||||
name,
|
||||
"privilege_remove_permission",
|
||||
{"permission": permission_del}
|
||||
])
|
||||
# Add members
|
||||
if len(permission_add) > 0:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{
|
||||
"permission": permission_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(permission_del) > 0:
|
||||
commands.append([
|
||||
name,
|
||||
"privilege_remove_permission",
|
||||
{"permission": permission_del}
|
||||
])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -280,8 +252,11 @@ def main():
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{"permission": permission}])
|
||||
permission = gen_add_list(
|
||||
permission, res_find.get("memberof_permission"))
|
||||
if permission:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{"permission": permission}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "privilege":
|
||||
@@ -296,10 +271,11 @@ def main():
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_remove_permission",
|
||||
{
|
||||
"permission": permission,
|
||||
}])
|
||||
permission = gen_intersection_list(
|
||||
permission, res_find.get("memberof_permission"))
|
||||
if permission:
|
||||
commands.append([name, "privilege_remove_permission",
|
||||
{"permission": permission}])
|
||||
|
||||
elif state == "renamed":
|
||||
if not rename:
|
||||
@@ -318,7 +294,8 @@ def main():
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""ansible-freeipa iparole module implementation."""
|
||||
|
||||
@@ -21,6 +20,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -69,7 +72,7 @@ options:
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "renamed"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
@@ -100,9 +103,9 @@ EXAMPLES = """
|
||||
# pylint: disable=no-name-in-module
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, gen_add_del_lists, compare_args_ipa
|
||||
import six
|
||||
|
||||
IPAAnsibleModule, gen_add_del_lists, compare_args_ipa, \
|
||||
gen_intersection_list, ensure_fqdn
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -142,9 +145,22 @@ def check_parameters(module):
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if state == "renamed":
|
||||
if action == "member":
|
||||
invalid.extend(['description', 'rename'])
|
||||
module.fail_json(
|
||||
msg="Invalid action 'member' with state 'renamed'.")
|
||||
invalid = [
|
||||
"description",
|
||||
"user", "group",
|
||||
"host", "hostgroup",
|
||||
"service",
|
||||
"privilege",
|
||||
]
|
||||
|
||||
if state == "present":
|
||||
invalid = ["rename"]
|
||||
if action == "member":
|
||||
invalid.extend(['description'])
|
||||
|
||||
if state == "absent":
|
||||
invalid.extend(['description', 'rename'])
|
||||
@@ -154,28 +170,15 @@ def check_parameters(module):
|
||||
module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
|
||||
def member_intersect(module, attr, memberof, res_find):
|
||||
"""Filter member arguments from role found by intersection."""
|
||||
params = module.params_get(attr)
|
||||
if not res_find:
|
||||
return params
|
||||
filtered = []
|
||||
if params:
|
||||
existing = res_find.get(memberof, [])
|
||||
filtered = list(set(params) & set(existing))
|
||||
return filtered
|
||||
|
||||
|
||||
def member_difference(module, attr, memberof, res_find):
|
||||
"""Filter member arguments from role found by difference."""
|
||||
params = module.params_get(attr)
|
||||
if not res_find:
|
||||
return params
|
||||
filtered = []
|
||||
if params:
|
||||
existing = res_find.get(memberof, [])
|
||||
filtered = list(set(params) - set(existing))
|
||||
return filtered
|
||||
def get_member_host_with_fqdn_lowercase(module, mod_member):
|
||||
"""Retrieve host members from module, as FQDN, lowercase."""
|
||||
default_domain = module.ipa_get_domain()
|
||||
hosts = module.params_get(mod_member)
|
||||
return (
|
||||
[ensure_fqdn(host, default_domain).lower() for host in hosts]
|
||||
if hosts
|
||||
else hosts
|
||||
)
|
||||
|
||||
|
||||
def ensure_absent_state(module, name, action, res_find):
|
||||
@@ -187,23 +190,41 @@ def ensure_absent_state(module, name, action, res_find):
|
||||
|
||||
if action == "member":
|
||||
|
||||
members = member_intersect(
|
||||
module, 'privilege', 'memberof_privilege', res_find)
|
||||
if members:
|
||||
commands.append([name, "role_remove_privilege",
|
||||
{"privilege": members}])
|
||||
_members = module.params_get_lowercase("privilege")
|
||||
if _members is not None:
|
||||
del_list = gen_intersection_list(
|
||||
_members,
|
||||
result_get_value_lowercase(res_find, "memberof_privilege")
|
||||
)
|
||||
if del_list:
|
||||
commands.append([name, "role_remove_privilege",
|
||||
{"privilege": del_list}])
|
||||
|
||||
member_args = {}
|
||||
for key in ['user', 'group', 'host', 'hostgroup']:
|
||||
items = member_intersect(
|
||||
module, key, 'member_%s' % key, res_find)
|
||||
if items:
|
||||
member_args[key] = items
|
||||
for key in ['user', 'group', 'hostgroup']:
|
||||
_members = module.params_get_lowercase(key)
|
||||
if _members:
|
||||
del_list = gen_intersection_list(
|
||||
_members,
|
||||
result_get_value_lowercase(res_find, "member_%s" % key)
|
||||
)
|
||||
if del_list:
|
||||
member_args[key] = del_list
|
||||
|
||||
_services = filter_service(module, res_find,
|
||||
lambda res, svc: res.startswith(svc))
|
||||
# ensure hosts are FQDN.
|
||||
_members = get_member_host_with_fqdn_lowercase(module, "host")
|
||||
if _members:
|
||||
del_list = gen_intersection_list(
|
||||
_members, res_find.get('member_host'))
|
||||
if del_list:
|
||||
member_args["host"] = del_list
|
||||
|
||||
_services = get_service_param(module, "service")
|
||||
if _services:
|
||||
member_args['service'] = _services
|
||||
_existing = result_get_value_lowercase(res_find, "member_service")
|
||||
items = gen_intersection_list(_services.keys(), _existing)
|
||||
if items:
|
||||
member_args["service"] = [_services[key] for key in items]
|
||||
|
||||
# Only add remove command if there's at least one member no manage.
|
||||
if member_args:
|
||||
@@ -212,66 +233,112 @@ def ensure_absent_state(module, name, action, res_find):
|
||||
return commands
|
||||
|
||||
|
||||
def filter_service(module, res_find, predicate):
|
||||
def get_service_param(module, key):
|
||||
"""
|
||||
Filter service based on predicate.
|
||||
Retrieve dict of services, with realm, from the module parameters.
|
||||
|
||||
Compare service name with existing ones matching
|
||||
at least until `@` from principal name.
|
||||
|
||||
Predicate is a callable that accepts the existing service, and the
|
||||
modified service to be compared to.
|
||||
As the services are compared in a case insensitive manner, but
|
||||
are recorded in a case preserving way, a dict mapping the services
|
||||
in lowercase to the provided module parameter is generated, so
|
||||
that dict keys can be used for comparison and the values are used
|
||||
with IPA API.
|
||||
"""
|
||||
_services = []
|
||||
service = module.params_get('service')
|
||||
if service:
|
||||
existing = [to_text(x) for x in res_find.get('member_service', [])]
|
||||
for svc in service:
|
||||
svc = svc if '@' in svc else ('%s@' % svc)
|
||||
found = [x for x in existing if predicate(x, svc)]
|
||||
_services.extend(found)
|
||||
_services = module.params_get(key)
|
||||
if _services is not None:
|
||||
ipa_realm = module.ipa_get_realm()
|
||||
_services = [
|
||||
to_text(svc) if '@' in svc else ('%s@%s' % (svc, ipa_realm))
|
||||
for svc in _services
|
||||
]
|
||||
if _services:
|
||||
_services = {svc.lower(): svc for svc in _services}
|
||||
return _services
|
||||
|
||||
|
||||
def result_get_value_lowercase(res_find, key, default=None):
|
||||
"""
|
||||
Retrieve a member of a dictionary converted to lowercase.
|
||||
|
||||
If field data is a string it is returned in lowercase. If
|
||||
field data is a list or tuple, it is assumed that all values
|
||||
are strings and the result is a list of strings in lowercase.
|
||||
|
||||
If 'key' is not found in the dictionary, returns 'default'.
|
||||
"""
|
||||
existing = res_find.get(key)
|
||||
if existing is not None:
|
||||
if isinstance(existing, (list, tuple)):
|
||||
existing = [to_text(item).lower() for item in existing]
|
||||
if isinstance(existing, (str, unicode)):
|
||||
existing = existing.lower()
|
||||
else:
|
||||
existing = default
|
||||
return existing
|
||||
|
||||
|
||||
def gen_services_add_del_lists(module, mod_member, res_find, res_member):
|
||||
"""Generate add/del lists for service principals."""
|
||||
add_list, del_list = None, None
|
||||
_services = get_service_param(module, mod_member)
|
||||
if _services is not None:
|
||||
_existing = result_get_value_lowercase(res_find, res_member)
|
||||
add_list, del_list = gen_add_del_lists(_services.keys(), _existing)
|
||||
if add_list:
|
||||
add_list = [_services[key] for key in add_list]
|
||||
if del_list:
|
||||
del_list = [to_text(item) for item in del_list]
|
||||
return add_list, del_list
|
||||
|
||||
|
||||
def ensure_role_with_members_is_present(module, name, res_find, action):
|
||||
"""Define commands to ensure member are present for action `role`."""
|
||||
commands = []
|
||||
privilege_add, privilege_del = gen_add_del_lists(
|
||||
module.params_get("privilege"),
|
||||
res_find.get('memberof_privilege', []))
|
||||
|
||||
if privilege_add:
|
||||
commands.append([name, "role_add_privilege",
|
||||
{"privilege": privilege_add}])
|
||||
if action == "role" and privilege_del:
|
||||
commands.append([name, "role_remove_privilege",
|
||||
{"privilege": privilege_del}])
|
||||
_members = module.params_get_lowercase("privilege")
|
||||
if _members:
|
||||
add_list, del_list = gen_add_del_lists(
|
||||
_members,
|
||||
result_get_value_lowercase(res_find, "memberof_privilege")
|
||||
)
|
||||
|
||||
if add_list:
|
||||
commands.append([name, "role_add_privilege",
|
||||
{"privilege": add_list}])
|
||||
if action == "role" and del_list:
|
||||
commands.append([name, "role_remove_privilege",
|
||||
{"privilege": del_list}])
|
||||
|
||||
add_members = {}
|
||||
del_members = {}
|
||||
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
add_list, del_list = gen_add_del_lists(
|
||||
module.params_get(key),
|
||||
res_find.get('member_%s' % key, [])
|
||||
)
|
||||
if add_list:
|
||||
add_members[key] = add_list
|
||||
if del_list:
|
||||
del_members[key] = [to_text(item) for item in del_list]
|
||||
for key in ["user", "group", "hostgroup"]:
|
||||
_members = module.params_get_lowercase(key)
|
||||
if _members is not None:
|
||||
add_list, del_list = gen_add_del_lists(
|
||||
_members,
|
||||
result_get_value_lowercase(res_find, "member_%s" % key)
|
||||
)
|
||||
if add_list:
|
||||
add_members[key] = add_list
|
||||
if del_list:
|
||||
del_members[key] = del_list
|
||||
|
||||
service = [
|
||||
to_text(svc)
|
||||
if '@' in svc
|
||||
else ('%s@%s' % (svc, module.ipa_get_realm()))
|
||||
for svc in (module.params_get('service') or [])
|
||||
]
|
||||
existing = [str(svc) for svc in res_find.get('member_service', [])]
|
||||
add_list, del_list = gen_add_del_lists(service, existing)
|
||||
if add_list:
|
||||
add_members['service'] = add_list
|
||||
if del_list:
|
||||
del_members['service'] = [to_text(item) for item in del_list]
|
||||
# ensure hosts are FQDN.
|
||||
_members = get_member_host_with_fqdn_lowercase(module, "host")
|
||||
if _members:
|
||||
add_list, del_list = gen_add_del_lists(
|
||||
_members, res_find.get('member_host'))
|
||||
if add_list:
|
||||
add_members["host"] = add_list
|
||||
if del_list:
|
||||
del_members["host"] = del_list
|
||||
|
||||
(add_services, del_services) = gen_services_add_del_lists(
|
||||
module, "service", res_find, "member_service")
|
||||
if add_services:
|
||||
add_members["service"] = add_services
|
||||
if del_services:
|
||||
del_members["service"] = del_services
|
||||
|
||||
if add_members:
|
||||
commands.append([name, "role_add_member", add_members])
|
||||
@@ -282,67 +349,24 @@ def ensure_role_with_members_is_present(module, name, res_find, action):
|
||||
return commands
|
||||
|
||||
|
||||
def ensure_members_are_present(module, name, res_find):
|
||||
"""Define commands to ensure members are present for action `member`."""
|
||||
commands = []
|
||||
|
||||
members = member_difference(
|
||||
module, 'privilege', 'memberof_privilege', res_find)
|
||||
if members:
|
||||
commands.append([name, "role_add_privilege",
|
||||
{"privilege": members}])
|
||||
|
||||
member_args = {}
|
||||
for key in ['user', 'group', 'host', 'hostgroup']:
|
||||
items = member_difference(
|
||||
module, key, 'member_%s' % key, res_find)
|
||||
if items:
|
||||
member_args[key] = items
|
||||
|
||||
_services = filter_service(module, res_find,
|
||||
lambda res, svc: not res.startswith(svc))
|
||||
if _services:
|
||||
member_args['service'] = _services
|
||||
|
||||
if member_args:
|
||||
commands.append([name, "role_add_member", member_args])
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
"""Process the result of a command, looking for errors."""
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
if "failed" in result and len(result["failed"]) > 0:
|
||||
for item in result["failed"]:
|
||||
failed_item = result["failed"][item]
|
||||
for member_type in failed_item:
|
||||
for member, failure in failed_item[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def role_commands_for_name(module, state, action, name):
|
||||
"""Define commands for the Role module."""
|
||||
commands = []
|
||||
|
||||
rename = module.params_get("rename")
|
||||
|
||||
res_find = find_role(module, name)
|
||||
|
||||
if state == "renamed":
|
||||
args = gen_args(module)
|
||||
if res_find is None:
|
||||
module.fail_json(msg="No role '%s'" % name)
|
||||
else:
|
||||
commands.append([name, 'role_mod', args])
|
||||
|
||||
if state == "present":
|
||||
args = gen_args(module)
|
||||
|
||||
if action == "role":
|
||||
if res_find is None:
|
||||
if rename is not None:
|
||||
module.fail_json(msg="Cannot `rename` inexistent role.")
|
||||
commands.append([name, 'role_add', args])
|
||||
res_find = {}
|
||||
else:
|
||||
@@ -388,7 +412,7 @@ def create_module():
|
||||
action=dict(type="str", default="role",
|
||||
choices=["role", "member"]),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
choices=["present", "absent", "renamed"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[],
|
||||
@@ -423,7 +447,8 @@ def main():
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -183,21 +186,23 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT
|
||||
ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH
|
||||
VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM
|
||||
LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT
|
||||
oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s
|
||||
4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc
|
||||
xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1
|
||||
UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q
|
||||
eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs
|
||||
5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic
|
||||
uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH
|
||||
2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no
|
||||
obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC
|
||||
/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq
|
||||
- >
|
||||
MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8x
|
||||
DTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVG
|
||||
FYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEr
|
||||
aELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmj
|
||||
fMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQI
|
||||
Nt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJ
|
||||
EkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMB
|
||||
AAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV
|
||||
0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJP
|
||||
rp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfw
|
||||
Q5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHo
|
||||
VOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+s
|
||||
KC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA
|
||||
5yqV7MVq
|
||||
action: member
|
||||
state: present
|
||||
|
||||
@@ -224,7 +229,8 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, encode_certificate, \
|
||||
gen_add_del_lists, ipalib_errors
|
||||
gen_add_del_lists, gen_add_list, gen_intersection_list, ipalib_errors, \
|
||||
api_get_realm, to_text
|
||||
|
||||
|
||||
def find_service(module, name):
|
||||
@@ -283,9 +289,7 @@ def gen_args_smb(netbiosname, ok_as_delegate, ok_to_auth_as_delegate):
|
||||
return _args
|
||||
|
||||
|
||||
def check_parameters(module, state, action, names, parameters):
|
||||
assert isinstance(parameters, dict)
|
||||
|
||||
def check_parameters(module, state, action, names):
|
||||
# invalid parameters for everything but state 'present', action 'service'.
|
||||
invalid = ['pac_type', 'auth_ind', 'skip_host_check',
|
||||
'force', 'requires_pre_auth', 'ok_as_delegate',
|
||||
@@ -307,8 +311,8 @@ def check_parameters(module, state, action, names, parameters):
|
||||
invalid = ['delete_continue']
|
||||
|
||||
if (
|
||||
not parameters.get('smb', False)
|
||||
and parameters.get('netbiosname')
|
||||
not module.params_get('smb')
|
||||
and module.params_get('netbiosname')
|
||||
):
|
||||
module.fail_json(
|
||||
msg="Argument 'netbiosname' can not be used without "
|
||||
@@ -405,23 +409,6 @@ def init_ansible_module():
|
||||
return ansible_module
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
if "failed" in result and len(result["failed"]) > 0:
|
||||
for item in result["failed"]:
|
||||
failed_item = result["failed"][item]
|
||||
for member_type in failed_item:
|
||||
for member, failure in failed_item[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = init_ansible_module()
|
||||
|
||||
@@ -447,23 +434,6 @@ def main():
|
||||
|
||||
host = ansible_module.params_get("host")
|
||||
|
||||
allow_create_keytab_user = ansible_module.params_get(
|
||||
"allow_create_keytab_user")
|
||||
allow_create_keytab_group = ansible_module.params_get(
|
||||
"allow_create_keytab_group")
|
||||
allow_create_keytab_host = ansible_module.params_get(
|
||||
"allow_create_keytab_host")
|
||||
allow_create_keytab_hostgroup = ansible_module.params_get(
|
||||
"allow_create_keytab_hostgroup")
|
||||
|
||||
allow_retrieve_keytab_user = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_user")
|
||||
allow_retrieve_keytab_group = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_group")
|
||||
allow_retrieve_keytab_host = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_host")
|
||||
allow_retrieve_keytab_hostgroup = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_hostgroup")
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# action
|
||||
@@ -472,7 +442,7 @@ def main():
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# check parameters
|
||||
check_parameters(ansible_module, state, action, names, vars())
|
||||
check_parameters(ansible_module, state, action, names)
|
||||
|
||||
# Init
|
||||
|
||||
@@ -489,9 +459,48 @@ def main():
|
||||
msg="Skipping host check is not supported by your IPA version")
|
||||
|
||||
commands = []
|
||||
keytab_members = ["user", "group", "host", "hostgroup"]
|
||||
|
||||
for name in names:
|
||||
res_find = find_service(ansible_module, name)
|
||||
res_principals = []
|
||||
|
||||
keytab = {
|
||||
"retrieve": {
|
||||
"allow": {k: [] for k in keytab_members},
|
||||
"disallow": {k: [] for k in keytab_members},
|
||||
},
|
||||
"create": {
|
||||
"allow": {k: [] for k in keytab_members},
|
||||
"disallow": {k: [] for k in keytab_members},
|
||||
},
|
||||
}
|
||||
certificate_add, certificate_del = [], []
|
||||
host_add, host_del = [], []
|
||||
principal_add, principal_del = [], []
|
||||
|
||||
if principal and res_find:
|
||||
# When comparing principals to the existing ones,
|
||||
# the REALM is needded, and are added here for those
|
||||
# that do not have it.
|
||||
principal = [
|
||||
p if "@" in p
|
||||
else "%s@%s" % (p, api_get_realm())
|
||||
for p in principal
|
||||
]
|
||||
principal = list(set(principal))
|
||||
|
||||
# Create list of existing principal aliases as strings
|
||||
# to compare with provided ones.
|
||||
canonicalname = {
|
||||
to_text(p)
|
||||
for p in res_find.get("krbcanonicalname", [])
|
||||
}
|
||||
res_principals = [
|
||||
to_text(elem)
|
||||
for elem in res_find.get("krbprincipalname", [])
|
||||
]
|
||||
res_principals = list(set(res_principals) - canonicalname)
|
||||
|
||||
if state == "present":
|
||||
if action == "service":
|
||||
@@ -520,37 +529,8 @@ def main():
|
||||
|
||||
if res_find is None:
|
||||
commands.append([name, 'service_add', args])
|
||||
|
||||
certificate_add = certificate or []
|
||||
certificate_del = []
|
||||
host_add = host or []
|
||||
host_del = []
|
||||
principal_add = principal or []
|
||||
principal_del = []
|
||||
allow_create_keytab_user_add = \
|
||||
allow_create_keytab_user or []
|
||||
allow_create_keytab_user_del = []
|
||||
allow_create_keytab_group_add = \
|
||||
allow_create_keytab_group or []
|
||||
allow_create_keytab_group_del = []
|
||||
allow_create_keytab_host_add = \
|
||||
allow_create_keytab_host or []
|
||||
allow_create_keytab_host_del = []
|
||||
allow_create_keytab_hostgroup_add = \
|
||||
allow_create_keytab_hostgroup or []
|
||||
allow_create_keytab_hostgroup_del = []
|
||||
allow_retrieve_keytab_user_add = \
|
||||
allow_retrieve_keytab_user or []
|
||||
allow_retrieve_keytab_user_del = []
|
||||
allow_retrieve_keytab_group_add = \
|
||||
allow_retrieve_keytab_group or []
|
||||
allow_retrieve_keytab_group_del = []
|
||||
allow_retrieve_keytab_host_add = \
|
||||
allow_retrieve_keytab_host or []
|
||||
allow_retrieve_keytab_host_del = []
|
||||
allow_retrieve_keytab_hostgroup_add = \
|
||||
allow_retrieve_keytab_hostgroup or []
|
||||
allow_retrieve_keytab_hostgroup_del = []
|
||||
# Use an empty res_find to manage members
|
||||
res_find = {}
|
||||
|
||||
else:
|
||||
for remove in ['skip_host_check', 'force']:
|
||||
@@ -563,207 +543,54 @@ def main():
|
||||
args.get("krbprincipalauthind", [""]) ==
|
||||
res_find.get("krbprincipalauthind", [""])
|
||||
)
|
||||
):
|
||||
):
|
||||
del args["krbprincipalauthind"]
|
||||
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "service_mod", args])
|
||||
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
# Manage members
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get('managedby_host', []))
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get('managedby_host'))
|
||||
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_find.get("principal"))
|
||||
|
||||
(allow_create_keytab_user_add,
|
||||
allow_create_keytab_user_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_user, res_find.get(
|
||||
'ipaallowedtoperform_write_keys_user',
|
||||
[]))
|
||||
(allow_retrieve_keytab_user_add,
|
||||
allow_retrieve_keytab_user_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_user, res_find.get(
|
||||
'ipaallowedtoperform_read_keys_user',
|
||||
[]))
|
||||
(allow_create_keytab_group_add,
|
||||
allow_create_keytab_group_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_group, res_find.get(
|
||||
'ipaallowedtoperform_write_keys_group',
|
||||
[]))
|
||||
(allow_retrieve_keytab_group_add,
|
||||
allow_retrieve_keytab_group_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_group,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_group',
|
||||
[]))
|
||||
(allow_create_keytab_host_add,
|
||||
allow_create_keytab_host_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_host,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_write_keys_host',
|
||||
[]))
|
||||
(allow_retrieve_keytab_host_add,
|
||||
allow_retrieve_keytab_host_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_host,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_host',
|
||||
[]))
|
||||
(allow_create_keytab_hostgroup_add,
|
||||
allow_create_keytab_hostgroup_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_hostgroup,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_write_keys_hostgroup',
|
||||
[]))
|
||||
(allow_retrieve_keytab_hostgroup_add,
|
||||
allow_retrieve_keytab_hostgroup_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_hostgroup,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_hostgroup',
|
||||
[]))
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_principals)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No service '%s'" % name)
|
||||
|
||||
existing = res_find.get('usercertificate', [])
|
||||
if certificate is None:
|
||||
certificate_add = []
|
||||
else:
|
||||
certificate_add = [c for c in certificate
|
||||
if c not in existing]
|
||||
certificate_del = []
|
||||
host_add = host or []
|
||||
host_del = []
|
||||
principal_add = principal or []
|
||||
principal_del = []
|
||||
certificate_add = gen_add_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
allow_create_keytab_user_add = \
|
||||
allow_create_keytab_user or []
|
||||
allow_create_keytab_user_del = []
|
||||
allow_create_keytab_group_add = \
|
||||
allow_create_keytab_group or []
|
||||
allow_create_keytab_group_del = []
|
||||
allow_create_keytab_host_add = \
|
||||
allow_create_keytab_host or []
|
||||
allow_create_keytab_host_del = []
|
||||
allow_create_keytab_hostgroup_add = \
|
||||
allow_create_keytab_hostgroup or []
|
||||
allow_create_keytab_hostgroup_del = []
|
||||
allow_retrieve_keytab_user_add = \
|
||||
allow_retrieve_keytab_user or []
|
||||
allow_retrieve_keytab_user_del = []
|
||||
allow_retrieve_keytab_group_add = \
|
||||
allow_retrieve_keytab_group or []
|
||||
allow_retrieve_keytab_group_del = []
|
||||
allow_retrieve_keytab_host_add = \
|
||||
allow_retrieve_keytab_host or []
|
||||
allow_retrieve_keytab_host_del = []
|
||||
allow_retrieve_keytab_hostgroup_add = \
|
||||
allow_retrieve_keytab_hostgroup or []
|
||||
allow_retrieve_keytab_hostgroup_del = []
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get('managedby_host'))
|
||||
|
||||
# Add principals
|
||||
for _principal in principal_add:
|
||||
commands.append([name, "service_add_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
principal_add = gen_add_list(principal, res_principals)
|
||||
|
||||
# Remove principals
|
||||
for _principal in principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
|
||||
for _certificate in certificate_add:
|
||||
commands.append([name, "service_add_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
# Remove certificates
|
||||
for _certificate in certificate_del:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
|
||||
# Add hosts.
|
||||
if host is not None and len(host) > 0 and len(host_add) > 0:
|
||||
commands.append([name, "service_add_host",
|
||||
{"host": host_add}])
|
||||
# Remove hosts
|
||||
if host is not None and len(host) > 0 and len(host_del) > 0:
|
||||
commands.append([name, "service_remove_host",
|
||||
{"host": host_del}])
|
||||
|
||||
# Allow create keytab
|
||||
if len(allow_create_keytab_user_add) > 0 or \
|
||||
len(allow_create_keytab_group_add) > 0 or \
|
||||
len(allow_create_keytab_host_add) > 0 or \
|
||||
len(allow_create_keytab_hostgroup_add) > 0:
|
||||
commands.append(
|
||||
[name, "service_allow_create_keytab",
|
||||
{'user': allow_create_keytab_user_add,
|
||||
'group': allow_create_keytab_group_add,
|
||||
'host': allow_create_keytab_host_add,
|
||||
'hostgroup': allow_create_keytab_hostgroup_add
|
||||
}])
|
||||
|
||||
# Disallow create keytab
|
||||
if len(allow_create_keytab_user_del) > 0 or \
|
||||
len(allow_create_keytab_group_del) > 0 or \
|
||||
len(allow_create_keytab_host_del) > 0 or \
|
||||
len(allow_create_keytab_hostgroup_del) > 0:
|
||||
commands.append(
|
||||
[name, "service_disallow_create_keytab",
|
||||
{'user': allow_create_keytab_user_del,
|
||||
'group': allow_create_keytab_group_del,
|
||||
'host': allow_create_keytab_host_del,
|
||||
'hostgroup': allow_create_keytab_hostgroup_del
|
||||
}])
|
||||
|
||||
# Allow retrieve keytab
|
||||
if len(allow_retrieve_keytab_user_add) > 0 or \
|
||||
len(allow_retrieve_keytab_group_add) > 0 or \
|
||||
len(allow_retrieve_keytab_host_add) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_add) > 0:
|
||||
commands.append(
|
||||
[name, "service_allow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user_add,
|
||||
'group': allow_retrieve_keytab_group_add,
|
||||
'host': allow_retrieve_keytab_host_add,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup_add
|
||||
}])
|
||||
|
||||
# Disllow retrieve keytab
|
||||
if len(allow_retrieve_keytab_user_del) > 0 or \
|
||||
len(allow_retrieve_keytab_group_del) > 0 or \
|
||||
len(allow_retrieve_keytab_host_del) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_del) > 0:
|
||||
commands.append(
|
||||
[name, "service_disallow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user_del,
|
||||
'group': allow_retrieve_keytab_group_del,
|
||||
'host': allow_retrieve_keytab_host_del,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup_del
|
||||
}])
|
||||
# get keytab management lists for any 'action'.
|
||||
for perm in ["create", "retrieve"]:
|
||||
oper = "write" if perm == "create" else "read"
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
add_list, del_list = (
|
||||
gen_add_del_lists(
|
||||
ansible_module.params_get(
|
||||
"allow_%s_keytab_%s" % (perm, key)
|
||||
),
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_%s_keys_%s'
|
||||
% (oper, key)
|
||||
)
|
||||
)
|
||||
)
|
||||
keytab[perm]["allow"][key] = add_list
|
||||
# Only remove members if action is 'service'
|
||||
if action == "service":
|
||||
keytab[perm]["disallow"][key] = del_list
|
||||
|
||||
elif state == "absent":
|
||||
if action == "service":
|
||||
@@ -775,55 +602,30 @@ def main():
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No service '%s'" % name)
|
||||
|
||||
# Remove principals
|
||||
if principal is not None:
|
||||
for _principal in principal:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
# Remove certificates
|
||||
if certificate is not None:
|
||||
existing = res_find.get('usercertificate', [])
|
||||
for _certificate in certificate:
|
||||
if _certificate in existing:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
principal_del = gen_intersection_list(
|
||||
principal, res_principals)
|
||||
|
||||
# Add hosts
|
||||
if host is not None:
|
||||
commands.append(
|
||||
[name, "service_remove_host", {"host": host}])
|
||||
certificate_del = gen_intersection_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
# Allow create keytab
|
||||
if allow_create_keytab_user is not None or \
|
||||
allow_create_keytab_group is not None or \
|
||||
allow_create_keytab_host is not None or \
|
||||
allow_create_keytab_hostgroup is not None:
|
||||
commands.append(
|
||||
[name, "service_disallow_create_keytab",
|
||||
{'user': allow_create_keytab_user,
|
||||
'group': allow_create_keytab_group,
|
||||
'host': allow_create_keytab_host,
|
||||
'hostgroup': allow_create_keytab_hostgroup
|
||||
}])
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find.get("managedby_host"))
|
||||
|
||||
# Allow retriev keytab
|
||||
if allow_retrieve_keytab_user is not None or \
|
||||
allow_retrieve_keytab_group is not None or \
|
||||
allow_retrieve_keytab_host is not None or \
|
||||
allow_retrieve_keytab_hostgroup is not None:
|
||||
commands.append(
|
||||
[name, "service_disallow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user,
|
||||
'group': allow_retrieve_keytab_group,
|
||||
'host': allow_retrieve_keytab_host,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup
|
||||
}])
|
||||
for perm in ["create", "retrieve"]:
|
||||
oper = "write" if perm == "create" else "read"
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
res_param = (
|
||||
'ipaallowedtoperform_%s_keys_%s'
|
||||
% (oper, key)
|
||||
)
|
||||
module_params = ansible_module.params_get(
|
||||
"allow_%s_keytab_%s" % (perm, key)
|
||||
)
|
||||
existing = res_find.get(res_param)
|
||||
del_list = (
|
||||
gen_intersection_list(module_params, existing)
|
||||
)
|
||||
keytab[perm]["disallow"][key] = del_list
|
||||
|
||||
elif state == "disabled":
|
||||
if action == "service":
|
||||
@@ -836,16 +638,53 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid action '%s' for state '%s'" %
|
||||
(action, state))
|
||||
# Members are not managed when disabling service.
|
||||
# Continue with next 'name'.
|
||||
continue
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Manage members
|
||||
if principal_add:
|
||||
commands.append([name, "service_add_principal",
|
||||
{"krbprincipalname": principal_add}])
|
||||
if principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{"krbprincipalname": principal_del}])
|
||||
|
||||
if certificate_add:
|
||||
commands.append([name, "service_add_cert",
|
||||
{"usercertificate": certificate_add}])
|
||||
if certificate_del:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{"usercertificate": certificate_del}])
|
||||
|
||||
if host_add:
|
||||
commands.append([name, "service_add_host",
|
||||
{"host": host_add}])
|
||||
if host_del:
|
||||
commands.append([name, "service_remove_host",
|
||||
{"host": host_del}])
|
||||
|
||||
# manage keytab permissions.
|
||||
for perm in ["create", "retrieve"]:
|
||||
for mode in ["allow", "disallow"]:
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
if keytab[perm][mode][key]:
|
||||
commands.append([
|
||||
name,
|
||||
"service_%s_%s_keytab" % (mode, perm),
|
||||
keytab[perm][mode]
|
||||
])
|
||||
break
|
||||
|
||||
# Check mode exit
|
||||
if ansible_module.check_mode:
|
||||
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -185,7 +188,7 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
gen_intersection_list, api_get_domain, ensure_fqdn
|
||||
|
||||
|
||||
def find_sudorule(module, name):
|
||||
@@ -294,17 +297,19 @@ def main():
|
||||
hostcategory = ansible_module.params_get("hostcategory") # noqa
|
||||
nomembers = ansible_module.params_get("nomembers") # noqa
|
||||
host = ansible_module.params_get("host")
|
||||
hostgroup = ansible_module.params_get("hostgroup")
|
||||
user = ansible_module.params_get("user")
|
||||
group = ansible_module.params_get("group")
|
||||
hostgroup = ansible_module.params_get_lowercase("hostgroup")
|
||||
user = ansible_module.params_get_lowercase("user")
|
||||
group = ansible_module.params_get_lowercase("group")
|
||||
allow_sudocmd = ansible_module.params_get('allow_sudocmd')
|
||||
allow_sudocmdgroup = ansible_module.params_get('allow_sudocmdgroup')
|
||||
allow_sudocmdgroup = \
|
||||
ansible_module.params_get_lowercase('allow_sudocmdgroup')
|
||||
deny_sudocmd = ansible_module.params_get('deny_sudocmd')
|
||||
deny_sudocmdgroup = ansible_module.params_get('deny_sudocmdgroup')
|
||||
deny_sudocmdgroup = \
|
||||
ansible_module.params_get_lowercase('deny_sudocmdgroup')
|
||||
sudooption = ansible_module.params_get("sudooption")
|
||||
order = ansible_module.params_get("order")
|
||||
runasuser = ansible_module.params_get("runasuser")
|
||||
runasgroup = ansible_module.params_get("runasgroup")
|
||||
runasuser = ansible_module.params_get_lowercase("runasuser")
|
||||
runasgroup = ansible_module.params_get_lowercase("runasgroup")
|
||||
action = ansible_module.params_get("action")
|
||||
|
||||
# state
|
||||
@@ -371,8 +376,26 @@ def main():
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
default_domain = api_get_domain()
|
||||
|
||||
# Ensure host is not short hostname.
|
||||
if host:
|
||||
host = list(
|
||||
{ensure_fqdn(value.lower(), default_domain) for value in host}
|
||||
)
|
||||
|
||||
commands = []
|
||||
host_add, host_del = [], []
|
||||
user_add, user_del = [], []
|
||||
group_add, group_del = [], []
|
||||
hostgroup_add, hostgroup_del = [], []
|
||||
allow_cmd_add, allow_cmd_del = [], []
|
||||
allow_cmdgroup_add, allow_cmdgroup_del = [], []
|
||||
deny_cmd_add, deny_cmd_del = [], []
|
||||
deny_cmdgroup_add, deny_cmdgroup_del = [], []
|
||||
sudooption_add, sudooption_del = [], []
|
||||
runasuser_add, runasuser_del = [], []
|
||||
runasgroup_add, runasgroup_del = [], []
|
||||
|
||||
for name in names:
|
||||
# Make sure sudorule exists
|
||||
@@ -456,95 +479,31 @@ def main():
|
||||
sudooption_add, sudooption_del = gen_add_del_lists(
|
||||
sudooption, res_find.get('ipasudoopt', []))
|
||||
|
||||
# runasuser attribute can be used with both IPA and
|
||||
# non-IPA (external) users. IPA will handle the correct
|
||||
# attribute to properly store data, so we need to compare
|
||||
# the provided list against both users and external
|
||||
# users list.
|
||||
runasuser_add, runasuser_del = gen_add_del_lists(
|
||||
runasuser, res_find.get('ipasudorunas_user', []))
|
||||
runasuser,
|
||||
(
|
||||
res_find.get('ipasudorunas_user', [])
|
||||
+ res_find.get('ipasudorunasextuser', [])
|
||||
)
|
||||
)
|
||||
|
||||
# runasgroup attribute can be used with both IPA and
|
||||
# non-IPA (external) groups. IPA will handle the correct
|
||||
# attribute to properly store data, so we need to compare
|
||||
# the provided list against both groups and external
|
||||
# groups list.
|
||||
runasgroup_add, runasgroup_del = gen_add_del_lists(
|
||||
runasgroup, res_find.get('ipasudorunas_group', []))
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if len(host_add) > 0 or len(hostgroup_add) > 0:
|
||||
commands.append([name, "sudorule_add_host",
|
||||
{
|
||||
"host": host_add,
|
||||
"hostgroup": hostgroup_add,
|
||||
}])
|
||||
# Remove hosts and hostgroups
|
||||
if len(host_del) > 0 or len(hostgroup_del) > 0:
|
||||
commands.append([name, "sudorule_remove_host",
|
||||
{
|
||||
"host": host_del,
|
||||
"hostgroup": hostgroup_del,
|
||||
}])
|
||||
|
||||
# Add users and groups
|
||||
if len(user_add) > 0 or len(group_add) > 0:
|
||||
commands.append([name, "sudorule_add_user",
|
||||
{
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
}])
|
||||
# Remove users and groups
|
||||
if len(user_del) > 0 or len(group_del) > 0:
|
||||
commands.append([name, "sudorule_remove_user",
|
||||
{
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}])
|
||||
|
||||
# Add commands allowed
|
||||
if len(allow_cmd_add) > 0 or len(allow_cmdgroup_add) > 0:
|
||||
commands.append([name, "sudorule_add_allow_command",
|
||||
{"sudocmd": allow_cmd_add,
|
||||
"sudocmdgroup": allow_cmdgroup_add,
|
||||
}])
|
||||
|
||||
if len(allow_cmd_del) > 0 or len(allow_cmdgroup_del) > 0:
|
||||
commands.append([name, "sudorule_remove_allow_command",
|
||||
{"sudocmd": allow_cmd_del,
|
||||
"sudocmdgroup": allow_cmdgroup_del
|
||||
}])
|
||||
|
||||
# Add commands denied
|
||||
if len(deny_cmd_add) > 0 or len(deny_cmdgroup_add) > 0:
|
||||
commands.append([name, "sudorule_add_deny_command",
|
||||
{"sudocmd": deny_cmd_add,
|
||||
"sudocmdgroup": deny_cmdgroup_add,
|
||||
}])
|
||||
|
||||
if len(deny_cmd_del) > 0 or len(deny_cmdgroup_del) > 0:
|
||||
commands.append([name, "sudorule_remove_deny_command",
|
||||
{"sudocmd": deny_cmd_del,
|
||||
"sudocmdgroup": deny_cmdgroup_del
|
||||
}])
|
||||
|
||||
# Add RunAS Users
|
||||
if len(runasuser_add) > 0:
|
||||
commands.append([name, "sudorule_add_runasuser",
|
||||
{"user": runasuser_add}])
|
||||
# Remove RunAS Users
|
||||
if len(runasuser_del) > 0:
|
||||
commands.append([name, "sudorule_remove_runasuser",
|
||||
{"user": runasuser_del}])
|
||||
|
||||
# Add RunAS Groups
|
||||
if len(runasgroup_add) > 0:
|
||||
commands.append([name, "sudorule_add_runasgroup",
|
||||
{"group": runasgroup_add}])
|
||||
# Remove RunAS Groups
|
||||
if len(runasgroup_del) > 0:
|
||||
commands.append([name, "sudorule_remove_runasgroup",
|
||||
{"group": runasgroup_del}])
|
||||
|
||||
# Add sudo options
|
||||
for sudoopt in sudooption_add:
|
||||
commands.append([name, "sudorule_add_option",
|
||||
{"ipasudoopt": sudoopt}])
|
||||
|
||||
# Remove sudo options
|
||||
for sudoopt in sudooption_del:
|
||||
commands.append([name, "sudorule_remove_option",
|
||||
{"ipasudoopt": sudoopt}])
|
||||
runasgroup,
|
||||
(
|
||||
res_find.get('ipasudorunasgroup_group', [])
|
||||
+ res_find.get('ipasudorunasextgroup', [])
|
||||
)
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -555,102 +514,61 @@ def main():
|
||||
# deny_sudocmdgroup, sudooption, runasuser, runasgroup
|
||||
# and res_find to only try to add the items that not in
|
||||
# the sudorule already
|
||||
if host is not None and \
|
||||
"memberhost_host" in res_find:
|
||||
host = gen_add_list(
|
||||
host, res_find["memberhost_host"])
|
||||
if hostgroup is not None and \
|
||||
"memberhost_hostgroup" in res_find:
|
||||
hostgroup = gen_add_list(
|
||||
hostgroup, res_find["memberhost_hostgroup"])
|
||||
if user is not None and \
|
||||
"memberuser_user" in res_find:
|
||||
user = gen_add_list(
|
||||
user, res_find["memberuser_user"])
|
||||
if group is not None and \
|
||||
"memberuser_group" in res_find:
|
||||
group = gen_add_list(
|
||||
group, res_find["memberuser_group"])
|
||||
if allow_sudocmd is not None and \
|
||||
"memberallowcmd_sudocmd" in res_find:
|
||||
allow_sudocmd = gen_add_list(
|
||||
allow_sudocmd, res_find["memberallowcmd_sudocmd"])
|
||||
if allow_sudocmdgroup is not None and \
|
||||
"memberallowcmd_sudocmdgroup" in res_find:
|
||||
allow_sudocmdgroup = gen_add_list(
|
||||
if host is not None:
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get("memberhost_host"))
|
||||
if hostgroup is not None:
|
||||
hostgroup_add = gen_add_list(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
if user is not None:
|
||||
user_add = gen_add_list(
|
||||
user, res_find.get("memberuser_user"))
|
||||
if group is not None:
|
||||
group_add = gen_add_list(
|
||||
group, res_find.get("memberuser_group"))
|
||||
if allow_sudocmd is not None:
|
||||
allow_cmd_add = gen_add_list(
|
||||
allow_sudocmd,
|
||||
res_find.get("memberallowcmd_sudocmd")
|
||||
)
|
||||
if allow_sudocmdgroup is not None:
|
||||
allow_cmdgroup_add = gen_add_list(
|
||||
allow_sudocmdgroup,
|
||||
res_find["memberallowcmd_sudocmdgroup"])
|
||||
if deny_sudocmd is not None and \
|
||||
"memberdenycmd_sudocmd" in res_find:
|
||||
deny_sudocmd = gen_add_list(
|
||||
deny_sudocmd, res_find["memberdenycmd_sudocmd"])
|
||||
if deny_sudocmdgroup is not None and \
|
||||
"memberdenycmd_sudocmdgroup" in res_find:
|
||||
deny_sudocmdgroup = gen_add_list(
|
||||
res_find.get("memberallowcmd_sudocmdgroup")
|
||||
)
|
||||
if deny_sudocmd is not None:
|
||||
deny_cmd_add = gen_add_list(
|
||||
deny_sudocmd,
|
||||
res_find.get("memberdenycmd_sudocmd")
|
||||
)
|
||||
if deny_sudocmdgroup is not None:
|
||||
deny_cmdgroup_add = gen_add_list(
|
||||
deny_sudocmdgroup,
|
||||
res_find["memberdenycmd_sudocmdgroup"])
|
||||
if sudooption is not None and \
|
||||
"ipasudoopt" in res_find:
|
||||
sudooption = gen_add_list(
|
||||
sudooption, res_find["ipasudoopt"])
|
||||
if runasuser is not None and \
|
||||
"ipasudorunas_user" in res_find:
|
||||
runasuser = gen_add_list(
|
||||
runasuser, res_find["ipasudorunas_user"])
|
||||
if runasgroup is not None and \
|
||||
"ipasudorunasgroup_group" in res_find:
|
||||
runasgroup = gen_add_list(
|
||||
runasgroup, res_find["ipasudorunasgroup_group"])
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if host is not None or hostgroup is not None:
|
||||
commands.append([name, "sudorule_add_host",
|
||||
{
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
|
||||
# Add users and groups
|
||||
if user is not None or group is not None:
|
||||
commands.append([name, "sudorule_add_user",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
}])
|
||||
|
||||
# Add commands
|
||||
if allow_sudocmd is not None \
|
||||
or allow_sudocmdgroup is not None:
|
||||
commands.append([name, "sudorule_add_allow_command",
|
||||
{"sudocmd": allow_sudocmd,
|
||||
"sudocmdgroup": allow_sudocmdgroup,
|
||||
}])
|
||||
|
||||
# Add commands
|
||||
if deny_sudocmd is not None \
|
||||
or deny_sudocmdgroup is not None:
|
||||
commands.append([name, "sudorule_add_deny_command",
|
||||
{"sudocmd": deny_sudocmd,
|
||||
"sudocmdgroup": deny_sudocmdgroup,
|
||||
}])
|
||||
|
||||
# Add RunAS Users
|
||||
if runasuser is not None and len(runasuser) > 0:
|
||||
commands.append([name, "sudorule_add_runasuser",
|
||||
{"user": runasuser}])
|
||||
|
||||
# Add RunAS Groups
|
||||
if runasgroup is not None and len(runasgroup) > 0:
|
||||
commands.append([name, "sudorule_add_runasgroup",
|
||||
{"group": runasgroup}])
|
||||
|
||||
# Add options
|
||||
res_find.get("memberdenycmd_sudocmdgroup")
|
||||
)
|
||||
if sudooption is not None:
|
||||
existing_opts = res_find.get('ipasudoopt', [])
|
||||
for sudoopt in sudooption:
|
||||
if sudoopt not in existing_opts:
|
||||
commands.append([name, "sudorule_add_option",
|
||||
{"ipasudoopt": sudoopt}])
|
||||
sudooption_add = gen_add_list(
|
||||
sudooption, res_find.get("ipasudoopt"))
|
||||
# runasuser attribute can be used with both IPA and
|
||||
# non-IPA (external) users, so we need to compare
|
||||
# the provided list against both users and external
|
||||
# users list.
|
||||
if runasuser is not None:
|
||||
runasuser_add = gen_add_list(
|
||||
runasuser,
|
||||
(list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', [])))
|
||||
)
|
||||
# runasgroup attribute can be used with both IPA and
|
||||
# non-IPA (external) groups, so we need to compare
|
||||
# the provided list against both users and external
|
||||
# groups list.
|
||||
if runasgroup is not None:
|
||||
runasgroup_add = gen_add_list(
|
||||
runasgroup,
|
||||
(list(res_find.get("ipasudorunasgroup_group", []))
|
||||
+ list(res_find.get("ipasudorunasextgroup", [])))
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
if action == "sudorule":
|
||||
@@ -667,127 +585,70 @@ def main():
|
||||
# and res_find to only try to remove the items that are
|
||||
# in sudorule
|
||||
if host is not None:
|
||||
if "memberhost_host" in res_find:
|
||||
host = gen_intersection_list(
|
||||
host, res_find["memberhost_host"])
|
||||
else:
|
||||
host = None
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find.get("memberhost_host"))
|
||||
|
||||
if hostgroup is not None:
|
||||
if "memberhost_hostgroup" in res_find:
|
||||
hostgroup = gen_intersection_list(
|
||||
hostgroup, res_find["memberhost_hostgroup"])
|
||||
else:
|
||||
hostgroup = None
|
||||
hostgroup_del = gen_intersection_list(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
|
||||
if user is not None:
|
||||
if "memberuser_user" in res_find:
|
||||
user = gen_intersection_list(
|
||||
user, res_find["memberuser_user"])
|
||||
else:
|
||||
user = None
|
||||
user_del = gen_intersection_list(
|
||||
user, res_find.get("memberuser_user"))
|
||||
|
||||
if group is not None:
|
||||
if "memberuser_group" in res_find:
|
||||
group = gen_intersection_list(
|
||||
group, res_find["memberuser_group"])
|
||||
else:
|
||||
group = None
|
||||
group_del = gen_intersection_list(
|
||||
group, res_find.get("memberuser_group"))
|
||||
|
||||
if allow_sudocmd is not None:
|
||||
if "memberallowcmd_sudocmd" in res_find:
|
||||
allow_sudocmd = gen_intersection_list(
|
||||
allow_sudocmd,
|
||||
res_find["memberallowcmd_sudocmd"])
|
||||
else:
|
||||
allow_sudocmd = None
|
||||
allow_cmd_del = gen_intersection_list(
|
||||
allow_sudocmd,
|
||||
res_find.get("memberallowcmd_sudocmd")
|
||||
)
|
||||
if allow_sudocmdgroup is not None:
|
||||
if "memberallowcmd_sudocmdgroup" in res_find:
|
||||
allow_sudocmdgroup = gen_intersection_list(
|
||||
allow_sudocmdgroup,
|
||||
res_find["memberallowcmd_sudocmdgroup"])
|
||||
else:
|
||||
allow_sudocmdgroup = None
|
||||
allow_cmdgroup_del = gen_intersection_list(
|
||||
allow_sudocmdgroup,
|
||||
res_find.get("memberallowcmd_sudocmdgroup")
|
||||
)
|
||||
if deny_sudocmd is not None:
|
||||
if "memberdenycmd_sudocmd" in res_find:
|
||||
deny_sudocmd = gen_intersection_list(
|
||||
deny_sudocmd,
|
||||
res_find["memberdenycmd_sudocmd"])
|
||||
else:
|
||||
deny_sudocmd = None
|
||||
deny_cmd_del = gen_intersection_list(
|
||||
deny_sudocmd,
|
||||
res_find.get("memberdenycmd_sudocmd")
|
||||
)
|
||||
if deny_sudocmdgroup is not None:
|
||||
if "memberdenycmd_sudocmdgroup" in res_find:
|
||||
deny_sudocmdgroup = gen_intersection_list(
|
||||
deny_sudocmdgroup,
|
||||
res_find["memberdenycmd_sudocmdgroup"])
|
||||
else:
|
||||
deny_sudocmdgroup = None
|
||||
deny_cmdgroup_del = gen_intersection_list(
|
||||
deny_sudocmdgroup,
|
||||
res_find.get("memberdenycmd_sudocmdgroup")
|
||||
)
|
||||
if sudooption is not None:
|
||||
if "ipasudoopt" in res_find:
|
||||
sudooption = gen_intersection_list(
|
||||
sudooption, res_find["ipasudoopt"])
|
||||
else:
|
||||
sudooption = None
|
||||
sudooption_del = gen_intersection_list(
|
||||
sudooption, res_find.get("ipasudoopt"))
|
||||
# runasuser attribute can be used with both IPA and
|
||||
# non-IPA (external) users, so we need to compare
|
||||
# the provided list against both users and external
|
||||
# users list.
|
||||
if runasuser is not None:
|
||||
if "ipasudorunas_user" in res_find:
|
||||
runasuser = gen_intersection_list(
|
||||
runasuser, res_find["ipasudorunas_user"])
|
||||
else:
|
||||
runasuser = None
|
||||
runasuser_del = gen_intersection_list(
|
||||
runasuser,
|
||||
(
|
||||
list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', []))
|
||||
)
|
||||
)
|
||||
# runasgroup attribute can be used with both IPA and
|
||||
# non-IPA (external) groups, so we need to compare
|
||||
# the provided list against both groups and external
|
||||
# groups list.
|
||||
if runasgroup is not None:
|
||||
if "ipasudorunasgroup_group" in res_find:
|
||||
runasgroup = gen_intersection_list(
|
||||
runasgroup,
|
||||
res_find["ipasudorunasgroup_group"])
|
||||
else:
|
||||
runasgroup = None
|
||||
|
||||
# Remove hosts and hostgroups
|
||||
if host is not None or hostgroup is not None:
|
||||
commands.append([name, "sudorule_remove_host",
|
||||
{
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
|
||||
# Remove users and groups
|
||||
if user is not None or group is not None:
|
||||
commands.append([name, "sudorule_remove_user",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
}])
|
||||
|
||||
# Remove allow commands
|
||||
if allow_sudocmd is not None \
|
||||
or allow_sudocmdgroup is not None:
|
||||
commands.append([name, "sudorule_remove_allow_command",
|
||||
{"sudocmd": allow_sudocmd,
|
||||
"sudocmdgroup": allow_sudocmdgroup
|
||||
}])
|
||||
|
||||
# Remove deny commands
|
||||
if deny_sudocmd is not None \
|
||||
or deny_sudocmdgroup is not None:
|
||||
commands.append([name, "sudorule_remove_deny_command",
|
||||
{"sudocmd": deny_sudocmd,
|
||||
"sudocmdgroup": deny_sudocmdgroup
|
||||
}])
|
||||
|
||||
# Remove RunAS Users
|
||||
if runasuser is not None:
|
||||
commands.append([name, "sudorule_remove_runasuser",
|
||||
{"user": runasuser}])
|
||||
|
||||
# Remove RunAS Groups
|
||||
if runasgroup is not None:
|
||||
commands.append([name, "sudorule_remove_runasgroup",
|
||||
{"group": runasgroup}])
|
||||
|
||||
# Remove options
|
||||
if sudooption is not None:
|
||||
existing_opts = res_find.get('ipasudoopt', [])
|
||||
for sudoopt in sudooption:
|
||||
if sudoopt in existing_opts:
|
||||
commands.append([name,
|
||||
"sudorule_remove_option",
|
||||
{"ipasudoopt": sudoopt}])
|
||||
runasgroup_del = gen_intersection_list(
|
||||
runasgroup,
|
||||
(
|
||||
list(res_find.get(
|
||||
"ipasudorunasgroup_group", []))
|
||||
+ list(res_find.get(
|
||||
"ipasudorunasextgroup", []))
|
||||
)
|
||||
)
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is None:
|
||||
@@ -812,6 +673,99 @@ def main():
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Manage members.
|
||||
# Manage hosts and hostgroups
|
||||
if host_add or hostgroup_add:
|
||||
commands.append([name, "sudorule_add_host",
|
||||
{
|
||||
"host": host_add,
|
||||
"hostgroup": hostgroup_add,
|
||||
}])
|
||||
if host_del or hostgroup_del:
|
||||
commands.append([name, "sudorule_remove_host",
|
||||
{
|
||||
"host": host_del,
|
||||
"hostgroup": hostgroup_del,
|
||||
}])
|
||||
|
||||
# Manage users and groups
|
||||
if user_add or group_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_user",
|
||||
{"user": user_add, "group": group_add}
|
||||
])
|
||||
if user_del or group_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_user",
|
||||
{"user": user_del, "group": group_del}
|
||||
])
|
||||
|
||||
# Manage commands allowed
|
||||
if allow_cmd_add or allow_cmdgroup_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_allow_command",
|
||||
{
|
||||
"sudocmd": allow_cmd_add,
|
||||
"sudocmdgroup": allow_cmdgroup_add,
|
||||
}
|
||||
])
|
||||
if allow_cmd_del or allow_cmdgroup_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_allow_command",
|
||||
{
|
||||
"sudocmd": allow_cmd_del,
|
||||
"sudocmdgroup": allow_cmdgroup_del
|
||||
}
|
||||
])
|
||||
# Manage commands denied
|
||||
if deny_cmd_add or deny_cmdgroup_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_deny_command",
|
||||
{
|
||||
"sudocmd": deny_cmd_add,
|
||||
"sudocmdgroup": deny_cmdgroup_add,
|
||||
}
|
||||
])
|
||||
if deny_cmd_del or deny_cmdgroup_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_deny_command",
|
||||
{
|
||||
"sudocmd": deny_cmd_del,
|
||||
"sudocmdgroup": deny_cmdgroup_del
|
||||
}
|
||||
])
|
||||
# Manage RunAS users
|
||||
if runasuser_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_runasuser", {"user": runasuser_add}
|
||||
])
|
||||
if runasuser_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_runasuser", {"user": runasuser_del}
|
||||
])
|
||||
|
||||
# Manage RunAS Groups
|
||||
if runasgroup_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_runasgroup", {"group": runasgroup_add}
|
||||
])
|
||||
if runasgroup_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_runasgroup",
|
||||
{"group": runasgroup_del}
|
||||
])
|
||||
# Manage sudo options
|
||||
if sudooption_add:
|
||||
for option in sudooption_add:
|
||||
commands.append([
|
||||
name, "sudorule_add_option", {"ipasudoopt": option}
|
||||
])
|
||||
if sudooption_del:
|
||||
for option in sudooption_del:
|
||||
commands.append([
|
||||
name, "sudorule_remove_option", {"ipasudoopt": option}
|
||||
])
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'supported_by': 'community',
|
||||
'status': ['preview'],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -471,41 +474,31 @@ user:
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \
|
||||
encode_certificate, load_cert_from_str, DN_x500_text, to_text
|
||||
import six
|
||||
encode_certificate, load_cert_from_str, DN_x500_text, to_text, \
|
||||
ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_user(module, name, preserved=False):
|
||||
def find_user(module, name):
|
||||
_args = {
|
||||
"all": True,
|
||||
"uid": name,
|
||||
}
|
||||
if preserved:
|
||||
_args["preserved"] = preserved
|
||||
|
||||
_result = module.ipa_command("user_find", name, _args)
|
||||
try:
|
||||
_result = module.ipa_command("user_show", name, _args).get("result")
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one user '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
# Transform each principal to a string
|
||||
_result = _result["result"][0]
|
||||
if "krbprincipalname" in _result \
|
||||
and _result["krbprincipalname"] is not None:
|
||||
_list = []
|
||||
for x in _result["krbprincipalname"]:
|
||||
_list.append(str(x))
|
||||
_result["krbprincipalname"] = _list
|
||||
certs = _result.get("usercertificate")
|
||||
if certs is not None:
|
||||
_result["usercertificate"] = [encode_certificate(x)
|
||||
for x in certs]
|
||||
return _result
|
||||
|
||||
return None
|
||||
# Transform each principal to a string
|
||||
_result["krbprincipalname"] = [
|
||||
to_text(x) for x in (_result.get("krbprincipalname") or [])
|
||||
]
|
||||
_result["usercertificate"] = [
|
||||
encode_certificate(x) for x in (_result.get("usercertificate") or [])
|
||||
]
|
||||
return _result
|
||||
|
||||
|
||||
def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
@@ -696,8 +689,8 @@ def check_certmapdata(data):
|
||||
|
||||
i = data.find("<I>", 4)
|
||||
s = data.find("<S>", i) # pylint: disable=invalid-name
|
||||
issuer = data[i+3:s]
|
||||
subject = data[s+3:]
|
||||
issuer = data[i + 3:s]
|
||||
subject = data[s + 3:]
|
||||
|
||||
if i < 0 or s < 0 or "CN" not in issuer or "CN" not in subject:
|
||||
return False
|
||||
@@ -821,14 +814,17 @@ def main():
|
||||
# general
|
||||
name=dict(type="list", aliases=["login"], default=None,
|
||||
required=False),
|
||||
users=dict(type="list", aliases=["login"], default=None,
|
||||
users=dict(type="list",
|
||||
aliases=["login"],
|
||||
default=None,
|
||||
options=dict(
|
||||
# Here name is a simple string
|
||||
name=dict(type="str", required=True),
|
||||
# Add user specific parameters
|
||||
**user_spec
|
||||
),
|
||||
elements='dict', required=False),
|
||||
elements='dict',
|
||||
required=False),
|
||||
|
||||
# deleted
|
||||
preserve=dict(required=False, type='bool', default=None),
|
||||
@@ -1079,12 +1075,6 @@ def main():
|
||||
|
||||
# Make sure user exists
|
||||
res_find = find_user(ansible_module, name)
|
||||
# Also search for preserved user if the user could not be found
|
||||
if res_find is None:
|
||||
res_find_preserved = find_user(ansible_module, name,
|
||||
preserved=True)
|
||||
else:
|
||||
res_find_preserved = None
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
@@ -1098,10 +1088,6 @@ def main():
|
||||
departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, noprivate, nomembers)
|
||||
|
||||
# Also check preserved users
|
||||
if res_find is None and res_find_preserved is not None:
|
||||
res_find = res_find_preserved
|
||||
|
||||
if action == "user":
|
||||
# Found the user
|
||||
if res_find is not None:
|
||||
@@ -1304,16 +1290,16 @@ def main():
|
||||
gen_certmapdata_args(_data)])
|
||||
|
||||
elif state == "absent":
|
||||
# Also check preserved users
|
||||
if res_find is None and res_find_preserved is not None:
|
||||
res_find = res_find_preserved
|
||||
|
||||
if action == "user":
|
||||
if res_find is not None:
|
||||
args = {}
|
||||
if preserve is not None:
|
||||
args["preserve"] = preserve
|
||||
commands.append([name, "user_del", args])
|
||||
if (
|
||||
not res_find.get("preserved", False)
|
||||
or not args.get("preserve", False)
|
||||
):
|
||||
commands.append([name, "user_del", args])
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
@@ -1364,17 +1350,18 @@ def main():
|
||||
commands.append([name, "user_remove_certmapdata",
|
||||
gen_certmapdata_args(_data)])
|
||||
elif state == "undeleted":
|
||||
if res_find_preserved is not None:
|
||||
commands.append([name, "user_undel", {}])
|
||||
if res_find is not None:
|
||||
if res_find.get("preserved", False):
|
||||
commands.append([name, "user_undel", {}])
|
||||
else:
|
||||
raise ValueError("No preserved user '%s'" % name)
|
||||
raise ValueError("No user '%s'" % name)
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is not None:
|
||||
if res_find["nsaccountlock"]:
|
||||
commands.append([name, "user_enable", {}])
|
||||
else:
|
||||
raise ValueError("No disabled user '%s'" % name)
|
||||
raise ValueError("No user '%s'" % name)
|
||||
|
||||
elif state == "disabled":
|
||||
if res_find is not None:
|
||||
@@ -1386,6 +1373,8 @@ def main():
|
||||
elif state == "unlocked":
|
||||
if res_find is not None:
|
||||
commands.append([name, "user_unlock", {}])
|
||||
else:
|
||||
raise ValueError("No user '%s'" % name)
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -631,7 +634,7 @@ def main():
|
||||
vault_password_file=dict(type="str", required=False, default=None,
|
||||
no_log=False,
|
||||
aliases=[
|
||||
'password_file', "old_password_file"
|
||||
'password_file', "old_password_file"
|
||||
]),
|
||||
new_password=dict(type="str", required=False, default=None,
|
||||
no_log=True),
|
||||
|
||||
@@ -3,5 +3,4 @@ pytest>=2.7
|
||||
pytest-sourceorder>=0.5
|
||||
pytest-split-tests>=1.0.3
|
||||
pytest-testinfra>=5.0
|
||||
jmespath>=0.9 # needed for the `json_query` filter
|
||||
pyyaml>=3
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.0',
|
||||
'supported_by': 'community',
|
||||
@@ -40,8 +43,9 @@ author:
|
||||
|
||||
EXAMPLES = '''
|
||||
# Get IPA_BACKUP_DIR from ipaplatform
|
||||
- name: ipabackup_get_backup_dir:
|
||||
register result
|
||||
- name: Get IPA_BACKUP_DIR from ipaplatform
|
||||
ipabackup_get_backup_dir:
|
||||
register: result
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
@@ -57,7 +61,7 @@ from ipaplatform.paths import paths
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
argument_spec={},
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
|
||||
10
roles/ipabackup/vars/Ubuntu-18.04.yml
Normal file
10
roles/ipabackup/vars/Ubuntu-18.04.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# vars/Ubuntu.yml
|
||||
---
|
||||
ipaserver_packages: [ "freeipa-server" ]
|
||||
ipaserver_packages_dns: [ "freeipa-server-dns" ]
|
||||
ipaserver_packages_adtrust: [ "freeipa-server-trust-ad" ]
|
||||
ipaserver_packages_firewalld: [ "firewalld" ]
|
||||
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
|
||||
# to the way python-ipalib package is defined.
|
||||
# Package python2.7 must be installed before executing this role.
|
||||
ansible_python_interpreter: '/usr/bin/python2.7'
|
||||
@@ -33,7 +33,6 @@ Requirements
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* /usr/bin/kinit is required on the controller if a one time password (OTP) is used
|
||||
* python3-gssapi is required on the controller if a one time password (OTP) is used with keytab
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
try:
|
||||
import gssapi
|
||||
except ImportError:
|
||||
gssapi = None
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -41,12 +41,14 @@ def run_cmd(args, stdin=None):
|
||||
if stdin:
|
||||
p_in = subprocess.PIPE
|
||||
|
||||
p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err,
|
||||
close_fds=True)
|
||||
__temp, stderr = p.communicate(stdin)
|
||||
# pylint: disable=invalid-name
|
||||
with subprocess.Popen(
|
||||
args, stdin=p_in, stdout=p_out, stderr=p_err, close_fds=True
|
||||
) as p:
|
||||
__temp, stderr = p.communicate(stdin)
|
||||
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError(stderr)
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError(stderr)
|
||||
|
||||
|
||||
def kinit_password(principal, password, ccache_name, config):
|
||||
@@ -76,22 +78,17 @@ def kinit_keytab(principal, keytab, ccache_name, config):
|
||||
It uses the specified config file to kinit and stores the TGT
|
||||
in ccache_name.
|
||||
"""
|
||||
if gssapi is None:
|
||||
raise ImportError("gssapi is not available")
|
||||
|
||||
args = ["/usr/bin/kinit", "-kt", keytab, "-c", ccache_name, principal]
|
||||
old_config = os.environ.get('KRB5_CONFIG')
|
||||
os.environ['KRB5_CONFIG'] = config
|
||||
os.environ["KRB5_CONFIG"] = config
|
||||
|
||||
try:
|
||||
name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
|
||||
store = {'ccache': ccache_name,
|
||||
'client_keytab': keytab}
|
||||
cred = gssapi.Credentials(name=name, store=store, usage='initiate')
|
||||
return cred
|
||||
return run_cmd(args)
|
||||
finally:
|
||||
if old_config is not None:
|
||||
os.environ['KRB5_CONFIG'] = old_config
|
||||
os.environ["KRB5_CONFIG"] = old_config
|
||||
else:
|
||||
os.environ.pop('KRB5_CONFIG', None)
|
||||
os.environ.pop("KRB5_CONFIG", None)
|
||||
|
||||
|
||||
KRB5CONF_TEMPLATE = """
|
||||
@@ -124,8 +121,9 @@ KRB5CONF_TEMPLATE = """
|
||||
"""
|
||||
|
||||
|
||||
class ActionModule(ActionBase):
|
||||
class ActionModule(ActionBase): # pylint: disable=too-few-public-methods
|
||||
|
||||
# pylint: disable=too-many-return-statements
|
||||
def run(self, tmp=None, task_vars=None):
|
||||
"""
|
||||
Handle credential cache transfer.
|
||||
@@ -145,8 +143,9 @@ class ActionModule(ActionBase):
|
||||
Then the IPA commands can use this credential cache file.
|
||||
"""
|
||||
if task_vars is None:
|
||||
task_vars = dict()
|
||||
task_vars = {}
|
||||
|
||||
# pylint: disable=super-with-arguments
|
||||
result = super(ActionModule, self).run(tmp, task_vars)
|
||||
principal = self._task.args.get('principal', None)
|
||||
keytab = self._task.args.get('keytab', None)
|
||||
@@ -164,7 +163,7 @@ class ActionModule(ActionBase):
|
||||
return result
|
||||
|
||||
data = self._execute_module(module_name='ipaclient_get_facts',
|
||||
module_args=dict(), task_vars=task_vars)
|
||||
module_args={}, task_vars=task_vars)
|
||||
|
||||
try:
|
||||
domain = data['ansible_facts']['ipa']['domain']
|
||||
@@ -191,7 +190,7 @@ class ActionModule(ActionBase):
|
||||
ipa_realm=realm,
|
||||
ipa_lifetime=lifetime))
|
||||
|
||||
with open(krb5conf_name, 'w') as f:
|
||||
with open(krb5conf_name, 'w') as f: # pylint: disable=invalid-name
|
||||
f.write(content)
|
||||
|
||||
if password:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user