mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-27 13:53:06 +00:00
Compare commits
119 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 | ||
|
|
700d2b7335 | ||
|
|
2f9791f6c5 | ||
|
|
f41104520e | ||
|
|
d81994475e |
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
|
||||
|
||||
14
.github/workflows/lint.yml
vendored
14
.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,7 +69,7 @@ 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
|
||||
|
||||
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,8 +38,10 @@ repos:
|
||||
entry: utils/ansible-doc-test
|
||||
# args: ['-v', 'roles', 'plugins']
|
||||
files: ^.*.py$
|
||||
- repo: https://github.com/koalaman/shellcheck-precommit
|
||||
rev: v0.8.0
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
args: ["--severity=warning"] # Only show errors and warnings
|
||||
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
|
||||
@@ -25,3 +25,24 @@
|
||||
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
|
||||
@@ -419,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.
|
||||
@@ -566,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]
|
||||
@@ -590,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.
|
||||
@@ -776,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):
|
||||
"""
|
||||
@@ -1034,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
|
||||
@@ -1049,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.
|
||||
@@ -1074,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
|
||||
@@ -1178,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.
|
||||
|
||||
@@ -79,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
|
||||
@@ -86,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 = """
|
||||
@@ -116,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 = """
|
||||
@@ -129,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:
|
||||
@@ -146,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:
|
||||
@@ -163,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
|
||||
|
||||
|
||||
@@ -194,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),
|
||||
),
|
||||
@@ -228,6 +353,8 @@ def main():
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
if names is None:
|
||||
names = []
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
@@ -236,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
|
||||
@@ -250,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)
|
||||
|
||||
@@ -392,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()
|
||||
@@ -68,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:
|
||||
@@ -87,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']
|
||||
@@ -132,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()
|
||||
@@ -299,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,
|
||||
@@ -365,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 = [
|
||||
@@ -418,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]
|
||||
|
||||
@@ -59,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"]
|
||||
"""
|
||||
@@ -83,6 +91,7 @@ EXAMPLES = """
|
||||
- ip_address: 2001:4860:4860::8888
|
||||
port: 53
|
||||
state: absent
|
||||
action: member
|
||||
|
||||
# Disable PTR record synchronization.
|
||||
- ipadnsconfig:
|
||||
@@ -118,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 = {}
|
||||
|
||||
@@ -137,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']
|
||||
@@ -165,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
|
||||
@@ -192,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)
|
||||
@@ -209,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.
|
||||
|
||||
@@ -1108,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
|
||||
|
||||
@@ -203,11 +203,13 @@ 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
|
||||
from ansible.module_utils import six
|
||||
@@ -217,31 +219,39 @@ 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 = [
|
||||
@@ -441,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():
|
||||
@@ -532,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__":
|
||||
|
||||
@@ -181,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
|
||||
@@ -198,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
|
||||
|
||||
@@ -308,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")
|
||||
@@ -376,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
|
||||
@@ -393,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:
|
||||
@@ -440,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,
|
||||
@@ -491,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":
|
||||
@@ -588,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)
|
||||
|
||||
|
||||
@@ -238,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")
|
||||
@@ -307,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 = []
|
||||
@@ -316,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
|
||||
@@ -353,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:
|
||||
@@ -424,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":
|
||||
@@ -494,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:
|
||||
@@ -587,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(
|
||||
|
||||
@@ -101,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):
|
||||
@@ -183,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")
|
||||
@@ -223,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
|
||||
@@ -246,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:
|
||||
@@ -283,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
|
||||
|
||||
@@ -139,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):
|
||||
@@ -281,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:
|
||||
|
||||
@@ -72,7 +72,7 @@ options:
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "renamed"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
@@ -103,10 +103,10 @@ 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
|
||||
IPAAnsibleModule, gen_add_del_lists, compare_args_ipa, \
|
||||
gen_intersection_list, ensure_fqdn
|
||||
from ansible.module_utils import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
@@ -145,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'])
|
||||
@@ -157,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):
|
||||
@@ -190,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:
|
||||
@@ -215,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])
|
||||
@@ -285,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:
|
||||
@@ -391,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=[],
|
||||
@@ -426,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)
|
||||
|
||||
@@ -289,10 +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):
|
||||
if not isinstance(parameters, dict):
|
||||
raise AssertionError("parameters is not a 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',
|
||||
@@ -314,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 "
|
||||
@@ -437,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
|
||||
@@ -462,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
|
||||
|
||||
@@ -479,11 +459,26 @@ 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
|
||||
@@ -534,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']:
|
||||
@@ -577,75 +543,22 @@ 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_principals)
|
||||
|
||||
(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:
|
||||
@@ -653,137 +566,31 @@ def main():
|
||||
|
||||
certificate_add = gen_add_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
certificate_del = []
|
||||
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get("managedby_host"))
|
||||
host_del = []
|
||||
host, res_find.get('managedby_host'))
|
||||
|
||||
principal_add = gen_add_list(principal, res_principals)
|
||||
principal_del = []
|
||||
|
||||
allow_create_keytab_user_add = gen_add_list(
|
||||
allow_create_keytab_user,
|
||||
res_find.get("ipaallowedtoperform_write_keys_user")
|
||||
)
|
||||
|
||||
allow_create_keytab_user_del = []
|
||||
allow_create_keytab_group_add = gen_add_list(
|
||||
allow_create_keytab_group,
|
||||
res_find.get("ipaallowedtoperform_write_keys_group")
|
||||
)
|
||||
allow_create_keytab_group_del = []
|
||||
allow_create_keytab_host_add = gen_add_list(
|
||||
allow_create_keytab_host,
|
||||
res_find.get("ipaallowedtoperform_write_keys_host")
|
||||
)
|
||||
allow_create_keytab_host_del = []
|
||||
allow_create_keytab_hostgroup_add = gen_add_list(
|
||||
allow_create_keytab_hostgroup,
|
||||
res_find.get(
|
||||
"ipaallowedtoperform_write_keys_hostgroup")
|
||||
)
|
||||
allow_create_keytab_hostgroup_del = []
|
||||
allow_retrieve_keytab_user_add = gen_add_list(
|
||||
allow_retrieve_keytab_user,
|
||||
res_find.get("ipaallowedtoperform_read_keys_user")
|
||||
)
|
||||
allow_retrieve_keytab_user_del = []
|
||||
allow_retrieve_keytab_group_add = gen_add_list(
|
||||
allow_retrieve_keytab_group,
|
||||
res_find.get("ipaallowedtoperform_read_keys_group")
|
||||
)
|
||||
allow_retrieve_keytab_group_del = []
|
||||
allow_retrieve_keytab_host_add = gen_add_list(
|
||||
allow_retrieve_keytab_host,
|
||||
res_find.get("ipaallowedtoperform_read_keys_host")
|
||||
)
|
||||
allow_retrieve_keytab_host_del = []
|
||||
allow_retrieve_keytab_hostgroup_add = gen_add_list(
|
||||
allow_retrieve_keytab_hostgroup,
|
||||
res_find.get("ipaallowedtoperform_read_keys_hostgroup")
|
||||
)
|
||||
allow_retrieve_keytab_hostgroup_del = []
|
||||
|
||||
if principal_add:
|
||||
commands.append([name, "service_add_principal",
|
||||
{"krbprincipalname": principal_add}])
|
||||
if principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{"krbprincipalname": principal_del}])
|
||||
|
||||
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":
|
||||
@@ -795,97 +602,30 @@ def main():
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No service '%s'" % name)
|
||||
|
||||
# Remove principals
|
||||
principal_del = gen_intersection_list(
|
||||
principal, res_principals)
|
||||
if principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{"krbprincipalname": principal_del}])
|
||||
|
||||
# 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,
|
||||
}])
|
||||
certificate_del = gen_intersection_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
# Add hosts
|
||||
host = gen_intersection_list(
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find.get("managedby_host"))
|
||||
if host is not None:
|
||||
commands.append(
|
||||
[name, "service_remove_host", {"host": host}])
|
||||
|
||||
allow_create_keytab_user_del = gen_intersection_list(
|
||||
allow_create_keytab_user,
|
||||
res_find.get("ipaallowedtoperform_write_keys_user")
|
||||
)
|
||||
allow_create_keytab_group_del = gen_intersection_list(
|
||||
allow_create_keytab_group,
|
||||
res_find.get("ipaallowedtoperform_write_keys_group")
|
||||
)
|
||||
allow_create_keytab_host_del = gen_intersection_list(
|
||||
allow_create_keytab_host,
|
||||
res_find.get("ipaallowedtoperform_write_keys_host")
|
||||
)
|
||||
allow_create_keytab_hostgroup_del = gen_intersection_list(
|
||||
allow_create_keytab_hostgroup,
|
||||
res_find.get(
|
||||
"ipaallowedtoperform_write_keys_hostgroup")
|
||||
)
|
||||
|
||||
# Allow create keytab
|
||||
if any([
|
||||
allow_create_keytab_user_del,
|
||||
allow_create_keytab_group_del,
|
||||
allow_create_keytab_host_del,
|
||||
allow_create_keytab_hostgroup_del
|
||||
]):
|
||||
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_user_del = gen_intersection_list(
|
||||
allow_retrieve_keytab_user,
|
||||
res_find.get("ipaallowedtoperform_read_keys_user")
|
||||
)
|
||||
allow_retrieve_keytab_group_del = gen_intersection_list(
|
||||
allow_retrieve_keytab_group,
|
||||
res_find.get("ipaallowedtoperform_read_keys_group")
|
||||
)
|
||||
allow_retrieve_keytab_host_del = gen_intersection_list(
|
||||
allow_retrieve_keytab_host,
|
||||
res_find.get("ipaallowedtoperform_read_keys_host")
|
||||
)
|
||||
allow_retrieve_keytab_hostgroup_del = \
|
||||
gen_intersection_list(
|
||||
allow_retrieve_keytab_hostgroup,
|
||||
res_find.get(
|
||||
"ipaallowedtoperform_read_keys_hostgroup")
|
||||
)
|
||||
|
||||
# Allow retriev keytab
|
||||
if any([
|
||||
allow_retrieve_keytab_user_del,
|
||||
allow_retrieve_keytab_group_del,
|
||||
allow_retrieve_keytab_host_del,
|
||||
allow_retrieve_keytab_hostgroup_del
|
||||
]):
|
||||
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":
|
||||
@@ -898,9 +638,50 @@ 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, fail_on_member_errors=True)
|
||||
|
||||
@@ -188,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):
|
||||
@@ -297,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
|
||||
@@ -374,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
|
||||
@@ -480,95 +500,11 @@ def main():
|
||||
runasgroup_add, runasgroup_del = gen_add_del_lists(
|
||||
runasgroup,
|
||||
(
|
||||
res_find.get('ipasudorunas_group', [])
|
||||
res_find.get('ipasudorunasgroup_group', [])
|
||||
+ res_find.get('ipasudorunasextgroup', [])
|
||||
)
|
||||
)
|
||||
|
||||
# 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}])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No sudorule '%s'" % name)
|
||||
@@ -578,56 +514,47 @@ 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"])
|
||||
res_find.get("memberdenycmd_sudocmdgroup")
|
||||
)
|
||||
if sudooption is not None:
|
||||
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
|
||||
and (
|
||||
"ipasudorunas_user" in res_find
|
||||
or "ipasudorunasextuser" in res_find
|
||||
)
|
||||
):
|
||||
runasuser = gen_add_list(
|
||||
if runasuser is not None:
|
||||
runasuser_add = gen_add_list(
|
||||
runasuser,
|
||||
(list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', [])))
|
||||
@@ -636,69 +563,13 @@ def main():
|
||||
# non-IPA (external) groups, so we need to compare
|
||||
# the provided list against both users and external
|
||||
# groups list.
|
||||
if (
|
||||
runasgroup is not None
|
||||
and (
|
||||
"ipasudorunasgroup_group" in res_find
|
||||
or "ipasudorunasextgroup" in res_find
|
||||
)
|
||||
):
|
||||
runasgroup = gen_add_list(
|
||||
if runasgroup is not None:
|
||||
runasgroup_add = gen_add_list(
|
||||
runasgroup,
|
||||
(list(res_find.get("ipasudorunasgroup_group", []))
|
||||
+ list(res_find.get("ipasudorunasextgroup", [])))
|
||||
)
|
||||
|
||||
# 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
|
||||
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}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "sudorule":
|
||||
if res_find is not None:
|
||||
@@ -714,153 +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
|
||||
or "ipasudorunasextuser" in res_find
|
||||
):
|
||||
runasuser = gen_intersection_list(
|
||||
runasuser,
|
||||
(
|
||||
list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get(
|
||||
'ipasudorunasextuser', []))
|
||||
)
|
||||
runasuser_del = gen_intersection_list(
|
||||
runasuser,
|
||||
(
|
||||
list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', []))
|
||||
)
|
||||
else:
|
||||
runasuser = None
|
||||
)
|
||||
# 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
|
||||
or "ipasudorunasextgroup" in res_find
|
||||
):
|
||||
runasgroup = gen_intersection_list(
|
||||
runasgroup,
|
||||
(
|
||||
list(res_find.get(
|
||||
"ipasudorunasgroup_group", []))
|
||||
+ list(res_find.get(
|
||||
"ipasudorunasextgroup", []))
|
||||
)
|
||||
runasgroup_del = gen_intersection_list(
|
||||
runasgroup,
|
||||
(
|
||||
list(res_find.get(
|
||||
"ipasudorunasgroup_group", []))
|
||||
+ list(res_find.get(
|
||||
"ipasudorunasextgroup", []))
|
||||
)
|
||||
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}])
|
||||
)
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is None:
|
||||
@@ -885,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(
|
||||
|
||||
@@ -474,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
|
||||
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,
|
||||
@@ -824,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),
|
||||
@@ -1082,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":
|
||||
@@ -1101,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:
|
||||
@@ -1307,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(
|
||||
@@ -1367,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:
|
||||
@@ -1389,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)
|
||||
|
||||
@@ -634,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),
|
||||
|
||||
@@ -45,7 +45,7 @@ EXAMPLES = '''
|
||||
# Get IPA_BACKUP_DIR from ipaplatform
|
||||
- name: Get IPA_BACKUP_DIR from ipaplatform
|
||||
ipabackup_get_backup_dir:
|
||||
register result
|
||||
register: result
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
@@ -61,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)
|
||||
|
||||
@@ -21,10 +21,6 @@ from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
try:
|
||||
import gssapi
|
||||
except ImportError:
|
||||
gssapi = None
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -45,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):
|
||||
@@ -80,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 = """
|
||||
@@ -128,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.
|
||||
@@ -149,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)
|
||||
@@ -168,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']
|
||||
@@ -195,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:
|
||||
|
||||
@@ -133,7 +133,9 @@ def main():
|
||||
|
||||
# Add CA certs to a temporary NSS database
|
||||
try:
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(tmp_db.create_db)
|
||||
# pylint: enable=deprecated-method
|
||||
if "password_filename" not in argspec.args:
|
||||
tmp_db.create_db()
|
||||
else:
|
||||
@@ -181,7 +183,7 @@ def main():
|
||||
module.warn(
|
||||
"Some capabilities including the ipa command capability "
|
||||
"may not be available")
|
||||
except errors.PublicError as e2:
|
||||
except errors.PublicError as e2: # pylint: disable=invalid-name
|
||||
module.fail_json(
|
||||
msg="Cannot connect to the IPA server RPC interface: "
|
||||
"%s" % e2)
|
||||
|
||||
@@ -56,10 +56,12 @@ def is_ntpd_configured():
|
||||
ntpd_conf_section = re.compile(r'^\s*\[ntpd\]\s*$')
|
||||
|
||||
try:
|
||||
# pylint: disable=invalid-name
|
||||
with open(SERVER_SYSRESTORE_STATE) as f:
|
||||
for line in f.readlines():
|
||||
if ntpd_conf_section.match(line):
|
||||
return True
|
||||
# pylint: enable=invalid-name
|
||||
return False
|
||||
except IOError:
|
||||
return False
|
||||
@@ -71,7 +73,7 @@ def is_dns_configured():
|
||||
bind_conf_section = re.compile(r'^\s*dyndb\s+"ipa"\s+"[^"]+"\s+{$')
|
||||
|
||||
try:
|
||||
with open(NAMED_CONF) as f:
|
||||
with open(NAMED_CONF) as f: # pylint: disable=invalid-name
|
||||
for line in f.readlines():
|
||||
if bind_conf_section.match(line):
|
||||
return True
|
||||
@@ -103,7 +105,7 @@ def is_client_configured():
|
||||
# and /var/lib/ipa-client/sysrestore/sysrestore.state exists
|
||||
|
||||
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||
return (os.path.isfile(paths.IPA_DEFAULT_CONF) and fstore.has_files())
|
||||
return os.path.isfile(paths.IPA_DEFAULT_CONF) and fstore.has_files()
|
||||
|
||||
|
||||
def is_server_configured():
|
||||
@@ -129,7 +131,9 @@ def get_ipa_conf():
|
||||
|
||||
def get_ipa_version():
|
||||
try:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from ipapython import version
|
||||
# pylint: enable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return None
|
||||
else:
|
||||
@@ -156,7 +160,7 @@ def get_ipa_version():
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
argument_spec={},
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ def get_host_diff(ipa_host, module_host):
|
||||
:return: a dict representing the host attributes to apply
|
||||
"""
|
||||
non_updateable_keys = ['ip_address']
|
||||
data = dict()
|
||||
data = {}
|
||||
for key in non_updateable_keys:
|
||||
if key in module_host:
|
||||
del module_host[key]
|
||||
@@ -173,7 +173,7 @@ def get_module_host(module):
|
||||
:param module: the ansible module
|
||||
:returns: a dict representing the host attributes
|
||||
"""
|
||||
data = dict()
|
||||
data = {}
|
||||
certificates = module.params.get('certificates')
|
||||
if certificates:
|
||||
data['usercertificate'] = certificates
|
||||
@@ -189,7 +189,7 @@ def get_module_host(module):
|
||||
return data
|
||||
|
||||
|
||||
def ensure_host_present(module, api, ipahost):
|
||||
def ensure_host_present(module, _api, ipahost):
|
||||
"""
|
||||
Ensure host exists in IPA and has the same attributes.
|
||||
|
||||
@@ -216,13 +216,13 @@ def ensure_host_present(module, api, ipahost):
|
||||
# already has Keytab: true, then we need first to run
|
||||
# ipa host-disable in order to remove OTP and keytab
|
||||
if module.params.get('random') and ipahost['has_keytab'] is True:
|
||||
api.Command.host_disable(fqdn)
|
||||
_api.Command.host_disable(fqdn)
|
||||
|
||||
result = api.Command.host_mod(fqdn, **diffs)
|
||||
result = _api.Command.host_mod(fqdn, **diffs)
|
||||
# Save random password as it is not displayed by host-show
|
||||
if module.params.get('random'):
|
||||
randompassword = result['result']['randompassword']
|
||||
result = api.Command.host_show(fqdn)
|
||||
result = _api.Command.host_show(fqdn)
|
||||
if module.params.get('random'):
|
||||
result['result']['randompassword'] = randompassword
|
||||
module.exit_json(changed=True, host=result['result'])
|
||||
@@ -236,17 +236,17 @@ def ensure_host_present(module, api, ipahost):
|
||||
module_host = get_module_host(module)
|
||||
# force creation of host even if there is no DNS record
|
||||
module_host["force"] = True
|
||||
result = api.Command.host_add(fqdn, **module_host)
|
||||
result = _api.Command.host_add(fqdn, **module_host)
|
||||
# Save random password as it is not displayed by host-show
|
||||
if module.params.get('random'):
|
||||
randompassword = result['result']['randompassword']
|
||||
result = api.Command.host_show(fqdn)
|
||||
result = _api.Command.host_show(fqdn)
|
||||
if module.params.get('random'):
|
||||
result['result']['randompassword'] = randompassword
|
||||
module.exit_json(changed=True, host=result['result'])
|
||||
|
||||
|
||||
def ensure_host_absent(module, api, host):
|
||||
def ensure_host_absent(module, _api, host):
|
||||
"""
|
||||
Ensure host does not exist in IPA.
|
||||
|
||||
@@ -265,7 +265,7 @@ def ensure_host_absent(module, api, host):
|
||||
|
||||
fqdn = unicode(module.params.get('fqdn'))
|
||||
try:
|
||||
api.Command.host_del(fqdn)
|
||||
_api.Command.host_del(fqdn)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Failed to remove host: %s" % e)
|
||||
|
||||
|
||||
@@ -82,7 +82,9 @@ def main():
|
||||
|
||||
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(configure_nisdomain)
|
||||
# pylint: enable=deprecated-method
|
||||
if "statestore" not in argspec.args:
|
||||
# NUM_VERSION < 40500:
|
||||
configure_nisdomain(options=options, domain=domain)
|
||||
|
||||
@@ -113,7 +113,9 @@ def main():
|
||||
if sync_time is not None:
|
||||
if options.conf_ntp:
|
||||
# Attempt to configure and sync time with NTP server (chrony).
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(sync_time)
|
||||
# pylint: enable=deprecated-method
|
||||
if "options" not in argspec.args:
|
||||
synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
|
||||
fstore, statestore)
|
||||
@@ -149,8 +151,8 @@ def main():
|
||||
if options.ntp_servers:
|
||||
ntp_servers = options.ntp_servers
|
||||
|
||||
for s in ntp_servers:
|
||||
synced_ntp = timeconf.synconce_ntp(s, options.debug)
|
||||
for _ntp_server in ntp_servers:
|
||||
synced_ntp = timeconf.synconce_ntp(_ntp_server, options.debug)
|
||||
if synced_ntp:
|
||||
break
|
||||
|
||||
|
||||
7
roles/ipaclient/vars/Debian-10.yml
Normal file
7
roles/ipaclient/vars/Debian-10.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# vars/Debian.yml
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
# Debian Buster must use python2 as Python interpreter due
|
||||
# to the way freeipa-client package is defined.
|
||||
# You must install package python2.7 before executing this role.
|
||||
ansible_python_interpreter: '/usr/bin/python2'
|
||||
7
roles/ipaclient/vars/Ubuntu-18.04.yml
Normal file
7
roles/ipaclient/vars/Ubuntu-18.04.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
# vars/Ubuntu-18.04.yml
|
||||
---
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
# 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'
|
||||
@@ -199,7 +199,9 @@ def main():
|
||||
|
||||
ansible_log.debug("-- CUSTODIA IMPORT DM PASSWORD --")
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(custodia.import_dm_password)
|
||||
# pylint: enable=deprecated-method
|
||||
if "master_host_name" in argspec.args:
|
||||
custodia.import_dm_password(config.master_host_name)
|
||||
else:
|
||||
|
||||
@@ -56,8 +56,7 @@ from ansible.module_utils.ansible_ipa_replica import (
|
||||
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
),
|
||||
argument_spec={},
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -316,7 +316,9 @@ def main():
|
||||
ansible_log.debug("-- CONFIGURE DIRSRV --")
|
||||
# Configure dirsrv
|
||||
with redirect_stdout(ansible_log):
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(install_replica_ds)
|
||||
# pylint: enable=deprecated-method
|
||||
if "promote" in argspec.args:
|
||||
ds = install_replica_ds(config, options, ca_enabled,
|
||||
remote_api,
|
||||
@@ -336,9 +338,13 @@ def main():
|
||||
ca_file=cafile,
|
||||
pkcs12_info=dirsrv_pkcs12_info)
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
ansible_log.debug("-- INSTALL DNS RECORDS --")
|
||||
# pylint: enable=deprecated-method
|
||||
# Always try to install DNS records
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(install_dns_records)
|
||||
# pylint: enable=deprecated-method
|
||||
if "fstore" not in argspec.args:
|
||||
install_dns_records(config, options, remote_api)
|
||||
else:
|
||||
|
||||
@@ -202,7 +202,9 @@ def main():
|
||||
create_ipa_conf(fstore, config, ca_enabled,
|
||||
master=config.master_host_name)
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(install_http)
|
||||
# pylint: enable=deprecated-method
|
||||
if "promote" in argspec.args:
|
||||
install_http(
|
||||
config,
|
||||
|
||||
@@ -161,7 +161,9 @@ def main():
|
||||
ansible_log.debug("-- INSTALL_KRB --")
|
||||
|
||||
with redirect_stdout(ansible_log):
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(install_krb)
|
||||
# pylint: enable=deprecated-method
|
||||
if "promote" in argspec.args:
|
||||
install_krb(
|
||||
config,
|
||||
|
||||
@@ -286,7 +286,9 @@ def main():
|
||||
# asks for HTTP certificate in newer ipa versions. In these versions
|
||||
# create_ipa_conf has the additional master argument.
|
||||
change_master_for_certmonger = False
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(create_ipa_conf)
|
||||
# pylint: enable=deprecated-method
|
||||
if "master" in argspec.args:
|
||||
change_master_for_certmonger = True
|
||||
|
||||
@@ -418,7 +420,9 @@ def main():
|
||||
# check selinux status, http and DS ports, NTP conflicting services
|
||||
try:
|
||||
with redirect_stdout(ansible_log):
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(common_check)
|
||||
# pylint: enable=deprecated-method
|
||||
if "skip_mem_check" in argspec.args:
|
||||
common_check(options.no_ntp, options.skip_mem_check,
|
||||
options.setup_ca)
|
||||
|
||||
@@ -138,15 +138,15 @@ else:
|
||||
|
||||
try:
|
||||
from ipaclient.install import timeconf
|
||||
time_service = "chronyd"
|
||||
ntpinstance = None
|
||||
time_service = "chronyd" # pylint: disable=invalid-name
|
||||
ntpinstance = None # pylint: disable=invalid-name
|
||||
except ImportError:
|
||||
try:
|
||||
from ipaclient.install import ntpconf as timeconf
|
||||
except ImportError:
|
||||
from ipaclient import ntpconf as timeconf
|
||||
from ipaserver.install import ntpinstance
|
||||
time_service = "ntpd"
|
||||
time_service = "ntpd" # pylint: disable=invalid-name
|
||||
|
||||
else:
|
||||
# IPA version < 4.6
|
||||
@@ -162,10 +162,10 @@ else:
|
||||
filemode='a', console_format='%(message)s')
|
||||
|
||||
@contextlib_contextmanager
|
||||
def redirect_stdout(f):
|
||||
sys.stdout = f
|
||||
def redirect_stdout(stream):
|
||||
sys.stdout = stream
|
||||
try:
|
||||
yield f
|
||||
yield stream
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
@@ -202,7 +202,8 @@ else:
|
||||
self.module.debug(msg)
|
||||
# self.module.warn(msg)
|
||||
|
||||
class installer_obj(object):
|
||||
# pylint: disable=too-many-instance-attributes, useless-object-inheritance
|
||||
class installer_obj(object): # pylint: disable=invalid-name
|
||||
def __init__(self):
|
||||
# CompatServerReplicaInstall
|
||||
self.ca_cert_files = None
|
||||
@@ -244,10 +245,10 @@ else:
|
||||
# (attr, repr(value)))
|
||||
# return value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
logger.info(" --> ADDING missing installer.%s", attr)
|
||||
setattr(self, attr, None)
|
||||
return getattr(self, attr)
|
||||
def __getattr__(self, attrname):
|
||||
logger.info(" --> ADDING missing installer.%s", attrname)
|
||||
setattr(self, attrname, None)
|
||||
return getattr(self, attrname)
|
||||
|
||||
# def __setattr__(self, attr, value):
|
||||
# logger.debug(" --> Setting installer.%s to %s" %
|
||||
@@ -258,6 +259,9 @@ else:
|
||||
for name in self.__dict__:
|
||||
yield self, name
|
||||
|
||||
# pylint: enable=too-many-instance-attributes, useless-object-inheritance
|
||||
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
installer = installer_obj()
|
||||
options = installer
|
||||
|
||||
@@ -274,6 +278,7 @@ else:
|
||||
# ServerReplicaInstall
|
||||
options.subject_base = None
|
||||
options.ca_subject = None
|
||||
# pylint: enable=attribute-defined-outside-init
|
||||
|
||||
def gen_env_boostrap_finalize_core(etc_ipa, default_config):
|
||||
env = Env()
|
||||
@@ -295,9 +300,12 @@ else:
|
||||
# pylint: enable=no-member
|
||||
api.finalize()
|
||||
|
||||
def gen_ReplicaConfig():
|
||||
def gen_ReplicaConfig(): # pylint: disable=invalid-name
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class ExtendedReplicaConfig(ReplicaConfig):
|
||||
# pylint: disable=useless-super-delegation
|
||||
def __init__(self, top_dir=None):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(ExtendedReplicaConfig, self).__init__(top_dir)
|
||||
|
||||
# def __getattribute__(self, attr):
|
||||
@@ -306,12 +314,13 @@ else:
|
||||
# if attr not in ["__dict__", "knobs"]:
|
||||
# logger.debug(" <== Accessing config.%s (%s)" %
|
||||
# (attr, repr(value)))
|
||||
# return value
|
||||
# return value\
|
||||
# pylint: enable=useless-super-delegation
|
||||
|
||||
def __getattr__(self, attr):
|
||||
logger.info(" ==> ADDING missing config.%s", attr)
|
||||
setattr(self, attr, None)
|
||||
return getattr(self, attr)
|
||||
def __getattr__(self, attrname):
|
||||
logger.info(" ==> ADDING missing config.%s", attrname)
|
||||
setattr(self, attrname, None)
|
||||
return getattr(self, attrname)
|
||||
|
||||
# def __setattr__(self, attr, value):
|
||||
# logger.debug(" ==> Setting config.%s to %s" %
|
||||
@@ -322,7 +331,9 @@ else:
|
||||
def knobs(self):
|
||||
for name in self.__dict__:
|
||||
yield self, name
|
||||
# pylint: enable=too-many-instance-attributes
|
||||
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
# config = ReplicaConfig()
|
||||
config = ExtendedReplicaConfig()
|
||||
config.realm_name = api.env.realm
|
||||
@@ -338,10 +349,12 @@ else:
|
||||
config.basedn = api.env.basedn
|
||||
# config.subject_base = options.subject_base
|
||||
|
||||
# pylint: enable=attribute-defined-outside-init
|
||||
|
||||
return config
|
||||
|
||||
def replica_ds_init_info(ansible_log,
|
||||
config, options, ca_is_configured, remote_api,
|
||||
config, options_, ca_is_configured, remote_api,
|
||||
ds_ca_subject, ca_file,
|
||||
promote=False, pkcs12_info=None):
|
||||
|
||||
@@ -364,7 +377,7 @@ else:
|
||||
ca_subject = ds_ca_subject
|
||||
|
||||
ds = dsinstance.DsInstance(
|
||||
config_ldif=options.dirsrv_config_file)
|
||||
config_ldif=options_.dirsrv_config_file)
|
||||
ds.set_output(ansible_log)
|
||||
|
||||
# Source: ipaserver/install/dsinstance.py
|
||||
|
||||
10
roles/ipareplica/vars/Ubuntu-18.04.yml
Normal file
10
roles/ipareplica/vars/Ubuntu-18.04.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# vars/Ubuntu.yml
|
||||
---
|
||||
ipareplica_packages: [ "freeipa-server" ]
|
||||
ipareplica_packages_dns: [ "freeipa-server-dns" ]
|
||||
ipareplica_packages_adtrust: [ "freeipa-server-trust-ad" ]
|
||||
ipareplica_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'
|
||||
@@ -312,6 +312,7 @@ def main():
|
||||
|
||||
# Create the management framework config file and finalize api
|
||||
target_fname = paths.IPA_DEFAULT_CONF
|
||||
# pylint: disable=invalid-name, consider-using-with
|
||||
fd = open(target_fname, "w")
|
||||
fd.write("[global]\n")
|
||||
fd.write("host=%s\n" % options.host_name)
|
||||
@@ -331,6 +332,7 @@ def main():
|
||||
fd.write("ra_plugin=none\n")
|
||||
fd.write("mode=production\n")
|
||||
fd.close()
|
||||
# pylint: enable=invalid-name, consider-using-with
|
||||
|
||||
# Must be readable for everyone
|
||||
os.chmod(target_fname, 0o644)
|
||||
@@ -391,8 +393,8 @@ def main():
|
||||
ansible_log.info("Disabled p11-kit-proxy")
|
||||
|
||||
except (RuntimeError, ValueError, ScriptError,
|
||||
ipautil.CalledProcessError) as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
ipautil.CalledProcessError) as err:
|
||||
ansible_module.fail_json(msg=str(err))
|
||||
|
||||
ansible_module.exit_json(
|
||||
changed=True,
|
||||
|
||||
@@ -93,7 +93,9 @@ def main():
|
||||
# the ipa-server-install --uninstall
|
||||
ansible_module.log("Synchronizing time")
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(sync_time)
|
||||
# pylint: enable=deprecated-method
|
||||
if "options" not in argspec.args:
|
||||
synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
|
||||
fstore, sstore)
|
||||
|
||||
@@ -742,7 +742,7 @@ def main():
|
||||
validate_admin_password(options.admin_password)
|
||||
|
||||
# pkinit is not supported on DL0, don't allow related options
|
||||
|
||||
# pylint: disable=pointless-string-statement
|
||||
"""
|
||||
# replica install: if not options.replica_file is None:
|
||||
if (not options._replica_install and \
|
||||
@@ -755,6 +755,7 @@ def main():
|
||||
"don't use any pkinit-related options.")
|
||||
options.no_pkinit = True
|
||||
"""
|
||||
# pylint: enable=pointless-string-statement
|
||||
|
||||
if options.setup_dns:
|
||||
if len(options.forwarders) < 1 and not options.no_forwarders and \
|
||||
@@ -942,7 +943,9 @@ def main():
|
||||
else:
|
||||
realm_name = options.realm_name.upper()
|
||||
|
||||
# pylint: disable=deprecated-method
|
||||
argspec = inspect.getargspec(validate_domain_name)
|
||||
# pylint: enable=deprecated-method
|
||||
if "entity" in argspec.args:
|
||||
# NUM_VERSION >= 40690:
|
||||
try:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user