#!/usr/bin/python # Copyright (c) 2020, Christian Wollinger # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import annotations DOCUMENTATION = r""" module: ipwcli_dns short_description: Manage DNS Records for Ericsson IPWorks using C(ipwcli) version_added: '0.2.0' description: - Manage DNS records for the Ericsson IPWorks DNS server. The module uses the C(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. extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: full diff_mode: support: none 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 O(type=A) or O(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 O(type=SRV). type: int target: description: - Sets the target of the SRV record. - Required for O(type=SRV). type: str order: description: - Sets the order of the NAPTR record. - Required for O(type=NAPTR). type: int preference: description: - Sets the preference of the NAPTR record. - Required for O(type=NAPTR). type: int flags: description: - Sets one of the possible flags of NAPTR record. - Required for O(type=NAPTR). type: str choices: ['S', 'A', 'U', 'P'] service: description: - Sets the service of the NAPTR record. - Required for O(type=NAPTR). type: str replacement: description: - Sets the replacement of the NAPTR record. - Required for O(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 = r""" - 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 = r""" record: description: The created record from the input params. type: str returned: always """ from ansible.module_utils.basic import AnsibleModule class ResourceRecord: 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 = ( f"naptrrecord {self.dnsname} -set ttl={self.ttl};container={self.container};order={self.order};" f'preference={self.preference};flags="{self.flags}";service="{self.service}";replacement="{self.replacement}"' ) return record def create_srvrecord(self): # create SRV record with the given params record = ( f"srvrecord {self.dnsname} -set ttl={self.ttl};container={self.container};priority={self.priority};" f"weight={self.weight};port={self.port};target={self.target}" ) return record def create_arecord(self): # create A record with the given params if self.dnstype == "AAAA": record = f"aaaarecord {self.dnsname} {self.address} -set ttl={self.ttl};container={self.container}" else: record = f"arecord {self.dnsname} {self.address} -set ttl={self.ttl};container={self.container}" return record def list_record(self, record): # check if the record exists via list on ipwcli search = f"list {record.replace(';', '&&').replace('set', 'where')}" cmd = [ self.module.get_bin_path("ipwcli", True), f"-user={self.user}", f"-password={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 ( (f"ARecord {self.dnsname}" in out and rc == 0) or (f"SRVRecord {self.dnsname}" in out and rc == 0) or (f"NAPTRRecord {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 = f"create {record}" cmd = [ self.module.get_bin_path("ipwcli", True), f"-user={self.user}", f"-password={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 = f"delete {record.replace(';', '&&').replace('set', 'where')}" cmd = [ self.module.get_bin_path("ipwcli", True), f"-user={self.user}", f"-password={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"), ttl=dict(type="int", default=3600), state=dict(type="str", default="present", choices=["absent", "present"]), priority=dict(type="int", default=10), weight=dict(type="int", default=10), port=dict(type="int"), target=dict(type="str"), order=dict(type="int"), preference=dict(type="int"), flags=dict(type="str", choices=["S", "A", "U", "P"]), service=dict(type="str"), replacement=dict(type="str"), 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()