From 2caddf0212ae0f7e4f87db9e5dfbaa59dc8e1485 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Sat, 9 Dec 2017 11:24:20 +0530 Subject: [PATCH] Refactor Digital Ocean Domain (#29179) * Remove dopy dependency * Use DigitalOceanHelper class * Incorportated BondAnthony's changes Signed-off-by: Abhijeet Kasurde --- .../digital_ocean/digital_ocean_domain.py | 214 ++++++++---------- 1 file changed, 90 insertions(+), 124 deletions(-) diff --git a/lib/ansible/modules/cloud/digital_ocean/digital_ocean_domain.py b/lib/ansible/modules/cloud/digital_ocean/digital_ocean_domain.py index 0bface8a44..5568915c67 100644 --- a/lib/ansible/modules/cloud/digital_ocean/digital_ocean_domain.py +++ b/lib/ansible/modules/cloud/digital_ocean/digital_ocean_domain.py @@ -27,10 +27,11 @@ options: - Indicate desired state of the target. default: present choices: ['present', 'absent'] - api_token: + oauth_token: description: - DigitalOcean api token. version_added: "1.9.5" + aliases: ['api_token'] id: description: - Numeric, the droplet id you want to operate on. @@ -42,13 +43,12 @@ options: - The IP address to point a domain at. notes: - - Two environment variables can be used, DO_API_KEY and DO_API_TOKEN. They both refer to the v2 token. - - 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(api_token). + - Environment variables DO_OAUTH_TOKEN can be used for the oauth_token. + - 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). - 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. requirements: - "python >= 2.6" - - dopy ''' @@ -79,151 +79,119 @@ EXAMPLES = ''' ''' -import os import traceback - -try: - from dopy.manager import DoError, DoManager - HAS_DOPY = True -except ImportError as e: - HAS_DOPY = False - from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.digital_ocean import DigitalOceanHelper from ansible.module_utils._text import to_native +from ansible.module_utils.basic import env_fallback -class JsonfyMixIn(object): +class DoManager(DigitalOceanHelper, object): + def __init__(self, module): + super(DoManager, self).__init__(module) + self.domain_name = module.params.get('name', None) + self.domain_ip = module.params.get('ip', None) + self.domain_id = module.params.get('id', None) - def to_json(self): - return self.__dict__ + @staticmethod + def jsonify(response): + return response.status_code, response.json + def all_domains(self): + resp = self.get('domains/') + return resp -class DomainRecord(JsonfyMixIn): - manager = None - - def __init__(self, json): - self.__dict__.update(json) - update_attr = __init__ - - def update(self, data=None, record_type=None): - json = self.manager.edit_domain_record(self.domain_id, - self.id, - record_type if record_type is not None else self.record_type, - data if data is not None else self.data) - self.__dict__.update(json) - return self - - def destroy(self): - json = self.manager.destroy_domain_record(self.domain_id, self.id) - return json - - -class Domain(JsonfyMixIn): - manager = None - - def __init__(self, domain_json): - self.__dict__.update(domain_json) - - def destroy(self): - self.manager.destroy_domain(self.name) - - def records(self): - json = self.manager.all_domain_records(self.name) - return map(DomainRecord, json) - - @classmethod - def add(cls, name, ip): - json = cls.manager.new_domain(name, ip) - return cls(json) - - @classmethod - def setup(cls, api_token): - cls.manager = DoManager(None, api_token, api_version=2) - DomainRecord.manager = cls.manager - - @classmethod - def list_all(cls): - domains = cls.manager.all_domains() - return map(cls, domains) - - @classmethod - def find(cls, name=None, id=None): - if name is None and id is None: + def find(self): + if self.domain_name is None and self.domain_id is None: return False - domains = Domain.list_all() - - if id is not None: - for domain in domains: - if domain.id == id: - return domain - - if name is not None: - for domain in domains: - if domain.name == name: - return domain - + domains = self.all_domains() + status, json = self.jsonify(domains) + for domain in json['domains']: + if domain['name'] == self.domain_name: + return True return False + def add(self): + params = {'name': self.domain_name, 'ip_address': self.domain_ip} + resp = self.post('domains/', data=params) + status = resp.status_code + json = resp.json + if status == 201: + return json['domain'] + else: + return json + + def all_domain_records(self): + resp = self.get('domains/%s/records/' % self.domain_name) + return resp.json + + def domain_record(self): + resp = self.get('domains/%s' % self.domain_name) + status, json = self.jsonify(resp) + return json + + def destroy_domain(self): + resp = self.delete('domains/%s' % self.domain_name) + status, json = self.jsonify(resp) + if status == 204: + return True + else: + return json + + def edit_domain_record(self): + params = {'name': self.domain_name} + resp = self.put('domains/%s/records/%s' % (self.domain_name, self.domain_id), data=params) + return resp['domain_record'] + def core(module): - def getkeyordie(k): - v = module.params[k] - if v is None: - module.fail_json(msg='Unable to load %s' % k) - return v - - try: - api_token = module.params['api_token'] or os.environ['DO_API_TOKEN'] or os.environ['DO_API_KEY'] - except KeyError as e: - module.fail_json(msg='Unable to load %s' % e.message) - - state = module.params['state'] - - Domain.setup(api_token) - if state in ('present'): - domain = Domain.find(id=module.params["id"]) + do_manager = DoManager(module) + state = module.params.get('state') + domain = do_manager.find() + if state == 'present': if not domain: - domain = Domain.find(name=getkeyordie("name")) - - if not domain: - domain = Domain.add(getkeyordie("name"), - getkeyordie("ip")) - module.exit_json(changed=True, domain=domain.to_json()) + domain = do_manager.add() + if 'message' in domain: + module.fail_json(changed=False, msg=domain['message']) + else: + module.exit_json(changed=True, domain=domain) else: - records = domain.records() + records = do_manager.all_domain_records() at_record = None - for record in records: - if record.name == "@" and record.type == 'A': + for record in records['domain_records']: + if record['name'] == "@" and record['type'] == 'A': at_record = record - if not at_record.data == getkeyordie("ip"): - record.update(data=getkeyordie("ip"), record_type='A') - module.exit_json(changed=True, domain=Domain.find(id=record.id).to_json()) - - module.exit_json(changed=False, domain=domain.to_json()) - - elif state in ('absent'): - domain = None - if "id" in module.params: - domain = Domain.find(id=module.params["id"]) - - if not domain and "name" in module.params: - domain = Domain.find(name=module.params["name"]) + if not at_record['data'] == module.params.get('ip'): + do_manager.edit_domain_record() + module.exit_json(changed=True, domain=do_manager.find()) + else: + module.exit_json(changed=False, domain=do_manager.domain_record()) + elif state == 'absent': if not domain: - module.exit_json(changed=False, msg="Domain not found.") - - event_json = domain.destroy() - module.exit_json(changed=True, event=event_json) + module.fail_json(changed=False, msg="Domain not found") + else: + delete_event = do_manager.destroy_domain() + if not delete_event: + module.fail_json(changed=False, msg=delete_event['message']) + else: + module.exit_json(changed=True, event=None) + delete_event = do_manager.destroy_domain() + module.exit_json(changed=delete_event) def main(): module = AnsibleModule( argument_spec=dict( state=dict(choices=['present', 'absent'], default='present'), - api_token=dict(aliases=['API_TOKEN'], no_log=True), + oauth_token=dict( + aliases=['API_TOKEN'], + no_log=True, + fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN']) + ), name=dict(type='str'), id=dict(aliases=['droplet_id'], type='int'), ip=dict(type='str'), @@ -232,12 +200,10 @@ def main(): ['id', 'name'], ), ) - if not HAS_DOPY: - module.fail_json(msg='dopy required for this module') try: core(module) - except (DoError, Exception) as e: + except Exception as e: module.fail_json(msg=to_native(e), exception=traceback.format_exc())