1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# 4# Copyright: Ansible Project 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10 11ANSIBLE_METADATA = {'metadata_version': '1.1', 12 'status': ['stableinterface'], 13 'supported_by': 'community'} 14 15 16DOCUMENTATION = ''' 17--- 18module: digital_ocean_domain 19short_description: Create/delete a DNS domain in DigitalOcean 20description: 21 - Create/delete a DNS domain in DigitalOcean. 22version_added: "1.6" 23author: "Michael Gregson (@mgregson)" 24options: 25 state: 26 description: 27 - Indicate desired state of the target. 28 default: present 29 choices: ['present', 'absent'] 30 id: 31 description: 32 - Numeric, the droplet id you want to operate on. 33 aliases: ['droplet_id'] 34 name: 35 description: 36 - String, this is the name of the droplet - must be formatted by hostname rules, or the name of a SSH key, or the name of a domain. 37 ip: 38 description: 39 - An 'A' record for '@' ($ORIGIN) will be created with the value 'ip'. 'ip' is an IP version 4 address. 40extends_documentation_fragment: digital_ocean.documentation 41notes: 42 - Environment variables DO_OAUTH_TOKEN can be used for the oauth_token. 43 - As of Ansible 1.9.5 and 2.0, Version 2 of the DigitalOcean API is used, this removes C(client_id) and C(api_key) options in favor of C(oauth_token). 44 - If you are running Ansible 1.9.4 or earlier you might not be able to use the included version of this module as the API version used has been retired. 45 46requirements: 47 - "python >= 2.6" 48''' 49 50 51EXAMPLES = ''' 52# Create a domain 53 54- digital_ocean_domain: 55 state: present 56 name: my.digitalocean.domain 57 ip: 127.0.0.1 58 59# Create a droplet and a corresponding domain 60 61- digital_ocean: 62 state: present 63 name: test_droplet 64 size_id: 1gb 65 region_id: sgp1 66 image_id: ubuntu-14-04-x64 67 68 69 register: test_droplet 70 71- digital_ocean_domain: 72 state: present 73 name: "{{ test_droplet.droplet.name }}.my.domain" 74 ip: "{{ test_droplet.droplet.ip_address }}" 75 76''' 77 78import traceback 79from ansible.module_utils.basic import AnsibleModule 80from ansible.module_utils.digital_ocean import DigitalOceanHelper 81from ansible.module_utils._text import to_native 82 83 84class DoManager(DigitalOceanHelper, object): 85 def __init__(self, module): 86 super(DoManager, self).__init__(module) 87 self.domain_name = module.params.get('name', None) 88 self.domain_ip = module.params.get('ip', None) 89 self.domain_id = module.params.get('id', None) 90 91 @staticmethod 92 def jsonify(response): 93 return response.status_code, response.json 94 95 def all_domains(self): 96 resp = self.get('domains/') 97 return resp 98 99 def find(self): 100 if self.domain_name is None and self.domain_id is None: 101 return False 102 103 domains = self.all_domains() 104 status, json = self.jsonify(domains) 105 for domain in json['domains']: 106 if domain['name'] == self.domain_name: 107 return True 108 return False 109 110 def add(self): 111 params = {'name': self.domain_name, 'ip_address': self.domain_ip} 112 resp = self.post('domains/', data=params) 113 status = resp.status_code 114 json = resp.json 115 if status == 201: 116 return json['domain'] 117 else: 118 return json 119 120 def all_domain_records(self): 121 resp = self.get('domains/%s/records/' % self.domain_name) 122 return resp.json 123 124 def domain_record(self): 125 resp = self.get('domains/%s' % self.domain_name) 126 status, json = self.jsonify(resp) 127 return json 128 129 def destroy_domain(self): 130 resp = self.delete('domains/%s' % self.domain_name) 131 status, json = self.jsonify(resp) 132 if status == 204: 133 return True 134 else: 135 return json 136 137 def edit_domain_record(self, record): 138 params = {'name': '@', 139 'data': self.module.params.get('ip')} 140 resp = self.put('domains/%s/records/%s' % (self.domain_name, record['id']), data=params) 141 status, json = self.jsonify(resp) 142 143 return json['domain_record'] 144 145 def create_domain_record(self): 146 params = {'name': '@', 147 'type': 'A', 148 'data': self.module.params.get('ip')} 149 150 resp = self.post('domains/%s/records' % (self.domain_name), data=params) 151 status, json = self.jsonify(resp) 152 153 return json['domain_record'] 154 155 156def core(module): 157 do_manager = DoManager(module) 158 state = module.params.get('state') 159 160 domain = do_manager.find() 161 if state == 'present': 162 if not domain: 163 domain = do_manager.add() 164 if 'message' in domain: 165 module.fail_json(changed=False, msg=domain['message']) 166 else: 167 module.exit_json(changed=True, domain=domain) 168 else: 169 records = do_manager.all_domain_records() 170 at_record = None 171 for record in records['domain_records']: 172 if record['name'] == "@" and record['type'] == 'A': 173 at_record = record 174 175 if not at_record: 176 do_manager.create_domain_record() 177 module.exit_json(changed=True, domain=do_manager.find()) 178 elif not at_record['data'] == module.params.get('ip'): 179 do_manager.edit_domain_record(at_record) 180 module.exit_json(changed=True, domain=do_manager.find()) 181 else: 182 module.exit_json(changed=False, domain=do_manager.domain_record()) 183 184 elif state == 'absent': 185 if not domain: 186 module.exit_json(changed=False, msg="Domain not found") 187 else: 188 delete_event = do_manager.destroy_domain() 189 if not delete_event: 190 module.fail_json(changed=False, msg=delete_event['message']) 191 else: 192 module.exit_json(changed=True, event=None) 193 delete_event = do_manager.destroy_domain() 194 module.exit_json(changed=delete_event) 195 196 197def main(): 198 argument_spec = DigitalOceanHelper.digital_ocean_argument_spec() 199 argument_spec.update( 200 state=dict(choices=['present', 'absent'], default='present'), 201 name=dict(type='str'), 202 id=dict(aliases=['droplet_id'], type='int'), 203 ip=dict(type='str') 204 ) 205 206 module = AnsibleModule( 207 argument_spec=argument_spec, 208 required_one_of=( 209 ['id', 'name'], 210 ), 211 ) 212 213 try: 214 core(module) 215 except Exception as e: 216 module.fail_json(msg=to_native(e), exception=traceback.format_exc()) 217 218 219if __name__ == '__main__': 220 main() 221