mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-08-05 22:34:40 +00:00
194
README-dnszone.md
Normal file
194
README-dnszone.md
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
DNSZone Module
|
||||||
|
==============
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The dnszone module allows to configure zones in DNS server.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Add, remove, modify, enable or disable DNS zones.
|
||||||
|
|
||||||
|
|
||||||
|
Supported FreeIPA Versions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
FreeIPA versions 4.4.0 and up are supported by ipadnszone module.
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
**Controller**
|
||||||
|
* Ansible version: 2.8+
|
||||||
|
|
||||||
|
|
||||||
|
**Node**
|
||||||
|
* Supported FreeIPA version (see above)
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[ipaserver]
|
||||||
|
ipaserver.test.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Example playbook to create a simple DNS zone:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: dnszone present
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: present
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Example playbook to create a DNS zone with all currently supported variables:
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: dnszone present
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_sync_ptr: true
|
||||||
|
dynamic_update: true
|
||||||
|
dnssec: true
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
- ip_address: 8.8.4.4
|
||||||
|
port: 52
|
||||||
|
serial: 1234
|
||||||
|
refresh: 3600
|
||||||
|
retry: 900
|
||||||
|
expire: 1209600
|
||||||
|
minimum: 3600
|
||||||
|
ttl: 60
|
||||||
|
default_ttl: 90
|
||||||
|
name_server: ipaserver.test.local.
|
||||||
|
admin_email: admin.admin@example.com
|
||||||
|
nsec3param_rec: "1 7 100 0123456789abcdef"
|
||||||
|
skip_overlap_check: true
|
||||||
|
skip_nameserver_check: true
|
||||||
|
state: present
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Example playbook to disable a zone:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Playbook to disable DNS zone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Disable zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Example playbook to enable a zone:
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Playbook to enable DNS zone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Enable zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Example playbook to remove a zone:
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Playbook to remove DNS zone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Remove zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Variables
|
||||||
|
=========
|
||||||
|
|
||||||
|
ipadnszone
|
||||||
|
----------
|
||||||
|
|
||||||
|
Variable | Description | Required
|
||||||
|
-------- | ----------- | --------
|
||||||
|
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||||
|
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||||
|
`forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
|
||||||
|
| `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
|
||||||
|
| `port` - The custom port that should be used on this server. | no
|
||||||
|
`forward_policy` | The global forwarding policy. It can be one of `only`, `first`, or `none`. | no
|
||||||
|
`allow_sync_ptr` | Allow synchronization of forward (A, AAAA) and reverse (PTR) records (bool). | no
|
||||||
|
`state` | The state to ensure. It can be one of `present`, `enabled`, `disabled` or `absent`, default: `present`. | yes
|
||||||
|
`name_server`| Authoritative nameserver domain name | no
|
||||||
|
`admin_email`| Administrator e-mail address | no
|
||||||
|
`update_policy`| BIND update policy | no
|
||||||
|
`dynamic_update`| Allow dynamic updates | no
|
||||||
|
`dnssec`| Allow inline DNSSEC signing of records in the zone | no
|
||||||
|
`allow_transfer`| List of IP addresses or networks which are allowed to transfer the zone | no
|
||||||
|
`allow_query`| List of IP addresses or networks which are allowed to issue queries | no
|
||||||
|
`serial`| SOA record serial number | no
|
||||||
|
`refresh`| SOA record refresh time | no
|
||||||
|
`retry`| SOA record retry time | no
|
||||||
|
`expire`| SOA record expire time | no
|
||||||
|
`minimum`| How long should negative responses be cached | no
|
||||||
|
`ttl`| Time to live for records at zone apex | no
|
||||||
|
`default_ttl`| Time to live for records without explicit TTL definition | no
|
||||||
|
`nsec3param_rec`| NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt | no
|
||||||
|
`skip_overlap_check`| Force DNS zone creation even if it will overlap with an existing zone | no
|
||||||
|
`skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
|
||||||
|
|
||||||
|
|
||||||
|
Authors
|
||||||
|
=======
|
||||||
|
|
||||||
|
Sergio Oliveira Campos
|
||||||
@@ -12,6 +12,7 @@ Features
|
|||||||
* One-time-password (OTP) support for client installation
|
* One-time-password (OTP) support for client installation
|
||||||
* Repair mode for clients
|
* Repair mode for clients
|
||||||
* Modules for dns forwarder management
|
* Modules for dns forwarder management
|
||||||
|
* Modules for dns zone management
|
||||||
* Modules for group management
|
* Modules for group management
|
||||||
* Modules for hbacrule management
|
* Modules for hbacrule management
|
||||||
* Modules for hbacsvc management
|
* Modules for hbacsvc management
|
||||||
@@ -410,6 +411,7 @@ Modules in plugin/modules
|
|||||||
|
|
||||||
* [ipadnsconfig](README-dnsconfig.md)
|
* [ipadnsconfig](README-dnsconfig.md)
|
||||||
* [ipadnsforwardzone](README-dnsforwardzone.md)
|
* [ipadnsforwardzone](README-dnsforwardzone.md)
|
||||||
|
* [ipadnszone](README-dnszone.md)
|
||||||
* [ipagroup](README-group.md)
|
* [ipagroup](README-group.md)
|
||||||
* [ipahbacrule](README-hbacrule.md)
|
* [ipahbacrule](README-hbacrule.md)
|
||||||
* [ipahbacsvc](README-hbacsvc.md)
|
* [ipahbacsvc](README-hbacsvc.md)
|
||||||
@@ -425,3 +427,5 @@ Modules in plugin/modules
|
|||||||
* [ipatopologysuffix](README-topology.md)
|
* [ipatopologysuffix](README-topology.md)
|
||||||
* [ipauser](README-user.md)
|
* [ipauser](README-user.md)
|
||||||
* [ipavault](README-vault.md)
|
* [ipavault](README-vault.md)
|
||||||
|
|
||||||
|
If you want to write a new module please read [writing a new module](plugins/modules/README.md).
|
||||||
|
|||||||
11
playbooks/dnszone/disable-zone-forwarders.yml
Normal file
11
playbooks/dnszone/disable-zone-forwarders.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to disable DNS zone forwarders
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Disable zone forwarders.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forward_policy: none
|
||||||
11
playbooks/dnszone/dnszone-absent.yml
Normal file
11
playbooks/dnszone/dnszone-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to ensure DNS zone is absent
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Remove zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: absent
|
||||||
35
playbooks/dnszone/dnszone-all-params.yml
Normal file
35
playbooks/dnszone/dnszone-all-params.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
- name: dnszone present
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_sync_ptr: true
|
||||||
|
dynamic_update: true
|
||||||
|
dnssec: true
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
- ip_address: 8.8.4.4
|
||||||
|
port: 52
|
||||||
|
#serial: 1234
|
||||||
|
refresh: 3600
|
||||||
|
retry: 900
|
||||||
|
expire: 1209600
|
||||||
|
minimum: 3600
|
||||||
|
ttl: 60
|
||||||
|
default_ttl: 90
|
||||||
|
name_server: ipaserver.test.local.
|
||||||
|
admin_email: admin.admin@example.com
|
||||||
|
nsec3param_rec: "1 7 100 0123456789abcdef"
|
||||||
|
skip_overlap_check: true
|
||||||
|
skip_nameserver_check: true
|
||||||
|
state: present
|
||||||
11
playbooks/dnszone/dnszone-disable.yml
Normal file
11
playbooks/dnszone/dnszone-disable.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to disable DNS zone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Disable zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: disabled
|
||||||
11
playbooks/dnszone/dnszone-enable.yml
Normal file
11
playbooks/dnszone/dnszone-enable.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook to enable DNS zone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Enable zone.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: enabled
|
||||||
10
playbooks/dnszone/dnszone-present.yml
Normal file
10
playbooks/dnszone/dnszone-present.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
- name: dnszone present
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: present
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Authors:
|
# Authors:
|
||||||
|
# Sergio Oliveira Campos <seocam@redhat.com>
|
||||||
# Thomas Woerner <twoerner@redhat.com>
|
# Thomas Woerner <twoerner@redhat.com>
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 Red Hat
|
# Copyright (C) 2019 Red Hat
|
||||||
@@ -27,10 +28,12 @@ import tempfile
|
|||||||
import shutil
|
import shutil
|
||||||
import gssapi
|
import gssapi
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pprint import pformat
|
||||||
from ipalib import api
|
from ipalib import api
|
||||||
from ipalib import errors as ipalib_errors
|
from ipalib import errors as ipalib_errors # noqa
|
||||||
from ipalib.config import Env
|
from ipalib.config import Env
|
||||||
from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT
|
from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ipalib.install.kinit import kinit_password, kinit_keytab
|
from ipalib.install.kinit import kinit_password, kinit_keytab
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -38,7 +41,9 @@ except ImportError:
|
|||||||
from ipapython.ipautil import run
|
from ipapython.ipautil import run
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipalib.krb_utils import get_credentials_if_valid
|
from ipalib.krb_utils import get_credentials_if_valid
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils._text import to_text
|
from ansible.module_utils._text import to_text
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ipalib.x509 import Encoding
|
from ipalib.x509 import Encoding
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -52,7 +57,7 @@ if six.PY3:
|
|||||||
unicode = str
|
unicode = str
|
||||||
|
|
||||||
|
|
||||||
def valid_creds(module, principal):
|
def valid_creds(module, principal): # noqa
|
||||||
"""
|
"""
|
||||||
Get valid credintials matching the princial, try GSSAPI first
|
Get valid credintials matching the princial, try GSSAPI first
|
||||||
"""
|
"""
|
||||||
@@ -205,9 +210,24 @@ def date_format(value):
|
|||||||
raise ValueError("Invalid date '%s'" % value)
|
raise ValueError("Invalid date '%s'" % value)
|
||||||
|
|
||||||
|
|
||||||
def compare_args_ipa(module, args, ipa):
|
def compare_args_ipa(module, args, ipa): # noqa
|
||||||
|
"""Compare IPA obj attrs with the command args.
|
||||||
|
|
||||||
|
This function compares IPA objects attributes with the args the
|
||||||
|
module is intending to use to call a command. This is useful to know
|
||||||
|
if call to IPA server will be needed or not.
|
||||||
|
In other to compare we have to prepare the perform slight changes in
|
||||||
|
data formats.
|
||||||
|
|
||||||
|
Returns True if they are the same and False otherwise.
|
||||||
|
"""
|
||||||
|
base_debug_msg = "Ansible arguments and IPA commands differed. "
|
||||||
|
|
||||||
for key in args.keys():
|
for key in args.keys():
|
||||||
if key not in ipa:
|
if key not in ipa:
|
||||||
|
module.debug(
|
||||||
|
base_debug_msg + "Command key not present in IPA: %s" % key
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
arg = args[key]
|
arg = args[key]
|
||||||
@@ -220,25 +240,35 @@ def compare_args_ipa(module, args, ipa):
|
|||||||
if isinstance(ipa_arg, list):
|
if isinstance(ipa_arg, list):
|
||||||
if not isinstance(arg, list):
|
if not isinstance(arg, list):
|
||||||
arg = [arg]
|
arg = [arg]
|
||||||
|
if len(ipa_arg) != len(arg):
|
||||||
|
module.debug(
|
||||||
|
base_debug_msg
|
||||||
|
+ "List length doesn't match for key %s: %d %d"
|
||||||
|
% (key, len(arg), len(ipa_arg),)
|
||||||
|
)
|
||||||
|
return False
|
||||||
if isinstance(ipa_arg[0], str) and isinstance(arg[0], int):
|
if isinstance(ipa_arg[0], str) and isinstance(arg[0], int):
|
||||||
arg = [to_text(_arg) for _arg in arg]
|
arg = [to_text(_arg) for _arg in arg]
|
||||||
if isinstance(ipa_arg[0], unicode) and isinstance(arg[0], int):
|
if isinstance(ipa_arg[0], unicode) and isinstance(arg[0], int):
|
||||||
arg = [to_text(_arg) for _arg in arg]
|
arg = [to_text(_arg) for _arg in arg]
|
||||||
# module.warn("%s <=> %s" % (repr(arg), repr(ipa_arg)))
|
|
||||||
try:
|
try:
|
||||||
arg_set = set(arg)
|
arg_set = set(arg)
|
||||||
ipa_arg_set = set(ipa_arg)
|
ipa_arg_set = set(ipa_arg)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
if arg != ipa_arg:
|
if arg != ipa_arg:
|
||||||
# module.warn("%s != %s" % (repr(arg), repr(ipa_arg)))
|
module.debug(
|
||||||
|
base_debug_msg
|
||||||
|
+ "Different values: %s %s" % (arg, ipa_arg)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if arg_set != ipa_arg_set:
|
if arg_set != ipa_arg_set:
|
||||||
# module.warn("%s != %s" % (repr(arg), repr(ipa_arg)))
|
module.debug(
|
||||||
|
base_debug_msg
|
||||||
|
+ "Different set content: %s %s"
|
||||||
|
% (arg_set, ipa_arg_set,)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# module.warn("%s == %s" % (repr(arg), repr(ipa_arg)))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -289,6 +319,16 @@ def encode_certificate(cert):
|
|||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_port(port):
|
||||||
|
if not isinstance(port, int):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if 1 <= port <= 65535:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_ipv4_addr(ipaddr):
|
def is_ipv4_addr(ipaddr):
|
||||||
"""
|
"""
|
||||||
Test if figen IP address is a valid IPv4 address
|
Test if figen IP address is a valid IPv4 address
|
||||||
@@ -309,3 +349,294 @@ def is_ipv6_addr(ipaddr):
|
|||||||
except socket.error:
|
except socket.error:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleFreeIPAParams(dict):
|
||||||
|
def __init__(self, ansible_module):
|
||||||
|
self.update(ansible_module.params)
|
||||||
|
self.ansible_module = ansible_module
|
||||||
|
|
||||||
|
@property
|
||||||
|
def names(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
param = self.get(name)
|
||||||
|
if param is not None:
|
||||||
|
return _afm_convert(param)
|
||||||
|
|
||||||
|
|
||||||
|
class FreeIPABaseModule(AnsibleModule):
|
||||||
|
"""
|
||||||
|
Base class for FreeIPA Ansible modules.
|
||||||
|
|
||||||
|
Provides methods useful methods to be used by our modules.
|
||||||
|
|
||||||
|
This class should be overriten and instantiated for the module.
|
||||||
|
A basic implementation of an Ansible FreeIPA module expects its
|
||||||
|
class to:
|
||||||
|
|
||||||
|
1. Define a class attribute ``ipa_param_mapping``
|
||||||
|
2. Implement the method ``define_ipa_commands()``
|
||||||
|
3. Implement the method ``check_ipa_params()`` (optional)
|
||||||
|
|
||||||
|
After instantiating the class the method ``ipa_run()`` should be called.
|
||||||
|
|
||||||
|
Example (ansible-freeipa/plugins/modules/ipasomemodule.py):
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
ipa_param_mapping = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(FreeIPABaseModule, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Attributes to store kerberos credentials (if needed)
|
||||||
|
self.ccache_dir = None
|
||||||
|
self.ccache_name = None
|
||||||
|
|
||||||
|
# Status of an execution. Will be changed to True
|
||||||
|
# if something is actually peformed.
|
||||||
|
self.changed = False
|
||||||
|
|
||||||
|
# Status of the connection with the IPA server.
|
||||||
|
# We need to know if the connection was actually stablished
|
||||||
|
# before we start sending commands.
|
||||||
|
self.ipa_connected = False
|
||||||
|
|
||||||
|
# Commands to be executed
|
||||||
|
self.ipa_commands = []
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
"""
|
||||||
|
Return a dict to be passed to an IPA command.
|
||||||
|
|
||||||
|
The keys of ``ipa_param_mapping`` are also the keys of the return dict.
|
||||||
|
|
||||||
|
The values of ``ipa_param_mapping`` needs to be either:
|
||||||
|
* A str with the name of a defined method; or
|
||||||
|
* A key of ``AnsibleModule.param``.
|
||||||
|
|
||||||
|
In case of a method the return of the method will be set as value
|
||||||
|
for the return dict.
|
||||||
|
|
||||||
|
In case of a AnsibleModule.param the value of the param will be
|
||||||
|
set in the return dict. In addition to that boolean values will be
|
||||||
|
automaticaly converted to uppercase strings (as required by FreeIPA
|
||||||
|
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()
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
def check_ipa_params(self):
|
||||||
|
"""Validate ipa_params before command is called."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def define_ipa_commands(self):
|
||||||
|
"""Define commands that will be run in IPA server."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def api_command(self, command, name=None, args=None):
|
||||||
|
"""Execute a single command in IPA server."""
|
||||||
|
if args is None:
|
||||||
|
args = {}
|
||||||
|
|
||||||
|
if name is None:
|
||||||
|
return api_command_no_name(self, command, args)
|
||||||
|
|
||||||
|
return api_command(self, command, name, args)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
"""
|
||||||
|
Connect to IPA server.
|
||||||
|
|
||||||
|
Check the there are working Kerberos credentials to connect to
|
||||||
|
IPA server. If there are not we perform a temporary kinit
|
||||||
|
that will be terminated when exiting the context.
|
||||||
|
|
||||||
|
If the connection fails ``ipa_connected`` attribute will be set
|
||||||
|
to False.
|
||||||
|
"""
|
||||||
|
principal = self.ipa_params.ipaadmin_principal
|
||||||
|
password = self.ipa_params.ipaadmin_password
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not valid_creds(self, principal):
|
||||||
|
self.ccache_dir, self.ccache_name = temp_kinit(
|
||||||
|
principal, password,
|
||||||
|
)
|
||||||
|
|
||||||
|
api_connect()
|
||||||
|
|
||||||
|
except Exception as excpt:
|
||||||
|
self.fail_json(msg=str(excpt))
|
||||||
|
else:
|
||||||
|
self.ipa_connected = True
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
"""
|
||||||
|
Terminate a connection with the IPA server.
|
||||||
|
|
||||||
|
Deal with exceptions, destroy temporary kinit credentials and
|
||||||
|
exit the module with proper arguments.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if exc_val:
|
||||||
|
self.fail_json(msg=str(exc_val))
|
||||||
|
|
||||||
|
# TODO: shouldn't we also disconnect from api backend?
|
||||||
|
temp_kdestroy(self.ccache_dir, self.ccache_name)
|
||||||
|
|
||||||
|
self.exit_json(changed=self.changed, user=self.exit_args)
|
||||||
|
|
||||||
|
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"))
|
||||||
|
|
||||||
|
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."""
|
||||||
|
result = None
|
||||||
|
|
||||||
|
for name, command, args in self.ipa_commands:
|
||||||
|
try:
|
||||||
|
result = self.api_command(command, name, args)
|
||||||
|
except Exception as excpt:
|
||||||
|
self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt)))
|
||||||
|
else:
|
||||||
|
if "completed" in result:
|
||||||
|
if result["completed"] > 0:
|
||||||
|
self.changed = True
|
||||||
|
else:
|
||||||
|
self.changed = True
|
||||||
|
|
||||||
|
self.get_command_errors(command, result)
|
||||||
|
|
||||||
|
def require_ipa_attrs_change(self, command_args, ipa_attrs):
|
||||||
|
"""
|
||||||
|
Compare given args with current object attributes.
|
||||||
|
|
||||||
|
Returns True in case current IPA object attributes differ from
|
||||||
|
args passed to the module.
|
||||||
|
"""
|
||||||
|
equal = compare_args_ipa(self, command_args, ipa_attrs)
|
||||||
|
return not equal
|
||||||
|
|
||||||
|
def pdebug(self, value):
|
||||||
|
"""Debug with pretty formatting."""
|
||||||
|
self.debug(pformat(value))
|
||||||
|
|
||||||
|
def ipa_run(self):
|
||||||
|
"""Execute module actions."""
|
||||||
|
with self:
|
||||||
|
if not self.ipa_connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.check_ipa_params()
|
||||||
|
self.define_ipa_commands()
|
||||||
|
self._run_ipa_commands()
|
||||||
|
|||||||
80
plugins/modules/README.md
Normal file
80
plugins/modules/README.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Writing a new Ansible FreeIPA module
|
||||||
|
|
||||||
|
## Minimum requirements
|
||||||
|
A ansible-freeipa module should have:
|
||||||
|
|
||||||
|
* Code:
|
||||||
|
* A module file placed in `plugins/modules/<ipa_module_name>.py`
|
||||||
|
|
||||||
|
* Documentation:
|
||||||
|
* `README-<module_name>.md` file in the root directory and linked from the main README.md
|
||||||
|
* Example playbooks in `playbooks/<module_name>/` directory
|
||||||
|
|
||||||
|
* 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.
|
||||||
457
plugins/modules/ipadnszone.py
Normal file
457
plugins/modules/ipadnszone.py
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Authors:
|
||||||
|
# Sergio Oliveira Campos <seocam@redhat.com>
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 Red Hat
|
||||||
|
# see file 'COPYING' for use and warranty information
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
|
"metadata_version": "1.0",
|
||||||
|
"supported_by": "community",
|
||||||
|
"status": ["preview"],
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
module: ipadnszone
|
||||||
|
short description: Manage FreeIPA dnszone
|
||||||
|
description: Manage FreeIPA dnszone
|
||||||
|
options:
|
||||||
|
ipaadmin_principal:
|
||||||
|
description: The admin principal
|
||||||
|
default: admin
|
||||||
|
ipaadmin_password:
|
||||||
|
description: The admin password
|
||||||
|
required: false
|
||||||
|
|
||||||
|
forwarders:
|
||||||
|
description: The list of global DNS forwarders.
|
||||||
|
required: false
|
||||||
|
options:
|
||||||
|
ip_address:
|
||||||
|
description: The forwarder nameserver IP address list (IPv4 and IPv6).
|
||||||
|
required: true
|
||||||
|
port:
|
||||||
|
description: The port to forward requests to.
|
||||||
|
required: false
|
||||||
|
forward_policy:
|
||||||
|
description:
|
||||||
|
Global forwarding policy. Set to "none" to disable any configured
|
||||||
|
global forwarders.
|
||||||
|
required: false
|
||||||
|
choices: ['only', 'first', 'none']
|
||||||
|
allow_sync_ptr:
|
||||||
|
description:
|
||||||
|
Allow synchronization of forward (A, AAAA) and reverse (PTR) records.
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
state:
|
||||||
|
description: State to ensure
|
||||||
|
default: present
|
||||||
|
choices: ["present", "absent", "enabled", "disabled"]
|
||||||
|
name_server:
|
||||||
|
description: Authoritative nameserver domain name
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
admin_email:
|
||||||
|
description: Administrator e-mail address
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
update_policy: BIND update policy
|
||||||
|
description: Allow dynamic updates
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
dnssec:
|
||||||
|
description: Allow inline DNSSEC signing of records in the zone
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
allow_transfer:
|
||||||
|
description: List of IP addresses or networks which are allowed to transfer the zone
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
allow_query:
|
||||||
|
description: List of IP addresses or networks which are allowed to issue queries
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
serial:
|
||||||
|
description: SOA record serial number
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
refresh:
|
||||||
|
description: SOA record refresh time
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
retry:
|
||||||
|
description: SOA record retry time
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
expire:
|
||||||
|
description: SOA record expire time
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
minimum:
|
||||||
|
description: How long should negative responses be cached
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
ttl:
|
||||||
|
description: Time to live for records at zone apex
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
default_ttl:
|
||||||
|
description: Time to live for records without explicit TTL definition
|
||||||
|
required: false
|
||||||
|
type: int
|
||||||
|
nsec3param_rec:
|
||||||
|
description: NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt.
|
||||||
|
required: false
|
||||||
|
type: str
|
||||||
|
skip_overlap_check:
|
||||||
|
description: Force DNS zone creation even if it will overlap with an existing zone
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
skip_nameserver_check:
|
||||||
|
description: Force DNS zone creation even if nameserver is not resolvable
|
||||||
|
required: false
|
||||||
|
type: bool
|
||||||
|
""" # noqa: E501
|
||||||
|
|
||||||
|
EXAMPLES = """
|
||||||
|
---
|
||||||
|
# Ensure the zone is present (very minimal)
|
||||||
|
- ipadnszone:
|
||||||
|
name: test.example.com
|
||||||
|
|
||||||
|
# Ensure the zone is present (all available arguments)
|
||||||
|
- ipadnszone:
|
||||||
|
name: test.example.com
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
allow_sync_ptr: true
|
||||||
|
dynamic_update: true
|
||||||
|
dnssec: true
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
- ip_address: 8.8.4.4
|
||||||
|
port: 52
|
||||||
|
serial: 1234
|
||||||
|
refresh: 3600
|
||||||
|
retry: 900
|
||||||
|
expire: 1209600
|
||||||
|
minimum: 3600
|
||||||
|
ttl: 60
|
||||||
|
default_ttl: 90
|
||||||
|
name_server: ipaserver.test.local.
|
||||||
|
admin_email: admin.admin@example.com
|
||||||
|
nsec3param_rec: "1 7 100 0123456789abcdef"
|
||||||
|
skip_overlap_check: true
|
||||||
|
skip_nameserver_check: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# Ensure zone is present and disabled
|
||||||
|
- ipadnszone:
|
||||||
|
name: test.example.com
|
||||||
|
state: disabled
|
||||||
|
|
||||||
|
# Ensure zone is present and enabled
|
||||||
|
- ipadnszone:
|
||||||
|
name: test.example.com
|
||||||
|
state: enabled
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipapython.dnsutil import DNSName # noqa: E402
|
||||||
|
from ansible.module_utils.ansible_freeipa_module import (
|
||||||
|
FreeIPABaseModule,
|
||||||
|
is_ipv4_addr,
|
||||||
|
is_ipv6_addr,
|
||||||
|
is_valid_port,
|
||||||
|
) # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
class DNSZoneModule(FreeIPABaseModule):
|
||||||
|
|
||||||
|
ipa_param_mapping = {
|
||||||
|
# Direct Mapping
|
||||||
|
"idnsforwardpolicy": "forward_policy",
|
||||||
|
"idnssoaserial": "serial",
|
||||||
|
"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 validate_ips(self, ips, error_msg):
|
||||||
|
invalid_ips = [
|
||||||
|
ip for ip in ips if not is_ipv4_addr(ip) or is_ipv6_addr(ip)
|
||||||
|
]
|
||||||
|
if any(invalid_ips):
|
||||||
|
self.fail_json(msg=error_msg % invalid_ips)
|
||||||
|
|
||||||
|
def is_valid_nsec3param_rec(self, nsec3param_rec):
|
||||||
|
try:
|
||||||
|
part1, part2, part3, part4 = nsec3param_rec.split(" ")
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not all([part1.isdigit(), part2.isdigit(), part3.isdigit()]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not 0 <= int(part1) <= 255:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not 0 <= int(part2) <= 255:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not 0 <= int(part3) <= 65535:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
int(part4, 16)
|
||||||
|
except ValueError:
|
||||||
|
is_hex = False
|
||||||
|
else:
|
||||||
|
is_hex = True
|
||||||
|
|
||||||
|
even_digits = len(part4) % 2 == 0
|
||||||
|
is_dash = part4 == "-"
|
||||||
|
|
||||||
|
# If not hex with even digits or dash then
|
||||||
|
# part4 is invalid
|
||||||
|
if not ((is_hex and even_digits) or is_dash):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_ipa_nsec3paramrecord(self):
|
||||||
|
nsec3param_rec = self.ipa_params.nsec3param_rec
|
||||||
|
if nsec3param_rec is not None:
|
||||||
|
error_msg = (
|
||||||
|
"Invalid nsec3param_rec: %s. "
|
||||||
|
"Expected format: <0-255> <0-255> <0-65535> "
|
||||||
|
"even-length_hexadecimal_digits_or_hyphen"
|
||||||
|
) % nsec3param_rec
|
||||||
|
if not self.is_valid_nsec3param_rec(nsec3param_rec):
|
||||||
|
self.fail_json(msg=error_msg)
|
||||||
|
return nsec3param_rec
|
||||||
|
|
||||||
|
def get_ipa_idnsforwarders(self):
|
||||||
|
if self.ipa_params.forwarders is not None:
|
||||||
|
forwarders = []
|
||||||
|
for forwarder in self.ipa_params.forwarders:
|
||||||
|
ip_address = forwarder.get("ip_address")
|
||||||
|
if not (is_ipv4_addr(ip_address) or is_ipv6_addr(ip_address)):
|
||||||
|
self.fail_json(
|
||||||
|
msg="Invalid IP for DNS forwarder: %s" % ip_address
|
||||||
|
)
|
||||||
|
|
||||||
|
port = forwarder.get("port", None)
|
||||||
|
if port and not is_valid_port(port):
|
||||||
|
self.fail_json(
|
||||||
|
msg="Invalid port number for DNS forwarder: %s %s"
|
||||||
|
% (ip_address, port)
|
||||||
|
)
|
||||||
|
formatted_forwarder = ip_address
|
||||||
|
port = forwarder.get("port")
|
||||||
|
if port:
|
||||||
|
formatted_forwarder += " port %d" % port
|
||||||
|
forwarders.append(formatted_forwarder)
|
||||||
|
|
||||||
|
return forwarders
|
||||||
|
|
||||||
|
def get_ipa_idnsallowtransfer(self):
|
||||||
|
if self.ipa_params.allow_transfer is not None:
|
||||||
|
error_msg = "Invalid ip_address for DNS allow_transfer: %s"
|
||||||
|
self.validate_ips(self.ipa_params.allow_transfer, error_msg)
|
||||||
|
|
||||||
|
return (";".join(self.ipa_params.allow_transfer) or "none") + ";"
|
||||||
|
|
||||||
|
def get_ipa_idnsallowquery(self):
|
||||||
|
if self.ipa_params.allow_query is not None:
|
||||||
|
error_msg = "Invalid ip_address for DNS allow_query: %s"
|
||||||
|
self.validate_ips(self.ipa_params.allow_query, error_msg)
|
||||||
|
|
||||||
|
return (";".join(self.ipa_params.allow_query) or "any") + ";"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _replace_at_symbol_in_rname(rname):
|
||||||
|
"""
|
||||||
|
See RFC 1035 for more information.
|
||||||
|
|
||||||
|
Section 8. MAIL SUPPORT
|
||||||
|
https://tools.ietf.org/html/rfc1035#section-8
|
||||||
|
"""
|
||||||
|
if "@" not in rname:
|
||||||
|
return rname
|
||||||
|
|
||||||
|
name, domain = rname.split("@")
|
||||||
|
name = name.replace(".", r"\.")
|
||||||
|
|
||||||
|
return ".".join((name, domain))
|
||||||
|
|
||||||
|
def get_ipa_idnssoarname(self):
|
||||||
|
if self.ipa_params.admin_email is not None:
|
||||||
|
return DNSName(
|
||||||
|
self._replace_at_symbol_in_rname(self.ipa_params.admin_email)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_ipa_idnssoamname(self):
|
||||||
|
if self.ipa_params.name_server is not None:
|
||||||
|
return DNSName(self.ipa_params.name_server)
|
||||||
|
|
||||||
|
def get_ipa_skip_overlap_check(self):
|
||||||
|
if not self.zone and self.ipa_params.skip_overlap_check is not None:
|
||||||
|
return self.ipa_params.skip_overlap_check
|
||||||
|
|
||||||
|
def get_ipa_skip_nameserver_check(self):
|
||||||
|
if not self.zone and self.ipa_params.skip_nameserver_check is not None:
|
||||||
|
return self.ipa_params.skip_nameserver_check
|
||||||
|
|
||||||
|
def get_zone(self, zone_name):
|
||||||
|
get_zone_args = {"idnsname": zone_name, "all": True}
|
||||||
|
response = self.api_command("dnszone_find", args=get_zone_args)
|
||||||
|
|
||||||
|
if response["count"] == 1:
|
||||||
|
self.zone = response["result"][0]
|
||||||
|
self.is_zone_active = self.zone.get("idnszoneactive") == ["TRUE"]
|
||||||
|
return self.zone
|
||||||
|
|
||||||
|
# Zone doesn't exist yet
|
||||||
|
self.zone = None
|
||||||
|
self.is_zone_active = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def zone_name(self):
|
||||||
|
return self.ipa_params.name
|
||||||
|
|
||||||
|
def define_ipa_commands(self):
|
||||||
|
# Look for existing zone in IPA
|
||||||
|
self.get_zone(self.zone_name)
|
||||||
|
args = self.get_ipa_command_args()
|
||||||
|
just_added = False
|
||||||
|
|
||||||
|
if self.ipa_params.state in ["present", "enabled", "disabled"]:
|
||||||
|
if not self.zone:
|
||||||
|
# Since the zone doesn't exist we just create it
|
||||||
|
# with given args
|
||||||
|
self.add_ipa_command("dnszone_add", self.zone_name, args)
|
||||||
|
self.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, self.zone):
|
||||||
|
self.add_ipa_command("dnszone_mod", self.zone_name, args)
|
||||||
|
|
||||||
|
if self.ipa_params.state == "enabled" and not self.is_zone_active:
|
||||||
|
self.add_ipa_command("dnszone_enable", self.zone_name)
|
||||||
|
|
||||||
|
if self.ipa_params.state == "disabled" and self.is_zone_active:
|
||||||
|
self.add_ipa_command("dnszone_disable", self.zone_name)
|
||||||
|
|
||||||
|
if self.ipa_params.state == "absent":
|
||||||
|
if self.zone:
|
||||||
|
self.add_ipa_command("dnszone_del", self.zone_name)
|
||||||
|
|
||||||
|
# Due to a bug in FreeIPA dnszone-add won't set
|
||||||
|
# SOA Serial. The good news is that dnszone-mod does the job.
|
||||||
|
# See: https://pagure.io/freeipa/issue/8227
|
||||||
|
# Because of that, if the zone was just added with a given serial
|
||||||
|
# we run mod just after to workaround the bug
|
||||||
|
if just_added and self.ipa_params.serial is not None:
|
||||||
|
args = {
|
||||||
|
"idnssoaserial": self.ipa_params.serial,
|
||||||
|
}
|
||||||
|
self.add_ipa_command("dnszone_mod", self.zone_name, args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_argument_spec():
|
||||||
|
forwarder_spec = dict(
|
||||||
|
ip_address=dict(type=str, required=True),
|
||||||
|
port=dict(type=int, required=False, default=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
state=dict(
|
||||||
|
type="str",
|
||||||
|
default="present",
|
||||||
|
choices=["present", "absent", "enabled", "disabled"],
|
||||||
|
),
|
||||||
|
ipaadmin_principal=dict(type="str", default="admin"),
|
||||||
|
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||||
|
name=dict(type="str", default=None, required=True),
|
||||||
|
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"],
|
||||||
|
),
|
||||||
|
name_server=dict(type="str", required=False, default=None),
|
||||||
|
admin_email=dict(type="str", required=False, default=None),
|
||||||
|
allow_sync_ptr=dict(type="bool", required=False, default=None),
|
||||||
|
update_policy=dict(type="str", required=False, default=None),
|
||||||
|
dynamic_update=dict(type="bool", required=False, default=None),
|
||||||
|
dnssec=dict(type="bool", required=False, default=None),
|
||||||
|
allow_transfer=dict(type="list", required=False, default=None),
|
||||||
|
allow_query=dict(type="list", required=False, default=None),
|
||||||
|
serial=dict(type="int", required=False, default=None),
|
||||||
|
refresh=dict(type="int", required=False, default=None),
|
||||||
|
retry=dict(type="int", required=False, default=None),
|
||||||
|
expire=dict(type="int", required=False, default=None),
|
||||||
|
minimum=dict(type="int", required=False, default=None),
|
||||||
|
ttl=dict(type="int", required=False, default=None),
|
||||||
|
default_ttl=dict(type="int", required=False, default=None),
|
||||||
|
nsec3param_rec=dict(type="str", required=False, default=None),
|
||||||
|
skip_nameserver_check=dict(type="bool", required=False, default=None),
|
||||||
|
skip_overlap_check=dict(type="bool", required=False, default=None),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DNSZoneModule(argument_spec=get_argument_spec()).ipa_run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
151
tests/dnszone/test_dnszone.yml
Normal file
151
tests/dnszone/test_dnszone.yml
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
- name: Test dnszone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
- name: Ensure zone is absent.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure zone is present, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure zone is disabled.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: disabled
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure zone is disabled, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: disabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure zone is enabled.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: enabled
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure zone is enabled, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: enabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure forward_policy is none.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forward_policy: none
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure forward_policy is none, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forward_policy: none
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure forward_policy is first.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forward_policy: first
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure forward_policy is first, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forward_policy: first
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure first forwarder is set.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
port: 53
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure first and second forwarder are set.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
port: 53
|
||||||
|
- ip_address: 2001:4860:4860::8888
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure first and second forwarder are set, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 8.8.8.8
|
||||||
|
port: 53
|
||||||
|
- ip_address: 2001:4860:4860::8888
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure only second forwarder is set.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forwarders:
|
||||||
|
- ip_address: 2001:4860:4860::8888
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Nothing changes.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure no forwarders are set.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
forwarders: []
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
319
tests/dnszone/test_dnszone_mod.yml
Normal file
319
tests/dnszone/test_dnszone_mod.yml
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
---
|
||||||
|
- name: Test dnszone
|
||||||
|
hosts: ipaserver
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
- name: Ensure zone is absent.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
- name: Ensure zone is present.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_sync_ptr: true
|
||||||
|
dynamic_update: true
|
||||||
|
dnssec: true
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
serial: 1234
|
||||||
|
refresh: 3600
|
||||||
|
retry: 900
|
||||||
|
expire: 1209600
|
||||||
|
minimum: 3600
|
||||||
|
ttl: 60
|
||||||
|
default_ttl: 60
|
||||||
|
name_server: ipaserver.test.local.
|
||||||
|
skip_nameserver_check: true
|
||||||
|
admin_email: admin@example.com
|
||||||
|
nsec3param_rec: "1 7 100 abcd"
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set serial to 1234, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
serial: 1234
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set different nsec3param_rec.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
nsec3param_rec: "2 8 200 abcd"
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set same nsec3param_rec.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
nsec3param_rec: "2 8 200 abcd"
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set default_ttl to 1200
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
default_ttl: 1200
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set default_ttl to 1200, again
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
default_ttl: 1200
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set ttl to 900
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
ttl: 900
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set ttl to 900, again
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
ttl: 900
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set minimum to 1000
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
minimum: 1000
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set minimum to 1000, again
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
minimum: 1000
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set expire to 1209601
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
expire: 1209601
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set expire to 1209601, again
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
expire: 1209601
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set retry to 1200.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
retry: 1200
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set retry to 1200, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
retry: 1200
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set refresh to 4000.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
refresh: 4000
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set refresh to 4000, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
refresh: 4000
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set serial to 12345.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
serial: 12345
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set serial to 12345, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
serial: 12345
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set dnssec to false.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
dnssec: false
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set dnssec to false, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
dnssec: false
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set allow_sync_ptr to false.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_sync_ptr: false
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set allow_sync_ptr to false, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_sync_ptr: false
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Set dynamic_update to false.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
dynamic_update: false
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Set dynamic_update to false, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
dynamic_update: false
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Update allow_transfer.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
- 3.3.3.3
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Update allow_transfer, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_transfer:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
- 3.3.3.3
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Remove allow transfer.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_transfer: []
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Remove allow transfer, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_transfer: []
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Update allow_query.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
- 3.3.3.3
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Update allow_query, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_query:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 2.2.2.2
|
||||||
|
- 3.3.3.3
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Ensure allow query is empty.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_query: []
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Ensure allow query is empty, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
allow_query: []
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
|
|
||||||
|
- name: Update admin email.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
admin_email: admin2@example.com
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed
|
||||||
|
|
||||||
|
- name: Update admin email, again.
|
||||||
|
ipadnszone:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: testzone.local
|
||||||
|
admin_email: admin2@example.com
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed
|
||||||
Reference in New Issue
Block a user