From 0b365075408649f185611f25829fb289abc8a6bb Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Fri, 6 Dec 2013 17:03:08 -0600 Subject: [PATCH] Initial commit of rax_dns_record --- cloud/rax_dns_record | 242 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 cloud/rax_dns_record diff --git a/cloud/rax_dns_record b/cloud/rax_dns_record new file mode 100644 index 00000000000..0d0ca62d6a0 --- /dev/null +++ b/cloud/rax_dns_record @@ -0,0 +1,242 @@ +#!/usr/bin/python -tt +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: rax_dns_record +short_description: Manage DNS records on Rackspace Cloud DNS +description: + - Manage DNS records on Rackspace Cloud DNS +version_added: 1.5 +options: + api_key: + description: + - Rackspace API key (overrides C(credentials)) + comment: + description: + - Brief description of the domain. Maximum length of 160 characters + credentials: + description: + - File to find the Rackspace credentials in (ignored if C(api_key) and + C(username) are provided) + default: null + aliases: ['creds_file'] + data: + description: + - IP address for A/AAAA record, FQDN for CNAME/MX/NS, or text data for + SRV/TXT + required: True + domain: + description: + - Domain name to create the record in + required: True + name: + description: + - FWQN record name to create + required: True + priority: + description: + - Required for MX and SRV records, but forbidden for other record types. + If specified, must be an integer from 0 to 65535. + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present + ttl: + description: + - Time to live of domain in seconds + default: 3600 + type: + description: + - DNS record type + choices: ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'SRV', 'TXT'] + default: A + username: + description: + - Rackspace username (overrides C(credentials)) +requirements: [ "pyrax" ] +author: Matt Martz +notes: + - The following environment variables can be used, C(RAX_USERNAME), + C(RAX_API_KEY), C(RAX_CREDS_FILE), C(RAX_CREDENTIALS), C(RAX_REGION). + - C(RAX_CREDENTIALS) and C(RAX_CREDS_FILE) points to a credentials file + appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating) + - C(RAX_USERNAME) and C(RAX_API_KEY) obviate the use of a credentials file + - C(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...) +''' + +EXAMPLES = ''' +- name: Create record + hosts: all + gather_facts: False + tasks: + - name: Record create request + local_action: + module: rax_dns_record + credentials: ~/.raxpub + domain: example.org + name: www.example.org + data: 127.0.0.1 + type: A + register: rax_dns_record +''' + +import sys +import os + +from types import NoneType + +try: + import pyrax +except ImportError: + print("failed=True msg='pyrax required for this module'") + sys.exit(1) + +NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) + + +def to_dict(obj): + instance = {} + for key in dir(obj): + value = getattr(obj, key) + if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): + instance[key] = value + return instance + + +def rax_dns_record(module, comment, data, domain, name, priority, record_type, + state, ttl): + changed = False + + dns = pyrax.cloud_dns + + if state == 'present': + if not priority and record_type in ['MX', 'SRV']: + module.fail_json(msg='A "priority" attribute is required for ' + 'creating a MX or SRV record') + + try: + domain = dns.find(name=domain) + except Exception, e: + module.fail_json(msg='%s' % e.message) + + try: + record = domain.find_record(record_type, name=name) + except pyrax.exceptions.DomainRecordNotUnique, e: + module.fail_json(msg='%s' % e.message) + except pyrax.exceptions.DomainRecordNotFound, e: + try: + record_data = { + 'type': record_type, + 'name': name, + 'data': data, + 'ttl': ttl + } + if comment: + record_data.update(dict(comment=comment)) + if priority: + record_data.update(dict(priority=priority)) + + record = domain.add_records([record_data])[0] + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + update = {} + if comment != getattr(record, 'comment', None): + update['comment'] = comment + if ttl != getattr(record, 'ttl', None): + update['ttl'] = ttl + if priority != getattr(record, 'priority', None): + update['priority'] = priority + if data != getattr(record, 'data', None): + update['data'] = data + + if update: + try: + record.update(**update) + changed = True + record.get() + except Exception, e: + module.fail_json(msg='%s' % e.message) + + elif state == 'absent': + try: + domain = dns.find(name=domain) + except Exception, e: + module.fail_json(msg='%s' % e.message) + + try: + record = domain.find_record(record_type, name=name, data=data) + except pyrax.exceptions.DomainRecordNotFound, e: + record = {} + pass + except pyrax.exceptions.DomainRecordNotUnique, e: + module.fail_json(msg='%s' % e.message) + + if record: + try: + record.delete() + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + module.exit_json(changed=changed, record=to_dict(record)) + + +def main(): + argument_spec = rax_argument_spec() + argument_spec.update( + dict( + comment=dict(), + data=dict(required=True), + domain=dict(required=True), + name=dict(required=True), + priority=dict(type='int'), + state=dict(default='present', choices=['present', 'absent']), + ttl=dict(type='int', default=3600), + type=dict(default='A', choices=['A', 'AAAA', 'CNAME', 'MX', 'NS', + 'SRV', 'TXT']) + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=rax_required_together(), + ) + + comment = module.params.get('comment') + data = module.params.get('data') + domain = module.params.get('domain') + name = module.params.get('name') + priority = module.params.get('priority') + state = module.params.get('state') + ttl = module.params.get('ttl') + record_type = module.params.get('type') + + setup_rax_module(module, pyrax) + + rax_dns_record(module, comment, data, domain, name, priority, record_type, + state, ttl) + + +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.rax import * + +### invoke the module +main()