xoxys.general/plugins/modules/corenetworks_dns.py
Robert Kaussow c024883a2b
Some checks failed
continuous-integration/drone/push Build is failing
feat: add throttle option to corenetworks_dns module
2021-02-19 13:38:30 +01:00

266 lines
7.5 KiB
Python

# -*- 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),
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()