Compare commits

..

63 Commits

Author SHA1 Message Date
Thomas Woerner
a1f882ac52 Merge pull request #694 from rjeffman/ipaservice_code_optimization
ipaservice: code refactor
2021-12-23 14:10:18 +01:00
Rafael Guterres Jeffman
8d99ce4207 Merge pull request #711 from t-woerner/automember_fixes
Automember fixes
2021-12-23 10:03:18 -03:00
Rafael Guterres Jeffman
e3e7d71474 Fix automember test
Automember client test was failing as it was using 'name' along with 'state: rebuilt'.
2021-12-22 14:09:28 -03:00
Thomas Woerner
285fb6b8e0 automember: Add support for action: orphans_removed
The removal of group or hostgroup orphans has been added to the automember
module.

It can be ensured that orphans have been removed using action: orphans_removed
The automember_type needs to be set for this.

New examples have been added to README-automember.md

New playbooks:
- playbooks/automember/automember-group-orphans-removed.yml
- playbooks/automember/automember-hostgroup-orphans-removed.yml

New tests:
- tests/automember/test_automember_orphans_removed.yml
2021-12-22 15:27:13 +01:00
Thomas Woerner
d2648b142a automember: Add automember default group handling
The fallback group and hostgroup for unmached entries can be set and
unset using default_group. If default_group is empty, then the default
group will be unset.

DN and ipa_get_based provided by ansible_freeipa_module are used in the
code.

New parameters:
- default_group: Default (fallback) group for all unmatched entries.

New parameters and examples have been added to README-automember.md

New playbooks:
- playbooks/automember/automember-default-group-not-set.yml
- playbooks/automember/automember-default-group-set.yml
- playbooks/automember/automember-default-hostgroup-not-set.yml
- playbooks/automember/automember-default-hostgroup-set.yml

New tests:
- tests/automember/test_automember_default_group.yml
2021-12-22 15:26:57 +01:00
Thomas Woerner
67e192242c automember: Add automember state: rebuilt
There was state: rebuild before, but the code was incomplete and was not
able to run properly.

New parameters:
- users: Limit the rebuild to the given users only
- hosts: Limit the rebuild to the given hosts only
- no_wait: Don't wait for rebuilding membership

New parameters and examples have been added to README-automember.md

tests/automember/test_automember_client_context.yml has been using
state: rebuild and lacked the automember_type parameter.

grouping was used in functions and has been replaced by automember_type.

Some typos in examples have been fixed also.

New playbooks:
- playbooks/automember/automember-group-membership-all-users-rebuilt.yml
- playbooks/automember/automember-group-membership-users-rebuilt.yml
- playbooks/automember/automember-hostgroup-membership-all-hosts-rebuilt.yml
- playbooks/automember/automember-hostgroup-membership-hosts-rebuilt.yml

New tests:
- tests/automember/test_automember_rebuilt.yml
2021-12-22 12:50:08 +01:00
Thomas Woerner
9eefc1ae7c ansible_freeipa_module: New api_get_basedn, IPAAnsibleModule.ipa_get_basedn
These functions have been added to get the basedb from api.env for use
with DN for example.

api_get_basedn is returning api.env.basedn
IPAAnsibleModule.ipa_get_basedn is a wrapper for api_get_basedn
2021-12-22 12:17:10 +01:00
Rafael Guterres Jeffman
b9d0b35e83 Merge pull request #709 from t-woerner/automationhub_fixes2
More Automation Hub fixes
2021-12-14 17:59:46 -03:00
Thomas Woerner
85006d611f Fix ansible-test reported pep8 errors
These are indent issues, one item per line for argument_specs items
containing options dicts and missing or overflow spaces for comments
and dict delimiters.
2021-12-14 18:37:37 +01:00
Thomas Woerner
82412ef761 ipabackup_get_backup_dir.py: Add missing ":" in example 2021-12-14 18:24:37 +01:00
Thomas Woerner
2e178e5a38 Ignore file for ansible-test sanity 2.12
These are skips for python 2.6 with import-2.6!skip and
compile-2.6!skip, ignores for the exit_raw_json function in
ansible_freeipa_module.py for use with ipavault, the ignore of using
automatic field numbering for the string format function, the use of
string split without maxsplits (not working in Python2), skips of the
shebang tests for scripts in tests and utils and the ignore of missing
collection_name for deprecate function calls in
ansible_freeipa_module.py.
2021-12-14 18:23:55 +01:00
Thomas Woerner
f47d134335 utils/gen_module_docs.py: Drop duplicate setup_adtrust key 2021-12-14 17:53:25 +01:00
Thomas Woerner
541c514aa9 Add version for ansible deprecated calls 2021-12-14 17:52:58 +01:00
Thomas Woerner
85257b9d03 build-galaxy-release: Real cleanup of ipabackup_get_backup_dir.py link
plugins/modules/ipabackup_* needs to be cleaned up not
plugins/action/ipabackup_*
2021-12-14 17:26:19 +01:00
Thomas Woerner
e7f902ca48 Merge pull request #671 from rjeffman/baseclass_playground
Deprecate FreeIPABaseModule in favor of IPAAnsibleModule.
2021-12-13 14:03:14 +01:00
Thomas Woerner
a1bfa608e2 Merge pull request #705 from rjeffman/fix_yamllint_issues
yamllint: Fix missing document start.
2021-12-10 16:30:48 +01:00
Rafael Guterres Jeffman
eefd94d13f yamllint: Fix missing document start.
Recently added Ubuntu configuration filesi for roles miss document
start marker.
2021-12-09 22:16:26 -03:00
Thomas Woerner
667177db07 Merge pull request #699 from rjeffman/ubuntu_fix_roles
Fix role issues in Debian based distros.
2021-12-09 15:55:40 +01:00
Thomas Woerner
f301ad55aa Merge pull request #703 from jh23453/patch-2
correct comment in example playbook
2021-12-09 15:48:18 +01:00
Thomas Woerner
fd9ecc246d Merge pull request #702 from jh23453/patch-1
Login shell is called defaultshell and not defaultlogin
2021-12-09 14:24:27 +01:00
Thomas Woerner
b15552540c Merge pull request #698 from rjeffman/ci_centos9_stream_images
upstream ci: Build images for CentOS 9 Stream.
2021-12-09 12:58:17 +01:00
jh23453
25b41b7eca correct comment in example playbook
Insert "ensure maxlife is set to 49 in global policy" instead of the wrong comment from cut&paste.
2021-12-08 17:27:08 +01:00
jh23453
b53f2a08d6 Login shell is called defaultshell and not defaultlogin
The example didn't work for me with the following error (on freeipa 4.9.8):

TASK [display default login shell] **************************************************************************************************
fatal: [freeipa1.example.org]: FAILED! => {"msg": "
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'defaultlogin'

Using the correct variable defaultshell works for me.
2021-12-08 17:20:10 +01:00
Rafael Guterres Jeffman
f2b3e88d5a ipaservice: code refactor.
This PR refactors ipaservice to reduce the number of variables (in
favor of a 'struct') and to group member management code so that it
can be leveraged, and not partially duplicated, between the states
and actions.

Altough this code is less direct that the previous one, it will reduce
the number fo changes to be made if changes to member management is
required.
2021-11-30 14:35:16 -03:00
Rafael Guterres Jeffman
e001ecb1de upstream ci: Build images for CentOS 9 Stream.
This patch add support for building testing images for CentOS 9 Stream.
2021-11-30 13:43:49 -03:00
Rafael Guterres Jeffman
44b3c3003d Debian Buster: Fix "No module named 'ipapython'".
When using ipaclient role under Debian 10 (Buster), it is required
that the python interpreter is set to Python 2.7 as freeipa-client
package pulls in Python 2.7 dependencies, and does not work with
Python 3.

This patch adds configuration to properly set python interpreter.

Based on the work by Marc Richter (@The-Judge on Github).

Fix issue #607.
2021-11-29 16:27:32 -03:00
Rafael Guterres Jeffman
c7a3b26ec4 Ubuntu 18.04: Fix role instalation for Ubuntu Bionic Beaver.
Ubuntu Bionic Beaver must use python2 as Python interpreter due to
the way python-ipalib package is defined.

Before using the role for installation, one  must install package
python2.7 before executing this role.

Based on the work by Manuel Laurent (@mlaurent205 on Github).
2021-11-29 16:24:20 -03:00
Thomas Woerner
9252284504 Merge pull request #668 from rjeffman/ipaservice_use_default_error_handler
ipaservice: Use IPAAnsibleModule member result handler.
2021-11-29 15:38:08 +01:00
Thomas Woerner
9665769cfc Merge pull request #683 from rjeffman/module_utils_lowercase_get
ansible_module_utils: Add method to get parameters as lowercase.
2021-11-29 13:39:37 +01:00
Rafael Guterres Jeffman
7d02d4d409 ipaservice: Remove custom error handler.
Use IPAAnsibleModule default error handler for member arguments instead
of a custom one.
2021-11-26 10:55:57 -03:00
Rafael Guterres Jeffman
fb5f59db23 ipaservice: Use gen_*_lists to avoid unneded API calls.
When managing ipaservice members, gen_add_del_lists, gen_add_list and
get_intersection_list should be used and the result tested for empty
sets so already existing or missing members are not added or removed
again.

This changes fixes this behavior, by applying these functions to all
ipaservice members.
2021-11-25 15:16:44 -03:00
Thomas Woerner
90e8098bf4 Merge pull request #690 from rjeffman/shellcheck
CI: Add supoprt for Shellcheck
2021-11-25 13:53:26 +01:00
Thomas Woerner
d615d3d4ed Merge pull request #675 from rjeffman/automember_idempotence_issue
automember: Fix behavior of unused parameters.
2021-11-25 13:50:04 +01:00
Rafael Guterres Jeffman
8ba665213f linters: Fix shellcheck warnings in 'utils' scripts. 2021-11-24 21:01:29 -03:00
Rafael Guterres Jeffman
036891d09a shellcheck: Run shellcheck as a Github action.
Add shellcheck as an action to be executed on every PR.
2021-11-24 20:20:19 -03:00
Rafael Guterres Jeffman
93a441494d pre-commit: Add shellcheck to pre-commit configuration.
`Shellcheck` is a linter tool for shell scripts that is also used in
Automation Hub.

This change adds a pre-commit hook to run shellcheck on shell scripts.
The hook uses a Docker image, which needs to be downloaded on the first
run. It works well  with `podman`.
2021-11-24 20:20:19 -03:00
Rafael Guterres Jeffman
77b72af1a2 Merge pull request #692 from t-woerner/add_backup_cleanup_to_build_galaxy_release
build-galaxy-release: Cleanup of ipabackup_get_backup_dir.py link
2021-11-24 20:20:13 -03:00
Thomas Woerner
4bfde9b61a Merge pull request #612 from rjeffman/ci_use_ansible_core
CI: Test modules against Ansible core 2.11 and latest Ansible
2021-11-24 14:14:49 +01:00
Thomas Woerner
f113e7071e build-galaxy-release: Cleanup of ipabackup_get_backup_dir.py link
The link for plugins/modules/ipabackup_get_backup_dir.py from
roles/ipabackup/library/ipabackup_get_backup_dir.py was left over
after the script finished.
2021-11-24 13:02:24 +01:00
Rafael Guterres Jeffman
ef9f7fcb84 Merge pull request #691 from t-woerner/master_automationhub_fixes
Changes needed to pass Automation Hub tests
2021-11-24 08:50:37 -03:00
Thomas Woerner
5c5b05a74f pylint upstream: Ignore __metaclass__
__metaclass__ is required for all plugins to be able to pass
Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
fa94b14e91 yamllint: Fix EXAMPLE issues
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
cd3646ad67 shellcheck: Double quote to prevent globbing and word splitting
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
2e8c2f881f Use ansible.module_utils.six instead of six
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
b88cab07ff Add missing whitespace around arithmetic operator
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
f406de14e8 ipaclient_get_facts: Fix closing bracket does not match indentation
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
a86970efe1 Remove "’" from yaml files
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
0af208d271 Replace asserts with raise AssertionError
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
10b16a3bbf Remove non-module shebang
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Thomas Woerner
5c871242a7 Add __future__ imports and __metaclass__ for automationhub
This patch is needed to pass Automation Hub tests.
2021-11-24 10:07:02 +01:00
Rafael Guterres Jeffman
2f9791f6c5 DNSZone: Use IPAAnsibleModule.
As FreeIPABaseModule is deprecated, change DNSZone to use
IPAAnsibleModule.
2021-11-16 15:51:55 -03:00
Rafael Guterres Jeffman
f41104520e automountlocation: Use IPAAnsibleModule.
As FreeIPABaseModule is deprecated, change AutomountLocation to use
IPAAnsibleModule.
2021-11-16 15:51:55 -03:00
Rafael Guterres Jeffman
d81994475e Deprecate FreeIPABaseModule in favor of IPAAnsibleModule.
This patch add several deprecate warnings to FreeIPABaseModule, and
creates adapters to ease conversion of client classes to
IPAAnsibleModule.

There is no 'ipa_commands' management in IPAAnsibleModule, as 'command's
is a list of tuples containing '(command, name, args)', and should be
managed by the module itself. Commands with no arguments should use an
empty dictionary as 'args'.

The 'ipa_run' method should be replaced by:

```
exit_args = {}
ipaapi_context = self.params_get("ipaapi_context")
with self.ipa_connect(context=ipaapi_context):
    self.check_ipa_params()
    self.define_ipa_commands()
    changed = self.execute_ipa_commands(
                self.ipa_commands,
                result_handler=my_custom_handler,
                exit_args=exit_args
            )
self.exit_json(changed=changed, **exit_args)
```

The 'process_command_result' method should be changed to a result
handler:

```
def my_result_handler(self, result, command, name, args, exit_args):
    """Process command result.""'
```

Use of 'ipa_params' should be replaced by IPAAnsibleModule.params_get.
If 'get_ipa_command_args' is used, then the mapping can be created with
class IPAParamMapping (formelly AnsibleFreeIPAParams), which also
enables the same property-like usage of 'ipa_params':

```
param_mapping = IPAParamMapping(module, mapping)
```

The goal is to have all ansible-freeipa modules using the same codebase,
reducing code duplication, and allowing better object composition, for
example, with the IPAParamMapping class.
2021-11-16 15:51:55 -03:00
Rafael Guterres Jeffman
612b60766e CI: Test modules against different Ansible versions.
Currently, upstream CI test documentation against different Ansible
versions, but playbook tests are only executed with Ansible 2.9 series.
This patch add support for running playbook tests against Ansible 2.9,
ansible-core 2.11, and against latest version of Ansible.

As running all the tests for every PR would take too long, the tests
for every PR use only Anisble 2.9, and are executed on Fedora-latest
and CentOS 7 and 8.

A new pipeline for nightly tests was added, which runs the tests in the
same distros, using Ansible 2.9, latest and Ansible-core 2.11.
2021-11-16 12:03:08 -03:00
Thomas Woerner
d6eaf91225 Merge pull request #670 from rjeffman/ipapermission_fixes
ipaprivilege: Fix permissions handling.
2021-11-12 13:19:41 +01:00
Thomas Woerner
cb95248ef5 Merge pull request #665 from rjeffman/fix_sudorule_idempotency
sudorule: Fix runas with external users and groups.
2021-11-12 13:09:58 +01:00
Rafael Guterres Jeffman
ca0aed54b0 ansible_module_utils: Add method to get parameters as lowercase.
Many module member attributes must be handled in a case insensitive
manner. To ease handling these cases, a function and a method to get
the module parameters converted to lowercase is provided.
2021-11-11 16:58:48 -03:00
Rafael Guterres Jeffman
17bba27abf automember: Fix behavior of unused parameters.
If a task with 'action: automember' tried to modify an automember rule
and did not provide either 'inclusive' or 'exclusive' parameters, the
regex for the missing arguments would be removed.

This patch fixes this behavior to only modify those parameters that
were set on the task, and leave the missing parameters in the state
they were before the task.

Tests have been updated to verify expected behavior.

Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1976922
2021-11-03 18:01:40 -03:00
Rafael Guterres Jeffman
22f31d02f2 sudorule: Fix runas with external users and groups.
When setting 'runasuser' or 'runasgroup' for a sudorule, either IPA or
external users and groups can be used, but only IPA users and groups
were being searched for when modifying the attributes, making this task
not idempotent if an external group or user was used..

This patch fixes this issue by comparing users and groups to the IPA
and external setting.

The IPA CLI commands are slightly confusing, as the sudorule-add and
sudorule-mod display separate options for internal and external users
and groups, but these options are deprecated and do not work anymore,
in favor of sudorule-add-runasuser and sudorule-add-runasgroup, which
don't diferentiate between internal and external users, from the CLI
user perspective.
2021-11-03 15:49:09 -03:00
Thomas Woerner
5e9a2e8c2e Merge pull request #667 from rjeffman/fix_service_principal_idempotent_issue
ipaservice: Fix idempotent behavior for principal aliases.
2021-11-03 15:52:07 +01:00
Rafael Guterres Jeffman
0757bfee0a ipaprivilege: Fix module execution in check_mode.
This patch removes the custom command result handler in favor of the
IPAAnsibleModule default member handler and fixes creation of add and
del lists of permissions, fixing the behavior of the moudule execution
when 'check_mode: yes'.
2021-10-26 11:37:07 -03:00
Rafael Guterres Jeffman
1eff04a09a ipaprivilege: fix creation of add/del lists for permissions.
A wrong parameter 'member_permission' was being used to obtain the
existing permissions, and was changed to 'memberof_permission'.
2021-10-26 09:43:13 -03:00
Rafael Guterres Jeffman
f43831407b ipaservice: Fix idempotent behavior for principal aliases.
When creating the lists to add/remove principal aliases, if the realm
was not specified, the alias would be used as it did not matched the
existing one, which has the realm part.

This patch fixes the add/del list creation by adding the current API
realm to each alias that does not have the realm part and then use
this modified list to be compared against the existing principal list.

This change also allows the use of the whole list in a single call to
the IPA API to add/remove the principals, instead of a call for every
one item in the list.
2021-10-25 16:15:16 -03:00
150 changed files with 2855 additions and 1008 deletions

View File

@@ -76,3 +76,11 @@ jobs:
run: |
pip install pylint==2.10.2
pylint plugins --disable=import-error
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@1.1.0

View File

@@ -38,3 +38,8 @@ repos:
entry: utils/ansible-doc-test
# args: ['-v', 'roles', 'plugins']
files: ^.*.py$
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.8.0
hooks:
- id: shellcheck
args: ["--severity=warning"] # Only show errors and warnings

View File

@@ -104,13 +104,160 @@ Example playbook to add an inclusive condition to an existing rule
ipaadmin_password: SomeADMINpassword
name: "My domain hosts"
description: "my automember condition"
automember_tye: hostgroup
automember_type: hostgroup
action: member
inclusive:
- key: fqdn
expression: ".*.mydomain.com"
```
Example playbook to ensure group membership for all users has been rebuilt
```yaml
- name: Playbook to ensure group membership for all users has been rebuilt
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: rebuilt
```
Example playbook to ensure group membership for given users has been rebuilt
```yaml
- name: Playbook to ensure group membership for given users has been rebuilt
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
users:
- user1
- user2
state: rebuilt
```
Example playbook to ensure hostgroup membership for all hosts has been rebuilt
```yaml
- name: Playbook to ensure hostgroup membership for all hosts has been rebuilt
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: rebuilt
```
Example playbook to ensure hostgroup membership for given hosts has been rebuilt
```yaml
- name: Playbook to ensure hostgroup membership for given hosts has been rebuilt
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
hosts:
- host1.mydomain.com
- host2.mydomain.com
state: rebuilt
```
Example playbook to ensure default group fallback_group for all unmatched group entries is set
```yaml
- name: Playbook to ensure default group fallback_group for all unmatched group entries is set
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
default_group: fallback_group
```
Example playbook to ensure default group for all unmatched group entries is not set
```yaml
- name: Playbook to ensure default group for all unmatched group entries is not set
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
default_group: ""
automember_type: group
state: absent
```
Example playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries
```yaml
- name: Playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: fallback_hostgroup
```
Example playbook to ensure default hostgroup for all unmatched group entries is not set
```yaml
- name: Playbook to ensure default hostgroup for all unmatched group entries is not set
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: ""
state: absent
```
Example playbook to ensure all orphan automember group rules are removed:
```yaml
- name: Playbook to ensure all orphan automember group rules are removed
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: orphans_removed
```
Example playbook to ensure all orphan automember hostgroup rules are removed:
```yaml
- name: Playbook to ensure all orphan automember hostgroup rules are removed
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: orphans_removed
```
Variables
---------
@@ -129,11 +276,16 @@ Variable | Description | Required
`automember_type` | Grouping to which the rule applies. It can be one of `group`, `hostgroup`. | yes
`inclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': inclusive_regex}` | no
`exclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': exclusive_regex}` | no
`users` | Users to rebuild membership for. | no
`hosts` | Hosts to rebuild membership for. | no
`no_wait` | Don't wait for rebuilding membership. | no
`default_group` | Default (fallback) group for all unmatched entries. Use the empty string "" for ensuring the default group is not set. | no
`action` | Work on automember or member level. It can be one of `member` or `automember` and defaults to `automember`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, 'rebuilt'. 'orphans_removed' default: `present`. | no
Authors
=======
Mark Hahl
Thomas Woerner

View File

@@ -56,12 +56,12 @@ Example playbook to read config options:
register: result
- name: display default login shell
debug:
msg: '{{ result.config.defaultlogin }}'
msg: '{{ result.config.defaultshell }}'
- name: ensure defaultloginshell and maxusernamelength are set as required
ipaconfig:
ipaadmin_password: password
defaultlogin: /bin/bash
defaultshell: /bin/bash
maxusername: 64
```

View File

@@ -81,7 +81,7 @@ Example playbook to ensure maxlife is set to 49 in global policy:
become: true
tasks:
# Ensure absence of pwpolicies for group ops
# Ensure maxlife is set to 49 in global policy
- ipapwpolicy:
ipaadmin_password: SomeADMINpassword
maxlife: 49

View File

@@ -0,0 +1,30 @@
FROM quay.io/centos/centos:stream9
ENV container=docker
RUN rm -fv /var/cache/dnf/metadata_lock.pid; \
dnf makecache; \
dnf --assumeyes install \
/usr/bin/python3 \
/usr/bin/python3-config \
/usr/bin/dnf-3 \
sudo \
bash \
systemd \
procps-ng \
iproute && \
dnf clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*; \
rm -rf /var/cache/dnf/;
STOPSIGNAL RTMIN+3
VOLUME ["/sys/fs/cgroup"]
CMD ["/usr/sbin/init"]

View File

@@ -0,0 +1,18 @@
---
driver:
name: docker
platforms:
- name: centos-9-build
image: "quay.io/centos/centos:stream9"
dockerfile: Dockerfile
hostname: ipaserver.test.local
dns_servers:
- 8.8.8.8
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
command: /usr/sbin/init
privileged: true
provisioner:
name: ansible
playbooks:
prepare: ../resources/playbooks/prepare-build.yml

View File

@@ -1,7 +1,7 @@
---
# IPA depends on IPv6 and without it dirsrv service won't start.
- name: Ensure IPv6 is ENABLED
sysctl:
ansible.posix.sysctl:
name: "{{ item.name }}"
value: "{{ item.value }}"
sysctl_set: yes
@@ -19,14 +19,14 @@
# This is needed in some IPA versions in order to get KRA enabled.
# See https://pagure.io/freeipa/issue/7906 for more information.
- name: stat protected_regular
stat:
ansible.builtin.stat:
path: /proc/sys/fs/protected_regular
register: result
- name: Ensure fs.protected_regular is disabled
sysctl:
ansible.posix.sysctl:
name: fs.protected_regular
value: '0'
value: 0
sysctl_set: yes
state: present
reload: yes

View File

@@ -11,7 +11,7 @@
#
# To avoid this problem we create the directories before starting IPA.
- name: Ensure lock dirs for DS exists
file:
ansible.builtin.file:
state: directory
owner: dirsrv
group: dirsrv
@@ -22,6 +22,6 @@
- /var/lock/dirsrv/slapd-TEST-LOCAL/
- name: Ensure IPA server is up an running
service:
ansible.builtin.service:
name: ipa
state: started

View File

@@ -0,0 +1,10 @@
---
- name: Automember default group not set
hosts: ipaserver
become: true
tasks:
- name: Ensure automember default group is not set
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
default_group: ""

View File

@@ -0,0 +1,10 @@
---
- name: Automember default group set
hosts: ipaserver
become: true
tasks:
- name: Ensure automember default group is set
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
default_group: fallback_group

View File

@@ -0,0 +1,10 @@
---
- name: Automember default hostgroup not set
hosts: ipaserver
become: true
tasks:
- name: Ensure automember default hostgroup is not set
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: ""

View File

@@ -0,0 +1,10 @@
---
- name: Automember default hostgroup set
hosts: ipaserver
become: true
tasks:
- name: Ensure automember default hostgroup is set
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: fallback_hostgroup

View File

@@ -0,0 +1,10 @@
---
- name: Automember group membership for all users rebuilt example
hosts: ipaserver
become: true
tasks:
- name: Ensure group automember rule admins is present
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: rebuilt

View File

@@ -0,0 +1,12 @@
---
- name: Automember group membership for given users rebuilt example
hosts: ipaserver
become: true
tasks:
- name: Ensure group membership for given users has been rebuilt
ipaautomember:
ipaadmin_password: SomeADMINpassword
users:
- user1
- user2
state: rebuilt

View File

@@ -0,0 +1,10 @@
---
- name: Automember orphan group rules are removed example
hosts: ipaserver
become: true
tasks:
- name: Ensure orphan group rules are removed
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: orphans_removed

View File

@@ -0,0 +1,10 @@
---
- name: Automember hostgroup membership for all hosts rebuilt example
hosts: ipaserver
become: true
tasks:
- name: Ensure hostgroup membership for all hosts has been rebuilt
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: rebuilt

View File

@@ -0,0 +1,12 @@
---
- name: Automember hostgroup membership for given hosts rebuilt example
hosts: ipaserver
become: true
tasks:
- name: Ensure hostgroup membership for given hosts has been rebuilt
ipaautomember:
ipaadmin_password: SomeADMINpassword
hosts:
- host1.mydomain.com
- host2.mydomain.com
state: rebuilt

View File

@@ -0,0 +1,10 @@
---
- name: Automember orphan hostgroup rules are removed example
hosts: ipaserver
become: true
tasks:
- name: Ensure orphan hostgroup rules are removed
ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: orphans_removed

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
class ModuleDocFragment(object): # pylint: disable=R0205,R0903
DOCUMENTATION = r"""

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
__all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
"DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT",
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
@@ -100,7 +103,7 @@ else:
import socket
import base64
import six
from ansible.module_utils import six
try:
from collections.abc import Mapping # noqa
@@ -397,6 +400,14 @@ else:
def module_params_get(module, name):
return _afm_convert(module.params.get(name))
def module_params_get_lowercase(module, name):
value = _afm_convert(module.params.get(name))
if isinstance(value, list):
value = [v.lower() for v in value]
if isinstance(value, (str, unicode)):
value = value.lower()
return value
def api_get_domain():
return api.env.domain
@@ -408,6 +419,9 @@ else:
def api_get_realm():
return api.env.realm
def api_get_basedn():
return api.env.basedn
def gen_add_del_lists(user_list, res_list):
"""
Generate the lists for the addition and removal of members.
@@ -555,10 +569,76 @@ else:
print(jsonify(kwargs))
sys.exit(0)
class AnsibleFreeIPAParams(Mapping):
def __init__(self, ansible_module):
class IPAParamMapping(Mapping):
"""
Provides IPA API mapping to playbook parameters or computed values.
It can be used to define a mapping of playbook parameters
or methods that provide computed values to IPA API arguments.
Playbook parameters can be retrieved as properties,
and the set of IPA arguments for a command can be
retrived with ``get_ipa_command_args()``. The keys for
``param_mapping`` are also the keys of the argument set.
The values of ``param_mapping`` can be either:
* a str representing a key of ``AnsibleModule.params``.
* a callable.
In case of an ``AnsibleModule.param`` the value of the playbook
param will be used for that argument. If it is a ``callable``,
the value returned by the execution of it will be used.
Example:
-------
def check_params(ipa_params):
# Module parameters can be accessed as properties.
if len(ipa_params.name) == 0:
ipa_params.ansible_module.fail_json(msg="No given name.")
def define_ipa_commands(self):
# Create the argument dict from the defined mapping.
args = self.get_ipa_command_args()
_commands = [("obj-name", "some_ipa_command", args)]
return _commands
def a_method_for_a_computed_param():
return "Some computed value"
def main():
ansible_module = SomeIPAModule(argument_spec=dict(
name=dict(type="list", aliases=["cn"], required=True),
state=dict(type="str", default="present",
choices=["present", "absent"]),
module_param=(type="str", required=False),
)
)
# Define the playbook to IPA API mapping
ipa_param_mapping = {
"arg_to_be_passed_to_ipa_command": "module_param",
"another_arg": a_method_for_a_computed_param,
}
ipa_params = IPAParamMapping(
ansible_module,
param_mapping=ipa_param_mapping
)
check_params(ipa_params)
comands = define_ipa_commands(ipa_params)
ansible_module.execute_ipa_commands(commands)
"""
def __init__(self, ansible_module, param_mapping=None):
self.mapping = ansible_module.params
self.ansible_module = ansible_module
self.param_mapping = param_mapping or {}
def __getitem__(self, key):
param = self.mapping[key]
@@ -579,6 +659,36 @@ else:
def __getattr__(self, name):
return self.get(name)
def get_ipa_command_args(self, **kwargs):
"""Return a dict to be passed to an IPA command."""
args = {}
for ipa_param_name, param_name in self.param_mapping.items():
# Check if param_name is actually a param
if param_name in self.ansible_module.params:
value = self.ansible_module.params_get(param_name)
if isinstance(value, bool):
value = "TRUE" if value else "FALSE"
# Since param wasn't a param check if it's a method name
elif callable(param_name):
value = param_name(**kwargs)
# We don't have a way to guess the value so fail.
else:
self.ansible_module.fail_json(
msg=(
"Couldn't get a value for '%s'. Option '%s' is "
"not a module argument neither a defined method."
)
% (ipa_param_name, param_name)
)
if value is not None:
args[ipa_param_name] = value
return args
class IPAAnsibleModule(AnsibleModule):
"""
IPA Ansible Module.
@@ -699,6 +809,18 @@ else:
"""
return module_params_get(self, name)
def params_get_lowercase(self, name):
"""
Retrieve value set for module parameter as lowercase, if not None.
Parameters
----------
name: string
The name of the parameter to retrieve.
"""
return module_params_get_lowercase(self, name)
def params_fail_used_invalid(self, invalid_params, state, action=None):
"""
Fail module execution if one of the invalid parameters is not None.
@@ -763,6 +885,11 @@ else:
"""Retrieve IPA API realm."""
return api_get_realm()
@staticmethod
def ipa_get_basedn():
"""Retrieve IPA API basedn."""
return api_get_basedn()
@staticmethod
def ipa_command_exists(command):
"""
@@ -1011,6 +1138,11 @@ else:
# pylint: disable=super-with-arguments
super(FreeIPABaseModule, self).__init__(*args, **kwargs)
self.deprecate(
msg="FreeIPABaseModule is deprecated. Use IPAAnsibleModule.",
version="1.5.0"
)
# Status of an execution. Will be changed to True
# if something is actually peformed.
self.changed = False
@@ -1026,11 +1158,6 @@ else:
# Module exit arguments.
self.exit_args = {}
# Wrapper around the AnsibleModule.params.
# Return the actual params but performing transformations
# when needed.
self.ipa_params = AnsibleFreeIPAParams(self)
def get_ipa_command_args(self, **kwargs):
"""
Return a dict to be passed to an IPA command.
@@ -1051,97 +1178,77 @@ else:
server).
"""
args = {}
for ipa_param_name, param_name in self.ipa_param_mapping.items():
# Check if param_name is actually a param
if param_name in self.ipa_params:
value = self.ipa_params.get(param_name)
if isinstance(value, bool):
value = "TRUE" if value else "FALSE"
# Since param wasn't a param check if it's a method name
elif hasattr(self, param_name):
method = getattr(self, param_name)
if callable(method):
value = method(**kwargs)
# We don't have a way to guess the value so fail.
else:
self.fail_json(
msg=(
"Couldn't get a value for '%s'. Option '%s' is "
"not a module argument neither a defined method."
)
% (ipa_param_name, param_name)
)
if value is not None:
args[ipa_param_name] = value
return args
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
"Use 'AnsibleFreeIPAParams.get_ipa_command_args()', "
"Instantiate it using the class 'ipa_params_mapping'."
),
version="1.5.0"
)
mapping = IPAParamMapping(self, self.ipa_param_mapping)
return mapping.get_ipa_command_args(**kwargs)
def check_ipa_params(self):
"""Validate ipa_params before command is called."""
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
),
version="1.5.0"
)
pass # pylint: disable=unnecessary-pass
def define_ipa_commands(self):
"""Define commands that will be run in IPA server."""
raise NotImplementedError
def get_command_errors(self, command, result):
"""Look for erros into command results."""
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
for item in result.get("failed", tuple()):
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if (
"already a member" in failure
or "not a member" in failure
):
continue
errors.append(
"%s: %s %s: %s"
% (command, member_type, member, failure)
)
if len(errors) > 0:
self.fail_json(", ".join("errors")) # pylint: disable=E1121
def add_ipa_command(self, command, name=None, args=None):
"""Add a command to the list of commands to be executed."""
self.ipa_commands.append((name, command, args or {}))
def _run_ipa_commands(self):
"""Execute commands in self.ipa_commands."""
if self.check_mode:
self.changed = len(self.ipa_commands) > 0
return
self.changed = self.execute_ipa_commands(
self.ipa_commands,
result_handler=self.process_results.__func__,
exit_args=self.exit_args
)
result = None
def process_results(
self, result, command, name, args, exit_args
): # pylint: disable=unused-argument
"""
Process an API command result.
for name, command, args in self.ipa_commands:
try:
result = self.ipa_command(command, name, args)
except Exception as excpt:
self.fail_json(msg="%s: %s: %s" % (command, name,
str(excpt)))
else:
self.process_command_result(name, command, args, result)
self.get_command_errors(command, result)
This method must be overriden in subclasses if 'exit_args'
is to be modified.
"""
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
),
version="1.5.0"
)
self.process_command_result(name, command, args, result)
def process_command_result(self, _name, _command, _args, result):
"""
Process an API command result.
This method can be overriden in subclasses, and
change self.exit_values
to return data in the result for the controller.
change self.exit_values to return data in the
result for the controller.
"""
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
"To aid in porting to IPAAnsibleModule, change to "
"'FreeIPABaseModule.process_results'."
),
version="1.5.0"
)
if "completed" in result:
if result["completed"] > 0:
self.changed = True
@@ -1155,12 +1262,26 @@ else:
Returns True in case current IPA object attributes differ from
args passed to the module.
"""
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule. "
"FreeIPABaseModule require_ipa_attrs_change() is "
"deprecated. Use ansible_freeipa_module.compare_args()."
),
version="1.5.0"
)
equal = compare_args_ipa(self, command_args, ipa_attrs)
return not equal
def ipa_run(self):
"""Execute module actions."""
ipaapi_context = self.ipa_params.get("ipaapi_context")
self.deprecate(
msg=(
"FreeIPABaseModule is deprecated. Use IPAAnsibleModule."
),
version="1.5.0"
)
ipaapi_context = self.params_get("ipaapi_context")
with self.ipa_connect(context=ipaapi_context):
self.check_ipa_params()
self.define_ipa_commands()

View File

@@ -1,6 +1,5 @@
# Writing a new Ansible FreeIPA module
## Minimum requirements
A ansible-freeipa module should have:
* Code:
@@ -13,68 +12,4 @@ A ansible-freeipa module should have:
* Tests:
* Test cases (also playbooks) defined in `tests/<module_name>/test_<something>.yml`. It's ok to have multiple files in this directory.
## Code
The module file have to start with the python shebang line, license header and definition of the constants `ANSIBLE_METADATA`, `DOCUMENTATION`, `EXAMPLES` and `RETURNS`. Those constants need to be defined before the code (even imports). See https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#starting-a-new-module for more information.
Although it's use is not yet required, ansible-freeipa provides `FreeIPABaseModule` as a helper class for the implementation of new modules. See the example bellow:
```python
from ansible.module_utils.ansible_freeipa_module import FreeIPABaseModule
class SomeIPAModule(FreeIPABaseModule):
ipa_param_mapping = {
"arg_to_be_passed_to_ipa_command": "module_param",
"another_arg": "get_another_module_param",
}
def get_another_module_param(self):
another_module_param = self.ipa_params.another_module_param
# Validate or modify another_module_param ...
return another_module_param
def check_ipa_params(self):
# Validate your params here ...
# Example:
if not self.ipa_params.module_param in VALID_OPTIONS:
self.fail_json(msg="Invalid value for argument module_param")
def define_ipa_commands(self):
args = self.get_ipa_command_args()
self.add_ipa_command("some_ipa_command", name="obj-name", args=args)
def main():
ipa_module = SomeIPAModule(argument_spec=dict(
module_param=dict(type="str", default=None, required=False),
another_module_param=dict(type="str", default=None, required=False),
))
ipa_module.ipa_run()
if __name__ == "__main__":
main()
```
In the example above, the module will call the command `some_ipa_command`, using "obj-name" as name and, `arg_to_be_passed_to_ipa_command` and `another_arg` as arguments.
The values of the arguments will be determined by the class attribute `ipa_param_mapping`.
In the case of `arg_to_be_passed_to_ipa_command` the key (`module_param`) is defined in the module `argument_specs` so the value of the argument is actually used.
On the other hand, `another_arg` as mapped to something else: a callable method. In this case the method will be called and it's result used as value for `another_arg`.
**NOTE**: Keep mind that to take advantage of the parameters mapping defined in `ipa_param_mapping` you will have to call `args = self.get_ipa_command_args()` and use `args` in your command. There is no implicit call of this method.
## Disclaimer
The `FreeIPABaseModule` is new and might not be suitable to all cases and every module yet. In case you need to extend it's functionality for a new module please open an issue or PR and we'll be happy to discuss it.
Use the script `utils/new_module` to create the stub files for a new module.

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -21,6 +20,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
@@ -77,6 +79,20 @@ options:
description: The expression of the regex
type: str
required: true
users:
description: Users to rebuild membership for.
type: list
required: false
hosts:
description: Hosts to rebuild membership for.
type: list
required: false
no_wait:
description: Don't wait for rebuilding membership.
type: bool
default_group:
description: Default (fallback) group for all unmatched entries.
type: str
action:
description: Work on automember or member level
default: automember
@@ -84,10 +100,11 @@ options:
state:
description: State to ensure
default: present
choices: ["present", "absent"]
choices: ["present", "absent", "rebuilt", "orphans_removed"]
author:
- Mark Hahl
- Jake Reynolds
- Thomas Woerner
"""
EXAMPLES = """
@@ -100,7 +117,7 @@ EXAMPLES = """
state: present
inclusive:
- key: "mail"
expression: "example.com$
expression: "example.com"
# Delete an automember rule
- ipaautomember:
@@ -114,12 +131,78 @@ EXAMPLES = """
- ipaautomember:
ipaadmin_password: SomeADMINpassword
name: "My domain hosts"
automember_tye: hostgroup
automember_type: hostgroup
action: member
inclusive:
- key: fqdn
expression: ".*.mydomain.com"
# Ensure group membership for all users has been rebuilt
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: rebuilt
# Ensure group membership for given users has been rebuilt
- ipaautomember:
ipaadmin_password: SomeADMINpassword
users:
- user1
- user2
state: rebuilt
# Ensure hostgroup membership for all hosts has been rebuilt
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: rebuilt
# Ensure hostgroup membership for given hosts has been rebuilt
- ipaautomember:
ipaadmin_password: SomeADMINpassword
hosts:
- host1.mydomain.com
- host2.mydomain.com
state: rebuilt
# Ensure default group fallback_group for all unmatched group entries is set
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
default_group: fallback_group
# Ensure default group for all unmatched group entries is not set
- ipaautomember:
ipaadmin_password: SomeADMINpassword
default_group: ""
automember_type: group
state: absent
# Ensure default hostgroup fallback_hostgroup for all unmatched group entries
# is set
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: fallback_hostgroup
# Ensure default hostgroup for all unmatched group entries is not set
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
default_group: ""
state: absent
# Example playbook to ensure all orphan automember group rules are removed:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: group
state: orphans_removed
# Example playbook to ensure all orphan automember hostgroup rules are removed:
- ipaautomember:
ipaadmin_password: SomeADMINpassword
automember_type: hostgroup
state: orphans_removed
"""
RETURN = """
@@ -127,14 +210,14 @@ RETURN = """
from ansible.module_utils.ansible_freeipa_module import (
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors, DN
)
def find_automember(module, name, grouping):
def find_automember(module, name, automember_type):
_args = {
"all": True,
"type": grouping
"type": automember_type
}
try:
@@ -144,13 +227,40 @@ def find_automember(module, name, grouping):
return _result["result"]
def gen_condition_args(grouping,
def find_automember_orphans(module, automember_type):
_args = {
"all": True,
"type": automember_type
}
try:
_result = module.ipa_command_no_name("automember_find_orphans", _args)
except ipalib_errors.NotFound:
return None
return _result
def find_automember_default_group(module, automember_type):
_args = {
"all": True,
"type": automember_type
}
try:
_result = module.ipa_command_no_name("automember_default_group_show",
_args)
except ipalib_errors.NotFound:
return None
return _result["result"]
def gen_condition_args(automember_type,
key,
inclusiveregex=None,
exclusiveregex=None):
_args = {}
if grouping is not None:
_args['type'] = grouping
if automember_type is not None:
_args['type'] = automember_type
if key is not None:
_args['key'] = key
if inclusiveregex is not None:
@@ -161,13 +271,23 @@ def gen_condition_args(grouping,
return _args
def gen_args(description, grouping):
def gen_rebuild_args(automember_type, rebuild_users, rebuild_hosts, no_wait):
_args = {"no_wait": no_wait}
if automember_type is not None:
_args['type'] = automember_type
if rebuild_users is not None:
_args["users"] = rebuild_users
if rebuild_hosts is not None:
_args["hosts"] = rebuild_hosts
return _args
def gen_args(description, automember_type):
_args = {}
if description is not None:
_args["description"] = description
if grouping is not None:
_args['type'] = grouping
if automember_type is not None:
_args['type'] = automember_type
return _args
@@ -192,28 +312,35 @@ def main():
argument_spec=dict(
# general
inclusive=dict(type="list",
aliases=["automemberinclusiveregex"], default=None,
aliases=["automemberinclusiveregex"],
default=None,
options=dict(
key=dict(type="str", required=True),
expression=dict(type="str", required=True)
),
elements="dict", required=False),
exclusive=dict(type="list", aliases=[
"automemberexclusiveregex"], default=None,
elements="dict",
required=False),
exclusive=dict(type="list",
aliases=["automemberexclusiveregex"],
default=None,
options=dict(
key=dict(type="str", required=True),
expression=dict(type="str", required=True)
),
elements="dict", required=False),
elements="dict",
required=False),
name=dict(type="list", aliases=["cn"],
default=None, required=True),
default=None, required=False),
description=dict(type="str", default=None),
automember_type=dict(type='str', required=False,
choices=['group', 'hostgroup']),
no_wait=dict(type="bool", default=None),
default_group=dict(type="str", default=None),
action=dict(type="str", default="automember",
choices=["member", "automember"]),
state=dict(type="str", default="present",
choices=["present", "absent", "rebuild"]),
choices=["present", "absent", "rebuilt",
"orphans_removed"]),
users=dict(type="list", default=None),
hosts=dict(type="list", default=None),
),
@@ -226,6 +353,8 @@ def main():
# general
names = ansible_module.params_get("name")
if names is None:
names = []
# present
description = ansible_module.params_get("description")
@@ -234,6 +363,12 @@ def main():
inclusive = ansible_module.params_get("inclusive")
exclusive = ansible_module.params_get("exclusive")
# no_wait for rebuilt
no_wait = ansible_module.params_get("no_wait")
# default_group
default_group = ansible_module.params_get("default_group")
# action
action = ansible_module.params_get("action")
# state
@@ -248,12 +383,51 @@ def main():
# Check parameters
invalid = []
if state != "rebuild":
invalid = ["rebuild_hosts", "rebuild_users"]
if state in ["rebuilt", "orphans_removed"]:
invalid = ["name", "description", "exclusive", "inclusive",
"default_group"]
if not automember_type and state != "rebuild":
if action == "member":
ansible_module.fail_json(
msg="'automember_type' is required unless state: rebuild")
msg="'action=member' is not usable with state '%s'" % state)
if state == "rebuilt":
if automember_type == "group" and rebuild_hosts is not None:
ansible_module.fail_json(
msg="state %s: hosts can not be set when type is '%s'" %
(state, automember_type))
if automember_type == "hostgroup" and rebuild_users is not None:
ansible_module.fail_json(
msg="state %s: users can not be set when type is '%s'" %
(state, automember_type))
elif state == "orphans_removed":
invalid.extend(["users", "hosts"])
if not automember_type:
ansible_module.fail_json(
msg="'automember_type' is required unless state: rebuilt")
else:
if default_group is not None:
for param in ["name", "exclusive", "inclusive", "users", "hosts"
"no_wait"]:
if ansible_module.params.get(param) is not None:
msg = "Cannot use {0} together with default_group"
ansible_module.fail_json(msg=msg.format(param))
if action == "member":
ansible_module.fail_json(
msg="Cannot use default_group with action:member")
if state == "absent":
ansible_module.fail_json(
msg="Cannot use default_group with state:absent")
else:
invalid = ["users", "hosts", "no_wait"]
if not automember_type:
ansible_module.fail_json(
msg="'automember_type' is required.")
ansible_module.params_fail_used_invalid(invalid, state, action)
@@ -307,15 +481,21 @@ def main():
commands.append([name, 'automember_add', args])
res_find = {}
inclusive_add, inclusive_del = gen_add_del_lists(
transform_conditions(inclusive or []),
res_find.get("automemberinclusiveregex", [])
)
if inclusive is not None:
inclusive_add, inclusive_del = gen_add_del_lists(
transform_conditions(inclusive),
res_find.get("automemberinclusiveregex", [])
)
else:
inclusive_add, inclusive_del = [], []
exclusive_add, exclusive_del = gen_add_del_lists(
transform_conditions(exclusive or []),
res_find.get("automemberexclusiveregex", [])
)
if exclusive is not None:
exclusive_add, exclusive_del = gen_add_del_lists(
transform_conditions(exclusive),
res_find.get("automemberexclusiveregex", [])
)
else:
exclusive_add, exclusive_del = [], []
elif action == "member":
if res_find is None:
@@ -384,16 +564,45 @@ def main():
'automember_remove_condition',
condition_args])
elif state == "rebuild":
if automember_type:
commands.append([None, 'automember_rebuild',
{"type": automember_type}])
if rebuild_users:
commands.append([None, 'automember_rebuild',
{"users": rebuild_users}])
if rebuild_hosts:
commands.append([None, 'automember_rebuild',
{"hosts": rebuild_hosts}])
if len(names) == 0:
if state == "rebuilt":
args = gen_rebuild_args(automember_type, rebuild_users,
rebuild_hosts, no_wait)
commands.append([None, 'automember_rebuild', args])
elif state == "orphans_removed":
res_find = find_automember_orphans(ansible_module,
automember_type)
if res_find["count"] > 0:
commands.append([None, 'automember_find_orphans',
{'type': automember_type,
'remove': True}])
elif default_group is not None and state == "present":
res_find = find_automember_default_group(ansible_module,
automember_type)
if default_group == "":
if isinstance(res_find["automemberdefaultgroup"], list):
commands.append([None,
'automember_default_group_remove',
{'type': automember_type}])
ansible_module.warn("commands: %s" % repr(commands))
else:
dn_default_group = [DN(('cn', default_group),
('cn', '%ss' % automember_type),
('cn', 'accounts'),
ansible_module.ipa_get_basedn())]
if repr(res_find["automemberdefaultgroup"]) != \
repr(dn_default_group):
commands.append(
[None, 'automember_default_group_set',
{'type': automember_type,
'automemberdefaultgroup': default_group}])
else:
ansible_module.fail_json(msg="Invalid operation")
# Execute commands

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
# Chris Procter <cprocter@redhat.com>
@@ -19,6 +18,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -65,13 +68,16 @@ RETURN = '''
'''
from ansible.module_utils.ansible_freeipa_module import (
FreeIPABaseModule, ipalib_errors
IPAAnsibleModule, ipalib_errors
)
class AutomountLocation(FreeIPABaseModule):
class AutomountLocation(IPAAnsibleModule):
ipa_param_mapping = {}
def __init__(self, *args, **kwargs):
# pylint: disable=super-with-arguments
super(AutomountLocation, self).__init__(*args, **kwargs)
self.commands = []
def get_location(self, location):
try:
@@ -84,40 +90,28 @@ class AutomountLocation(FreeIPABaseModule):
return response.get("result", None)
def check_ipa_params(self):
if len(self.ipa_params.name) == 0:
if len(self.params_get("name")) == 0:
self.fail_json(msg="At least one location must be provided.")
def define_ipa_commands(self):
state = self.params_get("state")
for location_name in self.ipa_params.name:
for location_name in self.params_get("name"):
location = self.get_location(location_name)
if not location and self.ipa_params.state == "present":
if not location and state == "present":
# does not exist and is wanted
self.add_ipa_command(
"automountlocation_add",
name=location_name,
args=None,
)
elif location and self.ipa_params.state == "absent":
self.commands.append(
(location_name, "automountlocation_add", {}))
elif location and state == "absent":
# exists and is not wanted
self.add_ipa_command(
"automountlocation_del",
name=location_name,
args=None,
)
self.commands.append(
(location_name, "automountlocation_del", {}))
def main():
ipa_module = AutomountLocation(
argument_spec=dict(
ipaadmin_principal=dict(type="str",
default="admin"
),
ipaadmin_password=dict(type="str",
required=False,
no_log=True
),
state=dict(type='str',
default='present',
choices=['present', 'absent']
@@ -129,7 +123,12 @@ def main():
),
),
)
ipa_module.ipa_run()
ipaapi_context = ipa_module.params_get("ipaapi_context")
with ipa_module.ipa_connect(context=ipaapi_context):
ipa_module.check_ipa_params()
ipa_module.define_ipa_commands()
changed = ipa_module.execute_ipa_commands(ipa_module.commands)
ipa_module.exit_json(changed=changed)
if __name__ == "__main__":

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -296,7 +299,7 @@ def main():
"KDC:Disable Last Success",
"KDC:Disable Lockout",
"KDC:Disable Default Preauth for SPNs",
""]), # noqa E128
""]), # noqa E128
selinuxusermaporder=dict(type="list", required=False,
aliases=['ipaselinuxusermaporder']),
selinuxusermapdefault=dict(type="str", required=False,
@@ -362,15 +365,15 @@ def main():
if params.get("ipadomainresolutionorder", None):
params["ipadomainresolutionorder"] = \
":".join(params["ipadomainresolutionorder"])
":".join(params["ipadomainresolutionorder"])
if params.get("ipausersearchfields", None):
params["ipausersearchfields"] = \
",".join(params["ipausersearchfields"])
",".join(params["ipausersearchfields"])
if params.get("ipagroupsearchfields", None):
params["ipagroupsearchfields"] = \
",".join(params["ipagroupsearchfields"])
",".join(params["ipagroupsearchfields"])
# verify limits on INT values.
args_with_limits = [
@@ -415,12 +418,12 @@ def main():
if ansible_module.argument_spec.get(k):
arg_type = ansible_module.argument_spec[k]['type']
if k in (
'ipaselinuxusermaporder', 'domain_resolution_order'
):
'ipaselinuxusermaporder', 'domain_resolution_order'
):
exit_args[k] = result.get(key)[0].split('$')
elif k in (
'usersearch', 'groupsearch'
):
'usersearch', 'groupsearch'
):
exit_args[k] = result.get(key)[0].split(',')
elif isinstance(value, str) and arg_type == "list":
exit_args[k] = [value]

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -21,6 +20,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -162,24 +165,23 @@ def gen_args(module, state, dnsconfig, forwarders, forward_policy,
def main():
forwarder_spec = dict(
ip_address=dict(type=str, required=True),
port=dict(type=int, required=False, default=None)
ip_address=dict(type=str, required=True),
port=dict(type=int, required=False, default=None)
)
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# dnsconfig
forwarders=dict(type='list', default=None, required=False,
options=dict(**forwarder_spec)),
forward_policy=dict(type='str', required=False, default=None,
choices=['only', 'first', 'none']),
allow_sync_ptr=dict(type='bool', required=False, default=None),
argument_spec=dict(
# dnsconfig
forwarders=dict(type='list', default=None, required=False,
options=dict(**forwarder_spec)),
forward_policy=dict(type='str', required=False, default=None,
choices=['only', 'first', 'none']),
allow_sync_ptr=dict(type='bool', required=False, default=None),
# general
state=dict(type="str", default="present",
choices=["present", "absent"]),
)
# general
state=dict(type="str", default="present",
choices=["present", "absent"]),
)
)
ansible_module._ansible_debug = True

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
"""DNS Record ansible-freeipa module."""
ANSIBLE_METADATA = {
@@ -866,8 +869,7 @@ from ansible.module_utils.ansible_freeipa_module import \
import dns.reversename
import dns.resolver
import six
from ansible.module_utils import six
if six.PY3:
unicode = str
@@ -1106,12 +1108,13 @@ def configure_module():
name=dict(type="list", aliases=["record_name"], default=None,
required=False),
records=dict(type="list", default=None,
records=dict(type="list",
default=None,
options=dict(
# Here name is a simple string
name=dict(type='str', required=True,
aliases=['record_name']),
**record_spec),
# Here name is a simple string
name=dict(type='str', required=True,
aliases=['record_name']),
**record_spec),
),
# general

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -200,45 +203,55 @@ dnszone:
from ipapython.dnsutil import DNSName # noqa: E402
from ansible.module_utils.ansible_freeipa_module import (
FreeIPABaseModule,
IPAAnsibleModule,
is_ip_address,
is_ip_network_address,
is_valid_port,
ipalib_errors
ipalib_errors,
compare_args_ipa,
IPAParamMapping,
) # noqa: E402
import netaddr
import six
from ansible.module_utils import six
if six.PY3:
unicode = str
class DNSZoneModule(FreeIPABaseModule):
class DNSZoneModule(IPAAnsibleModule):
ipa_param_mapping = {
# Direct Mapping
"idnsforwardpolicy": "forward_policy",
"idnssoarefresh": "refresh",
"idnssoaretry": "retry",
"idnssoaexpire": "expire",
"idnssoaminimum": "minimum",
"dnsttl": "ttl",
"dnsdefaultttl": "default_ttl",
"idnsallowsyncptr": "allow_sync_ptr",
"idnsallowdynupdate": "dynamic_update",
"idnssecinlinesigning": "dnssec",
"idnsupdatepolicy": "update_policy",
# Mapping by method
"idnsforwarders": "get_ipa_idnsforwarders",
"idnsallowtransfer": "get_ipa_idnsallowtransfer",
"idnsallowquery": "get_ipa_idnsallowquery",
"idnssoamname": "get_ipa_idnssoamname",
"idnssoarname": "get_ipa_idnssoarname",
"skip_nameserver_check": "get_ipa_skip_nameserver_check",
"skip_overlap_check": "get_ipa_skip_overlap_check",
"nsec3paramrecord": "get_ipa_nsec3paramrecord",
}
def __init__(self, *args, **kwargs):
# pylint: disable=super-with-arguments
super(DNSZoneModule, self).__init__(*args, **kwargs)
ipa_param_mapping = {
# Direct Mapping
"idnsforwardpolicy": "forward_policy",
"idnssoarefresh": "refresh",
"idnssoaretry": "retry",
"idnssoaexpire": "expire",
"idnssoaminimum": "minimum",
"dnsttl": "ttl",
"dnsdefaultttl": "default_ttl",
"idnsallowsyncptr": "allow_sync_ptr",
"idnsallowdynupdate": "dynamic_update",
"idnssecinlinesigning": "dnssec",
"idnsupdatepolicy": "update_policy",
# Mapping by method
"idnsforwarders": self.get_ipa_idnsforwarders,
"idnsallowtransfer": self.get_ipa_idnsallowtransfer,
"idnsallowquery": self.get_ipa_idnsallowquery,
"idnssoamname": self.get_ipa_idnssoamname,
"idnssoarname": self.get_ipa_idnssoarname,
"skip_nameserver_check": self.get_ipa_skip_nameserver_check,
"skip_overlap_check": self.get_ipa_skip_overlap_check,
"nsec3paramrecord": self.get_ipa_nsec3paramrecord,
}
self.commands = []
self.ipa_params = IPAParamMapping(self, ipa_param_mapping)
self.exit_args = {}
def validate_ips(self, ips, error_msg):
invalid_ips = [
@@ -438,39 +451,34 @@ class DNSZoneModule(FreeIPABaseModule):
for zone_name in self.get_zone_names():
# Look for existing zone in IPA
zone, is_zone_active = self.get_zone(zone_name)
args = self.get_ipa_command_args(zone=zone)
args = self.ipa_params.get_ipa_command_args(zone=zone)
if self.ipa_params.state in ["present", "enabled", "disabled"]:
if not zone:
# Since the zone doesn't exist we just create it
# with given args
self.add_ipa_command("dnszone_add", zone_name, args)
self.commands.append((zone_name, "dnszone_add", args))
is_zone_active = True
# just_added = True
else:
# Zone already exist so we need to verify if given args
# matches the current config. If not we updated it.
if self.require_ipa_attrs_change(args, zone):
self.add_ipa_command("dnszone_mod", zone_name, args)
if not compare_args_ipa(self, args, zone):
self.commands.append((zone_name, "dnszone_mod", args))
if self.ipa_params.state == "enabled" and not is_zone_active:
self.add_ipa_command("dnszone_enable", zone_name)
self.commands.append((zone_name, "dnszone_enable", {}))
if self.ipa_params.state == "disabled" and is_zone_active:
self.add_ipa_command("dnszone_disable", zone_name)
self.commands.append((zone_name, "dnszone_disable", {}))
if self.ipa_params.state == "absent" and zone is not None:
self.add_ipa_command("dnszone_del", zone_name)
self.commands.append((zone_name, "dnszone_del", {}))
def process_command_result(self, name, command, args, result):
# pylint: disable=super-with-arguments
super(DNSZoneModule, self).process_command_result(
name, command, args, result
)
def process_results(self, _result, command, name, _args, exit_args):
if command == "dnszone_add" and self.ipa_params.name_from_ip:
dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
dnszone_exit_args['name'] = name
exit_args.setdefault('dnszone', {})["name"] = name
def get_argument_spec():
@@ -529,12 +537,24 @@ def get_argument_spec():
def main():
DNSZoneModule(
ansible_module = DNSZoneModule(
argument_spec=get_argument_spec(),
mutually_exclusive=[["name", "name_from_ip"]],
required_one_of=[["name", "name_from_ip"]],
supports_check_mode=True,
).ipa_run()
)
exit_args = {}
ipaapi_context = ansible_module.params_get("ipaapi_context")
with ansible_module.ipa_connect(context=ipaapi_context):
ansible_module.check_ipa_params()
ansible_module.define_ipa_commands()
changed = ansible_module.execute_ipa_commands(
ansible_module.commands,
result_handler=DNSZoneModule.process_results,
exit_args=exit_args
)
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -390,13 +393,13 @@ def main():
# If yes: modify
# Also if it is a modification from nonposix to posix
# or nonposix to external.
if not compare_args_ipa(ansible_module, args,
res_find) or \
(
not is_posix_group(res_find) and
not is_external_group(res_find) and
(posix or external)
):
if not compare_args_ipa(
ansible_module, args, res_find
) or (
not is_posix_group(res_find) and
not is_external_group(res_find) and
(posix or external)
):
if posix:
args['posix'] = True
if external:

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -403,7 +406,7 @@ host:
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors
import six
from ansible.module_utils import six
if six.PY3:
unicode = str
@@ -436,7 +439,7 @@ def find_dnsrecord(module, name):
This function may raise ipalib_errors.NotFound in some cases,
and it should be handled by the caller.
"""
domain_name = name[name.find(".")+1:]
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
_args = {
@@ -1228,7 +1231,7 @@ def main():
}])
if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0:
domain_name = name[name.find(".")+1:]
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
_args = {"idnsname": host_name}
@@ -1245,7 +1248,7 @@ def main():
"dnsrecord_add", _args])
if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
domain_name = name[name.find(".")+1:]
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
# There seems to be an issue with dnsrecord_del (not
@@ -1361,7 +1364,7 @@ def main():
if "arecord" in dnsrecord_args or \
"aaaarecord" in dnsrecord_args:
domain_name = name[name.find(".")+1:]
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
dnsrecord_args["idnsname"] = host_name

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
"""ansible-freeipa module to manage FreeIPA privileges."""
@@ -108,8 +111,9 @@ RETURN = """
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
import six
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
gen_intersection_list
from ansible.module_utils import six
if six.PY3:
unicode = str
@@ -126,22 +130,6 @@ def find_privilege(module, name):
return _result["result"]
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors):
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
for failed_item in result.get("failed", []):
failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
@@ -230,47 +218,31 @@ def main():
if action == "privilege":
# Found the privilege
if res_find is not None:
res_cmp = {
k: v for k, v in res_find.items()
if k not in [
"objectclass", "cn", "dn",
"memberof_permisssion"
]
}
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if args and not compare_args_ipa(ansible_module, args,
res_cmp):
cmp = {"description": res_find.get("description")}
if not compare_args_ipa(ansible_module, args, cmp):
commands.append([name, "privilege_mod", args])
else:
commands.append([name, "privilege_add", args])
res_find = {}
member_args = {}
if permission:
member_args['permission'] = permission
# Generate addition and removal lists
permission_add, permission_del = gen_add_del_lists(
permission, res_find.get("memberof_permission")
)
if not compare_args_ipa(ansible_module, member_args,
res_find):
# Generate addition and removal lists
permission_add, permission_del = gen_add_del_lists(
permission, res_find.get("member_permission"))
# Add members
if len(permission_add) > 0:
commands.append([name, "privilege_add_permission",
{
"permission": permission_add,
}])
# Remove members
if len(permission_del) > 0:
commands.append([
name,
"privilege_remove_permission",
{"permission": permission_del}
])
# Add members
if len(permission_add) > 0:
commands.append([name, "privilege_add_permission",
{
"permission": permission_add,
}])
# Remove members
if len(permission_del) > 0:
commands.append([
name,
"privilege_remove_permission",
{"permission": permission_del}
])
elif action == "member":
if res_find is None:
@@ -280,8 +252,11 @@ def main():
if permission is None:
ansible_module.fail_json(msg="No permission given")
commands.append([name, "privilege_add_permission",
{"permission": permission}])
permission = gen_add_list(
permission, res_find.get("memberof_permission"))
if permission:
commands.append([name, "privilege_add_permission",
{"permission": permission}])
elif state == "absent":
if action == "privilege":
@@ -296,10 +271,11 @@ def main():
if permission is None:
ansible_module.fail_json(msg="No permission given")
commands.append([name, "privilege_remove_permission",
{
"permission": permission,
}])
permission = gen_intersection_list(
permission, res_find.get("memberof_permission"))
if permission:
commands.append([name, "privilege_remove_permission",
{"permission": permission}])
elif state == "renamed":
if not rename:
@@ -318,7 +294,8 @@ def main():
# Execute commands
changed = ansible_module.execute_ipa_commands(commands, result_handler)
changed = ansible_module.execute_ipa_commands(
commands, fail_on_member_errors=True)
# Done

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""ansible-freeipa iparole module implementation."""
@@ -21,6 +20,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -101,7 +104,7 @@ EXAMPLES = """
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, gen_add_del_lists, compare_args_ipa
import six
from ansible.module_utils import six
if six.PY3:
@@ -196,7 +199,7 @@ def ensure_absent_state(module, name, action, res_find):
member_args = {}
for key in ['user', 'group', 'host', 'hostgroup']:
items = member_intersect(
module, key, 'member_%s' % key, res_find)
module, key, 'member_%s' % key, res_find)
if items:
member_args[key] = items
@@ -295,7 +298,7 @@ def ensure_members_are_present(module, name, res_find):
member_args = {}
for key in ['user', 'group', 'host', 'hostgroup']:
items = member_difference(
module, key, 'member_%s' % key, res_find)
module, key, 'member_%s' % key, res_find)
if items:
member_args[key] = items

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -183,21 +186,23 @@ EXAMPLES = """
ipaadmin_password: SomeADMINpassword
name: HTTP/www.example.com
certificate:
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT
ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH
VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM
LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT
oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s
4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc
xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1
UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q
eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs
5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic
uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH
2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no
obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC
/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq
- >
MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8x
DTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVG
FYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEr
aELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmj
fMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQI
Nt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJ
EkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMB
AAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV
0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJP
rp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfw
Q5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHo
VOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+s
KC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA
5yqV7MVq
action: member
state: present
@@ -224,7 +229,8 @@ RETURN = """
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, encode_certificate, \
gen_add_del_lists, ipalib_errors
gen_add_del_lists, gen_add_list, gen_intersection_list, ipalib_errors, \
api_get_realm, to_text
def find_service(module, name):
@@ -283,9 +289,7 @@ def gen_args_smb(netbiosname, ok_as_delegate, ok_to_auth_as_delegate):
return _args
def check_parameters(module, state, action, names, parameters):
assert isinstance(parameters, dict)
def check_parameters(module, state, action, names):
# invalid parameters for everything but state 'present', action 'service'.
invalid = ['pac_type', 'auth_ind', 'skip_host_check',
'force', 'requires_pre_auth', 'ok_as_delegate',
@@ -307,8 +311,8 @@ def check_parameters(module, state, action, names, parameters):
invalid = ['delete_continue']
if (
not parameters.get('smb', False)
and parameters.get('netbiosname')
not module.params_get('smb')
and module.params_get('netbiosname')
):
module.fail_json(
msg="Argument 'netbiosname' can not be used without "
@@ -405,23 +409,6 @@ def init_ansible_module():
return ansible_module
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors):
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
def main():
ansible_module = init_ansible_module()
@@ -447,23 +434,6 @@ def main():
host = ansible_module.params_get("host")
allow_create_keytab_user = ansible_module.params_get(
"allow_create_keytab_user")
allow_create_keytab_group = ansible_module.params_get(
"allow_create_keytab_group")
allow_create_keytab_host = ansible_module.params_get(
"allow_create_keytab_host")
allow_create_keytab_hostgroup = ansible_module.params_get(
"allow_create_keytab_hostgroup")
allow_retrieve_keytab_user = ansible_module.params_get(
"allow_retrieve_keytab_user")
allow_retrieve_keytab_group = ansible_module.params_get(
"allow_retrieve_keytab_group")
allow_retrieve_keytab_host = ansible_module.params_get(
"allow_retrieve_keytab_host")
allow_retrieve_keytab_hostgroup = ansible_module.params_get(
"allow_retrieve_keytab_hostgroup")
delete_continue = ansible_module.params_get("delete_continue")
# action
@@ -472,7 +442,7 @@ def main():
state = ansible_module.params_get("state")
# check parameters
check_parameters(ansible_module, state, action, names, vars())
check_parameters(ansible_module, state, action, names)
# Init
@@ -489,9 +459,48 @@ def main():
msg="Skipping host check is not supported by your IPA version")
commands = []
keytab_members = ["user", "group", "host", "hostgroup"]
for name in names:
res_find = find_service(ansible_module, name)
res_principals = []
keytab = {
"retrieve": {
"allow": {k: [] for k in keytab_members},
"disallow": {k: [] for k in keytab_members},
},
"create": {
"allow": {k: [] for k in keytab_members},
"disallow": {k: [] for k in keytab_members},
},
}
certificate_add, certificate_del = [], []
host_add, host_del = [], []
principal_add, principal_del = [], []
if principal and res_find:
# When comparing principals to the existing ones,
# the REALM is needded, and are added here for those
# that do not have it.
principal = [
p if "@" in p
else "%s@%s" % (p, api_get_realm())
for p in principal
]
principal = list(set(principal))
# Create list of existing principal aliases as strings
# to compare with provided ones.
canonicalname = {
to_text(p)
for p in res_find.get("krbcanonicalname", [])
}
res_principals = [
to_text(elem)
for elem in res_find.get("krbprincipalname", [])
]
res_principals = list(set(res_principals) - canonicalname)
if state == "present":
if action == "service":
@@ -520,37 +529,8 @@ def main():
if res_find is None:
commands.append([name, 'service_add', args])
certificate_add = certificate or []
certificate_del = []
host_add = host or []
host_del = []
principal_add = principal or []
principal_del = []
allow_create_keytab_user_add = \
allow_create_keytab_user or []
allow_create_keytab_user_del = []
allow_create_keytab_group_add = \
allow_create_keytab_group or []
allow_create_keytab_group_del = []
allow_create_keytab_host_add = \
allow_create_keytab_host or []
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = \
allow_create_keytab_hostgroup or []
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = \
allow_retrieve_keytab_user or []
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = \
allow_retrieve_keytab_group or []
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = \
allow_retrieve_keytab_host or []
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = []
# Use an empty res_find to manage members
res_find = {}
else:
for remove in ['skip_host_check', 'force']:
@@ -563,207 +543,54 @@ def main():
args.get("krbprincipalauthind", [""]) ==
res_find.get("krbprincipalauthind", [""])
)
):
):
del args["krbprincipalauthind"]
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "service_mod", args])
certificate_add, certificate_del = gen_add_del_lists(
certificate, res_find.get("usercertificate"))
# Manage members
certificate_add, certificate_del = gen_add_del_lists(
certificate, res_find.get("usercertificate"))
host_add, host_del = gen_add_del_lists(
host, res_find.get('managedby_host', []))
host_add, host_del = gen_add_del_lists(
host, res_find.get('managedby_host'))
principal_add, principal_del = gen_add_del_lists(
principal, res_find.get("principal"))
(allow_create_keytab_user_add,
allow_create_keytab_user_del) = \
gen_add_del_lists(
allow_create_keytab_user, res_find.get(
'ipaallowedtoperform_write_keys_user',
[]))
(allow_retrieve_keytab_user_add,
allow_retrieve_keytab_user_del) = \
gen_add_del_lists(
allow_retrieve_keytab_user, res_find.get(
'ipaallowedtoperform_read_keys_user',
[]))
(allow_create_keytab_group_add,
allow_create_keytab_group_del) = \
gen_add_del_lists(
allow_create_keytab_group, res_find.get(
'ipaallowedtoperform_write_keys_group',
[]))
(allow_retrieve_keytab_group_add,
allow_retrieve_keytab_group_del) = \
gen_add_del_lists(
allow_retrieve_keytab_group,
res_find.get(
'ipaallowedtoperform_read_keys_group',
[]))
(allow_create_keytab_host_add,
allow_create_keytab_host_del) = \
gen_add_del_lists(
allow_create_keytab_host,
res_find.get(
'ipaallowedtoperform_write_keys_host',
[]))
(allow_retrieve_keytab_host_add,
allow_retrieve_keytab_host_del) = \
gen_add_del_lists(
allow_retrieve_keytab_host,
res_find.get(
'ipaallowedtoperform_read_keys_host',
[]))
(allow_create_keytab_hostgroup_add,
allow_create_keytab_hostgroup_del) = \
gen_add_del_lists(
allow_create_keytab_hostgroup,
res_find.get(
'ipaallowedtoperform_write_keys_hostgroup',
[]))
(allow_retrieve_keytab_hostgroup_add,
allow_retrieve_keytab_hostgroup_del) = \
gen_add_del_lists(
allow_retrieve_keytab_hostgroup,
res_find.get(
'ipaallowedtoperform_read_keys_hostgroup',
[]))
principal_add, principal_del = gen_add_del_lists(
principal, res_principals)
elif action == "member":
if res_find is None:
ansible_module.fail_json(msg="No service '%s'" % name)
existing = res_find.get('usercertificate', [])
if certificate is None:
certificate_add = []
else:
certificate_add = [c for c in certificate
if c not in existing]
certificate_del = []
host_add = host or []
host_del = []
principal_add = principal or []
principal_del = []
certificate_add = gen_add_list(
certificate, res_find.get("usercertificate"))
allow_create_keytab_user_add = \
allow_create_keytab_user or []
allow_create_keytab_user_del = []
allow_create_keytab_group_add = \
allow_create_keytab_group or []
allow_create_keytab_group_del = []
allow_create_keytab_host_add = \
allow_create_keytab_host or []
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = \
allow_create_keytab_hostgroup or []
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = \
allow_retrieve_keytab_user or []
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = \
allow_retrieve_keytab_group or []
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = \
allow_retrieve_keytab_host or []
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = []
host_add = gen_add_list(
host, res_find.get('managedby_host'))
# Add principals
for _principal in principal_add:
commands.append([name, "service_add_principal",
{
"krbprincipalname":
_principal,
}])
principal_add = gen_add_list(principal, res_principals)
# Remove principals
for _principal in principal_del:
commands.append([name, "service_remove_principal",
{
"krbprincipalname":
_principal,
}])
for _certificate in certificate_add:
commands.append([name, "service_add_cert",
{
"usercertificate":
_certificate,
}])
# Remove certificates
for _certificate in certificate_del:
commands.append([name, "service_remove_cert",
{
"usercertificate":
_certificate,
}])
# Add hosts.
if host is not None and len(host) > 0 and len(host_add) > 0:
commands.append([name, "service_add_host",
{"host": host_add}])
# Remove hosts
if host is not None and len(host) > 0 and len(host_del) > 0:
commands.append([name, "service_remove_host",
{"host": host_del}])
# Allow create keytab
if len(allow_create_keytab_user_add) > 0 or \
len(allow_create_keytab_group_add) > 0 or \
len(allow_create_keytab_host_add) > 0 or \
len(allow_create_keytab_hostgroup_add) > 0:
commands.append(
[name, "service_allow_create_keytab",
{'user': allow_create_keytab_user_add,
'group': allow_create_keytab_group_add,
'host': allow_create_keytab_host_add,
'hostgroup': allow_create_keytab_hostgroup_add
}])
# Disallow create keytab
if len(allow_create_keytab_user_del) > 0 or \
len(allow_create_keytab_group_del) > 0 or \
len(allow_create_keytab_host_del) > 0 or \
len(allow_create_keytab_hostgroup_del) > 0:
commands.append(
[name, "service_disallow_create_keytab",
{'user': allow_create_keytab_user_del,
'group': allow_create_keytab_group_del,
'host': allow_create_keytab_host_del,
'hostgroup': allow_create_keytab_hostgroup_del
}])
# Allow retrieve keytab
if len(allow_retrieve_keytab_user_add) > 0 or \
len(allow_retrieve_keytab_group_add) > 0 or \
len(allow_retrieve_keytab_host_add) > 0 or \
len(allow_retrieve_keytab_hostgroup_add) > 0:
commands.append(
[name, "service_allow_retrieve_keytab",
{'user': allow_retrieve_keytab_user_add,
'group': allow_retrieve_keytab_group_add,
'host': allow_retrieve_keytab_host_add,
'hostgroup': allow_retrieve_keytab_hostgroup_add
}])
# Disllow retrieve keytab
if len(allow_retrieve_keytab_user_del) > 0 or \
len(allow_retrieve_keytab_group_del) > 0 or \
len(allow_retrieve_keytab_host_del) > 0 or \
len(allow_retrieve_keytab_hostgroup_del) > 0:
commands.append(
[name, "service_disallow_retrieve_keytab",
{'user': allow_retrieve_keytab_user_del,
'group': allow_retrieve_keytab_group_del,
'host': allow_retrieve_keytab_host_del,
'hostgroup': allow_retrieve_keytab_hostgroup_del
}])
# get keytab management lists for any 'action'.
for perm in ["create", "retrieve"]:
oper = "write" if perm == "create" else "read"
for key in ["user", "group", "host", "hostgroup"]:
add_list, del_list = (
gen_add_del_lists(
ansible_module.params_get(
"allow_%s_keytab_%s" % (perm, key)
),
res_find.get(
'ipaallowedtoperform_%s_keys_%s'
% (oper, key)
)
)
)
keytab[perm]["allow"][key] = add_list
# Only remove members if action is 'service'
if action == "service":
keytab[perm]["disallow"][key] = del_list
elif state == "absent":
if action == "service":
@@ -775,55 +602,30 @@ def main():
if res_find is None:
ansible_module.fail_json(msg="No service '%s'" % name)
# Remove principals
if principal is not None:
for _principal in principal:
commands.append([name, "service_remove_principal",
{
"krbprincipalname":
_principal,
}])
# Remove certificates
if certificate is not None:
existing = res_find.get('usercertificate', [])
for _certificate in certificate:
if _certificate in existing:
commands.append([name, "service_remove_cert",
{
"usercertificate":
_certificate,
}])
principal_del = gen_intersection_list(
principal, res_principals)
# Add hosts
if host is not None:
commands.append(
[name, "service_remove_host", {"host": host}])
certificate_del = gen_intersection_list(
certificate, res_find.get("usercertificate"))
# Allow create keytab
if allow_create_keytab_user is not None or \
allow_create_keytab_group is not None or \
allow_create_keytab_host is not None or \
allow_create_keytab_hostgroup is not None:
commands.append(
[name, "service_disallow_create_keytab",
{'user': allow_create_keytab_user,
'group': allow_create_keytab_group,
'host': allow_create_keytab_host,
'hostgroup': allow_create_keytab_hostgroup
}])
host_del = gen_intersection_list(
host, res_find.get("managedby_host"))
# Allow retriev keytab
if allow_retrieve_keytab_user is not None or \
allow_retrieve_keytab_group is not None or \
allow_retrieve_keytab_host is not None or \
allow_retrieve_keytab_hostgroup is not None:
commands.append(
[name, "service_disallow_retrieve_keytab",
{'user': allow_retrieve_keytab_user,
'group': allow_retrieve_keytab_group,
'host': allow_retrieve_keytab_host,
'hostgroup': allow_retrieve_keytab_hostgroup
}])
for perm in ["create", "retrieve"]:
oper = "write" if perm == "create" else "read"
for key in ["user", "group", "host", "hostgroup"]:
res_param = (
'ipaallowedtoperform_%s_keys_%s'
% (oper, key)
)
module_params = ansible_module.params_get(
"allow_%s_keytab_%s" % (perm, key)
)
existing = res_find.get(res_param)
del_list = (
gen_intersection_list(module_params, existing)
)
keytab[perm]["disallow"][key] = del_list
elif state == "disabled":
if action == "service":
@@ -836,16 +638,53 @@ def main():
ansible_module.fail_json(
msg="Invalid action '%s' for state '%s'" %
(action, state))
# Members are not managed when disabling service.
# Continue with next 'name'.
continue
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Manage members
if principal_add:
commands.append([name, "service_add_principal",
{"krbprincipalname": principal_add}])
if principal_del:
commands.append([name, "service_remove_principal",
{"krbprincipalname": principal_del}])
if certificate_add:
commands.append([name, "service_add_cert",
{"usercertificate": certificate_add}])
if certificate_del:
commands.append([name, "service_remove_cert",
{"usercertificate": certificate_del}])
if host_add:
commands.append([name, "service_add_host",
{"host": host_add}])
if host_del:
commands.append([name, "service_remove_host",
{"host": host_del}])
# manage keytab permissions.
for perm in ["create", "retrieve"]:
for mode in ["allow", "disallow"]:
for key in ["user", "group", "host", "hostgroup"]:
if keytab[perm][mode][key]:
commands.append([
name,
"service_%s_%s_keytab" % (mode, perm),
keytab[perm][mode]
])
break
# Check mode exit
if ansible_module.check_mode:
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
# Execute commands
changed = ansible_module.execute_ipa_commands(commands, result_handler)
changed = ansible_module.execute_ipa_commands(
commands, fail_on_member_errors=True)
# Done
ansible_module.exit_json(changed=changed, **exit_args)

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -456,11 +459,31 @@ def main():
sudooption_add, sudooption_del = gen_add_del_lists(
sudooption, res_find.get('ipasudoopt', []))
# runasuser attribute can be used with both IPA and
# non-IPA (external) users. IPA will handle the correct
# attribute to properly store data, so we need to compare
# the provided list against both users and external
# users list.
runasuser_add, runasuser_del = gen_add_del_lists(
runasuser, res_find.get('ipasudorunas_user', []))
runasuser,
(
res_find.get('ipasudorunas_user', [])
+ res_find.get('ipasudorunasextuser', [])
)
)
# runasgroup attribute can be used with both IPA and
# non-IPA (external) groups. IPA will handle the correct
# attribute to properly store data, so we need to compare
# the provided list against both groups and external
# groups list.
runasgroup_add, runasgroup_del = gen_add_del_lists(
runasgroup, res_find.get('ipasudorunas_group', []))
runasgroup,
(
res_find.get('ipasudorunas_group', [])
+ res_find.get('ipasudorunasextgroup', [])
)
)
# Add hosts and hostgroups
if len(host_add) > 0 or len(hostgroup_add) > 0:
@@ -593,14 +616,38 @@ def main():
"ipasudoopt" in res_find:
sudooption = gen_add_list(
sudooption, res_find["ipasudoopt"])
if runasuser is not None and \
"ipasudorunas_user" in res_find:
# runasuser attribute can be used with both IPA and
# non-IPA (external) users, so we need to compare
# the provided list against both users and external
# users list.
if (
runasuser is not None
and (
"ipasudorunas_user" in res_find
or "ipasudorunasextuser" in res_find
)
):
runasuser = gen_add_list(
runasuser, res_find["ipasudorunas_user"])
if runasgroup is not None and \
"ipasudorunasgroup_group" in res_find:
runasuser,
(list(res_find.get('ipasudorunas_user', []))
+ list(res_find.get('ipasudorunasextuser', [])))
)
# runasgroup attribute can be used with both IPA and
# non-IPA (external) groups, so we need to compare
# the provided list against both users and external
# groups list.
if (
runasgroup is not None
and (
"ipasudorunasgroup_group" in res_find
or "ipasudorunasextgroup" in res_find
)
):
runasgroup = gen_add_list(
runasgroup, res_find["ipasudorunasgroup_group"])
runasgroup,
(list(res_find.get("ipasudorunasgroup_group", []))
+ list(res_find.get("ipasudorunasextgroup", [])))
)
# Add hosts and hostgroups
if host is not None or hostgroup is not None:
@@ -724,17 +771,43 @@ def main():
sudooption, res_find["ipasudoopt"])
else:
sudooption = None
# runasuser attribute can be used with both IPA and
# non-IPA (external) users, so we need to compare
# the provided list against both users and external
# users list.
if runasuser is not None:
if "ipasudorunas_user" in res_find:
if (
"ipasudorunas_user" in res_find
or "ipasudorunasextuser" in res_find
):
runasuser = gen_intersection_list(
runasuser, res_find["ipasudorunas_user"])
runasuser,
(
list(res_find.get('ipasudorunas_user', []))
+ list(res_find.get(
'ipasudorunasextuser', []))
)
)
else:
runasuser = None
# runasgroup attribute can be used with both IPA and
# non-IPA (external) groups, so we need to compare
# the provided list against both groups and external
# groups list.
if runasgroup is not None:
if "ipasudorunasgroup_group" in res_find:
if (
"ipasudorunasgroup_group" in res_find
or "ipasudorunasextgroup" in res_find
):
runasgroup = gen_intersection_list(
runasgroup,
res_find["ipasudorunasgroup_group"])
(
list(res_find.get(
"ipasudorunasgroup_group", []))
+ list(res_find.get(
"ipasudorunasextgroup", []))
)
)
else:
runasgroup = None

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'supported_by': 'community',
'status': ['preview'],

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -472,7 +475,7 @@ user:
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \
encode_certificate, load_cert_from_str, DN_x500_text, to_text
import six
from ansible.module_utils import six
if six.PY3:
unicode = str
@@ -696,8 +699,8 @@ def check_certmapdata(data):
i = data.find("<I>", 4)
s = data.find("<S>", i) # pylint: disable=invalid-name
issuer = data[i+3:s]
subject = data[s+3:]
issuer = data[i + 3:s]
subject = data[s + 3:]
if i < 0 or s < 0 or "CN" not in issuer or "CN" not in subject:
return False
@@ -821,14 +824,17 @@ def main():
# general
name=dict(type="list", aliases=["login"], default=None,
required=False),
users=dict(type="list", aliases=["login"], default=None,
users=dict(type="list",
aliases=["login"],
default=None,
options=dict(
# Here name is a simple string
name=dict(type="str", required=True),
# Add user specific parameters
**user_spec
),
elements='dict', required=False),
elements='dict',
required=False),
# deleted
preserve=dict(required=False, type='bool', default=None),

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
@@ -631,7 +634,7 @@ def main():
vault_password_file=dict(type="str", required=False, default=None,
no_log=False,
aliases=[
'password_file', "old_password_file"
'password_file', "old_password_file"
]),
new_password=dict(type="str", required=False, default=None,
no_log=True),

View File

@@ -3,5 +3,4 @@ pytest>=2.7
pytest-sourceorder>=0.5
pytest-split-tests>=1.0.3
pytest-testinfra>=5.0
jmespath>=0.9 # needed for the `json_query` filter
pyyaml>=3

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
@@ -40,8 +43,9 @@ author:
EXAMPLES = '''
# Get IPA_BACKUP_DIR from ipaplatform
- name: ipabackup_get_backup_dir:
register result
- name: Get IPA_BACKUP_DIR from ipaplatform
ipabackup_get_backup_dir:
register: result
'''
RETURN = '''

View File

@@ -0,0 +1,10 @@
# vars/Ubuntu.yml
---
ipaserver_packages: [ "freeipa-server" ]
ipaserver_packages_dns: [ "freeipa-server-dns" ]
ipaserver_packages_adtrust: [ "freeipa-server-trust-ad" ]
ipaserver_packages_firewalld: [ "firewalld" ]
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
# to the way python-ipalib package is defined.
# Package python2.7 must be installed before executing this role.
ansible_python_interpreter: '/usr/bin/python2.7'

View File

@@ -17,6 +17,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
try:
import gssapi
except ImportError:

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,6 +1,9 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
---
module: ipaclient_get_facts
@@ -12,9 +15,9 @@ author:
import os
import re
import six
from ansible.module_utils import six
try:
from six.moves.configparser import RawConfigParser
from ansible.module_utils.six.moves.configparser import RawConfigParser
except ImportError:
from ConfigParser import RawConfigParser
@@ -81,7 +84,8 @@ def is_dogtag_configured(subsystem):
# ca / kra is configured when the directory
# /var/lib/pki/pki-tomcat/[ca|kra] # exists
available_subsystems = {'ca', 'kra'}
assert subsystem in available_subsystems
if subsystem not in available_subsystems:
raise AssertionError("Subsystem '%s' not available" % subsystem)
return os.path.isdir(os.path.join(VAR_LIB_PKI_TOMCAT, subsystem))
@@ -120,7 +124,7 @@ def get_ipa_conf():
basedn=basedn,
realm=realm,
domain=domain
)
)
def get_ipa_version():
@@ -147,7 +151,7 @@ def get_ipa_version():
vendor_version=version.VENDOR_VERSION,
version=version.VERSION,
version_info=version_info
)
)
def main():
@@ -193,7 +197,7 @@ def main():
module.exit_json(
changed=False,
ansible_facts=dict(ipa=facts)
)
)
if __name__ == '__main__':

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -20,6 +19,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}
@@ -121,9 +124,9 @@ host:
'''
import os
import six
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils import six
from ipalib import api, errors
from ipaplatform.paths import paths

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
@@ -179,7 +182,7 @@ def main():
if password is None and admin_keytab is None:
module.fail_json(msg="Password or admin_keytab is needed")
client_domain = hostname[hostname.find(".")+1:]
client_domain = hostname[hostname.find(".") + 1:]
nolog = tuple()
env = {'PATH': SECURE_PATH}
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
@@ -156,7 +159,7 @@ def main():
options.krb5_offline_passwords = not options.no_krb5_offline_passwords
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
client_domain = hostname[hostname.find(".")+1:]
client_domain = hostname[hostname.find(".") + 1:]
if configure_sssd_conf(fstore, cli_realm, cli_domain, cli_server,
options, client_domain, hostname):

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
@@ -197,7 +200,7 @@ import socket
import inspect
try:
from six.moves.configparser import RawConfigParser
from ansible.module_utils.six.moves.configparser import RawConfigParser
except ImportError:
from ConfigParser import RawConfigParser
@@ -319,7 +322,7 @@ def main():
if options.domain_name is None and options.servers is not None:
if len(options.servers) > 0:
options.domain_name = options.servers[0][
options.servers[0].find(".")+1:]
options.servers[0].find(".") + 1:]
try:
self = options
@@ -701,7 +704,7 @@ def main():
cli_domain_source = ds.domain_source
logger.debug("will use discovered domain: %s", cli_domain)
client_domain = hostname[hostname.find(".")+1:]
client_domain = hostname[hostname.find(".") + 1:]
if ret in (ipadiscovery.NO_LDAP_SERVER, ipadiscovery.NOT_IPA_SERVER) \
or not ds.server:

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
@@ -131,7 +134,7 @@ def main():
kdc = module.params.get('kdc')
kinit_attempts = module.params.get('kinit_attempts')
client_domain = hostname[hostname.find(".")+1:]
client_domain = hostname[hostname.find(".") + 1:]
host_principal = 'host/%s@%s' % (hostname, realm)
sssd = True

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
__all__ = ["gssapi", "version", "ipadiscovery", "api", "errors", "x509",
"constants", "sysrestore", "certmonger", "certstore",
"delete_persistent_client_session_data", "ScriptError",
@@ -60,7 +63,7 @@ else:
# See ipapython/version.py
IPA_MAJOR, IPA_MINOR, IPA_RELEASE = [int(x) for x in
VERSION.split(".", 2)]
IPA_PYTHON_VERSION = IPA_MAJOR*10000 + IPA_MINOR*100 + IPA_RELEASE
IPA_PYTHON_VERSION = IPA_MAJOR * 10000 + IPA_MINOR * 100 + IPA_RELEASE
else:
IPA_PYTHON_VERSION = NUM_VERSION

View File

@@ -0,0 +1,7 @@
---
# vars/Debian.yml
ipaclient_packages: [ "freeipa-client" ]
# Debian Buster must use python2 as Python interpreter due
# to the way freeipa-client package is defined.
# You must install package python2.7 before executing this role.
ansible_python_interpreter: '/usr/bin/python2'

View File

@@ -0,0 +1,7 @@
# vars/Ubuntu-18.04.yml
---
ipaclient_packages: [ "freeipa-client" ]
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
# to the way python-ipalib package is defined.
# Package python2.7 must be installed before executing this role.
ansible_python_interpreter: '/usr/bin/python2.7'

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
@@ -63,7 +64,6 @@ RETURN = '''
'''
import os
import six
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_ipa_replica import (
@@ -72,6 +72,8 @@ from ansible.module_utils.ansible_ipa_replica import (
gen_remote_api, api
)
from ansible.module_utils import six
if six.PY3:
unicode = str

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,6 +21,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
@@ -194,7 +195,6 @@ RETURN = '''
import os
import tempfile
import traceback
import six
from shutil import copyfile
from ansible.module_utils.basic import AnsibleModule
@@ -209,6 +209,7 @@ from ansible.module_utils.ansible_ipa_replica import (
dns, no_matching_interface_for_ip_address_warning, adtrust,
constants, api, redirect_stdout, replica_conn_check, tasks
)
from ansible.module_utils import six
if six.PY3:
unicode = str

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
@@ -22,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',

Some files were not shown because too many files have changed in this diff Show More