mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-27 05:43:05 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1f882ac52 | ||
|
|
8d99ce4207 | ||
|
|
e3e7d71474 | ||
|
|
285fb6b8e0 | ||
|
|
d2648b142a | ||
|
|
67e192242c | ||
|
|
9eefc1ae7c | ||
|
|
b9d0b35e83 | ||
|
|
85006d611f | ||
|
|
82412ef761 | ||
|
|
2e178e5a38 | ||
|
|
f47d134335 | ||
|
|
541c514aa9 | ||
|
|
85257b9d03 | ||
|
|
e7f902ca48 | ||
|
|
a1bfa608e2 | ||
|
|
eefd94d13f | ||
|
|
667177db07 | ||
|
|
f301ad55aa | ||
|
|
fd9ecc246d | ||
|
|
b15552540c | ||
|
|
25b41b7eca | ||
|
|
b53f2a08d6 | ||
|
|
f2b3e88d5a | ||
|
|
e001ecb1de | ||
|
|
44b3c3003d | ||
|
|
c7a3b26ec4 | ||
|
|
9252284504 | ||
|
|
9665769cfc | ||
|
|
7d02d4d409 | ||
|
|
fb5f59db23 | ||
|
|
90e8098bf4 | ||
|
|
d615d3d4ed | ||
|
|
8ba665213f | ||
|
|
036891d09a | ||
|
|
93a441494d | ||
|
|
77b72af1a2 | ||
|
|
4bfde9b61a | ||
|
|
f113e7071e | ||
|
|
ef9f7fcb84 | ||
|
|
5c5b05a74f | ||
|
|
fa94b14e91 | ||
|
|
cd3646ad67 | ||
|
|
2e8c2f881f | ||
|
|
b88cab07ff | ||
|
|
f406de14e8 | ||
|
|
a86970efe1 | ||
|
|
0af208d271 | ||
|
|
10b16a3bbf | ||
|
|
5c871242a7 | ||
|
|
2f9791f6c5 | ||
|
|
f41104520e | ||
|
|
d81994475e | ||
|
|
612b60766e | ||
|
|
d6eaf91225 | ||
|
|
cb95248ef5 | ||
|
|
ca0aed54b0 | ||
|
|
17bba27abf | ||
|
|
22f31d02f2 | ||
|
|
5e9a2e8c2e | ||
|
|
0757bfee0a | ||
|
|
1eff04a09a | ||
|
|
f43831407b | ||
|
|
17dd8e4ec6 | ||
|
|
0d57d69a99 |
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
30
molecule/centos-9-build/Dockerfile
Normal file
30
molecule/centos-9-build/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
FROM quay.io/centos/centos:stream9
|
||||
ENV container=docker
|
||||
|
||||
RUN rm -fv /var/cache/dnf/metadata_lock.pid; \
|
||||
dnf makecache; \
|
||||
dnf --assumeyes install \
|
||||
/usr/bin/python3 \
|
||||
/usr/bin/python3-config \
|
||||
/usr/bin/dnf-3 \
|
||||
sudo \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
iproute && \
|
||||
dnf clean all; \
|
||||
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
|
||||
rm -f /lib/systemd/system/multi-user.target.wants/*;\
|
||||
rm -f /etc/systemd/system/*.wants/*;\
|
||||
rm -f /lib/systemd/system/local-fs.target.wants/*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
|
||||
rm -f /lib/systemd/system/basic.target.wants/*;\
|
||||
rm -f /lib/systemd/system/anaconda.target.wants/*; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
VOLUME ["/sys/fs/cgroup"]
|
||||
|
||||
CMD ["/usr/sbin/init"]
|
||||
18
molecule/centos-9-build/molecule.yml
Normal file
18
molecule/centos-9-build/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-9-build
|
||||
image: "quay.io/centos/centos:stream9"
|
||||
dockerfile: Dockerfile
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare-build.yml
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# IPA depends on IPv6 and without it dirsrv service won't start.
|
||||
- name: Ensure IPv6 is ENABLED
|
||||
sysctl:
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.name }}"
|
||||
value: "{{ item.value }}"
|
||||
sysctl_set: yes
|
||||
@@ -19,14 +19,14 @@
|
||||
# This is needed in some IPA versions in order to get KRA enabled.
|
||||
# See https://pagure.io/freeipa/issue/7906 for more information.
|
||||
- name: stat protected_regular
|
||||
stat:
|
||||
ansible.builtin.stat:
|
||||
path: /proc/sys/fs/protected_regular
|
||||
register: result
|
||||
|
||||
- name: Ensure fs.protected_regular is disabled
|
||||
sysctl:
|
||||
ansible.posix.sysctl:
|
||||
name: fs.protected_regular
|
||||
value: '0'
|
||||
value: 0
|
||||
sysctl_set: yes
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# To avoid this problem we create the directories before starting IPA.
|
||||
- name: Ensure lock dirs for DS exists
|
||||
file:
|
||||
ansible.builtin.file:
|
||||
state: directory
|
||||
owner: dirsrv
|
||||
group: dirsrv
|
||||
@@ -22,6 +22,6 @@
|
||||
- /var/lock/dirsrv/slapd-TEST-LOCAL/
|
||||
|
||||
- name: Ensure IPA server is up an running
|
||||
service:
|
||||
ansible.builtin.service:
|
||||
name: ipa
|
||||
state: started
|
||||
|
||||
10
playbooks/automember/automember-default-group-not-set.yml
Normal file
10
playbooks/automember/automember-default-group-not-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default group not set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default group is not set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: ""
|
||||
10
playbooks/automember/automember-default-group-set.yml
Normal file
10
playbooks/automember/automember-default-group-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default group set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default group is set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: fallback_group
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default hostgroup not set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default hostgroup is not set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: ""
|
||||
10
playbooks/automember/automember-default-hostgroup-set.yml
Normal file
10
playbooks/automember/automember-default-hostgroup-set.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember default hostgroup set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure automember default hostgroup is set
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: fallback_hostgroup
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember group membership for all users rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure group automember rule admins is present
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automember group membership for given users rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure group membership for given users has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
users:
|
||||
- user1
|
||||
- user2
|
||||
state: rebuilt
|
||||
10
playbooks/automember/automember-group-orphans-removed.yml
Normal file
10
playbooks/automember/automember-group-orphans-removed.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember orphan group rules are removed example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure orphan group rules are removed
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: orphans_removed
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember hostgroup membership for all hosts rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure hostgroup membership for all hosts has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Automember hostgroup membership for given hosts rebuilt example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure hostgroup membership for given hosts has been rebuilt
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- host1.mydomain.com
|
||||
- host2.mydomain.com
|
||||
state: rebuilt
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Automember orphan hostgroup rules are removed example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: Ensure orphan hostgroup rules are removed
|
||||
ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: orphans_removed
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object): # pylint: disable=R0205,R0903
|
||||
DOCUMENTATION = r"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -22,6 +21,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
__all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
|
||||
"DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT",
|
||||
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
|
||||
@@ -100,7 +103,7 @@ else:
|
||||
|
||||
import socket
|
||||
import base64
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
|
||||
try:
|
||||
from collections.abc import Mapping # noqa
|
||||
@@ -397,6 +400,14 @@ else:
|
||||
def module_params_get(module, name):
|
||||
return _afm_convert(module.params.get(name))
|
||||
|
||||
def module_params_get_lowercase(module, name):
|
||||
value = _afm_convert(module.params.get(name))
|
||||
if isinstance(value, list):
|
||||
value = [v.lower() for v in value]
|
||||
if isinstance(value, (str, unicode)):
|
||||
value = value.lower()
|
||||
return value
|
||||
|
||||
def api_get_domain():
|
||||
return api.env.domain
|
||||
|
||||
@@ -408,6 +419,9 @@ else:
|
||||
def api_get_realm():
|
||||
return api.env.realm
|
||||
|
||||
def api_get_basedn():
|
||||
return api.env.basedn
|
||||
|
||||
def gen_add_del_lists(user_list, res_list):
|
||||
"""
|
||||
Generate the lists for the addition and removal of members.
|
||||
@@ -555,10 +569,76 @@ else:
|
||||
print(jsonify(kwargs))
|
||||
sys.exit(0)
|
||||
|
||||
class AnsibleFreeIPAParams(Mapping):
|
||||
def __init__(self, ansible_module):
|
||||
class IPAParamMapping(Mapping):
|
||||
"""
|
||||
Provides IPA API mapping to playbook parameters or computed values.
|
||||
|
||||
It can be used to define a mapping of playbook parameters
|
||||
or methods that provide computed values to IPA API arguments.
|
||||
|
||||
Playbook parameters can be retrieved as properties,
|
||||
and the set of IPA arguments for a command can be
|
||||
retrived with ``get_ipa_command_args()``. The keys for
|
||||
``param_mapping`` are also the keys of the argument set.
|
||||
|
||||
The values of ``param_mapping`` can be either:
|
||||
* a str representing a key of ``AnsibleModule.params``.
|
||||
* a callable.
|
||||
|
||||
In case of an ``AnsibleModule.param`` the value of the playbook
|
||||
param will be used for that argument. If it is a ``callable``,
|
||||
the value returned by the execution of it will be used.
|
||||
|
||||
Example:
|
||||
-------
|
||||
def check_params(ipa_params):
|
||||
# Module parameters can be accessed as properties.
|
||||
if len(ipa_params.name) == 0:
|
||||
ipa_params.ansible_module.fail_json(msg="No given name.")
|
||||
|
||||
|
||||
def define_ipa_commands(self):
|
||||
# Create the argument dict from the defined mapping.
|
||||
args = self.get_ipa_command_args()
|
||||
|
||||
_commands = [("obj-name", "some_ipa_command", args)]
|
||||
return _commands
|
||||
|
||||
|
||||
def a_method_for_a_computed_param():
|
||||
return "Some computed value"
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = SomeIPAModule(argument_spec=dict(
|
||||
name=dict(type="list", aliases=["cn"], required=True),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
module_param=(type="str", required=False),
|
||||
)
|
||||
)
|
||||
|
||||
# Define the playbook to IPA API mapping
|
||||
ipa_param_mapping = {
|
||||
"arg_to_be_passed_to_ipa_command": "module_param",
|
||||
"another_arg": a_method_for_a_computed_param,
|
||||
}
|
||||
ipa_params = IPAParamMapping(
|
||||
ansible_module,
|
||||
param_mapping=ipa_param_mapping
|
||||
)
|
||||
|
||||
check_params(ipa_params)
|
||||
comands = define_ipa_commands(ipa_params)
|
||||
|
||||
ansible_module.execute_ipa_commands(commands)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ansible_module, param_mapping=None):
|
||||
self.mapping = ansible_module.params
|
||||
self.ansible_module = ansible_module
|
||||
self.param_mapping = param_mapping or {}
|
||||
|
||||
def __getitem__(self, key):
|
||||
param = self.mapping[key]
|
||||
@@ -579,6 +659,36 @@ else:
|
||||
def __getattr__(self, name):
|
||||
return self.get(name)
|
||||
|
||||
def get_ipa_command_args(self, **kwargs):
|
||||
"""Return a dict to be passed to an IPA command."""
|
||||
args = {}
|
||||
for ipa_param_name, param_name in self.param_mapping.items():
|
||||
|
||||
# Check if param_name is actually a param
|
||||
if param_name in self.ansible_module.params:
|
||||
value = self.ansible_module.params_get(param_name)
|
||||
if isinstance(value, bool):
|
||||
value = "TRUE" if value else "FALSE"
|
||||
|
||||
# Since param wasn't a param check if it's a method name
|
||||
elif callable(param_name):
|
||||
value = param_name(**kwargs)
|
||||
|
||||
# We don't have a way to guess the value so fail.
|
||||
else:
|
||||
self.ansible_module.fail_json(
|
||||
msg=(
|
||||
"Couldn't get a value for '%s'. Option '%s' is "
|
||||
"not a module argument neither a defined method."
|
||||
)
|
||||
% (ipa_param_name, param_name)
|
||||
)
|
||||
|
||||
if value is not None:
|
||||
args[ipa_param_name] = value
|
||||
|
||||
return args
|
||||
|
||||
class IPAAnsibleModule(AnsibleModule):
|
||||
"""
|
||||
IPA Ansible Module.
|
||||
@@ -699,6 +809,18 @@ else:
|
||||
"""
|
||||
return module_params_get(self, name)
|
||||
|
||||
def params_get_lowercase(self, name):
|
||||
"""
|
||||
Retrieve value set for module parameter as lowercase, if not None.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: string
|
||||
The name of the parameter to retrieve.
|
||||
|
||||
"""
|
||||
return module_params_get_lowercase(self, name)
|
||||
|
||||
def params_fail_used_invalid(self, invalid_params, state, action=None):
|
||||
"""
|
||||
Fail module execution if one of the invalid parameters is not None.
|
||||
@@ -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()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Writing a new Ansible FreeIPA module
|
||||
|
||||
## Minimum requirements
|
||||
A ansible-freeipa module should have:
|
||||
|
||||
* Code:
|
||||
@@ -13,68 +12,4 @@ A ansible-freeipa module should have:
|
||||
* Tests:
|
||||
* Test cases (also playbooks) defined in `tests/<module_name>/test_<something>.yml`. It's ok to have multiple files in this directory.
|
||||
|
||||
## Code
|
||||
|
||||
The module file have to start with the python shebang line, license header and definition of the constants `ANSIBLE_METADATA`, `DOCUMENTATION`, `EXAMPLES` and `RETURNS`. Those constants need to be defined before the code (even imports). See https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#starting-a-new-module for more information.
|
||||
|
||||
|
||||
Although it's use is not yet required, ansible-freeipa provides `FreeIPABaseModule` as a helper class for the implementation of new modules. See the example bellow:
|
||||
|
||||
```python
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import FreeIPABaseModule
|
||||
|
||||
|
||||
class SomeIPAModule(FreeIPABaseModule):
|
||||
ipa_param_mapping = {
|
||||
"arg_to_be_passed_to_ipa_command": "module_param",
|
||||
"another_arg": "get_another_module_param",
|
||||
}
|
||||
|
||||
def get_another_module_param(self):
|
||||
another_module_param = self.ipa_params.another_module_param
|
||||
|
||||
# Validate or modify another_module_param ...
|
||||
|
||||
return another_module_param
|
||||
|
||||
def check_ipa_params(self):
|
||||
|
||||
# Validate your params here ...
|
||||
|
||||
# Example:
|
||||
if not self.ipa_params.module_param in VALID_OPTIONS:
|
||||
self.fail_json(msg="Invalid value for argument module_param")
|
||||
|
||||
def define_ipa_commands(self):
|
||||
args = self.get_ipa_command_args()
|
||||
|
||||
self.add_ipa_command("some_ipa_command", name="obj-name", args=args)
|
||||
|
||||
|
||||
def main():
|
||||
ipa_module = SomeIPAModule(argument_spec=dict(
|
||||
module_param=dict(type="str", default=None, required=False),
|
||||
another_module_param=dict(type="str", default=None, required=False),
|
||||
))
|
||||
ipa_module.ipa_run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In the example above, the module will call the command `some_ipa_command`, using "obj-name" as name and, `arg_to_be_passed_to_ipa_command` and `another_arg` as arguments.
|
||||
|
||||
The values of the arguments will be determined by the class attribute `ipa_param_mapping`.
|
||||
|
||||
In the case of `arg_to_be_passed_to_ipa_command` the key (`module_param`) is defined in the module `argument_specs` so the value of the argument is actually used.
|
||||
|
||||
On the other hand, `another_arg` as mapped to something else: a callable method. In this case the method will be called and it's result used as value for `another_arg`.
|
||||
|
||||
**NOTE**: Keep mind that to take advantage of the parameters mapping defined in `ipa_param_mapping` you will have to call `args = self.get_ipa_command_args()` and use `args` in your command. There is no implicit call of this method.
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
The `FreeIPABaseModule` is new and might not be suitable to all cases and every module yet. In case you need to extend it's functionality for a new module please open an issue or PR and we'll be happy to discuss it.
|
||||
Use the script `utils/new_module` to create the stub files for a new module.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -21,6 +20,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
@@ -77,6 +79,20 @@ options:
|
||||
description: The expression of the regex
|
||||
type: str
|
||||
required: true
|
||||
users:
|
||||
description: Users to rebuild membership for.
|
||||
type: list
|
||||
required: false
|
||||
hosts:
|
||||
description: Hosts to rebuild membership for.
|
||||
type: list
|
||||
required: false
|
||||
no_wait:
|
||||
description: Don't wait for rebuilding membership.
|
||||
type: bool
|
||||
default_group:
|
||||
description: Default (fallback) group for all unmatched entries.
|
||||
type: str
|
||||
action:
|
||||
description: Work on automember or member level
|
||||
default: automember
|
||||
@@ -84,10 +100,11 @@ options:
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "rebuilt", "orphans_removed"]
|
||||
author:
|
||||
- Mark Hahl
|
||||
- Jake Reynolds
|
||||
- Thomas Woerner
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
@@ -100,7 +117,7 @@ EXAMPLES = """
|
||||
state: present
|
||||
inclusive:
|
||||
- key: "mail"
|
||||
expression: "example.com$
|
||||
expression: "example.com"
|
||||
|
||||
# Delete an automember rule
|
||||
- ipaautomember:
|
||||
@@ -114,12 +131,78 @@ EXAMPLES = """
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "My domain hosts"
|
||||
automember_tye: hostgroup
|
||||
automember_type: hostgroup
|
||||
action: member
|
||||
inclusive:
|
||||
- key: fqdn
|
||||
expression: ".*.mydomain.com"
|
||||
|
||||
# Ensure group membership for all users has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: rebuilt
|
||||
|
||||
# Ensure group membership for given users has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
users:
|
||||
- user1
|
||||
- user2
|
||||
state: rebuilt
|
||||
|
||||
# Ensure hostgroup membership for all hosts has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: rebuilt
|
||||
|
||||
# Ensure hostgroup membership for given hosts has been rebuilt
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- host1.mydomain.com
|
||||
- host2.mydomain.com
|
||||
state: rebuilt
|
||||
|
||||
# Ensure default group fallback_group for all unmatched group entries is set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
default_group: fallback_group
|
||||
|
||||
# Ensure default group for all unmatched group entries is not set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
default_group: ""
|
||||
automember_type: group
|
||||
state: absent
|
||||
|
||||
# Ensure default hostgroup fallback_hostgroup for all unmatched group entries
|
||||
# is set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: fallback_hostgroup
|
||||
|
||||
# Ensure default hostgroup for all unmatched group entries is not set
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
default_group: ""
|
||||
state: absent
|
||||
|
||||
# Example playbook to ensure all orphan automember group rules are removed:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: group
|
||||
state: orphans_removed
|
||||
|
||||
# Example playbook to ensure all orphan automember hostgroup rules are removed:
|
||||
- ipaautomember:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
automember_type: hostgroup
|
||||
state: orphans_removed
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -127,14 +210,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors, DN
|
||||
)
|
||||
|
||||
|
||||
def find_automember(module, name, grouping):
|
||||
def find_automember(module, name, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": grouping
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -144,13 +227,40 @@ def find_automember(module, name, grouping):
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_condition_args(grouping,
|
||||
def find_automember_orphans(module, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
_result = module.ipa_command_no_name("automember_find_orphans", _args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return _result
|
||||
|
||||
|
||||
def find_automember_default_group(module, automember_type):
|
||||
_args = {
|
||||
"all": True,
|
||||
"type": automember_type
|
||||
}
|
||||
|
||||
try:
|
||||
_result = module.ipa_command_no_name("automember_default_group_show",
|
||||
_args)
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_condition_args(automember_type,
|
||||
key,
|
||||
inclusiveregex=None,
|
||||
exclusiveregex=None):
|
||||
_args = {}
|
||||
if grouping is not None:
|
||||
_args['type'] = grouping
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
if key is not None:
|
||||
_args['key'] = key
|
||||
if inclusiveregex is not None:
|
||||
@@ -161,13 +271,23 @@ def gen_condition_args(grouping,
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args(description, grouping):
|
||||
def gen_rebuild_args(automember_type, rebuild_users, rebuild_hosts, no_wait):
|
||||
_args = {"no_wait": no_wait}
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
if rebuild_users is not None:
|
||||
_args["users"] = rebuild_users
|
||||
if rebuild_hosts is not None:
|
||||
_args["hosts"] = rebuild_hosts
|
||||
return _args
|
||||
|
||||
|
||||
def gen_args(description, automember_type):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if grouping is not None:
|
||||
_args['type'] = grouping
|
||||
|
||||
if automember_type is not None:
|
||||
_args['type'] = automember_type
|
||||
return _args
|
||||
|
||||
|
||||
@@ -192,28 +312,35 @@ def main():
|
||||
argument_spec=dict(
|
||||
# general
|
||||
inclusive=dict(type="list",
|
||||
aliases=["automemberinclusiveregex"], default=None,
|
||||
aliases=["automemberinclusiveregex"],
|
||||
default=None,
|
||||
options=dict(
|
||||
key=dict(type="str", required=True),
|
||||
expression=dict(type="str", required=True)
|
||||
),
|
||||
elements="dict", required=False),
|
||||
exclusive=dict(type="list", aliases=[
|
||||
"automemberexclusiveregex"], default=None,
|
||||
elements="dict",
|
||||
required=False),
|
||||
exclusive=dict(type="list",
|
||||
aliases=["automemberexclusiveregex"],
|
||||
default=None,
|
||||
options=dict(
|
||||
key=dict(type="str", required=True),
|
||||
expression=dict(type="str", required=True)
|
||||
),
|
||||
elements="dict", required=False),
|
||||
elements="dict",
|
||||
required=False),
|
||||
name=dict(type="list", aliases=["cn"],
|
||||
default=None, required=True),
|
||||
default=None, required=False),
|
||||
description=dict(type="str", default=None),
|
||||
automember_type=dict(type='str', required=False,
|
||||
choices=['group', 'hostgroup']),
|
||||
no_wait=dict(type="bool", default=None),
|
||||
default_group=dict(type="str", default=None),
|
||||
action=dict(type="str", default="automember",
|
||||
choices=["member", "automember"]),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "rebuild"]),
|
||||
choices=["present", "absent", "rebuilt",
|
||||
"orphans_removed"]),
|
||||
users=dict(type="list", default=None),
|
||||
hosts=dict(type="list", default=None),
|
||||
),
|
||||
@@ -226,6 +353,8 @@ def main():
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
if names is None:
|
||||
names = []
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
@@ -234,6 +363,12 @@ def main():
|
||||
inclusive = ansible_module.params_get("inclusive")
|
||||
exclusive = ansible_module.params_get("exclusive")
|
||||
|
||||
# no_wait for rebuilt
|
||||
no_wait = ansible_module.params_get("no_wait")
|
||||
|
||||
# default_group
|
||||
default_group = ansible_module.params_get("default_group")
|
||||
|
||||
# action
|
||||
action = ansible_module.params_get("action")
|
||||
# state
|
||||
@@ -248,12 +383,51 @@ def main():
|
||||
# Check parameters
|
||||
invalid = []
|
||||
|
||||
if state != "rebuild":
|
||||
invalid = ["rebuild_hosts", "rebuild_users"]
|
||||
if state in ["rebuilt", "orphans_removed"]:
|
||||
invalid = ["name", "description", "exclusive", "inclusive",
|
||||
"default_group"]
|
||||
|
||||
if not automember_type and state != "rebuild":
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required unless state: rebuild")
|
||||
msg="'action=member' is not usable with state '%s'" % state)
|
||||
|
||||
if state == "rebuilt":
|
||||
if automember_type == "group" and rebuild_hosts is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="state %s: hosts can not be set when type is '%s'" %
|
||||
(state, automember_type))
|
||||
if automember_type == "hostgroup" and rebuild_users is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="state %s: users can not be set when type is '%s'" %
|
||||
(state, automember_type))
|
||||
|
||||
elif state == "orphans_removed":
|
||||
invalid.extend(["users", "hosts"])
|
||||
|
||||
if not automember_type:
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required unless state: rebuilt")
|
||||
|
||||
else:
|
||||
if default_group is not None:
|
||||
for param in ["name", "exclusive", "inclusive", "users", "hosts"
|
||||
"no_wait"]:
|
||||
if ansible_module.params.get(param) is not None:
|
||||
msg = "Cannot use {0} together with default_group"
|
||||
ansible_module.fail_json(msg=msg.format(param))
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot use default_group with action:member")
|
||||
if state == "absent":
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot use default_group with state:absent")
|
||||
|
||||
else:
|
||||
invalid = ["users", "hosts", "no_wait"]
|
||||
|
||||
if not automember_type:
|
||||
ansible_module.fail_json(
|
||||
msg="'automember_type' is required.")
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
@@ -307,15 +481,21 @@ def main():
|
||||
commands.append([name, 'automember_add', args])
|
||||
res_find = {}
|
||||
|
||||
inclusive_add, inclusive_del = gen_add_del_lists(
|
||||
transform_conditions(inclusive or []),
|
||||
res_find.get("automemberinclusiveregex", [])
|
||||
)
|
||||
if inclusive is not None:
|
||||
inclusive_add, inclusive_del = gen_add_del_lists(
|
||||
transform_conditions(inclusive),
|
||||
res_find.get("automemberinclusiveregex", [])
|
||||
)
|
||||
else:
|
||||
inclusive_add, inclusive_del = [], []
|
||||
|
||||
exclusive_add, exclusive_del = gen_add_del_lists(
|
||||
transform_conditions(exclusive or []),
|
||||
res_find.get("automemberexclusiveregex", [])
|
||||
)
|
||||
if exclusive is not None:
|
||||
exclusive_add, exclusive_del = gen_add_del_lists(
|
||||
transform_conditions(exclusive),
|
||||
res_find.get("automemberexclusiveregex", [])
|
||||
)
|
||||
else:
|
||||
exclusive_add, exclusive_del = [], []
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -384,16 +564,45 @@ def main():
|
||||
'automember_remove_condition',
|
||||
condition_args])
|
||||
|
||||
elif state == "rebuild":
|
||||
if automember_type:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"type": automember_type}])
|
||||
if rebuild_users:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"users": rebuild_users}])
|
||||
if rebuild_hosts:
|
||||
commands.append([None, 'automember_rebuild',
|
||||
{"hosts": rebuild_hosts}])
|
||||
if len(names) == 0:
|
||||
if state == "rebuilt":
|
||||
args = gen_rebuild_args(automember_type, rebuild_users,
|
||||
rebuild_hosts, no_wait)
|
||||
commands.append([None, 'automember_rebuild', args])
|
||||
|
||||
elif state == "orphans_removed":
|
||||
res_find = find_automember_orphans(ansible_module,
|
||||
automember_type)
|
||||
if res_find["count"] > 0:
|
||||
commands.append([None, 'automember_find_orphans',
|
||||
{'type': automember_type,
|
||||
'remove': True}])
|
||||
|
||||
elif default_group is not None and state == "present":
|
||||
res_find = find_automember_default_group(ansible_module,
|
||||
automember_type)
|
||||
|
||||
if default_group == "":
|
||||
if isinstance(res_find["automemberdefaultgroup"], list):
|
||||
commands.append([None,
|
||||
'automember_default_group_remove',
|
||||
{'type': automember_type}])
|
||||
ansible_module.warn("commands: %s" % repr(commands))
|
||||
|
||||
else:
|
||||
dn_default_group = [DN(('cn', default_group),
|
||||
('cn', '%ss' % automember_type),
|
||||
('cn', 'accounts'),
|
||||
ansible_module.ipa_get_basedn())]
|
||||
if repr(res_find["automemberdefaultgroup"]) != \
|
||||
repr(dn_default_group):
|
||||
commands.append(
|
||||
[None, 'automember_default_group_set',
|
||||
{'type': automember_type,
|
||||
'automemberdefaultgroup': default_group}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Invalid operation")
|
||||
|
||||
# Execute commands
|
||||
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -296,7 +299,7 @@ def main():
|
||||
"KDC:Disable Last Success",
|
||||
"KDC:Disable Lockout",
|
||||
"KDC:Disable Default Preauth for SPNs",
|
||||
""]), # noqa E128
|
||||
""]), # noqa E128
|
||||
selinuxusermaporder=dict(type="list", required=False,
|
||||
aliases=['ipaselinuxusermaporder']),
|
||||
selinuxusermapdefault=dict(type="str", required=False,
|
||||
@@ -362,15 +365,15 @@ def main():
|
||||
|
||||
if params.get("ipadomainresolutionorder", None):
|
||||
params["ipadomainresolutionorder"] = \
|
||||
":".join(params["ipadomainresolutionorder"])
|
||||
":".join(params["ipadomainresolutionorder"])
|
||||
|
||||
if params.get("ipausersearchfields", None):
|
||||
params["ipausersearchfields"] = \
|
||||
",".join(params["ipausersearchfields"])
|
||||
",".join(params["ipausersearchfields"])
|
||||
|
||||
if params.get("ipagroupsearchfields", None):
|
||||
params["ipagroupsearchfields"] = \
|
||||
",".join(params["ipagroupsearchfields"])
|
||||
",".join(params["ipagroupsearchfields"])
|
||||
|
||||
# verify limits on INT values.
|
||||
args_with_limits = [
|
||||
@@ -415,12 +418,12 @@ def main():
|
||||
if ansible_module.argument_spec.get(k):
|
||||
arg_type = ansible_module.argument_spec[k]['type']
|
||||
if k in (
|
||||
'ipaselinuxusermaporder', 'domain_resolution_order'
|
||||
):
|
||||
'ipaselinuxusermaporder', 'domain_resolution_order'
|
||||
):
|
||||
exit_args[k] = result.get(key)[0].split('$')
|
||||
elif k in (
|
||||
'usersearch', 'groupsearch'
|
||||
):
|
||||
'usersearch', 'groupsearch'
|
||||
):
|
||||
exit_args[k] = result.get(key)[0].split(',')
|
||||
elif isinstance(value, str) and arg_type == "list":
|
||||
exit_args[k] = [value]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -21,6 +20,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
"""DNS Record ansible-freeipa module."""
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
@@ -866,8 +869,7 @@ from ansible.module_utils.ansible_freeipa_module import \
|
||||
import dns.reversename
|
||||
import dns.resolver
|
||||
|
||||
import six
|
||||
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -1106,12 +1108,13 @@ def configure_module():
|
||||
name=dict(type="list", aliases=["record_name"], default=None,
|
||||
required=False),
|
||||
|
||||
records=dict(type="list", default=None,
|
||||
records=dict(type="list",
|
||||
default=None,
|
||||
options=dict(
|
||||
# Here name is a simple string
|
||||
name=dict(type='str', required=True,
|
||||
aliases=['record_name']),
|
||||
**record_spec),
|
||||
# Here name is a simple string
|
||||
name=dict(type='str', required=True,
|
||||
aliases=['record_name']),
|
||||
**record_spec),
|
||||
),
|
||||
|
||||
# general
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -200,45 +203,55 @@ dnszone:
|
||||
|
||||
from ipapython.dnsutil import DNSName # noqa: E402
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
FreeIPABaseModule,
|
||||
IPAAnsibleModule,
|
||||
is_ip_address,
|
||||
is_ip_network_address,
|
||||
is_valid_port,
|
||||
ipalib_errors
|
||||
ipalib_errors,
|
||||
compare_args_ipa,
|
||||
IPAParamMapping,
|
||||
) # noqa: E402
|
||||
import netaddr
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
class DNSZoneModule(FreeIPABaseModule):
|
||||
class DNSZoneModule(IPAAnsibleModule):
|
||||
|
||||
ipa_param_mapping = {
|
||||
# Direct Mapping
|
||||
"idnsforwardpolicy": "forward_policy",
|
||||
"idnssoarefresh": "refresh",
|
||||
"idnssoaretry": "retry",
|
||||
"idnssoaexpire": "expire",
|
||||
"idnssoaminimum": "minimum",
|
||||
"dnsttl": "ttl",
|
||||
"dnsdefaultttl": "default_ttl",
|
||||
"idnsallowsyncptr": "allow_sync_ptr",
|
||||
"idnsallowdynupdate": "dynamic_update",
|
||||
"idnssecinlinesigning": "dnssec",
|
||||
"idnsupdatepolicy": "update_policy",
|
||||
# Mapping by method
|
||||
"idnsforwarders": "get_ipa_idnsforwarders",
|
||||
"idnsallowtransfer": "get_ipa_idnsallowtransfer",
|
||||
"idnsallowquery": "get_ipa_idnsallowquery",
|
||||
"idnssoamname": "get_ipa_idnssoamname",
|
||||
"idnssoarname": "get_ipa_idnssoarname",
|
||||
"skip_nameserver_check": "get_ipa_skip_nameserver_check",
|
||||
"skip_overlap_check": "get_ipa_skip_overlap_check",
|
||||
"nsec3paramrecord": "get_ipa_nsec3paramrecord",
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(DNSZoneModule, self).__init__(*args, **kwargs)
|
||||
|
||||
ipa_param_mapping = {
|
||||
# Direct Mapping
|
||||
"idnsforwardpolicy": "forward_policy",
|
||||
"idnssoarefresh": "refresh",
|
||||
"idnssoaretry": "retry",
|
||||
"idnssoaexpire": "expire",
|
||||
"idnssoaminimum": "minimum",
|
||||
"dnsttl": "ttl",
|
||||
"dnsdefaultttl": "default_ttl",
|
||||
"idnsallowsyncptr": "allow_sync_ptr",
|
||||
"idnsallowdynupdate": "dynamic_update",
|
||||
"idnssecinlinesigning": "dnssec",
|
||||
"idnsupdatepolicy": "update_policy",
|
||||
# Mapping by method
|
||||
"idnsforwarders": self.get_ipa_idnsforwarders,
|
||||
"idnsallowtransfer": self.get_ipa_idnsallowtransfer,
|
||||
"idnsallowquery": self.get_ipa_idnsallowquery,
|
||||
"idnssoamname": self.get_ipa_idnssoamname,
|
||||
"idnssoarname": self.get_ipa_idnssoarname,
|
||||
"skip_nameserver_check": self.get_ipa_skip_nameserver_check,
|
||||
"skip_overlap_check": self.get_ipa_skip_overlap_check,
|
||||
"nsec3paramrecord": self.get_ipa_nsec3paramrecord,
|
||||
}
|
||||
|
||||
self.commands = []
|
||||
self.ipa_params = IPAParamMapping(self, ipa_param_mapping)
|
||||
self.exit_args = {}
|
||||
|
||||
def validate_ips(self, ips, error_msg):
|
||||
invalid_ips = [
|
||||
@@ -438,39 +451,34 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
for zone_name in self.get_zone_names():
|
||||
# Look for existing zone in IPA
|
||||
zone, is_zone_active = self.get_zone(zone_name)
|
||||
args = self.get_ipa_command_args(zone=zone)
|
||||
args = self.ipa_params.get_ipa_command_args(zone=zone)
|
||||
|
||||
if self.ipa_params.state in ["present", "enabled", "disabled"]:
|
||||
if not zone:
|
||||
# Since the zone doesn't exist we just create it
|
||||
# with given args
|
||||
self.add_ipa_command("dnszone_add", zone_name, args)
|
||||
self.commands.append((zone_name, "dnszone_add", args))
|
||||
is_zone_active = True
|
||||
# just_added = True
|
||||
|
||||
else:
|
||||
# Zone already exist so we need to verify if given args
|
||||
# matches the current config. If not we updated it.
|
||||
if self.require_ipa_attrs_change(args, zone):
|
||||
self.add_ipa_command("dnszone_mod", zone_name, args)
|
||||
if not compare_args_ipa(self, args, zone):
|
||||
self.commands.append((zone_name, "dnszone_mod", args))
|
||||
|
||||
if self.ipa_params.state == "enabled" and not is_zone_active:
|
||||
self.add_ipa_command("dnszone_enable", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_enable", {}))
|
||||
|
||||
if self.ipa_params.state == "disabled" and is_zone_active:
|
||||
self.add_ipa_command("dnszone_disable", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_disable", {}))
|
||||
|
||||
if self.ipa_params.state == "absent" and zone is not None:
|
||||
self.add_ipa_command("dnszone_del", zone_name)
|
||||
self.commands.append((zone_name, "dnszone_del", {}))
|
||||
|
||||
def process_command_result(self, name, command, args, result):
|
||||
# pylint: disable=super-with-arguments
|
||||
super(DNSZoneModule, self).process_command_result(
|
||||
name, command, args, result
|
||||
)
|
||||
def process_results(self, _result, command, name, _args, exit_args):
|
||||
if command == "dnszone_add" and self.ipa_params.name_from_ip:
|
||||
dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
|
||||
dnszone_exit_args['name'] = name
|
||||
exit_args.setdefault('dnszone', {})["name"] = name
|
||||
|
||||
|
||||
def get_argument_spec():
|
||||
@@ -529,12 +537,24 @@ def get_argument_spec():
|
||||
|
||||
|
||||
def main():
|
||||
DNSZoneModule(
|
||||
ansible_module = DNSZoneModule(
|
||||
argument_spec=get_argument_spec(),
|
||||
mutually_exclusive=[["name", "name_from_ip"]],
|
||||
required_one_of=[["name", "name_from_ip"]],
|
||||
supports_check_mode=True,
|
||||
).ipa_run()
|
||||
)
|
||||
|
||||
exit_args = {}
|
||||
ipaapi_context = ansible_module.params_get("ipaapi_context")
|
||||
with ansible_module.ipa_connect(context=ipaapi_context):
|
||||
ansible_module.check_ipa_params()
|
||||
ansible_module.define_ipa_commands()
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
ansible_module.commands,
|
||||
result_handler=DNSZoneModule.process_results,
|
||||
exit_args=exit_args
|
||||
)
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -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:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -403,7 +406,7 @@ host:
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
|
||||
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors
|
||||
import six
|
||||
from ansible.module_utils import six
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
@@ -436,7 +439,7 @@ def find_dnsrecord(module, name):
|
||||
This function may raise ipalib_errors.NotFound in some cases,
|
||||
and it should be handled by the caller.
|
||||
"""
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
_args = {
|
||||
@@ -1228,7 +1231,7 @@ def main():
|
||||
}])
|
||||
|
||||
if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
_args = {"idnsname": host_name}
|
||||
@@ -1245,7 +1248,7 @@ def main():
|
||||
"dnsrecord_add", _args])
|
||||
|
||||
if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
|
||||
# There seems to be an issue with dnsrecord_del (not
|
||||
@@ -1361,7 +1364,7 @@ def main():
|
||||
|
||||
if "arecord" in dnsrecord_args or \
|
||||
"aaaarecord" in dnsrecord_args:
|
||||
domain_name = name[name.find(".")+1:]
|
||||
domain_name = name[name.find(".") + 1:]
|
||||
host_name = name[:name.find(".")]
|
||||
dnsrecord_args["idnsname"] = host_name
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
"""ansible-freeipa module to manage FreeIPA privileges."""
|
||||
|
||||
|
||||
@@ -108,8 +111,9 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
|
||||
import six
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -126,22 +130,6 @@ def find_privilege(module, name):
|
||||
return _result["result"]
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
for failed_item in result.get("failed", []):
|
||||
failed = result["failed"][failed_item]
|
||||
for member_type in failed:
|
||||
for member, failure in failed[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
@@ -230,47 +218,31 @@ def main():
|
||||
if action == "privilege":
|
||||
# Found the privilege
|
||||
if res_find is not None:
|
||||
res_cmp = {
|
||||
k: v for k, v in res_find.items()
|
||||
if k not in [
|
||||
"objectclass", "cn", "dn",
|
||||
"memberof_permisssion"
|
||||
]
|
||||
}
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if args and not compare_args_ipa(ansible_module, args,
|
||||
res_cmp):
|
||||
cmp = {"description": res_find.get("description")}
|
||||
if not compare_args_ipa(ansible_module, args, cmp):
|
||||
commands.append([name, "privilege_mod", args])
|
||||
else:
|
||||
commands.append([name, "privilege_add", args])
|
||||
res_find = {}
|
||||
|
||||
member_args = {}
|
||||
if permission:
|
||||
member_args['permission'] = permission
|
||||
# Generate addition and removal lists
|
||||
permission_add, permission_del = gen_add_del_lists(
|
||||
permission, res_find.get("memberof_permission")
|
||||
)
|
||||
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
|
||||
# Generate addition and removal lists
|
||||
permission_add, permission_del = gen_add_del_lists(
|
||||
permission, res_find.get("member_permission"))
|
||||
|
||||
# Add members
|
||||
if len(permission_add) > 0:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{
|
||||
"permission": permission_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(permission_del) > 0:
|
||||
commands.append([
|
||||
name,
|
||||
"privilege_remove_permission",
|
||||
{"permission": permission_del}
|
||||
])
|
||||
# Add members
|
||||
if len(permission_add) > 0:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{
|
||||
"permission": permission_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(permission_del) > 0:
|
||||
commands.append([
|
||||
name,
|
||||
"privilege_remove_permission",
|
||||
{"permission": permission_del}
|
||||
])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
@@ -280,8 +252,11 @@ def main():
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{"permission": permission}])
|
||||
permission = gen_add_list(
|
||||
permission, res_find.get("memberof_permission"))
|
||||
if permission:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{"permission": permission}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "privilege":
|
||||
@@ -296,10 +271,11 @@ def main():
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_remove_permission",
|
||||
{
|
||||
"permission": permission,
|
||||
}])
|
||||
permission = gen_intersection_list(
|
||||
permission, res_find.get("memberof_permission"))
|
||||
if permission:
|
||||
commands.append([name, "privilege_remove_permission",
|
||||
{"permission": permission}])
|
||||
|
||||
elif state == "renamed":
|
||||
if not rename:
|
||||
@@ -318,7 +294,8 @@ def main():
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""ansible-freeipa iparole module implementation."""
|
||||
|
||||
@@ -21,6 +20,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -183,21 +186,23 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT
|
||||
ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH
|
||||
VkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzM
|
||||
LJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIT
|
||||
oTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s
|
||||
4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpc
|
||||
xj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1
|
||||
UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+Q
|
||||
eNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs
|
||||
5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqic
|
||||
uPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH
|
||||
2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6no
|
||||
obyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC
|
||||
/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq
|
||||
- >
|
||||
MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8x
|
||||
DTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVG
|
||||
FYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEr
|
||||
aELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmj
|
||||
fMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQI
|
||||
Nt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJ
|
||||
EkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMB
|
||||
AAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV
|
||||
0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJP
|
||||
rp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfw
|
||||
Q5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHo
|
||||
VOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+s
|
||||
KC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA
|
||||
5yqV7MVq
|
||||
action: member
|
||||
state: present
|
||||
|
||||
@@ -224,7 +229,8 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, encode_certificate, \
|
||||
gen_add_del_lists, ipalib_errors
|
||||
gen_add_del_lists, gen_add_list, gen_intersection_list, ipalib_errors, \
|
||||
api_get_realm, to_text
|
||||
|
||||
|
||||
def find_service(module, name):
|
||||
@@ -283,9 +289,7 @@ def gen_args_smb(netbiosname, ok_as_delegate, ok_to_auth_as_delegate):
|
||||
return _args
|
||||
|
||||
|
||||
def check_parameters(module, state, action, names, parameters):
|
||||
assert isinstance(parameters, dict)
|
||||
|
||||
def check_parameters(module, state, action, names):
|
||||
# invalid parameters for everything but state 'present', action 'service'.
|
||||
invalid = ['pac_type', 'auth_ind', 'skip_host_check',
|
||||
'force', 'requires_pre_auth', 'ok_as_delegate',
|
||||
@@ -307,8 +311,8 @@ def check_parameters(module, state, action, names, parameters):
|
||||
invalid = ['delete_continue']
|
||||
|
||||
if (
|
||||
not parameters.get('smb', False)
|
||||
and parameters.get('netbiosname')
|
||||
not module.params_get('smb')
|
||||
and module.params_get('netbiosname')
|
||||
):
|
||||
module.fail_json(
|
||||
msg="Argument 'netbiosname' can not be used without "
|
||||
@@ -405,23 +409,6 @@ def init_ansible_module():
|
||||
return ansible_module
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, errors):
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
if "failed" in result and len(result["failed"]) > 0:
|
||||
for item in result["failed"]:
|
||||
failed_item = result["failed"][item]
|
||||
for member_type in failed_item:
|
||||
for member, failure in failed_item[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = init_ansible_module()
|
||||
|
||||
@@ -447,23 +434,6 @@ def main():
|
||||
|
||||
host = ansible_module.params_get("host")
|
||||
|
||||
allow_create_keytab_user = ansible_module.params_get(
|
||||
"allow_create_keytab_user")
|
||||
allow_create_keytab_group = ansible_module.params_get(
|
||||
"allow_create_keytab_group")
|
||||
allow_create_keytab_host = ansible_module.params_get(
|
||||
"allow_create_keytab_host")
|
||||
allow_create_keytab_hostgroup = ansible_module.params_get(
|
||||
"allow_create_keytab_hostgroup")
|
||||
|
||||
allow_retrieve_keytab_user = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_user")
|
||||
allow_retrieve_keytab_group = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_group")
|
||||
allow_retrieve_keytab_host = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_host")
|
||||
allow_retrieve_keytab_hostgroup = ansible_module.params_get(
|
||||
"allow_retrieve_keytab_hostgroup")
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# action
|
||||
@@ -472,7 +442,7 @@ def main():
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# check parameters
|
||||
check_parameters(ansible_module, state, action, names, vars())
|
||||
check_parameters(ansible_module, state, action, names)
|
||||
|
||||
# Init
|
||||
|
||||
@@ -489,9 +459,48 @@ def main():
|
||||
msg="Skipping host check is not supported by your IPA version")
|
||||
|
||||
commands = []
|
||||
keytab_members = ["user", "group", "host", "hostgroup"]
|
||||
|
||||
for name in names:
|
||||
res_find = find_service(ansible_module, name)
|
||||
res_principals = []
|
||||
|
||||
keytab = {
|
||||
"retrieve": {
|
||||
"allow": {k: [] for k in keytab_members},
|
||||
"disallow": {k: [] for k in keytab_members},
|
||||
},
|
||||
"create": {
|
||||
"allow": {k: [] for k in keytab_members},
|
||||
"disallow": {k: [] for k in keytab_members},
|
||||
},
|
||||
}
|
||||
certificate_add, certificate_del = [], []
|
||||
host_add, host_del = [], []
|
||||
principal_add, principal_del = [], []
|
||||
|
||||
if principal and res_find:
|
||||
# When comparing principals to the existing ones,
|
||||
# the REALM is needded, and are added here for those
|
||||
# that do not have it.
|
||||
principal = [
|
||||
p if "@" in p
|
||||
else "%s@%s" % (p, api_get_realm())
|
||||
for p in principal
|
||||
]
|
||||
principal = list(set(principal))
|
||||
|
||||
# Create list of existing principal aliases as strings
|
||||
# to compare with provided ones.
|
||||
canonicalname = {
|
||||
to_text(p)
|
||||
for p in res_find.get("krbcanonicalname", [])
|
||||
}
|
||||
res_principals = [
|
||||
to_text(elem)
|
||||
for elem in res_find.get("krbprincipalname", [])
|
||||
]
|
||||
res_principals = list(set(res_principals) - canonicalname)
|
||||
|
||||
if state == "present":
|
||||
if action == "service":
|
||||
@@ -520,37 +529,8 @@ def main():
|
||||
|
||||
if res_find is None:
|
||||
commands.append([name, 'service_add', args])
|
||||
|
||||
certificate_add = certificate or []
|
||||
certificate_del = []
|
||||
host_add = host or []
|
||||
host_del = []
|
||||
principal_add = principal or []
|
||||
principal_del = []
|
||||
allow_create_keytab_user_add = \
|
||||
allow_create_keytab_user or []
|
||||
allow_create_keytab_user_del = []
|
||||
allow_create_keytab_group_add = \
|
||||
allow_create_keytab_group or []
|
||||
allow_create_keytab_group_del = []
|
||||
allow_create_keytab_host_add = \
|
||||
allow_create_keytab_host or []
|
||||
allow_create_keytab_host_del = []
|
||||
allow_create_keytab_hostgroup_add = \
|
||||
allow_create_keytab_hostgroup or []
|
||||
allow_create_keytab_hostgroup_del = []
|
||||
allow_retrieve_keytab_user_add = \
|
||||
allow_retrieve_keytab_user or []
|
||||
allow_retrieve_keytab_user_del = []
|
||||
allow_retrieve_keytab_group_add = \
|
||||
allow_retrieve_keytab_group or []
|
||||
allow_retrieve_keytab_group_del = []
|
||||
allow_retrieve_keytab_host_add = \
|
||||
allow_retrieve_keytab_host or []
|
||||
allow_retrieve_keytab_host_del = []
|
||||
allow_retrieve_keytab_hostgroup_add = \
|
||||
allow_retrieve_keytab_hostgroup or []
|
||||
allow_retrieve_keytab_hostgroup_del = []
|
||||
# Use an empty res_find to manage members
|
||||
res_find = {}
|
||||
|
||||
else:
|
||||
for remove in ['skip_host_check', 'force']:
|
||||
@@ -563,207 +543,54 @@ def main():
|
||||
args.get("krbprincipalauthind", [""]) ==
|
||||
res_find.get("krbprincipalauthind", [""])
|
||||
)
|
||||
):
|
||||
):
|
||||
del args["krbprincipalauthind"]
|
||||
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "service_mod", args])
|
||||
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
# Manage members
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get('managedby_host', []))
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get('managedby_host'))
|
||||
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_find.get("principal"))
|
||||
|
||||
(allow_create_keytab_user_add,
|
||||
allow_create_keytab_user_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_user, res_find.get(
|
||||
'ipaallowedtoperform_write_keys_user',
|
||||
[]))
|
||||
(allow_retrieve_keytab_user_add,
|
||||
allow_retrieve_keytab_user_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_user, res_find.get(
|
||||
'ipaallowedtoperform_read_keys_user',
|
||||
[]))
|
||||
(allow_create_keytab_group_add,
|
||||
allow_create_keytab_group_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_group, res_find.get(
|
||||
'ipaallowedtoperform_write_keys_group',
|
||||
[]))
|
||||
(allow_retrieve_keytab_group_add,
|
||||
allow_retrieve_keytab_group_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_group,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_group',
|
||||
[]))
|
||||
(allow_create_keytab_host_add,
|
||||
allow_create_keytab_host_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_host,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_write_keys_host',
|
||||
[]))
|
||||
(allow_retrieve_keytab_host_add,
|
||||
allow_retrieve_keytab_host_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_host,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_host',
|
||||
[]))
|
||||
(allow_create_keytab_hostgroup_add,
|
||||
allow_create_keytab_hostgroup_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_create_keytab_hostgroup,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_write_keys_hostgroup',
|
||||
[]))
|
||||
(allow_retrieve_keytab_hostgroup_add,
|
||||
allow_retrieve_keytab_hostgroup_del) = \
|
||||
gen_add_del_lists(
|
||||
allow_retrieve_keytab_hostgroup,
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_read_keys_hostgroup',
|
||||
[]))
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_principals)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No service '%s'" % name)
|
||||
|
||||
existing = res_find.get('usercertificate', [])
|
||||
if certificate is None:
|
||||
certificate_add = []
|
||||
else:
|
||||
certificate_add = [c for c in certificate
|
||||
if c not in existing]
|
||||
certificate_del = []
|
||||
host_add = host or []
|
||||
host_del = []
|
||||
principal_add = principal or []
|
||||
principal_del = []
|
||||
certificate_add = gen_add_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
allow_create_keytab_user_add = \
|
||||
allow_create_keytab_user or []
|
||||
allow_create_keytab_user_del = []
|
||||
allow_create_keytab_group_add = \
|
||||
allow_create_keytab_group or []
|
||||
allow_create_keytab_group_del = []
|
||||
allow_create_keytab_host_add = \
|
||||
allow_create_keytab_host or []
|
||||
allow_create_keytab_host_del = []
|
||||
allow_create_keytab_hostgroup_add = \
|
||||
allow_create_keytab_hostgroup or []
|
||||
allow_create_keytab_hostgroup_del = []
|
||||
allow_retrieve_keytab_user_add = \
|
||||
allow_retrieve_keytab_user or []
|
||||
allow_retrieve_keytab_user_del = []
|
||||
allow_retrieve_keytab_group_add = \
|
||||
allow_retrieve_keytab_group or []
|
||||
allow_retrieve_keytab_group_del = []
|
||||
allow_retrieve_keytab_host_add = \
|
||||
allow_retrieve_keytab_host or []
|
||||
allow_retrieve_keytab_host_del = []
|
||||
allow_retrieve_keytab_hostgroup_add = \
|
||||
allow_retrieve_keytab_hostgroup or []
|
||||
allow_retrieve_keytab_hostgroup_del = []
|
||||
host_add = gen_add_list(
|
||||
host, res_find.get('managedby_host'))
|
||||
|
||||
# Add principals
|
||||
for _principal in principal_add:
|
||||
commands.append([name, "service_add_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
principal_add = gen_add_list(principal, res_principals)
|
||||
|
||||
# Remove principals
|
||||
for _principal in principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
|
||||
for _certificate in certificate_add:
|
||||
commands.append([name, "service_add_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
# Remove certificates
|
||||
for _certificate in certificate_del:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
|
||||
# Add hosts.
|
||||
if host is not None and len(host) > 0 and len(host_add) > 0:
|
||||
commands.append([name, "service_add_host",
|
||||
{"host": host_add}])
|
||||
# Remove hosts
|
||||
if host is not None and len(host) > 0 and len(host_del) > 0:
|
||||
commands.append([name, "service_remove_host",
|
||||
{"host": host_del}])
|
||||
|
||||
# Allow create keytab
|
||||
if len(allow_create_keytab_user_add) > 0 or \
|
||||
len(allow_create_keytab_group_add) > 0 or \
|
||||
len(allow_create_keytab_host_add) > 0 or \
|
||||
len(allow_create_keytab_hostgroup_add) > 0:
|
||||
commands.append(
|
||||
[name, "service_allow_create_keytab",
|
||||
{'user': allow_create_keytab_user_add,
|
||||
'group': allow_create_keytab_group_add,
|
||||
'host': allow_create_keytab_host_add,
|
||||
'hostgroup': allow_create_keytab_hostgroup_add
|
||||
}])
|
||||
|
||||
# Disallow create keytab
|
||||
if len(allow_create_keytab_user_del) > 0 or \
|
||||
len(allow_create_keytab_group_del) > 0 or \
|
||||
len(allow_create_keytab_host_del) > 0 or \
|
||||
len(allow_create_keytab_hostgroup_del) > 0:
|
||||
commands.append(
|
||||
[name, "service_disallow_create_keytab",
|
||||
{'user': allow_create_keytab_user_del,
|
||||
'group': allow_create_keytab_group_del,
|
||||
'host': allow_create_keytab_host_del,
|
||||
'hostgroup': allow_create_keytab_hostgroup_del
|
||||
}])
|
||||
|
||||
# Allow retrieve keytab
|
||||
if len(allow_retrieve_keytab_user_add) > 0 or \
|
||||
len(allow_retrieve_keytab_group_add) > 0 or \
|
||||
len(allow_retrieve_keytab_host_add) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_add) > 0:
|
||||
commands.append(
|
||||
[name, "service_allow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user_add,
|
||||
'group': allow_retrieve_keytab_group_add,
|
||||
'host': allow_retrieve_keytab_host_add,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup_add
|
||||
}])
|
||||
|
||||
# Disllow retrieve keytab
|
||||
if len(allow_retrieve_keytab_user_del) > 0 or \
|
||||
len(allow_retrieve_keytab_group_del) > 0 or \
|
||||
len(allow_retrieve_keytab_host_del) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_del) > 0:
|
||||
commands.append(
|
||||
[name, "service_disallow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user_del,
|
||||
'group': allow_retrieve_keytab_group_del,
|
||||
'host': allow_retrieve_keytab_host_del,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup_del
|
||||
}])
|
||||
# get keytab management lists for any 'action'.
|
||||
for perm in ["create", "retrieve"]:
|
||||
oper = "write" if perm == "create" else "read"
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
add_list, del_list = (
|
||||
gen_add_del_lists(
|
||||
ansible_module.params_get(
|
||||
"allow_%s_keytab_%s" % (perm, key)
|
||||
),
|
||||
res_find.get(
|
||||
'ipaallowedtoperform_%s_keys_%s'
|
||||
% (oper, key)
|
||||
)
|
||||
)
|
||||
)
|
||||
keytab[perm]["allow"][key] = add_list
|
||||
# Only remove members if action is 'service'
|
||||
if action == "service":
|
||||
keytab[perm]["disallow"][key] = del_list
|
||||
|
||||
elif state == "absent":
|
||||
if action == "service":
|
||||
@@ -775,55 +602,30 @@ def main():
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No service '%s'" % name)
|
||||
|
||||
# Remove principals
|
||||
if principal is not None:
|
||||
for _principal in principal:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{
|
||||
"krbprincipalname":
|
||||
_principal,
|
||||
}])
|
||||
# Remove certificates
|
||||
if certificate is not None:
|
||||
existing = res_find.get('usercertificate', [])
|
||||
for _certificate in certificate:
|
||||
if _certificate in existing:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{
|
||||
"usercertificate":
|
||||
_certificate,
|
||||
}])
|
||||
principal_del = gen_intersection_list(
|
||||
principal, res_principals)
|
||||
|
||||
# Add hosts
|
||||
if host is not None:
|
||||
commands.append(
|
||||
[name, "service_remove_host", {"host": host}])
|
||||
certificate_del = gen_intersection_list(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
# Allow create keytab
|
||||
if allow_create_keytab_user is not None or \
|
||||
allow_create_keytab_group is not None or \
|
||||
allow_create_keytab_host is not None or \
|
||||
allow_create_keytab_hostgroup is not None:
|
||||
commands.append(
|
||||
[name, "service_disallow_create_keytab",
|
||||
{'user': allow_create_keytab_user,
|
||||
'group': allow_create_keytab_group,
|
||||
'host': allow_create_keytab_host,
|
||||
'hostgroup': allow_create_keytab_hostgroup
|
||||
}])
|
||||
host_del = gen_intersection_list(
|
||||
host, res_find.get("managedby_host"))
|
||||
|
||||
# Allow retriev keytab
|
||||
if allow_retrieve_keytab_user is not None or \
|
||||
allow_retrieve_keytab_group is not None or \
|
||||
allow_retrieve_keytab_host is not None or \
|
||||
allow_retrieve_keytab_hostgroup is not None:
|
||||
commands.append(
|
||||
[name, "service_disallow_retrieve_keytab",
|
||||
{'user': allow_retrieve_keytab_user,
|
||||
'group': allow_retrieve_keytab_group,
|
||||
'host': allow_retrieve_keytab_host,
|
||||
'hostgroup': allow_retrieve_keytab_hostgroup
|
||||
}])
|
||||
for perm in ["create", "retrieve"]:
|
||||
oper = "write" if perm == "create" else "read"
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
res_param = (
|
||||
'ipaallowedtoperform_%s_keys_%s'
|
||||
% (oper, key)
|
||||
)
|
||||
module_params = ansible_module.params_get(
|
||||
"allow_%s_keytab_%s" % (perm, key)
|
||||
)
|
||||
existing = res_find.get(res_param)
|
||||
del_list = (
|
||||
gen_intersection_list(module_params, existing)
|
||||
)
|
||||
keytab[perm]["disallow"][key] = del_list
|
||||
|
||||
elif state == "disabled":
|
||||
if action == "service":
|
||||
@@ -836,16 +638,53 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid action '%s' for state '%s'" %
|
||||
(action, state))
|
||||
# Members are not managed when disabling service.
|
||||
# Continue with next 'name'.
|
||||
continue
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Manage members
|
||||
if principal_add:
|
||||
commands.append([name, "service_add_principal",
|
||||
{"krbprincipalname": principal_add}])
|
||||
if principal_del:
|
||||
commands.append([name, "service_remove_principal",
|
||||
{"krbprincipalname": principal_del}])
|
||||
|
||||
if certificate_add:
|
||||
commands.append([name, "service_add_cert",
|
||||
{"usercertificate": certificate_add}])
|
||||
if certificate_del:
|
||||
commands.append([name, "service_remove_cert",
|
||||
{"usercertificate": certificate_del}])
|
||||
|
||||
if host_add:
|
||||
commands.append([name, "service_add_host",
|
||||
{"host": host_add}])
|
||||
if host_del:
|
||||
commands.append([name, "service_remove_host",
|
||||
{"host": host_del}])
|
||||
|
||||
# manage keytab permissions.
|
||||
for perm in ["create", "retrieve"]:
|
||||
for mode in ["allow", "disallow"]:
|
||||
for key in ["user", "group", "host", "hostgroup"]:
|
||||
if keytab[perm][mode][key]:
|
||||
commands.append([
|
||||
name,
|
||||
"service_%s_%s_keytab" % (mode, perm),
|
||||
keytab[perm][mode]
|
||||
])
|
||||
break
|
||||
|
||||
# Check mode exit
|
||||
if ansible_module.check_mode:
|
||||
ansible_module.exit_json(changed=len(commands) > 0, **exit_args)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(commands, result_handler)
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, fail_on_member_errors=True)
|
||||
|
||||
# Done
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'supported_by': 'community',
|
||||
'status': ['preview'],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -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),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
@@ -631,7 +634,7 @@ def main():
|
||||
vault_password_file=dict(type="str", required=False, default=None,
|
||||
no_log=False,
|
||||
aliases=[
|
||||
'password_file', "old_password_file"
|
||||
'password_file', "old_password_file"
|
||||
]),
|
||||
new_password=dict(type="str", required=False, default=None,
|
||||
no_log=True),
|
||||
|
||||
@@ -3,5 +3,4 @@ pytest>=2.7
|
||||
pytest-sourceorder>=0.5
|
||||
pytest-split-tests>=1.0.3
|
||||
pytest-testinfra>=5.0
|
||||
jmespath>=0.9 # needed for the `json_query` filter
|
||||
pyyaml>=3
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
@@ -20,6 +19,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.0',
|
||||
'supported_by': 'community',
|
||||
@@ -40,8 +43,9 @@ author:
|
||||
|
||||
EXAMPLES = '''
|
||||
# Get IPA_BACKUP_DIR from ipaplatform
|
||||
- name: ipabackup_get_backup_dir:
|
||||
register result
|
||||
- name: Get IPA_BACKUP_DIR from ipaplatform
|
||||
ipabackup_get_backup_dir:
|
||||
register: result
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
|
||||
10
roles/ipabackup/vars/Ubuntu-18.04.yml
Normal file
10
roles/ipabackup/vars/Ubuntu-18.04.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# vars/Ubuntu.yml
|
||||
---
|
||||
ipaserver_packages: [ "freeipa-server" ]
|
||||
ipaserver_packages_dns: [ "freeipa-server-dns" ]
|
||||
ipaserver_packages_adtrust: [ "freeipa-server-trust-ad" ]
|
||||
ipaserver_packages_firewalld: [ "firewalld" ]
|
||||
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
|
||||
# to the way python-ipalib package is defined.
|
||||
# Package python2.7 must be installed before executing this role.
|
||||
ansible_python_interpreter: '/usr/bin/python2.7'
|
||||
@@ -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:
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
7
roles/ipaclient/vars/Debian-10.yml
Normal file
7
roles/ipaclient/vars/Debian-10.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# vars/Debian.yml
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
# Debian Buster must use python2 as Python interpreter due
|
||||
# to the way freeipa-client package is defined.
|
||||
# You must install package python2.7 before executing this role.
|
||||
ansible_python_interpreter: '/usr/bin/python2'
|
||||
7
roles/ipaclient/vars/Ubuntu-18.04.yml
Normal file
7
roles/ipaclient/vars/Ubuntu-18.04.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
# vars/Ubuntu-18.04.yml
|
||||
---
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
|
||||
# to the way python-ipalib package is defined.
|
||||
# Package python2.7 must be installed before executing this role.
|
||||
ansible_python_interpreter: '/usr/bin/python2.7'
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user