#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2020, Christian Wollinger # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = ''' --- module: ipwcli_dns short_description: Manage DNS Records for Ericsson IPWorks via ipwcli version_added: '0.2.0' description: - "Manage DNS records for the Ericsson IPWorks DNS server. The module will use the ipwcli to deploy the DNS records." requirements: - ipwcli (installed on Ericsson IPWorks) notes: - To make the DNS record changes effective, you need to run C(update dnsserver) on the ipwcli. options: dnsname: description: - Name of the record. required: true type: str type: description: - Type of the record. required: true type: str choices: [ NAPTR, SRV, A, AAAA ] container: description: - Sets the container zone for the record. required: true type: str address: description: - The IP address for the A or AAAA record. - Required for I(type=A) or I(type=AAAA). type: str ttl: description: - Sets the TTL of the record. type: int default: 3600 state: description: - Whether the record should exist or not. type: str choices: [ absent, present ] default: present priority: description: - Sets the priority of the SRV record. type: int default: 10 weight: description: - Sets the weight of the SRV record. type: int default: 10 port: description: - Sets the port of the SRV record. - Required for I(type=SRV). type: int target: description: - Sets the target of the SRV record. - Required for I(type=SRV). type: str order: description: - Sets the order of the NAPTR record. - Required for I(type=NAPTR). type: int preference: description: - Sets the preference of the NAPTR record. - Required for I(type=NAPTR). type: int flags: description: - Sets one of the possible flags of NAPTR record. - Required for I(type=NAPTR). type: str choices: ['S', 'A', 'U', 'P'] service: description: - Sets the service of the NAPTR record. - Required for I(type=NAPTR). type: str replacement: description: - Sets the replacement of the NAPTR record. - Required for I(type=NAPTR). type: str username: description: - Username to login on ipwcli. type: str required: true password: description: - Password to login on ipwcli. type: str required: true author: - Christian Wollinger (@cwollinger) ''' EXAMPLES = ''' - name: Create A record community.general.ipwcli_dns: dnsname: example.com type: A container: ZoneOne address: 127.0.0.1 - name: Remove SRV record if exists community.general.ipwcli_dns: dnsname: _sip._tcp.test.example.com type: SRV container: ZoneOne ttl: 100 state: absent target: example.com port: 5060 - name: Create NAPTR record community.general.ipwcli_dns: dnsname: test.example.com type: NAPTR preference: 10 container: ZoneOne ttl: 100 order: 10 service: 'SIP+D2T' replacement: '_sip._tcp.test.example.com.' flags: S ''' RETURN = ''' record: description: The created record from the input params type: str returned: always ''' from ansible.module_utils.basic import AnsibleModule import os class ResourceRecord(object): def __init__(self, module): self.module = module self.dnsname = module.params['dnsname'] self.dnstype = module.params['type'] self.container = module.params['container'] self.address = module.params['address'] self.ttl = module.params['ttl'] self.state = module.params['state'] self.priority = module.params['priority'] self.weight = module.params['weight'] self.port = module.params['port'] self.target = module.params['target'] self.order = module.params['order'] self.preference = module.params['preference'] self.flags = module.params['flags'] self.service = module.params['service'] self.replacement = module.params['replacement'] self.user = module.params['username'] self.password = module.params['password'] def create_naptrrecord(self): # create NAPTR record with the given params record = ('naptrrecord %s -set ttl=%s;container=%s;order=%s;preference=%s;flags="%s";service="%s";replacement="%s"' % (self.dnsname, self.ttl, self.container, self.order, self.preference, self.flags, self.service, self.replacement)) return record def create_srvrecord(self): # create SRV record with the given params record = ('srvrecord %s -set ttl=%s;container=%s;priority=%s;weight=%s;port=%s;target=%s' % (self.dnsname, self.ttl, self.container, self.priority, self.weight, self.port, self.target)) return record def create_arecord(self): # create A record with the given params if self.dnstype == 'AAAA': record = 'aaaarecord %s %s -set ttl=%s;container=%s' % (self.dnsname, self.address, self.ttl, self.container) else: record = 'arecord %s %s -set ttl=%s;container=%s' % (self.dnsname, self.address, self.ttl, self.container) return record def list_record(self, record): # check if the record exists via list on ipwcli search = 'list %s' % (record.replace(';', '&&').replace('set', 'where')) cmd = [ self.module.get_bin_path('ipwcli', True), '-user=%s' % self.user, '-password=%s' % self.password, ] rc, out, err = self.module.run_command(cmd, data=search) if 'Invalid username or password' in out: self.module.fail_json(msg='access denied at ipwcli login: Invalid username or password') if (('ARecord %s' % self.dnsname in out and rc == 0) or ('SRVRecord %s' % self.dnsname in out and rc == 0) or ('NAPTRRecord %s' % self.dnsname in out and rc == 0)): return True, rc, out, err return False, rc, out, err def deploy_record(self, record): # check what happens if create fails on ipworks stdin = 'create %s' % (record) cmd = [ self.module.get_bin_path('ipwcli', True), '-user=%s' % self.user, '-password=%s' % self.password, ] rc, out, err = self.module.run_command(cmd, data=stdin) if 'Invalid username or password' in out: self.module.fail_json(msg='access denied at ipwcli login: Invalid username or password') if '1 object(s) created.' in out: return rc, out, err else: self.module.fail_json(msg='record creation failed', stderr=out) def delete_record(self, record): # check what happens if create fails on ipworks stdin = 'delete %s' % (record.replace(';', '&&').replace('set', 'where')) cmd = [ self.module.get_bin_path('ipwcli', True), '-user=%s' % self.user, '-password=%s' % self.password, ] rc, out, err = self.module.run_command(cmd, data=stdin) if 'Invalid username or password' in out: self.module.fail_json(msg='access denied at ipwcli login: Invalid username or password') if '1 object(s) were updated.' in out: return rc, out, err else: self.module.fail_json(msg='record deletion failed', stderr=out) def run_module(): # define available arguments/parameters a user can pass to the module module_args = dict( dnsname=dict(type='str', required=True), type=dict(type='str', required=True, choices=['A', 'AAAA', 'SRV', 'NAPTR']), container=dict(type='str', required=True), address=dict(type='str', required=False), ttl=dict(type='int', required=False, default=3600), state=dict(type='str', default='present', choices=['absent', 'present']), priority=dict(type='int', required=False, default=10), weight=dict(type='int', required=False, default=10), port=dict(type='int', required=False), target=dict(type='str', required=False), order=dict(type='int', required=False), preference=dict(type='int', required=False), flags=dict(type='str', required=False, choices=['S', 'A', 'U', 'P']), service=dict(type='str', required=False), replacement=dict(type='str', required=False), username=dict(type='str', required=True), password=dict(type='str', required=True, no_log=True) ) # define result result = dict( changed=False, stdout='', stderr='', rc=0, record='' ) # supports check mode module = AnsibleModule( argument_spec=module_args, required_if=[ ['type', 'A', ['address']], ['type', 'AAAA', ['address']], ['type', 'SRV', ['port', 'target']], ['type', 'NAPTR', ['preference', 'order', 'service', 'replacement']], ], supports_check_mode=True ) user = ResourceRecord(module) if user.dnstype == 'NAPTR': record = user.create_naptrrecord() elif user.dnstype == 'SRV': record = user.create_srvrecord() elif user.dnstype == 'A' or user.dnstype == 'AAAA': record = user.create_arecord() found, rc, out, err = user.list_record(record) if found and user.state == 'absent': if module.check_mode: module.exit_json(changed=True) rc, out, err = user.delete_record(record) result['changed'] = True result['record'] = record result['rc'] = rc result['stdout'] = out result['stderr'] = err elif not found and user.state == 'present': if module.check_mode: module.exit_json(changed=True) rc, out, err = user.deploy_record(record) result['changed'] = True result['record'] = record result['rc'] = rc result['stdout'] = out result['stderr'] = err else: result['changed'] = False result['record'] = record result['rc'] = rc result['stdout'] = out result['stderr'] = err module.exit_json(**result) def main(): run_module() if __name__ == '__main__': main()