# -*- coding: utf-8 -*- """Module to control corenetworks DNS API.""" from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} DOCUMENTATION = r""" --- module: corenetworks_dns short_description: Interface with the DNS API of core-networks.de description: - "Manages DNS zones and records via the core networks API, see the docs: U(https://beta.api.core-networks.de/doc/)." options: api_user: description: - Account API username. If omitted, the environment variables C(CN_API_USER) and C(CN_API_PASSWORD) will be looked for. - You should prefere to use `api_token` or the `corenetworks_token` module to create one to prevent running into rate limits. type: str api_password: description: - Account API password. type: str api_token: description: - Account API token. type: str zone: description: - The name of the Zone to work with (e.g. "example.com"). - The Zone must already exist. zone: type: str required: true aliases: [ domain ] record: description: - Used record relative to the given zone. - Default is C(@) (e.g. the zone name). type: str default: "@" aliases: [ name ] type: description: - The type of DNS record to create. choices: [ "A", "ALIAS", "CNAME", "MX", "SPF", "URL", "TXT", "NS", "SRV", "NAPTR", "PTR", "AAAA", "SSHFP", "HINFO", "POOL" ] type: str ttl: description: - The TTL to give the new record in seconds. default: 3600 type: int value: description: - Record value. - Must be specified when trying to ensure a record exists. type: str solo: description: - Whether the record should be the only one for that record type and record name. - Only use with C(state=present). - This will delete all other records with the same record name and type. type: bool throttle: description: - Throttle API calls in seconds. default: 2 type: int state: description: - whether the record should exist or not choices: [ "present", "absent" ] default: present type: str requirements: - "corenetworks >= 0.1.4" author: "Robert Kaussow (@xoxys)" """ # noqa EXAMPLES = """ - name: Create a test.my.com A record to point to 127.0.0.1 corenetworks_dns: zone: my.com record: test type: A value: 127.0.0.1 delegate_to: localhost register: record - name: Create a my.com CNAME record to example.com corenetworks_dns: zone: my.com type: CNAME value: example.com state: present delegate_to: localhost - name: Change TTL value for a record corenetworks_dns: zone: my.com type: CNAME value: example.com ttl: 600 state: present delegate_to: localhost - name: Delete the record corenetworks_dns: zone: my.com type: CNAME value: example.com state: absent delegate_to: localhost """ RETURN = r"""# """ import copy import time import traceback CORENETWORKS_IMP_ERR = None try: from corenetworks import CoreNetworks from corenetworks.exceptions import CoreNetworksException HAS_CORENETWORKS = True except ImportError: CORENETWORKS_IMP_ERR = traceback.format_exc() HAS_CORENETWORKS = False from ansible.module_utils.basic import AnsibleModule, missing_required_lib def delete_records(client, module, zone, params, is_solo=False): changed = False search = copy.deepcopy(params) if is_solo: search.pop("data", None) search.pop("ttl", None) records = client.records(zone, params=search) for r in records: r["ttl"] = int(r["ttl"]) if is_solo: if not (r["data"] == params["data"] and r["ttl"] == params["ttl"]): changed = True if not module.check_mode: client.delete_record(zone, r) else: changed = True if not module.check_mode: client.delete_record(zone, r) return changed def add_record(client, module, zone, params): changed = False result = [] records = client.records(zone, params=params) if len(records) > 1: module.fail_json( msg="More than one record already exists for the given attributes. " "That should be impossible, please open an issue!" ) if len(records) == 0: changed = True if not module.check_mode: result = client.add_record(zone, params=params) return result, changed def main(): module = AnsibleModule( argument_spec=dict( api_user=dict(type="str"), api_password=dict(type="str", no_log=True), api_token=dict(type="str", no_log=True), zone=dict(type="str", required=True, aliases=["domain"]), record=dict(type="str", default="@", aliases=["name"]), type=dict( type="str", choices=[ "A", "ALIAS", "CNAME", "MX", "SPF", "URL", "TXT", "NS", "SRV", "NAPTR", "PTR", "AAAA", "SSHFP", "HINFO", "POOL" ] ), ttl=dict(type="int", default=3600), value=dict(type="str"), solo=dict(type="bool", default=False), throttle=dict(type="int", default=2), state=dict(type="str", choices=["present", "absent"], default="present"), ), required_together=[["record", "value"]], supports_check_mode=True, ) if not HAS_CORENETWORKS: module.fail_json(msg=missing_required_lib("corenetworks"), exception=CORENETWORKS_IMP_ERR) api_user = module.params.get("api_user") api_password = module.params.get("api_password") api_token = module.params.get("api_token") zone = module.params.get("zone") record = module.params.get("record") record_type = module.params.get("type") ttl = module.params.get("ttl") value = module.params.get("value") state = module.params.get("state") throttle = module.params.get("throttle") is_solo = module.params.get("solo") params = {"name": record, "ttl": ttl} # sanity checks if not record_type: if state == "present": module.fail_json(msg="Missing the record type") else: params["type"] = record_type if not value: if state == "present": module.fail_json(msg="Missing the record value") else: params["data"] = value if is_solo and state == "absent": module.fail_json(msg="solo=true can only be used with state=present") # perform actions try: # request throttling to workaround the rate limit time.sleep(throttle) changed = False if api_token: client = CoreNetworks(api_token=api_token, auto_commit=True) else: client = CoreNetworks(user=api_user, password=api_password, auto_commit=True) if state == "present": changed_solo = False if is_solo: changed_solo = delete_records(client, module, zone, params, is_solo=True) result, changed = add_record(client, module, zone, params) module.exit_json(changed=changed_solo + changed, result=result) # state is absent else: changed = delete_records(client, module, zone, params) module.exit_json(changed=changed) except CoreNetworksException as e: module.fail_json(msg="Failure in core networks API communication: {}".format(str(e))) module.fail_json(msg="Unknown what you wanted me to do") if __name__ == "__main__": main()