#!/usr/bin/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/>. ANSIBLE_METADATA = { "metadata_version": "1.0", "supported_by": "community", "status": ["preview"], } DOCUMENTATION = """ --- module: ipadnsconfig short description: Manage FreeIPA dnsconfig description: Manage FreeIPA dnsconfig 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"] """ EXAMPLES = """ # Ensure global DNS forward configuration, allowing PTR record synchronization. - ipadnsconfig: 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: forwarders: - ip_address: 2001:4860:4860::8888 port: 53 state: absent # Disable PTR record synchronization. - ipadnsconfig: allow_sync_ptr: no # Disable global forwarders. - ipadnsconfig: forward_policy: none """ RETURN = """ """ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ temp_kdestroy, valid_creds, api_connect, \ api_command_no_name, compare_args_ipa, module_params_get, \ is_ipv4_addr, is_ipv6_addr def find_dnsconfig(module): _args = { "all": True, } _result = api_command_no_name(module, "dnsconfig_show", _args) if "result" in _result: if _result["result"].get('idnsforwarders', None) is None: _result["result"]['idnsforwarders'] = [''] return _result["result"] else: 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'] = [ fwd for fwd in _forwarders if fwd not in 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 = AnsibleModule( argument_spec=dict( # general ipaadmin_principal=dict(type='str', default='admin'), ipaadmin_password=dict(type='str', no_log=True), # 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 # general ipaadmin_principal = module_params_get(ansible_module, "ipaadmin_principal") ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") forwarders = module_params_get(ansible_module, 'forwarders') or [] forward_policy = module_params_get(ansible_module, 'forward_policy') allow_sync_ptr = module_params_get(ansible_module, 'allow_sync_ptr') state = module_params_get(ansible_module, 'state') # Check parameters. invalid = [] if state == 'absent': invalid = ['forward_policy', 'allow_sync_ptr'] for x in invalid: if vars()[x] is not None: ansible_module.fail_json( msg="Argument '%s' can not be used with state '%s'" % (x, state)) # Init changed = False ccache_dir = None ccache_name = None try: if not valid_creds(ansible_module, ipaadmin_principal): ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, ipaadmin_password) api_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: api_command_no_name(ansible_module, '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) except Exception as e: ansible_module.fail_json(msg=str(e)) finally: temp_kdestroy(ccache_dir, ccache_name) # Done ansible_module.exit_json(changed=changed) if __name__ == "__main__": main()