Compare commits

...

62 Commits

Author SHA1 Message Date
Thomas Woerner
6b4fd03bc6 Merge pull request #686 from rjeffman/hbacrule_case_insensitive
hbacrule: Fix member management idempotence issues.
2022-01-13 16:28:43 +01:00
Thomas Woerner
095e6a4155 Merge pull request #684 from rjeffman/iparole_idempotence_issues
iparole: Fix idempotence issues
2022-01-13 16:27:43 +01:00
Rafael Guterres Jeffman
2cb11d44ec Merge pull request #729 from t-woerner/fix_new_ansible_test_findings
ansible-test: Fix new findings
2022-01-13 10:45:44 -03:00
Thomas Woerner
9499a3ed9f ansible-test: Fix new findings
ERROR: Found 6 pylint issue(s) which need to be resolved:
ERROR: plugins/modules/ipaserver_prepare.py:395:4: invalid-name: Variable name "e" doesn't conform to snake_case naming style
ERROR: roles/ipaserver/library/ipaserver_prepare.py:395:4: invalid-name: Variable name "e" doesn't conform to snake_case naming style
ERROR: roles/ipaserver/module_utils/ansible_ipa_server.py:333:12: invalid-name: Variable name "ds" doesn't conform to snake_case naming style
ERROR: roles/ipaserver/module_utils/ansible_ipa_server.py:348:12: invalid-name: Variable name "ds" doesn't conform to snake_case naming style
ERROR: roles/ipaserver/module_utils/ansible_ipa_server.py:361:12: invalid-name: Variable name "ip" doesn't conform to snake_case naming style
ERROR: roles/ipaserver/module_utils/ansible_ipa_server.py:364:12: invalid-name: Variable name "e" doesn't conform to snake_case naming style

e has been replaced with err, ds with _ds, ip with _ip.
2022-01-13 14:28:55 +01:00
Rafael Guterres Jeffman
7632f90edb Merge pull request #724 from t-woerner/enhance_utils_build-galaxy-release_sh
build-galaxy-release.sh: Use build dir, new options, checks, no reset
2022-01-13 10:26:25 -03:00
Rafael Guterres Jeffman
07e9d87e92 iparole: Skip ansible-test verifications for Python 2.6. 2022-01-13 10:20:28 -03:00
Rafael Guterres Jeffman
0cebb3e2a2 hbacrule: Fix member management idempotence issues.
Members of hbacrule must be compared in a case insensitive manner.
This patch fixes comparation of member parameters against existing
members by converting parameters to lowercase.

Also, there were some cases where a change with an empty set of members
was issued to IPA API, leading to a result of 'changed: yes' when
'changed: no' was expected. The fix involved a refactoring of the
member management code.
2022-01-13 10:19:06 -03:00
Rafael Guterres Jeffman
d2bcaa3b81 test playbooks: Add fact to define ipaserver_domain if not set.
Add a task to FreeIPA facts task file to ensure that the variable
'ipaserver_domain' is set.

The value is set form `ansible_facts['fqdn'], if available, or set to
`ipa.test`, otherwise.
2022-01-13 10:04:33 -03:00
Thomas Woerner
37ba14f164 Merge pull request #685 from rjeffman/hbacsvcgroup_case_insensitive
hbacsvcgroup: Fix member management idempotence issues.
2022-01-13 14:00:59 +01:00
Thomas Woerner
9b88207100 Merge pull request #708 from rjeffman/pylint_enable_roles
Enable pylint for ansible-freeipa roles.
2022-01-13 13:42:16 +01:00
Thomas Woerner
9d6a83dce7 Merge pull request #727 from rjeffman/shellcheck_no_docker
pre-commit: Use system shellcheck.
2022-01-13 13:40:20 +01:00
Rafael Guterres Jeffman
b489e2b8a8 Merge pull request #728 from t-woerner/pre_commit_ansible_lint_version_5_3_2
pre-commit: Update ansible-lint version to v5.3.2
2022-01-13 09:06:05 -03:00
Rafael Guterres Jeffman
1d18063497 pre-commit: Use system shellcheck.
The official ShellCheck pre-commit hook uses a docker image, but it
is, sometimes, unavailable. This change will use the system installed
ShellCheck executable and does not depend on the image download.
2022-01-13 08:52:40 -03:00
Thomas Woerner
7548c5afd1 pre-commit: Update ansible-lint version to v5.3.2
This fixes the import error for render_group from rich.console.
2022-01-13 12:46:27 +01:00
Thomas Woerner
27348d8f26 Merge pull request #726 from rjeffman/ghw_change_ansible_lint
Github Workflows: Run ansible-lint without an action.
2022-01-13 11:31:47 +01:00
Rafael Guterres Jeffman
7ba6ae348b Github Workflows: Run ansible-lint without an action.
We used a Github Action to run anisble-lint, but it has not have a
release since 2019, and has not been updated in a year. This action is
showing some issues when evaluating current playbooks.

This PR substitute the action previously used with a shell script
directly define in the workflow job. The ansible-core version was
pinned to the currently available on Fedora 25, 2.11.6.
2022-01-12 19:19:44 -03:00
Rafael Guterres Jeffman
a025e476ea iparole: Add tests to verify if capitalisation is ignored.
The test playbook provided adds some tests to verify if capitalization
of role members does not influence on the module behavior. It also adds
some tests to verify check_mode.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
a44ffbf3dd iparole: rename function get_lowercase to result_get_value_lowercase
Renamed function and improved its documentation to better explain  its
use and goals.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
846fdc0698 iparole: Fix idempotence issues with members.
IPA role members users, groups, hostgroups and privilege must be
compared in a case insensitive way, and either are stored in lowercase
or IPA API fixes the value for proper representation.

This patch forces all comparisons of this values to be performed in
lowercase, and also only modify the values if it is really needed.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
faace4f376 iparole: Ensure host members are lowercase and FQDN.
IPA Role host members should always be lowercase and FQDN. This
patch ensure that hosts are correctly compared and added as role
members.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
bde3eb8294 IPAAnsibleModule: cache IPA domain.
Some attributes retrieved by the IPA API backend don't change, and are
used more than once, in different places of the code. IPA API domain
is one of these attributes.

This patch adds a cache to the attribute, so there is only one request
for the API, improving access time to the object and alowing multiple
calls with no efficiency penalty.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
971fcc917a iparole: Case insensitive comparison of service members.
Service members in IPA role objects must be compared ignoring character
capitalization, but are stored in a case preserving manner.

This patch modifies the way service members are handled, creating a map
between a lowercase version of the service parameter and the parameter
itself, and using the map key to compare against existing services. The
mapped value is then added as role member, if necessary.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
13d7d714d7 iparole: Remove custom code in favor of commom functions.
Removed custom code used to create add/del lists in iparole in favor
of ansible_freeipa_module functions, and custom result_handler, to
reduce code duplication, as these methods have equivalent shared
versions.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
8a93627079 iparole: Removed unused code.
There was some unused code that was removed.
2022-01-12 19:03:33 -03:00
Rafael Guterres Jeffman
c24ff079d6 Merge pull request #725 from t-woerner/fix_new_ansible_test_findings
ansible-test fixes
2022-01-12 18:59:19 -03:00
Thomas Woerner
4f1a01b85b ansible-test fixes
ERROR: plugins/modules/ipaautomountmap.py:118:30: E203: whitespace before ':'

ERROR: Found 1 compile issue(s) on python 2.6 which need to be resolved:
ERROR: plugins/modules/ipasudorule.py:382:63: SyntaxError: {ensure_fqdn(value.lower(), default_domain) for value in host}
2022-01-12 16:54:52 +01:00
Thomas Woerner
886abee4e2 Merge pull request #674 from rjeffman/sudorule_fix_host_order
sudorule: Create FQDN from single hostnames
2022-01-12 16:10:21 +01:00
Rafael Guterres Jeffman
ce8487e394 pylint: Enable pylint for ansible-freeipa roles.
This patch enables pylint evaluation for ansible-freeipa roles in
both the local script 'utils/lint-check.sh' and in upstream CI.
2022-01-12 12:09:46 -03:00
Rafael Guterres Jeffman
bf5555271d pylint: Fix pylint issues with modules.
Fix pylint warnings raised by enabling linter on ansible-freeipa roles.
2022-01-12 12:09:46 -03:00
Rafael Guterres Jeffman
752fa1087d pylint: Add modules and names that should be ignored by linter.
This change configure pylint to ignore import modules that might not be
availble during development, and ignore names that are relevant in the
FreeIPA domain, even if they don't comply with PEP8.
2022-01-12 12:09:29 -03:00
Thomas Woerner
fe836b538d Merge pull request #721 from rjeffman/ipagroup_fix_member_management
ipagroup: Refactor and fix group member management.
2022-01-12 16:07:38 +01:00
Rafael Guterres Jeffman
746e4c0ffa Merge pull request #723 from t-woerner/sanity-ansible-test
Enable ansible-test in github workflow
2022-01-12 11:59:28 -03:00
Thomas Woerner
8fa29a9522 Enable ansible-test in github workflow
This test is using the galaxy_importer from ansible project. The
configuration file galaxy-importer.cfg is copied from linux-system-roles

    https://github.com/linux-system-roles/auto-maintenance/blob/master/\
    lsr_role2collection/galaxy-importer.cfg

The tests script has extra code to parse the output of the importer to
highlight errors and to exit with a proper error code.

The test can be used locally also with "sh tests/sanity/sanity.sh"

New files:
- .github/workflows/ansible-test.yml
- tests/sanity/galaxy-importer.cfg
- tests/sanity/sanity.sh
2022-01-12 15:42:04 +01:00
Thomas Woerner
de8d724663 build-galaxy-release.sh: Use build dir, new options, checks, no reset
The script is now using a build dir for the creation of the Ansible
Collection. Additionally only files known to the fit repo are pulled in
by default. The new "-a" option is pulling in all files from local repo.
The new -k" option can be used to keep the build dir for verification of
the changes to the files.

The colleciton is placed into the main repo dir and no git reset --hard
is used in the repo to preserve local changes.
2022-01-12 13:46:33 +01:00
Thomas Woerner
b401ba0354 Merge pull request #498 from chr15p/ipaautomountkey
add module to create and manage automount keys
2022-01-12 13:27:32 +01:00
Rafael Guterres Jeffman
dd700d956b Fixed automountkey code review issues.
Fixed several issues found during code review and change
AutomountkeyModule to use IPAAnsibleModule instead of deprecated
FreeIPABaseModule.
2022-01-11 17:52:20 -03:00
chrisp
3ca9982c73 New automount key management module
There is a new automount key module placed in the plugins folder:

    plugins/modules/ipaautomountkey.py

The server module allows to ensure presence and absence of automount
keys. The module requires an existing automount location and map to
place the key within.

Here is the documentation for the module:

    README-automountkey.md

New example playbooks have been added:

    playbooks/automount/automount-key-absent.yaml
    playbooks/automount/automount-key-present.yaml

New tests for the module:

    tests/automount/test_automountkey.yml
2022-01-11 14:12:49 -03:00
Thomas Woerner
6a1f61931d Merge pull request #497 from chr15p/ipaautomountmap
add module to create and manage automount maps
2022-01-11 18:01:25 +01:00
Rafael Guterres Jeffman
e1e8ff5916 Adapt automount to IPAAnsibleModule and add code review modifications. 2022-01-11 09:43:41 -03:00
Rafael Guterres Jeffman
3b08edda50 ipagroup: Refactor and fix group member management.
Currently, when adding an overlapping set of members causes playbook to
fail as the already existing members are added twice.

This patch refactors membership management by removing duplicate logic
and handling all changes to members in a single place. This change
removed code that was causing the execution failures.
2022-01-11 09:27:47 -03:00
chrisp
0d47429000 New automount map management module.
There is a new server management module placed in the plugins folder:

    plugins/modules/ipaautomountmap.py

The server module allows to ensure presence and absence of automount
maps. The module requires an existing automount location to place the
map within. It does not create any automount keys with in the map.

Here is the documentation for the module:

    README-automountmap.md

New example playbooks have been added:

    playbooks/automount/automount-map-absent.yaml
    playbooks/automount/automount-map-present.yaml

New tests for the module:

    tests/automount/test_automountmap.yml
2022-01-05 18:49:27 -03:00
Thomas Woerner
870dfec9df Merge pull request #697 from rjeffman/ci_fix_pytests_ansible_version
upstrem CI: Fix Ansible version in pytest playbooks.
2022-01-05 12:19:30 +01:00
Thomas Woerner
7e62ebd7b4 Merge pull request #696 from rjeffman/ci_centos9_stream
upstream CI:  Add support for CentOS 9 stream.
2022-01-05 12:18:46 +01:00
Thomas Woerner
081d0f658d Merge pull request #706 from rjeffman/ci_ansible_core_2_12
upstream CI: Enable nightly tests using ansible-core 2.12.
2022-01-05 12:17:43 +01:00
Thomas Woerner
d708fc4734 Merge pull request #704 from rjeffman/ansible_doc_test_ansible_2_12
upstream CI: Enable ansible-doc-test for ansible-core 2.12.
2022-01-05 12:17:19 +01:00
Thomas Woerner
4a4700191e Merge pull request #716 from rjeffman/iparole_add_state_renamed
iparole: Add state 'renamed'.
2022-01-05 11:37:03 +01:00
Thomas Woerner
8c88413ac1 Merge pull request #717 from rjeffman/ci_fix_kdc_unavailable
upstream CI: Wait for KDC to be available.
2022-01-04 13:41:25 +01:00
Rafael Guterres Jeffman
30c4748fe2 upstream CI: Wait for KDC to be available.
Sometimes the first test of a batch fails because it fails to grant a
TGT from Kerberos KDC as it is not yet fully working. By waiting until
a TGT can be acquired, these failures will not happen anymore.
2022-01-03 16:26:14 -03:00
Rafael Guterres Jeffman
68f775842d iparole: Add state 'renamed'.
All ansible-freeipa modules which allow object renaming should support
'state: renamed'.

This patch adds suport for the missing state, and fixes cases where a
user could try to rename the object and set its members, which would
fail depending on the operation order.

Fix #566
2021-12-29 11:16:55 -03:00
Rafael Guterres Jeffman
cf7fc949fe sudorule: Create FQDN from single hostnames
Single hostnames can be used for sudorule_add_host and will match fqdn
in IPA internally. Simple host names have to be extended to be FQDN to
be able to compare them for sudorule_host_add and sudorule_host_remove.

Fixes #672
2021-12-29 09:05:10 -03:00
Thomas Woerner
40e00a6234 Merge pull request #713 from rjeffman/remove_site_yml
Remove unused, old example of ipaclient deploy.
2021-12-28 15:23:24 +01:00
Rafael Guterres Jeffman
c24e5710da Merge pull request #714 from t-woerner/galaxy_tag_linux
galaxy.yml: Add linux tag for AH
2021-12-23 14:54:46 -03:00
Thomas Woerner
43a525139b galaxy.yml: Add linux tag for AH 2021-12-23 15:33:55 +01:00
Rafael Guterres Jeffman
e0bdfdfe32 Merge pull request #712 from t-woerner/readme_fixes
Readme fixes
2021-12-23 11:33:32 -03:00
Thomas Woerner
65937ed8c3 module README files: Drop extra module header in Variables section
The Variables and also the Return Variables sections contained an extra
header with the module name. This is only needed if there are more than
one module in the README.
2021-12-23 15:25:46 +01:00
Thomas Woerner
ec2c0c4b59 README.md: Add automount location, fix some README links
automount location was missing in README.md in the feature and also in
the README link section.

The links for location, permission, privilege and selfservice have been
wrongly using the ipa prefix for the module
2021-12-23 15:25:46 +01:00
Rafael Guterres Jeffman
753a8b0bd1 Remove unused, old example of ipaclient deploy. 2021-12-23 11:22:36 -03:00
Rafael Guterres Jeffman
e15c716906 upstream CI: Enable ansible-doc-test for ansible-core 2.12. 2021-12-10 11:28:04 -03:00
Rafael Guterres Jeffman
4167982208 upstream CI: Enable nightly tests using ansible-core 2.12.
This patch modifies the Python version used to be the latest available,
and add stages to execute the tests using ansible-core 2.12. As we
use Ubuntu 20.04, Python version 3.8 is avaiable.

Previously, ansible-core 2.12 was not available as it cannot be
installed with Python 3.6, which was the version used.
2021-12-09 22:34:30 -03:00
Rafael Guterres Jeffman
b140f04a9d hbacsvcgroup: Fix member management idempotence issues.
The hbacsvc members of hbacsvcgroup must be compared in a case
insensitive manner. This patch fixes comparation of member parameters
against existing members by converting parameters to lowercase, as it
is how the hbacsvc members are stored for hbacsvcgroups.

Also, there were some cases where a change with an empty set of members
was issued to IPA API, leading to a result of 'changed: yes' when
'changed: no' was expected. The fix involved a refactoring of the
hbacsvcgroup member management code.
2021-12-03 10:02:55 -03:00
Rafael Guterres Jeffman
214b6bba7e ci: Add support for CentOS 9 Stream on upstream CI.
This patch adds support for running upstream tests using Centos-9
stream images. Both pull request and nightly tests are updated.
2021-11-29 12:38:24 -03:00
Rafael Guterres Jeffman
700d2b7335 upstrem CI: Fix Ansible version in pytest playbooks.
When using group_tests, the pytest playbook was not receiving the
Ansible version to use, executing always with the latest available
version.

This patch fixes the behavior by passing the Ansible version to use
for tests to pytest_tests playbook.
2021-11-26 10:17:18 -03:00
94 changed files with 3393 additions and 729 deletions

17
.github/workflows/ansible-test.yml vendored Normal file
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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$

View File

@@ -262,9 +262,6 @@ Example playbook to ensure all orphan automember hostgroup rules are removed:
Variables
---------
ipaautomember
-------
Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no

112
README-automountkey.md Normal file
View 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

View File

@@ -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
View 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

View File

@@ -82,9 +82,6 @@ Example playbook to read config options:
Variables
=========
ipauser
-------
**General Variables:**
Variable | Description | Required

View File

@@ -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

View File

@@ -119,9 +119,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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -13,6 +13,7 @@ Features
* Repair mode for clients
* Backup and restore, also to and from controller
* Modules for automembership rule management
* Modules for automount location management
* Modules for config management
* Modules for delegation management
* Modules for dns config management
@@ -424,6 +425,7 @@ Modules in plugin/modules
=========================
* [ipaautomember](README-automember.md)
* [ipaautomountlocation](README-automountlocation.md)
* [ipaconfig](README-config.md)
* [ipadelegation](README-delegation.md)
* [ipadnsconfig](README-dnsconfig.md)
@@ -436,12 +438,12 @@ Modules in plugin/modules
* [ipahbacsvcgroup](README-hbacsvc.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)

View File

@@ -16,6 +16,7 @@ readme: "README.md"
license: "GPL-3.0-or-later"
tags:
- "linux"
- "system"
- "identity"
- "ipa"

View 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

View File

@@ -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

Binary file not shown.

View 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

View 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"

View 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

View 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

View 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

View File

@@ -875,10 +875,11 @@ 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():

View 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()

View 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()

View File

@@ -376,6 +376,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
@@ -440,45 +447,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 +459,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 +493,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)

View File

@@ -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(

View File

@@ -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

View File

@@ -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)

View File

@@ -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):
@@ -374,6 +374,13 @@ 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 = []

View File

@@ -61,7 +61,7 @@ from ipaplatform.paths import paths
def main():
module = AnsibleModule(
argument_spec=dict(),
argument_spec={},
supports_check_mode=True,
)

View File

@@ -45,12 +45,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):
@@ -128,8 +130,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 +152,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 +172,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 +199,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:

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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,
)

View File

@@ -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:

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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)

View File

@@ -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:

View File

@@ -105,23 +105,23 @@ else:
try:
from ipaclient.install import timeconf
from ipaclient.install.client import sync_time
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"
sync_time = None
time_service = "ntpd" # pylint: disable=invalid-name
sync_time = None # pylint: disable=invalid-name
from ipaserver.install import (
adtrust, bindinstance, ca, dns, dsinstance,
httpinstance, installutils, kra, krbinstance,
otpdinstance, custodiainstance, replication, service,
sysupgrade)
adtrust_imported = True
kra_imported = True
adtrust_imported = True # pylint: disable=invalid-name
kra_imported = True # pylint: disable=invalid-name
from ipaserver.install.installutils import (
BadHostError, get_fqdn, get_server_ip_address,
load_pkcs12, read_password, verify_fqdn,
@@ -158,9 +158,9 @@ else:
try:
from ipaserver.install import adtrustinstance
_server_trust_ad_installed = True
_server_trust_ad_installed = True # pylint: disable=invalid-name
except ImportError:
_server_trust_ad_installed = False
_server_trust_ad_installed = False # pylint: disable=invalid-name
try:
from ipaclient.install.client import check_ldap_conf
@@ -192,10 +192,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__
@@ -232,7 +232,9 @@ else:
self.module.debug(msg)
# self.module.warn(msg)
class options_obj(object):
# pylint: disable=too-few-public-methods, useless-object-inheritance
# pylint: disable=too-many-instance-attributes
class options_obj(object): # pylint: disable=invalid-name
def __init__(self):
self._replica_install = False
self.dnssec_master = False # future unknown
@@ -255,9 +257,14 @@ else:
for name in self.__dict__:
yield self, name
# pylint: enable=too-few-public-methods, useless-object-inheritance
# pylint: enable=too-many-instance-attributes
options = options_obj()
installer = options
# pylint: disable=attribute-defined-outside-init
# ServerMasterInstall
options.add_sids = True
options.add_agents = False
@@ -299,6 +306,9 @@ else:
options.ignore_topology_disconnect = False
options.ignore_last_of_role = False
# pylint: enable=attribute-defined-outside-init
# pylint: disable=invalid-name
def api_Backend_ldap2(host_name, setup_ca, connect=False):
# we are sure we have the configuration file ready.
cfg = dict(context='installer', confdir=paths.ETC_IPA, in_server=True,
@@ -312,15 +322,17 @@ else:
if connect:
api.Backend.ldap2.connect()
# pylint: enable=invalid-name
def ds_init_info(ansible_log, fstore, domainlevel, dirsrv_config_file,
realm_name, host_name, domain_name, dm_password,
idstart, idmax, subject_base, ca_subject,
no_hbac_allow, dirsrv_pkcs12_info, no_pkinit):
_no_hbac_allow, dirsrv_pkcs12_info, no_pkinit):
if not options.external_cert_files:
ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel,
config_ldif=dirsrv_config_file)
ds.set_output(ansible_log)
_ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel,
config_ldif=dirsrv_config_file)
_ds.set_output(ansible_log)
if options.dirsrv_cert_files:
_dirsrv_pkcs12_info = dirsrv_pkcs12_info
@@ -328,30 +340,30 @@ else:
_dirsrv_pkcs12_info = None
with redirect_stdout(ansible_log):
ds.init_info(realm_name, host_name, domain_name, dm_password,
subject_base, ca_subject, idstart, idmax,
# hbac_allow=not no_hbac_allow,
_dirsrv_pkcs12_info, setup_pkinit=not no_pkinit)
_ds.init_info(realm_name, host_name, domain_name, dm_password,
subject_base, ca_subject, idstart, idmax,
# hbac_allow=not no_hbac_allow,
_dirsrv_pkcs12_info, setup_pkinit=not no_pkinit)
else:
ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel)
ds.set_output(ansible_log)
_ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel)
_ds.set_output(ansible_log)
with redirect_stdout(ansible_log):
ds.init_info(realm_name, host_name, domain_name, dm_password,
subject_base, ca_subject, 1101, 1100, None,
setup_pkinit=not no_pkinit)
_ds.init_info(realm_name, host_name, domain_name, dm_password,
subject_base, ca_subject, 1101, 1100, None,
setup_pkinit=not no_pkinit)
return ds
return _ds
def ansible_module_get_parsed_ip_addresses(ansible_module,
param='ip_addresses'):
ip_addrs = []
for ip in ansible_module.params.get(param):
for _ip in ansible_module.params.get(param):
try:
ip_parsed = ipautil.CheckedIPAddress(ip)
except Exception as e:
ip_parsed = ipautil.CheckedIPAddress(_ip)
except Exception as err:
ansible_module.fail_json(
msg="Invalid IP Address %s: %s" % (ip, e))
msg="Invalid IP Address %s: %s" % (_ip, err))
ip_addrs.append(ip_parsed)
return ip_addrs

View File

@@ -57,14 +57,20 @@ disable =
fixme
[pylint.BASIC]
good-names = ex, i, j, k, Run, _, e, x, dn, cn, ip, os, unicode, __metaclass__
good-names =
ex, i, j, k, Run, _, e, x, dn, cn, ip, os, unicode, __metaclass__, ds
[pylint.IMPORTS]
ignored-modules =
ansible.module_utils.ansible_freeipa_module,
ansible.errors, ansible.plugins.action,
ansible.module_utils, ansible.module_utils.ansible_freeipa_module,
ipalib, ipalib.config, ipalib.constants, ipalib.krb_utils, ipalib.errors,
ipapython.ipautil, ipapython.dn, ipapython.version, ipapython.dnsutil,
ipaplatform.paths
ipapython.ipa_log_manager, ipapython,
ipaplatform, ipaplatform.paths, ipaplatform.tasks, ipapython.admintool,
ipaserver.install.installutils, ipaserver.install.server.install,
ipaserver.install,
ipaclient.install.ipachangeconf, ipaclient.install.client
[pylint.REFACTORING]
max-nested-blocks = 9

View File

@@ -1,8 +0,0 @@
---
- name: Playbook to install IPA clients
hosts: ipaclients
become: true
roles:
- role: ipaclient
state: present

View File

@@ -0,0 +1,154 @@
---
- name: Test automountmap
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: no
gather_facts: no
tasks:
- name: ensure test location TestLocation is present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
- name: ensure test map TestMap is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
- name: ensure key NewKeyName is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: NewKeyName
state: absent
- name: ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: NewKeyName
state: absent
- block:
### test the key creation, and modification
- name: ensure key TestKey is present
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
info: 192.168.122.1:/exports
state: present
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey is present again
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
info: 192.168.122.1:/exports
state: present
register: result
failed_when: result.failed or result.changed
## modify the key
- name: ensure key TestKey information has been updated
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
info: 192.168.122.1:/nfsshare
state: present
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey information has been updated again
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
info: 192.168.122.1:/nfsshare
state: present
register: result
failed_when: result.failed or result.changed
## modify the name
- name: ensure key TestKey has been renamed to NewKeyName
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
new_name: NewKeyName
state: renamed
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
state: absent
register: result
failed_when: result.failed or result.changed
- name: ensure key NewKeyName is present
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: NewKeyName
info: 192.168.122.1:/nfsshare
state: present
register: result
failed_when: result.failed or result.changed
- name: ensure failure when state is renamed and newname is not set
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: TestKey
state: renamed
register: result
failed_when: not result.failed
### cleanup after the tests
always:
- name: ensure key NewKeyName is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: NewKeyName
state: absent
- name: ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
map: TestMap
key: NewKeyName
state: absent
- name: ensure map TestMap is absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: absent

View File

@@ -0,0 +1,41 @@
---
- name: Test automountkey
hosts: ipaclients, ipaserver
become: no
gather_facts: no
tasks:
- name: Include FreeIPA facts.
include_tasks: ../env_freeipa_facts.yml
# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
ipaapi_context: server
location: NoLocation
map: NoMap
key: ThisShouldNotWork
register: result
failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*"))
when: ipa_host_is_client
# Import basic module tests, and execute with ipa_context set to 'client'.
# If ipaclients is set, it will be executed using the client, if not,
# ipaserver will be used.
#
# With this setup, tests can be executed against an IPA client, against
# an IPA server using "client" context, and ensure that tests are executed
# in upstream CI.
- name: Test automountlocation using client context, in client host.
import_playbook: test_automountkey.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients
- name: Test automountlocation using client context, in server host.
import_playbook: test_automountkey.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client

View File

@@ -0,0 +1,147 @@
---
- name: Test automountmap
hosts: ipaserver
become: no
gather_facts: no
tasks:
# setup environment
- name: ensure test maps are absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
- TestMap01
- TestMap02
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: absent
- name: ensure map TestMap is absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
- name: ensure location TestLocation is present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: present
# TESTS
- block:
- name: ensure map TestMap is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
desc: "this is a test map that should be deleted by the test"
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap is present again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap has a different description
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
desc: "this is a changed description that should be deleted by the test"
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap has a different description, again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
desc: "this is a changed description that should be deleted by the test"
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap is removed
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap has been removed
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap01 is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap01
location: TestLocation
desc: "this is a changed description that should be deleted by the test"
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap02 is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap02
location: TestLocation
desc: "this is a changed description that should be deleted by the test"
register: result
failed_when: result.failed or not result.changed
- name: ensure TestMap01 and TestMap02 are both absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
- TestMap01
- TestMap02
location: TestLocation
state: absent
register: result
failed_when: result.failed or not result.changed
- name: ensure TestMap01 and TestMap02 are both absent again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
- TestMap01
- TestMap02
location: TestLocation
state: absent
register: result
failed_when: result.failed or result.changed
# CLEAN UP
always:
- name: ensure test maps are absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
- TestMap01
- TestMap02
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: absent

View File

@@ -18,6 +18,17 @@ stages:
scenario: fedora-latest
ansible_version: ">=2.9,<2.10"
# CentOS 9
- stage: CentOS9_Ansible_2_9
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-9
ansible_version: ">=2.9,<2.10"
# CentOS 8
- stage: CentOS8_Ansible_2_9

View File

@@ -34,6 +34,15 @@ stages:
scenario: fedora-latest
ansible_version: "-core >=2.11,<2.12"
- stage: FedoraLatest_Ansible_Core_2_12
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.12,<2.13"
- stage: FedoraLatest_Ansible_latest
dependsOn: []
jobs:
@@ -43,6 +52,35 @@ stages:
scenario: fedora-latest
ansible_version: ""
# CentoOS 9
- stage: CentOS9_Ansible_2_9
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-9
ansible_version: ">=2.9,<2.10"
- stage: CentOS9_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-9
ansible_version: "-core >=2.11,<2.12"
- stage: CentOS9_Ansible_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-9
ansible_version: ""
# CentOS 8
- stage: CentOS8_Ansible_2_9
@@ -63,6 +101,15 @@ stages:
scenario: centos-8
ansible_version: "-core >=2.11,<2.12"
- stage: CentOS8_Ansible_Core_2_12
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-8
ansible_version: "-core >=2.12,<2.13"
- stage: CentOS8_Ansible_latest
dependsOn: []
jobs:
@@ -92,6 +139,15 @@ stages:
scenario: centos-7
ansible_version: "-core >=2.11,<2.12"
- stage: CentOS7_Ansible_Core_2_12
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.12,<2.13"
- stage: CentOS7_Ansible_latest
dependsOn: []
jobs:

View File

@@ -38,3 +38,4 @@ jobs:
parameters:
build_number: ${{ parameters.build_number }}
scenario: ${{ parameters.scenario }}
ansible_version: ${{ parameters.ansible_version }}

View File

@@ -14,7 +14,7 @@ parameters:
default: ""
- name: python_version
type: string
default: 3.6
default: 3.x
- name: build_number
type: string

View File

@@ -9,7 +9,7 @@ parameters:
default: ""
- name: python_version
type: string
default: 3.6
default: 3.x
jobs:
- job: Test_PyTests

View File

@@ -29,3 +29,15 @@
ipa_api_version: "{{ ipa_cmd_version.stdout_lines[1] }}"
ipa_host_is_client: "{{ (output.stdout_lines[-1] == 'CLIENT') | bool }}"
trust_test_is_supported: no
- block:
- name: Get Domain from server name
set_fact:
ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] | join ('.') }}"
when: "'fqdn' in ansible_facts"
- name: Set Domain to 'ipa.test' if FQDN could not be retrieved.
set_fact:
ipaserver_domain: "ipa.test"
when: "'fqdn' not in ansible_facts"
when: ipaserver_domain is not defined

View File

@@ -174,6 +174,129 @@
register: result
failed_when: result.changed or result.failed
- name: Ensure groups group3, group2, and group1 are absent
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group3,group2,group1
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Ensure group group1 is present
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
register: result
failed_when: not result.changed or result.failed
- name: Ensure users user1, user2 are present in group group1
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
action: member
register: result
failed_when: not result.changed or result.failed
- name: Ensure users user1, user2 and user3 are present in group group1
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
- user3
action: member
register: result
failed_when: not result.changed or result.failed
- name: Ensure users user1, user2 are present in group group1, again
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure users user1, user2 and user3 are present in group group1, again
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
- user3
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure group group1 is absent
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Ensure group group1 with users user1, user2 is present
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
register: result
failed_when: not result.changed or result.failed
- name: Ensure group group1 with users user1, user2 and user3 is present
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
- user3
register: result
failed_when: not result.changed or result.failed
- name: Ensure group group1 with users user1, user2 and user3 is present, again
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
- user3
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure only users user1, user2 are present in group group1
ipagroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: group1
user:
- user1
- user2
register: result
failed_when: not result.changed or result.failed
- name: Ensure group group3, group2 and group1 are absent
ipagroup:
ipaadmin_password: SomeADMINpassword

View File

@@ -0,0 +1,503 @@
---
- name: Test group
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: no
gather_facts: yes
vars:
user_list:
- User1
- uSer2
- usEr3
group_list:
- Group1
- gRoup2
- grOup3
host_list:
- HoSt01
- hOsT02
hostgroup_list:
- TestHostGroup
hbacsvc_list:
- Svc1
- sVC2
hbacsvcgroup_list:
- sVCgrOUp1
tasks:
- include_tasks: ../env_freeipa_facts.yml
- block:
# setup
- name: Ensure test hbacrule is absent
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
state: absent
- name: Ensure test users are present
ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: "{{ item }}"
first: First
last: Last
with_items: "{{ user_list }}"
- name: Ensure test groups are present
ipagroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ group_list }}"
- name: Ensure test hosts are present
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
force: yes
with_items: "{{ host_list }}"
- name: Ensure test hostgroups are present
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ hostgroup_list }}"
- name: Ensure test hbac services are present
ipahbacsvc:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ hbacsvc_list }}"
- name: Ensure test hbac service groups are present
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ hbacsvcgroup_list }}"
# Test with action: hbacrule
- name: Check if hbacrule present with members would trigger changes, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacrule is present with members, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacrule present with members would not trigger changes, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule is present with members, lowercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
hbacsvc:
- "{{ hbacsvc_list[0] | lower }}"
- "{{ hbacsvc_list[1] | lower }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | lower }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule is present with members, upercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
hbacsvc:
- "{{ hbacsvc_list[0] | upper }}"
- "{{ hbacsvc_list[1] | upper }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | upper }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure test hbacrule is absent
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
state: absent
# Test with action: members
- name: Ensure test hbacrule is present
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
- name: Check if hbacrule members present would trigger changes, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacrule members present, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
action: member
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacrule members present would not trigger changes, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
hbacsvc:
- "{{ hbacsvc_list[0] }}"
- "{{ hbacsvc_list[1] }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] }}"
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule members present, lowercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
hbacsvc:
- "{{ hbacsvc_list[0] | lower }}"
- "{{ hbacsvc_list[1] | lower }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | lower }}"
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule members present, upercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
hbacsvc:
- "{{ hbacsvc_list[0] | upper }}"
- "{{ hbacsvc_list[1] | upper }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | upper }}"
action: member
register: result
failed_when: result.changed or result.failed
# Test absent members
- name: Check if hbacrule members absent would trigger change, upercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
hbacsvc:
- "{{ hbacsvc_list[0] | upper }}"
- "{{ hbacsvc_list[1] | upper }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacrule members are absent, upercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
hbacsvc:
- "{{ hbacsvc_list[0] | upper }}"
- "{{ hbacsvc_list[1] | upper }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | upper }}"
action: member
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacrule members absent would not trigger change, upercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
hbacsvc:
- "{{ hbacsvc_list[0] | upper }}"
- "{{ hbacsvc_list[1] | upper }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule members are absent, mixed case
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacrule members are absent, lowercase
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
hbacsvc:
- "{{ hbacsvc_list[0] | lower }}"
- "{{ hbacsvc_list[1] | lower }}"
hbacsvcgroup:
- "{{ hbacsvcgroup_list[0] | lower }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
always:
- name: Ensure test hbacrule is absent
ipahbacrule:
ipaadmin_password: SomeADMINpassword
name: testrule
state: absent
- name: Ensure test users are absent
ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: "{{ item }}"
state: absent
with_items: "{{ user_list }}"
- name: Ensure test groups are absent
ipagroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ group_list }}"
- name: Ensure test hosts are absent
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
state: absent
with_items: "{{ host_list }}"
- name: Ensure test hostgroups are absent
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ hostgroup_list }}"
- name: Ensure test hbac services are absent
ipahbacsvc:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ hbacsvc_list }}"
- name: Ensure test hbac service groups are absent
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ hbacsvcgroup_list }}"

View File

@@ -0,0 +1,233 @@
---
- name: Test hbacsvcgroup member varying capitalization
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: no
gather_facts: no
vars:
hbacsvc_list:
- sVc1
- SvC2
tasks:
- block:
- name: Ensure test hbacsvcgroup is absent
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
state: absent
- name: Ensure test HBAC services are present
ipahbacsvc:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ hbacsvc_list }}"
- name: Ensure test hbacsvcgroup is present with duplicate hbacsvc
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc:
- sVc1
- SvC1
register: result
failed_when: not result.changed or result.failed
- name: Ensure test hbacsvc is absent from hbacsvcgroup, with duplicate hbacsvc
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc:
- sVc1
- SvC1
action: member
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Check if test hbacsvc absent, again, from hbacsvcgroup, with duplicate hbacsvc, would trigger changes
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc:
- svC1
- SVC1
action: member
state: absent
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure test hbacsvcgroup is absent
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacsvcgroup with members would trigger changes, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacsvcgroup is present with members, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacsvcgroup with members would not trigger changes, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup is present with members, lowercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | lower }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup is present with members, uppercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | upper }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure test hbacsvcgroup is absent
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
state: absent
- name: Ensure test hbacsvcgroup is present
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
- name: Check if hbacsvcgroup members would trigger changes, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
action: member
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacsvcgroup has members, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
action: member
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacsvcgroup members would not trigger changes, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
action: member
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup has members, lowercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | lower }}"
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup has members, uppercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | upper }}"
action: member
register: result
failed_when: result.changed or result.failed
- name: Check if hbacsvcgroup members absence would trigger changes, uppercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure hbacsvcgroup has members absent, uppercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | upper }}"
action: member
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Check if hbacsvcgroup members absence would not trigger changes, uppercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup has members absent, mixed case
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
- name: Ensure hbacsvcgroup has members absent, lowercase
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
hbacsvc: "{{ hbacsvc_list | lower }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
always:
- name: Ensure test hbac service group is absent
ipahbacsvcgroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
state: absent
- name: Ensure test hbac services are absent
ipahbacsvc:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: "{{ hbacsvc_list }}"
state: absent

View File

@@ -36,6 +36,7 @@
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: renamerole
rename: testrole
state: renamed
register: result
failed_when: not result.changed or result.failed
@@ -47,8 +48,9 @@
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: renamerole
rename: testrole
state: renamed
register: result
failed_when: result.changed
failed_when: result.changed or (not result.failed and "No role 'renamerole'" not in result.msg)
- name: Ensure role has member has privileges.
iparole:

View File

@@ -0,0 +1,450 @@
---
- name: Test role members
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: no
gather_facts: no
vars:
user_list:
- User1
- uSer2
- usEr3
group_list:
- Group1
- gRoup2
- grOup3
host_list:
- HoSt01
- hOsT02
hostgroup_list:
- TestHostGroup
service_list:
- MySVC/host01
tasks:
- include_tasks: ../env_freeipa_facts.yml
- block:
# setup
- name: Ensure test role is absent
iparole:
ipaadmin_password: SomeADMINpassword
name: testrule
state: absent
- name: Ensure test users are present
ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: "{{ item }}"
first: First
last: Last
with_items: "{{ user_list }}"
- name: Ensure test groups are present
ipagroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ group_list }}"
- name: Ensure test hosts are present
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
ip_address: 192.168.122.101
force: yes
with_items: "{{ host_list }}"
- name: Ensure test hostgroups are present
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
with_items: "{{ hostgroup_list }}"
- name: Ensure test services are present
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
with_items: "{{ service_list }}"
# Test with action: hbacrule
- name: Check role present with members would trigger a change, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure role is present with members, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
register: result
failed_when: not result.changed or result.failed
- name: Check role present with members would not trigger a change, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure role is present with members, lowercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | lower }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure role is present with members, upercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | upper }}"
register: result
failed_when: result.changed or result.failed
- name: Ensure test role is absent
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
# Test with action: members
- name: Ensure test role is present
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
- name: Check role members present would trigger a change, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
action: member
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure role is present with members, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
action: member
register: result
failed_when: not result.changed or result.failed
- name: Check role members present would not trigger a change, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
action: member
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure role is present with members, lowercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | lower }}"
action: member
register: result
failed_when: result.changed or result.failed
- name: Ensure role is present with members, upercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | upper }}"
action: member
register: result
failed_when: result.changed or result.failed
# # Test absent members
- name: Check role members absent would trigger a change, upercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: not result.changed or result.failed
- name: Ensure role members are absent, upercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | upper }}"
action: member
state: absent
register: result
failed_when: not result.changed or result.failed
- name: Check role members absent would not trigger a change, upercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | upper }}"
- "{{ user_list[2] | upper }}"
group:
- "{{ group_list[1] | upper }}"
- "{{ group_list[2] | upper }}"
host:
- "{{ host_list[0] | upper }}"
- "{{ host_list[1] | upper }}"
hostgroup:
- "{{ hostgroup_list[0] | upper }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | upper }}"
action: member
state: absent
check_mode: yes
register: result
failed_when: result.changed or result.failed
- name: Ensure role members are absent, mixed case
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] }}"
- "{{ user_list[2] }}"
group:
- "{{ group_list[1] }}"
- "{{ group_list[2] }}"
host:
- "{{ host_list[0] }}"
- "{{ host_list[1] }}"
hostgroup:
- "{{ hostgroup_list[0] }}"
service:
- "{{ service_list[0] }}.{{ ipaserver_domain }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
- name: Ensure role members are absent, lowercase
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- "{{ user_list[1] | lower }}"
- "{{ user_list[2] | lower }}"
group:
- "{{ group_list[1] | lower }}"
- "{{ group_list[2] | lower }}"
host:
- "{{ host_list[0] | lower }}"
- "{{ host_list[1] | lower }}"
hostgroup:
- "{{ hostgroup_list[0] | lower }}"
service:
- "{{ (service_list[0] + '.' + ipaserver_domain) | lower }}"
action: member
state: absent
register: result
failed_when: result.changed or result.failed
always:
- name: Ensure test role is absent
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
- name: Ensure test users are absent
ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: "{{ item }}"
state: absent
with_items: "{{ user_list }}"
- name: Ensure test groups are absent
ipagroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ group_list }}"
- name: Ensure test hosts are absent
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
state: absent
with_items: "{{ host_list }}"
- name: Ensure test hostgroups are absent
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}"
state: absent
with_items: "{{ hostgroup_list }}"
- name: Ensure test services are absent
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "{{ item }}.{{ ipaserver_domain }}"
continue: yes
state: absent
with_items: "{{ service_list }}"

View File

@@ -0,0 +1,5 @@
[galaxy-importer]
RUN_ANSIBLE_TEST = True
RUN_ANSIBLE_LINT = True
ANSIBLE_TEST_LOCAL_IMAGE = True
LOCAL_IMAGE_DOCKER = True

View File

@@ -18,11 +18,15 @@ plugins/modules/ipadnsrecord.py pylint:use-maxsplit-arg
plugins/modules/ipareplica_enable_ipa.py pylint:ansible-format-automatic-specification
plugins/modules/ipareplica_prepare.py pylint:ansible-format-automatic-specification
plugins/modules/ipareplica_test.py pylint:ansible-format-automatic-specification
plugins/modules/iparole.py compile-2.6!skip
plugins/modules/iparole.py import-2.6!skip
plugins/modules/ipaserver_setup_ca.py compile-2.6!skip
plugins/modules/ipaserver_setup_ca.py import-2.6!skip
plugins/modules/ipaserver_test.py pylint:ansible-format-automatic-specification
plugins/modules/ipaservice.py compile-2.6!skip
plugins/modules/ipaservice.py import-2.6!skip
plugins/modules/ipasudorule.py compile-2.6!skip
plugins/modules/ipasudorule.py import-2.6!skip
plugins/modules/ipavault.py compile-2.6!skip
plugins/modules/ipavault.py import-2.6!skip
roles/ipaclient/library/ipaclient_api.py pylint:ansible-format-automatic-specification
@@ -35,6 +39,7 @@ roles/ipaserver/library/ipaserver_test.py pylint:ansible-format-automatic-specif
roles/ipareplica/module_utils/ansible_ipa_replica.py pylint:ansible-format-automatic-specification
tests/external-signed-ca-with-automatic-copy/external-ca.sh shebang!skip
tests/pytests/conftest.py pylint:ansible-format-automatic-specification
tests/sanity/sanity.sh shebang!skip
tests/user/users.sh shebang!skip
tests/user/users_absent.sh shebang!skip
tests/utils.py pylint:ansible-format-automatic-specification

36
tests/sanity/sanity.sh Normal file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
VENV=/tmp/ansible-test-venv
ANSIBLE_COLLECTION=freeipa-ansible_freeipa
virtualenv "$VENV"
# shellcheck disable=SC1091
source "$VENV"/bin/activate
python -m pip install --upgrade pip
pip install galaxy_importer
rm -f "$ANSIBLE_COLLECTION"-*.tar.gz
rm -f importer_result.json
utils/build-galaxy-release.sh
export GALAXY_IMPORTER_CONFIG=tests/sanity/galaxy-importer.cfg
collection=$(ls -1 "$ANSIBLE_COLLECTION"-*.tar.gz)
echo "Running: python -m galaxy_importer.main $collection"
error=0
while read -r line;
do
if [[ $line == ERROR* ]]; then
((error++))
echo -e "\033[31;1m${line}\033[0m"
else
echo "$line"
fi
done < <(python -m galaxy_importer.main "$collection")
rm -rf "$VENV"
exit "$error"

View File

@@ -0,0 +1,151 @@
---
- name: Test sudorule with single hostnames.
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: no
gather_facts: no
tasks:
- block:
# setup test environment
- name: Get Domain from the server name
set_fact:
ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] | join ('.') }}"
when: ipaserver_domain is not defined
- name: Ensure test sudo rule is absent
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
state: absent
- name: Ensure test host exist
ipahost:
ipaadmin_password: SomeADMINpassword
hosts:
- name: "host01.{{ ipaserver_domain }}"
force: yes
- name: "host02.{{ ipaserver_domain }}"
force: yes
# start tests
- name: Ensure sudorule exist with host member using FQDN.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: "host01.{{ ipaserver_domain }}"
register: result
failed_when: result.failed or not result.changed
- name: Ensure sudorule host member using short hostname.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: host01
register: result
failed_when: result.failed or result.changed
- name: Ensure sudorule exist with another host using short name.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: host02
register: result
failed_when: result.failed or not result.changed
- name: Ensure sudorule exist with another host member using FQDN.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: "host02.{{ ipaserver_domain }}"
register: result
failed_when: result.failed or result.changed
- name: Ensure sudorule exist with another host member using FQDN.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: "host02.{{ ipaserver_domain }}"
register: result
failed_when: result.failed or result.changed
# cleanup for member tests.
- name: Ensure test sudo rule is absent
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
state: absent
- name: Ensure test sudo rule is absent
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
state: absent
# member tests
- name: Ensure test sudo rule is present
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
- name: Ensure sudorule host member using FQDN.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: "host01.{{ ipaserver_domain }}"
action: member
register: result
failed_when: result.failed or not result.changed
- name: Ensure sudorule host member using short hostname.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: host01
action: member
register: result
failed_when: result.failed or result.changed
- name: Ensure test sudo rule is absent
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
state: absent
- name: Ensure test sudo rule is present
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
- name: Ensure sudorule host member using FQDN.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: "host01.{{ ipaserver_domain }}"
action: member
register: result
failed_when: result.failed or not result.changed
- name: Ensure sudorule host member using short hostname.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
host: host01
action: member
register: result
failed_when: result.failed or result.changed
always:
# cleanup
- name: Ensure test sudo rule is absent
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: sudorule_for_hosts
state: absent
- name: Ensure test host is absent
ipahost:
ipaadmin_password: SomeADMINpassword
name:
- "host01.{{ ipaserver_domain }}"
- "host02.{{ ipaserver_domain }}"
state: absent

View File

@@ -1,10 +1,101 @@
#!/bin/bash
#
# Build Ansible Collection from ansible-freeipa repo
#
prog=$(basename "$0")
pwd=$(pwd)
usage() {
cat <<EOF
Usage: $prog [options] [namespace] [collection]
Build Anible Collection for ansible-freeipa.
The namespace defaults to freeipa an collection defaults to ansible_freeipa
if namespace and collection are not given. Namespace and collection can not
be givedn without the other one.
Options:
-a Add all files, no only files known to git repo
-k Keep build directory
-h Print this help
EOF
}
all=0
keep=0
while getopts "ahk" arg; do
case $arg in
a)
all=1
;;
h)
usage
exit 0
;;
k)
keep=1
;;
\?)
echo
usage
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ $# != 0 ] && [ $# != 2 ]; then
usage
exit 1
fi
namespace="${1-freeipa}"
collection="${2-ansible_freeipa}"
if [ -z "$namespace" ]; then
echo "Namespace might not be empty"
exit 1
fi
if [ -z "$collection" ]; then
echo "Collection might not be empty"
exit 1
fi
collection_prefix="${namespace}.${collection}"
galaxy_version=$(git describe --tags | sed -e "s/^v//")
galaxy_version=$(git describe --tags 2>/dev/null | sed -e "s/^v//")
if [ -z "$galaxy_version" ]; then
echo "Version could not be detected"
exit 1
fi
echo "Builing galaxy release: ${namespace}-${collection}-${galaxy_version}"
GALAXY_BUILD=".galaxy-build"
if [ -e "$GALAXY_BUILD" ]; then
echo "Removing existing $GALAXY_BUILD ..."
rm -rf "$GALAXY_BUILD"
echo -e "\033[ARemoving existing $GALAXY_BUILD ... \033[32;1mDONE\033[0m"
fi
mkdir "$GALAXY_BUILD"
echo "Copying files to build dir $GALAXY_BUILD ..."
if [ $all == 1 ]; then
# Copy all files except galaxy build dir
for file in .[A-z]* [A-z]*; do
[[ "$file" == "${GALAXY_BUILD}" ]] && continue
cp -a "$file" "${GALAXY_BUILD}/"
done
else
# git ls-tree is quoting, therefore ignore SC2046: Quote this to prevent
# word splitting
# shellcheck disable=SC2046
tar -cf - $(git ls-tree HEAD --name-only -r) | (cd "$GALAXY_BUILD/" && tar -xf -)
fi
echo -e "\033[ACopying files to build dir $GALAXY_BUILD ... \033[32;1mDONE\033[0m"
cd "$GALAXY_BUILD" || exit 1
sed -i -e "s/version: .*/version: \"$galaxy_version\"/" galaxy.yml
sed -i -e "s/namespace: .*/namespace: \"$namespace\"/" galaxy.yml
sed -i -e "s/name: .*/name: \"$collection\"/" galaxy.yml
@@ -69,22 +160,21 @@ find . -name "README*.md" -print0 |
done
echo -e "\033[AFixing examples in plugins/modules... \033[32;1mDONE\033[0m"
echo "Fixing playbbooks in tests..."
echo "Fixing playbooks in tests..."
find tests -name "*.yml" -print0 |
while IFS= read -d '' -r line; do
python utils/galaxyfy-playbook.py "$line" "ipa" "$collection_prefix"
done
echo -e "\033[AFixing playbooks in tests... \033[32;1mDONE\033[0m"
#git diff
ansible-galaxy collection build --force --output-path="$pwd"
ansible-galaxy collection build
cd "$pwd" || exit 1
rm plugins/module_utils/ansible_ipa_*
rm plugins/modules/ipaserver_*
rm plugins/modules/ipareplica_*
rm plugins/modules/ipaclient_*
rm plugins/modules/ipabackup_*
rm plugins/action/ipaclient_*
rmdir plugins/action
git reset --hard
if [ $keep == 0 ]; then
echo "Removing build dir $GALAXY_BUILD ..."
rm -rf "$GALAXY_BUILD"
echo -e "\033[ARemoving build dir $GALAXY_BUILD ... \033[32;1mDONE\033[0m"
else
echo "Keeping build dir $GALAXY_BUILD"
fi

View File

@@ -13,7 +13,7 @@ flake8 plugins utils roles setup.py
echo -e "${INFO}Running 'pydocstyle'...${RST}"
pydocstyle plugins utils roles setup.py
echo -e "${INFO}Running 'pylint'...${RST}"
pylint plugins setup.py
pylint plugins roles setup.py
ANSIBLE_LIBRARY="${ANSIBLE_LIBRARY:-plugins/modules}"
ANSIBLE_MODULE_UTILS="${ANSIBLE_MODULE_UTILS:-plugins/module_utils}"

View File

@@ -112,9 +112,6 @@ MORE EXAMPLE PLAYBOOKS HERE
Variables
---------
ipa$name
-------
Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no

View File

@@ -77,9 +77,6 @@ MORE EXAMPLE PLAYBOOKS HERE
Variables
---------
ipa$name
-------
Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no