Blame plugins/modules/ipadnsconfig.py

Packit Service ee01e6
#!/usr/bin/python
Packit Service 0a38ef
# -*- coding: utf-8 -*-
Packit Service 0a38ef
Packit Service 0a38ef
# Authors:
Packit Service 0a38ef
#   Rafael Guterres Jeffman <rjeffman@redhat.com>
Packit Service 0a38ef
#
Packit Service 0a38ef
# Copyright (C) 2019 Red Hat
Packit Service 0a38ef
# see file 'COPYING' for use and warranty information
Packit Service 0a38ef
#
Packit Service 0a38ef
# This program is free software; you can redistribute it and/or modify
Packit Service 0a38ef
# it under the terms of the GNU General Public License as published by
Packit Service 0a38ef
# the Free Software Foundation, either version 3 of the License, or
Packit Service 0a38ef
# (at your option) any later version.
Packit Service 0a38ef
#
Packit Service 0a38ef
# This program is distributed in the hope that it will be useful,
Packit Service 0a38ef
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 0a38ef
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 0a38ef
# GNU General Public License for more details.
Packit Service 0a38ef
#
Packit Service 0a38ef
# You should have received a copy of the GNU General Public License
Packit Service 0a38ef
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
ANSIBLE_METADATA = {
Packit Service 0a38ef
    "metadata_version": "1.0",
Packit Service 0a38ef
    "supported_by": "community",
Packit Service 0a38ef
    "status": ["preview"],
Packit Service 0a38ef
}
Packit Service 0a38ef
Packit Service 0a38ef
DOCUMENTATION = """
Packit Service 0a38ef
---
Packit Service 0a38ef
module: ipadnsconfig
Packit Service 0a38ef
short description: Manage FreeIPA dnsconfig
Packit Service 0a38ef
description: Manage FreeIPA dnsconfig
Packit Service 0a38ef
options:
Packit Service 0a38ef
  ipaadmin_principal:
Packit Service 0a38ef
    description: The admin principal
Packit Service 0a38ef
    default: admin
Packit Service 0a38ef
  ipaadmin_password:
Packit Service 0a38ef
    description: The admin password
Packit Service 0a38ef
    required: false
Packit Service 0a38ef
Packit Service 0a38ef
  forwarders:
Packit Service 0a38ef
    description: The list of global DNS forwarders.
Packit Service 0a38ef
    required: false
Packit Service 0a38ef
    options:
Packit Service 0a38ef
      ip_address:
Packit Service 0a38ef
        description: The forwarder nameserver IP address list (IPv4 and IPv6).
Packit Service 0a38ef
        required: true
Packit Service 0a38ef
      port:
Packit Service 0a38ef
        description: The port to forward requests to.
Packit Service 0a38ef
        required: false
Packit Service 0a38ef
  forward_policy:
Packit Service 0a38ef
    description:
Packit Service 0a38ef
      Global forwarding policy. Set to "none" to disable any configured
Packit Service 0a38ef
      global forwarders.
Packit Service 0a38ef
    required: false
Packit Service 0a38ef
    choices: ['only', 'first', 'none']
Packit Service 0a38ef
  allow_sync_ptr:
Packit Service 0a38ef
    description:
Packit Service 0a38ef
      Allow synchronization of forward (A, AAAA) and reverse (PTR) records.
Packit Service 0a38ef
    required: false
Packit Service 0a38ef
    type: bool
Packit Service 0a38ef
  state:
Packit Service 0a38ef
    description: State to ensure
Packit Service 0a38ef
    default: present
Packit Service 0a38ef
    choices: ["present", "absent"]
Packit Service 0a38ef
"""
Packit Service 0a38ef
Packit Service 0a38ef
EXAMPLES = """
Packit Service 0a38ef
# Ensure global DNS forward configuration, allowing PTR record synchronization.
Packit Service 0a38ef
- ipadnsconfig:
Packit Service 0a38ef
    forwarders:
Packit Service 0a38ef
      - ip_address: 8.8.4.4
Packit Service 0a38ef
      - ip_address: 2001:4860:4860::8888
Packit Service 0a38ef
        port: 53
Packit Service 0a38ef
    forward_policy: only
Packit Service 0a38ef
    allow_sync_ptr: yes
Packit Service 0a38ef
Packit Service 0a38ef
# Ensure forwarder is absent.
Packit Service 0a38ef
- ipadnsconfig:
Packit Service 0a38ef
    forwarders:
Packit Service 0a38ef
      - ip_address: 2001:4860:4860::8888
Packit Service 0a38ef
        port: 53
Packit Service 0a38ef
    state: absent
Packit Service 0a38ef
Packit Service 0a38ef
# Disable PTR record synchronization.
Packit Service 0a38ef
- ipadnsconfig:
Packit Service 0a38ef
    allow_sync_ptr: no
Packit Service 0a38ef
Packit Service 0a38ef
# Disable global forwarders.
Packit Service 0a38ef
- ipadnsconfig:
Packit Service 0a38ef
    forward_policy: none
Packit Service 0a38ef
"""
Packit Service 0a38ef
Packit Service 0a38ef
RETURN = """
Packit Service 0a38ef
"""
Packit Service 0a38ef
Packit Service 0a38ef
from ansible.module_utils.basic import AnsibleModule
Packit Service 0a38ef
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
Packit Service 0a38ef
    temp_kdestroy, valid_creds, api_connect, \
Packit Service 0a38ef
    api_command_no_name, compare_args_ipa, module_params_get, \
Packit Service 0a38ef
    is_ipv4_addr, is_ipv6_addr
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def find_dnsconfig(module):
Packit Service 0a38ef
    _args = {
Packit Service 0a38ef
        "all": True,
Packit Service 0a38ef
    }
Packit Service 0a38ef
Packit Service 0a38ef
    _result = api_command_no_name(module, "dnsconfig_show", _args)
Packit Service 0a38ef
Packit Service 0a38ef
    if "result" in _result:
Packit Service 0a38ef
        if _result["result"].get('idnsforwarders', None) is None:
Packit Service 0a38ef
            _result["result"]['idnsforwarders'] = ['']
Packit Service 0a38ef
        return _result["result"]
Packit Service 0a38ef
    else:
Packit Service 0a38ef
        module.fail_json(msg="Could not retrieve current DNS configuration.")
Packit Service 0a38ef
    return None
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def gen_args(module, state, dnsconfig, forwarders, forward_policy,
Packit Service 0a38ef
             allow_sync_ptr):
Packit Service 0a38ef
    _args = {}
Packit Service 0a38ef
Packit Service 0a38ef
    if forwarders:
Packit Service 0a38ef
        _forwarders = []
Packit Service 0a38ef
        for forwarder in forwarders:
Packit Service 0a38ef
            ip_address = forwarder.get('ip_address')
Packit Service 0a38ef
            port = forwarder.get('port')
Packit Service 0a38ef
            if not (is_ipv4_addr(ip_address) or is_ipv6_addr(ip_address)):
Packit Service 0a38ef
                module.fail_json(
Packit Service 0a38ef
                    msg="Invalid IP for DNS forwarder: %s" % ip_address)
Packit Service 0a38ef
            if port is None:
Packit Service 0a38ef
                _forwarders.append(ip_address)
Packit Service 0a38ef
            else:
Packit Service 0a38ef
                _forwarders.append('%s port %d' % (ip_address, port))
Packit Service 0a38ef
Packit Service 0a38ef
        global_forwarders = dnsconfig.get('idnsforwarders', [])
Packit Service 0a38ef
        if state == 'absent':
Packit Service 0a38ef
            _args['idnsforwarders'] = [
Packit Service 0a38ef
                fwd for fwd in global_forwarders if fwd not in _forwarders]
Packit Service 0a38ef
            # When all forwarders should be excluded, use an empty string ('').
Packit Service 0a38ef
            if not _args['idnsforwarders']:
Packit Service 0a38ef
                _args['idnsforwarders'] = ['']
Packit Service 0a38ef
Packit Service 0a38ef
        elif state == 'present':
Packit Service 0a38ef
            _args['idnsforwarders'] = [
Packit Service 0a38ef
                fwd for fwd in _forwarders if fwd not in global_forwarders]
Packit Service 0a38ef
            # If no forwarders should be added, remove argument.
Packit Service 0a38ef
            if not _args['idnsforwarders']:
Packit Service 0a38ef
                del _args['idnsforwarders']
Packit Service 0a38ef
Packit Service 0a38ef
        else:
Packit Service 0a38ef
            # shouldn't happen, but let's be paranoid.
Packit Service 0a38ef
            module.fail_json(msg="Invalid state: %s" % state)
Packit Service 0a38ef
Packit Service 0a38ef
    if forward_policy is not None:
Packit Service 0a38ef
        _args['idnsforwardpolicy'] = forward_policy
Packit Service 0a38ef
Packit Service 0a38ef
    if allow_sync_ptr is not None:
Packit Service 0a38ef
        _args['idnsallowsyncptr'] = 'TRUE' if allow_sync_ptr else 'FALSE'
Packit Service 0a38ef
Packit Service 0a38ef
    return _args
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def main():
Packit Service 0a38ef
    forwarder_spec = dict(
Packit Service 0a38ef
       ip_address=dict(type=str, required=True),
Packit Service 0a38ef
       port=dict(type=int, required=False, default=None)
Packit Service 0a38ef
    )
Packit Service 0a38ef
Packit Service 0a38ef
    ansible_module = AnsibleModule(
Packit Service 0a38ef
       argument_spec=dict(
Packit Service 0a38ef
           # general
Packit Service 0a38ef
           ipaadmin_principal=dict(type='str', default='admin'),
Packit Service 0a38ef
           ipaadmin_password=dict(type='str', no_log=True),
Packit Service 0a38ef
Packit Service 0a38ef
           # dnsconfig
Packit Service 0a38ef
           forwarders=dict(type='list', default=None, required=False,
Packit Service 0a38ef
                           options=dict(**forwarder_spec)),
Packit Service 0a38ef
           forward_policy=dict(type='str', required=False, default=None,
Packit Service 0a38ef
                               choices=['only', 'first', 'none']),
Packit Service 0a38ef
           allow_sync_ptr=dict(type='bool', required=False, default=None),
Packit Service 0a38ef
Packit Service 0a38ef
           # general
Packit Service 0a38ef
           state=dict(type="str", default="present",
Packit Service 0a38ef
                      choices=["present", "absent"]),
Packit Service 0a38ef
Packit Service 0a38ef
       )
Packit Service 0a38ef
    )
Packit Service 0a38ef
Packit Service 0a38ef
    ansible_module._ansible_debug = True
Packit Service 0a38ef
Packit Service 0a38ef
    # general
Packit Service 0a38ef
    ipaadmin_principal = module_params_get(ansible_module,
Packit Service 0a38ef
                                           "ipaadmin_principal")
Packit Service 0a38ef
    ipaadmin_password = module_params_get(ansible_module,
Packit Service 0a38ef
                                          "ipaadmin_password")
Packit Service 0a38ef
Packit Service 0a38ef
    forwarders = module_params_get(ansible_module, 'forwarders') or []
Packit Service 0a38ef
    forward_policy = module_params_get(ansible_module, 'forward_policy')
Packit Service 0a38ef
    allow_sync_ptr = module_params_get(ansible_module, 'allow_sync_ptr')
Packit Service 0a38ef
Packit Service 0a38ef
    state = module_params_get(ansible_module, 'state')
Packit Service 0a38ef
Packit Service 0a38ef
    # Check parameters.
Packit Service 0a38ef
    invalid = []
Packit Service 0a38ef
    if state == 'absent':
Packit Service 0a38ef
        invalid = ['forward_policy', 'allow_sync_ptr']
Packit Service 0a38ef
Packit Service 0a38ef
    for x in invalid:
Packit Service 0a38ef
        if vars()[x] is not None:
Packit Service 0a38ef
            ansible_module.fail_json(
Packit Service 0a38ef
                msg="Argument '%s' can not be used with state '%s'" %
Packit Service 0a38ef
                (x, state))
Packit Service 0a38ef
Packit Service 0a38ef
    # Init
Packit Service 0a38ef
Packit Service 0a38ef
    changed = False
Packit Service 0a38ef
    ccache_dir = None
Packit Service 0a38ef
    ccache_name = None
Packit Service 0a38ef
    try:
Packit Service 0a38ef
        if not valid_creds(ansible_module, ipaadmin_principal):
Packit Service 0a38ef
            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
Packit Service 0a38ef
                                                 ipaadmin_password)
Packit Service 0a38ef
        api_connect()
Packit Service 0a38ef
Packit Service 0a38ef
        res_find = find_dnsconfig(ansible_module)
Packit Service 0a38ef
        args = gen_args(ansible_module, state, res_find, forwarders,
Packit Service 0a38ef
                        forward_policy, allow_sync_ptr)
Packit Service 0a38ef
Packit Service 0a38ef
        # Execute command only if configuration changes.
Packit Service 0a38ef
        if not compare_args_ipa(ansible_module, args, res_find):
Packit Service 0a38ef
            try:
Packit Service 0a38ef
                api_command_no_name(ansible_module, 'dnsconfig_mod', args)
Packit Service 0a38ef
                # If command did not fail, something changed.
Packit Service 0a38ef
                changed = True
Packit Service 0a38ef
Packit Service 0a38ef
            except Exception as e:
Packit Service 0a38ef
                msg = str(e)
Packit Service 0a38ef
                ansible_module.fail_json(msg="dnsconfig_mod: %s" % msg)
Packit Service 0a38ef
Packit Service 0a38ef
    except Exception as e:
Packit Service 0a38ef
        ansible_module.fail_json(msg=str(e))
Packit Service 0a38ef
Packit Service 0a38ef
    finally:
Packit Service 0a38ef
        temp_kdestroy(ccache_dir, ccache_name)
Packit Service 0a38ef
Packit Service 0a38ef
    # Done
Packit Service 0a38ef
Packit Service 0a38ef
    ansible_module.exit_json(changed=changed)
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
if __name__ == "__main__":
Packit Service 0a38ef
    main()