Files
ansible-freeipa/plugins/modules/ipadnsconfig.py
Rafael Guterres Jeffman dead467982 dnsconfig: Fix management of forwarders.
If one tries to set a list of forwarders which include an already
existing forwarder, the existing forwarder is removed, and the list
of configured forwarders contain only the new ones.

This patch fixes this behavior by setting a union of the currently
available forwarders and the list of forwarders provided in the
playbook.

Tests were added to ensure this behavior.
2022-01-19 14:36:57 -03:00

233 lines
6.8 KiB
Python

# -*- coding: utf-8 -*-
# Authors:
# Rafael Guterres Jeffman <rjeffman@redhat.com>
#
# Copyright (C) 2019 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = """
---
module: ipadnsconfig
short description: Manage FreeIPA dnsconfig
description: Manage FreeIPA dnsconfig
extends_documentation_fragment:
- ipamodule_base_docs
options:
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"]
"""
EXAMPLES = """
# Ensure global DNS forward configuration, allowing PTR record synchronization.
- ipadnsconfig:
ipaadmin_password: SomeADMINpassword
forwarders:
- ip_address: 8.8.4.4
- ip_address: 2001:4860:4860::8888
port: 53
forward_policy: only
allow_sync_ptr: yes
# Ensure forwarder is absent.
- ipadnsconfig:
ipaadmin_password: SomeADMINpassword
forwarders:
- ip_address: 2001:4860:4860::8888
port: 53
state: absent
# Disable PTR record synchronization.
- ipadnsconfig:
ipaadmin_password: SomeADMINpassword
allow_sync_ptr: no
# Disable global forwarders.
- ipadnsconfig:
ipaadmin_password: SomeADMINpassword
forward_policy: none
"""
RETURN = """
"""
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, is_ipv4_addr, is_ipv6_addr
def find_dnsconfig(module):
_args = {
"all": True,
}
_result = module.ipa_command_no_name("dnsconfig_show", _args)
if "result" in _result:
if _result["result"].get('idnsforwarders', None) is None:
_result["result"]['idnsforwarders'] = ['']
return _result["result"]
module.fail_json(msg="Could not retrieve current DNS configuration.")
return None
def gen_args(module, state, dnsconfig, forwarders, forward_policy,
allow_sync_ptr):
_args = {}
if forwarders:
_forwarders = []
for forwarder in forwarders:
ip_address = forwarder.get('ip_address')
port = forwarder.get('port')
if not (is_ipv4_addr(ip_address) or is_ipv6_addr(ip_address)):
module.fail_json(
msg="Invalid IP for DNS forwarder: %s" % ip_address)
if port is None:
_forwarders.append(ip_address)
else:
_forwarders.append('%s port %d' % (ip_address, port))
global_forwarders = dnsconfig.get('idnsforwarders', [])
if state == 'absent':
_args['idnsforwarders'] = [
fwd for fwd in global_forwarders if fwd not in _forwarders]
# When all forwarders should be excluded, use an empty string ('').
if not _args['idnsforwarders']:
_args['idnsforwarders'] = ['']
elif state == 'present':
_args['idnsforwarders'] = \
list(set(list(_forwarders) + list(global_forwarders)))
# If no forwarders should be added, remove argument.
if not _args['idnsforwarders']:
del _args['idnsforwarders']
else:
# shouldn't happen, but let's be paranoid.
module.fail_json(msg="Invalid state: %s" % state)
if forward_policy is not None:
_args['idnsforwardpolicy'] = forward_policy
if allow_sync_ptr is not None:
_args['idnsallowsyncptr'] = 'TRUE' if allow_sync_ptr else 'FALSE'
return _args
def main():
forwarder_spec = dict(
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),
# general
state=dict(type="str", default="present",
choices=["present", "absent"]),
)
)
ansible_module._ansible_debug = True
# dnsconfig
forwarders = ansible_module.params_get('forwarders') or []
forward_policy = ansible_module.params_get('forward_policy')
allow_sync_ptr = ansible_module.params_get('allow_sync_ptr')
state = ansible_module.params_get('state')
# Check parameters.
invalid = []
if state == 'absent':
invalid = ['forward_policy', 'allow_sync_ptr']
ansible_module.params_fail_used_invalid(invalid, state)
# Init
changed = False
# Connect to IPA API
with ansible_module.ipa_connect():
res_find = find_dnsconfig(ansible_module)
args = gen_args(ansible_module, state, res_find, forwarders,
forward_policy, allow_sync_ptr)
# Execute command only if configuration changes.
if not compare_args_ipa(ansible_module, args, res_find):
try:
if not ansible_module.check_mode:
ansible_module.ipa_command_no_name('dnsconfig_mod', args)
# If command did not fail, something changed.
changed = True
except Exception as e:
msg = str(e)
ansible_module.fail_json(msg="dnsconfig_mod: %s" % msg)
# Done
ansible_module.exit_json(changed=changed)
if __name__ == "__main__":
main()