diff --git a/library/cloud/rax_dns b/library/cloud/rax_dns index f826cf00b95..57bc75acf5a 100644 --- a/library/cloud/rax_dns +++ b/library/cloud/rax_dns @@ -45,9 +45,9 @@ options: - Time to live of domain in seconds default: 3600 notes: - - "It is recommended that plays utilizing this module be run with C(serial: 1) - to avoid exceeding the API request limit imposed by the Rackspace CloudDNS - API" + - "It is recommended that plays utilizing this module be run with + C(serial: 1) to avoid exceeding the API request limit imposed by + the Rackspace CloudDNS API" author: Matt Martz extends_documentation_fragment: rackspace ''' diff --git a/library/cloud/rax_dns_record b/library/cloud/rax_dns_record index 49e14debc55..833b28f0383 100644 --- a/library/cloud/rax_dns_record +++ b/library/cloud/rax_dns_record @@ -34,8 +34,12 @@ options: required: True domain: description: - - Domain name to create the record in - required: True + - Domain name to create the record in. This is an invalid option when + type=PTR + loadbalancer: + description: + - Load Balancer ID to create a PTR record for. Only used with type=PTR + version_added: 1.7 name: description: - FQDN record name to create @@ -44,6 +48,10 @@ options: description: - Required for MX and SRV records, but forbidden for other record types. If specified, must be an integer from 0 to 65535. + server: + description: + - Server ID to create a PTR record for. Only used with type=PTR + version_added: 1.7 state: description: - Indicate desired state of the resource @@ -53,7 +61,7 @@ options: default: present ttl: description: - - Time to live of domain in seconds + - Time to live of record in seconds default: 3600 type: description: @@ -66,32 +74,45 @@ options: - NS - SRV - TXT - default: A + - PTR + required: true notes: - - "It is recommended that plays utilizing this module be run with C(serial: 1) - to avoid exceeding the API request limit imposed by the Rackspace CloudDNS - API" + - "It is recommended that plays utilizing this module be run with + C(serial: 1) to avoid exceeding the API request limit imposed by + the Rackspace CloudDNS API" + - To manipulate a C(PTR) record either C(loadbalancer) or C(server) must be + supplied + - C(PTR) record support was added in version 1.7 author: Matt Martz extends_documentation_fragment: rackspace ''' EXAMPLES = ''' -- name: Create record +- name: Create DNS Records hosts: all gather_facts: False tasks: - - name: Record create request + - name: Create A record local_action: module: rax_dns_record credentials: ~/.raxpub domain: example.org name: www.example.org - data: 127.0.0.1 + data: "{{ rax_accessipv4 }}" type: A - register: rax_dns_record + register: a_record + + - name: Create PTR record + local_action: + module: rax_dns_record + credentials: ~/.raxpub + server: "{{ rax_id }}" + name: "{{ inventory_hostname }}" + region: DFW + register: ptr_record ''' -from types import NoneType +from uuid import UUID try: import pyrax @@ -99,7 +120,7 @@ try: except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) +NON_CALLABLES = (basestring, bool, dict, int, list, type(None)) def to_dict(obj): @@ -111,8 +132,95 @@ def to_dict(obj): return instance -def rax_dns_record(module, comment, data, domain, name, priority, record_type, - state, ttl): +def rax_dns_record_ptr(module, data=None, comment=None, loadbalancer=None, + name=None, server=None, state='present', ttl=7200): + """Function for manipulating DNS PTR records""" + + changed = False + results = [] + + cs = pyrax.cloudservers + clb = pyrax.cloud_loadbalancers + dns = pyrax.cloud_dns + + if not cs or not clb or not dns: + module.fail_json(msg='Failed to instantiate client. This ' + 'typically indicates an invalid region or an ' + 'incorrectly capitalized region name.') + + if loadbalancer: + try: + UUID(loadbalancer) + found = [clb.get(loadbalancer)] + except: + for lb in clb.list(): + if loadbalancer == lb.name: + found.append(lb) + + if len(found) != 1: + module.fail_json(msg='Could not match a loadbalancer with %s' % + loadbalancer) + elif server: + try: + UUID(server) + found = [cs.servers.get(server)] + except: + found = cs.servers.list(search_opts=dict(name='^%s$' % server)) + if len(found) != 1: + module.fail_json(msg='Could not match a server with %s' % + server) + + item = found[0] + if state == 'present': + current = dns.list_ptr_records(item) + for record in current: + if record.data == data: + if record.ttl != ttl or record.name != name: + try: + dns.update_ptr_record(item, record, name, data, ttl) + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + record.ttl = ttl + record.name = name + results.append(to_dict(record)) + break + else: + results.append(to_dict(record)) + break + + if not results: + record = dict(name=name, type='PTR', data=data, ttl=ttl, + comment=comment) + try: + results = dns.add_ptr_records(item, [record]) + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + module.exit_json(changed=changed, records=results) + + elif state == 'absent': + current = dns.list_ptr_records(item) + for record in current: + if record.data == data: + results.append(to_dict(record)) + break + + if results: + try: + dns.delete_ptr_records(item, data) + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + module.exit_json(changed=changed, records=results) + + +def rax_dns_record(module, comment=None, data=None, domain=None, name=None, + priority=None, record_type='A', state='present', ttl=7200): + """Function for manipulating record types other than PTR""" + changed = False dns = pyrax.cloud_dns @@ -145,7 +253,7 @@ def rax_dns_record(module, comment, data, domain, name, priority, record_type, } if comment: record_data.update(dict(comment=comment)) - if priority: + if priority and record_type.upper() in ['MX', 'SRV']: record_data.update(dict(priority=priority)) record = domain.add_records([record_data])[0] @@ -201,19 +309,27 @@ def main(): dict( comment=dict(), data=dict(required=True), - domain=dict(required=True), + domain=dict(), + loadbalancer=dict(), name=dict(required=True), priority=dict(type='int'), + server=dict(), 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']) + type=dict(required=True, choices=['A', 'AAAA', 'CNAME', 'MX', 'NS', + 'SRV', 'TXT', 'PTR']) ) ) module = AnsibleModule( argument_spec=argument_spec, required_together=rax_required_together(), + mutually_exclusive=[ + ['server', 'loadbalancer', 'domain'], + ], + required_one_of=[ + ['server', 'loadbalancer', 'domain'], + ], ) if not HAS_PYRAX: @@ -222,16 +338,27 @@ def main(): comment = module.params.get('comment') data = module.params.get('data') domain = module.params.get('domain') + loadbalancer = module.params.get('loadbalancer') name = module.params.get('name') priority = module.params.get('priority') + server = module.params.get('server') 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) + if record_type.upper() == 'PTR': + if not server and not loadbalancer: + module.fail_json(msg='one of the following is required: ' + 'server,loadbalancer') + rax_dns_record_ptr(module, data=data, comment=comment, + loadbalancer=loadbalancer, name=name, server=server, + state=state, ttl=ttl) + else: + rax_dns_record(module, comment=comment, data=data, domain=domain, + name=name, priority=priority, record_type=record_type, + state=state, ttl=ttl) # import module snippets