class DOBlockStorage(object):
    def __init__(self, module):
        self.module = module
        self.rest = DigitalOceanHelper(module)

    def get_key_or_fail(self, k):
        v = self.module.params[k]
        if v is None:
            self.module.fail_json(msg='Unable to load %s' % k)
        return v

    def poll_action_for_complete_status(self, action_id):
        url = 'actions/{0}'.format(action_id)
        end_time = time.time() + self.module.params['timeout']
        while time.time() < end_time:
            time.sleep(2)
            response = self.rest.get(url)
            status = response.status_code
            json = response.json
            if status == 200:
                if json['action']['status'] == 'completed':
                    return True
                elif json['action']['status'] == 'errored':
                    raise DOBlockStorageException(json['message'])
        raise DOBlockStorageException('Unable to reach api.digitalocean.com')

    def get_attached_droplet_ID(self, volume_name, region):
        url = 'volumes?name={0}&region={1}'.format(volume_name, region)
        response = self.rest.get(url)
        status = response.status_code
        json = response.json
        if status == 200:
            volumes = json['volumes']
            if len(volumes) > 0:
                droplet_ids = volumes[0]['droplet_ids']
                if len(droplet_ids) > 0:
                    return droplet_ids[0]
            return None
        else:
            raise DOBlockStorageException(json['message'])

    def attach_detach_block_storage(self, method, volume_name, region,
                                    droplet_id):
        data = {
            'type': method,
            'volume_name': volume_name,
            'region': region,
            'droplet_id': droplet_id
        }
        response = self.rest.post('volumes/actions', data=data)
        status = response.status_code
        json = response.json
        if status == 202:
            return self.poll_action_for_complete_status(json['action']['id'])
        elif status == 200:
            return True
        elif status == 422:
            return False
        else:
            raise DOBlockStorageException(json['message'])

    def create_block_storage(self):
        volume_name = self.get_key_or_fail('volume_name')
        snapshot_id = self.module.params['snapshot_id']
        if snapshot_id:
            self.module.params['block_size'] = None
            self.module.params['region'] = None
            block_size = None
            region = None
        else:
            block_size = self.get_key_or_fail('block_size')
            region = self.get_key_or_fail('region')
        description = self.module.params['description']
        data = {
            'size_gigabytes': block_size,
            'name': volume_name,
            'description': description,
            'region': region,
            'snapshot_id': snapshot_id,
        }
        response = self.rest.post("volumes", data=data)
        status = response.status_code
        json = response.json
        if status == 201:
            self.module.exit_json(changed=True, id=json['volume']['id'])
        elif status == 409 and json['id'] == 'conflict':
            self.module.exit_json(changed=False)
        else:
            raise DOBlockStorageException(json['message'])

    def delete_block_storage(self):
        volume_name = self.get_key_or_fail('volume_name')
        region = self.get_key_or_fail('region')
        url = 'volumes?name={0}&region={1}'.format(volume_name, region)
        attached_droplet_id = self.get_attached_droplet_ID(volume_name, region)
        if attached_droplet_id is not None:
            self.attach_detach_block_storage('detach', volume_name, region,
                                             attached_droplet_id)
        response = self.rest.delete(url)
        status = response.status_code
        json = response.json
        if status == 204:
            self.module.exit_json(changed=True)
        elif status == 404:
            self.module.exit_json(changed=False)
        else:
            raise DOBlockStorageException(json['message'])

    def attach_block_storage(self):
        volume_name = self.get_key_or_fail('volume_name')
        region = self.get_key_or_fail('region')
        droplet_id = self.get_key_or_fail('droplet_id')
        attached_droplet_id = self.get_attached_droplet_ID(volume_name, region)
        if attached_droplet_id is not None:
            if attached_droplet_id == droplet_id:
                self.module.exit_json(changed=False)
            else:
                self.attach_detach_block_storage('detach', volume_name, region,
                                                 attached_droplet_id)
        changed_status = self.attach_detach_block_storage(
            'attach', volume_name, region, droplet_id)
        self.module.exit_json(changed=changed_status)

    def detach_block_storage(self):
        volume_name = self.get_key_or_fail('volume_name')
        region = self.get_key_or_fail('region')
        droplet_id = self.get_key_or_fail('droplet_id')
        changed_status = self.attach_detach_block_storage(
            'detach', volume_name, region, droplet_id)
        self.module.exit_json(changed=changed_status)
def core(module):
    state = module.params['state']
    name = module.params['name']
    resource_id = module.params['resource_id']
    resource_type = module.params['resource_type']

    rest = DigitalOceanHelper(module)

    if state == 'present':
        response = rest.get('tags/{0}'.format(name))
        status_code = response.status_code
        resp_json = response.json
        changed = False
        if status_code == 200 and resp_json['tag']['name'] == name:
            changed = False
        else:
            # Ensure Tag exists
            response = rest.post("tags", data={'name': name})
            status_code = response.status_code
            resp_json = response.json
            if status_code == 201:
                changed = True
            elif status_code == 422:
                changed = False
            else:
                module.exit_json(changed=False, data=resp_json)

        if resource_id is None:
            # No resource defined, we're done.
            module.exit_json(changed=changed, data=resp_json)
        else:
            # Check if resource is already tagged or not
            found = False
            url = "{0}?tag_name={1}".format(resource_type, name)
            if resource_type == 'droplet':
                url = "droplets?tag_name={0}".format(name)
            response = rest.get(url)
            status_code = response.status_code
            resp_json = response.json
            if status_code == 200:
                for resource in resp_json['droplets']:
                    if not found and resource['id'] == int(resource_id):
                        found = True
                        break
                if not found:
                    # If resource is not tagged, tag a resource
                    url = "tags/{0}/resources".format(name)
                    payload = {
                        'resources': [{
                            'resource_id': resource_id,
                            'resource_type': resource_type
                        }]
                    }
                    response = rest.post(url, data=payload)
                    if response.status_code == 204:
                        module.exit_json(changed=True)
                    else:
                        module.fail_json(
                            msg="error tagging resource '{0}': {1}".format(
                                resource_id, response.json["message"]))
                else:
                    # Already tagged resource
                    module.exit_json(changed=False)
            else:
                # Unable to find resource specified by user
                module.fail_json(msg=resp_json['message'])

    elif state == 'absent':
        if resource_id:
            url = "tags/{0}/resources".format(name)
            payload = {
                'resources': [{
                    'resource_id': resource_id,
                    'resource_type': resource_type
                }]
            }
            response = rest.delete(url, data=payload)
        else:
            url = "tags/{0}".format(name)
            response = rest.delete(url)
        if response.status_code == 204:
            module.exit_json(changed=True)
        else:
            module.exit_json(changed=False, data=response.json)
Example #3
0
class DODroplet(object):
    def __init__(self, module):
        self.rest = DigitalOceanHelper(module)
        self.module = module
        self.wait = self.module.params.pop('wait', True)
        self.wait_timeout = self.module.params.pop('wait_timeout', 120)
        self.unique_name = self.module.params.pop('unique_name', False)
        # pop the oauth token so we don't include it in the POST data
        self.module.params.pop('oauth_token')

    def get_by_id(self, droplet_id):
        if not droplet_id:
            return None
        response = self.rest.get('droplets/{0}'.format(droplet_id))
        json_data = response.json
        if response.status_code == 200:
            return json_data
        return None

    def get_by_name(self, droplet_name):
        if not droplet_name:
            return None
        page = 1
        while page is not None:
            response = self.rest.get('droplets?page={0}'.format(page))
            json_data = response.json
            if response.status_code == 200:
                for droplet in json_data['droplets']:
                    if droplet['name'] == droplet_name:
                        return {'droplet': droplet}
                if 'links' in json_data and 'pages' in json_data[
                        'links'] and 'next' in json_data['links']['pages']:
                    page += 1
                else:
                    page = None
        return None

    def get_addresses(self, data):
        """
         Expose IP addresses as their own property allowing users extend to additional tasks
        """
        _data = data
        for k, v in data.items():
            setattr(self, k, v)
        networks = _data['droplet']['networks']
        for network in networks.get('v4', []):
            if network['type'] == 'public':
                _data['ip_address'] = network['ip_address']
            else:
                _data['private_ipv4_address'] = network['ip_address']
        for network in networks.get('v6', []):
            if network['type'] == 'public':
                _data['ipv6_address'] = network['ip_address']
            else:
                _data['private_ipv6_address'] = network['ip_address']
        return _data

    def get_droplet(self):
        json_data = self.get_by_id(self.module.params['id'])
        if not json_data and self.unique_name:
            json_data = self.get_by_name(self.module.params['name'])
        return json_data

    def create(self):
        json_data = self.get_droplet()
        droplet_data = None
        if json_data:
            droplet_data = self.get_addresses(json_data)
            self.module.exit_json(changed=False, data=droplet_data)
        if self.module.check_mode:
            self.module.exit_json(changed=True)
        request_params = dict(self.module.params)
        del request_params['id']
        response = self.rest.post('droplets', data=request_params)
        json_data = response.json
        if response.status_code >= 400:
            self.module.fail_json(changed=False, msg=json_data['message'])
        if self.wait:
            json_data = self.ensure_power_on(json_data['droplet']['id'])
            droplet_data = self.get_addresses(json_data)
        self.module.exit_json(changed=True, data=droplet_data)

    def delete(self):
        json_data = self.get_droplet()
        if json_data:
            if self.module.check_mode:
                self.module.exit_json(changed=True)
            response = self.rest.delete('droplets/{0}'.format(
                json_data['droplet']['id']))
            json_data = response.json
            if response.status_code == 204:
                self.module.exit_json(changed=True, msg='Droplet deleted')
            self.module.fail_json(changed=False,
                                  msg='Failed to delete droplet')
        else:
            self.module.exit_json(changed=False, msg='Droplet not found')

    def ensure_power_on(self, droplet_id):
        end_time = time.time() + self.wait_timeout
        while time.time() < end_time:
            response = self.rest.get('droplets/{0}'.format(droplet_id))
            json_data = response.json
            if json_data['droplet']['status'] == 'active':
                return json_data
            time.sleep(min(2, end_time - time.time()))
        self.module.fail_json(msg='Wait for droplet powering on timeout')
Example #4
0
def core(module):
    state = module.params['state']
    name = module.params['name']

    rest = DigitalOceanHelper(module)

    results = dict(changed=False)

    response = rest.get('certificates')
    status_code = response.status_code
    resp_json = response.json

    if status_code != 200:
        module.fail_json(
            msg="Failed to retrieve certificates for DigitalOcean")

    if state == 'present':
        for cert in resp_json['certificates']:
            if cert['name'] == name:
                module.fail_json(msg="Certificate name %s already exists" %
                                 name)

        # Certificate does not exist, let us create it
        cert_data = dict(name=name,
                         private_key=module.params['private_key'],
                         leaf_certificate=module.params['leaf_certificate'])

        if module.params['certificate_chain'] is not None:
            cert_data.update(
                certificate_chain=module.params['certificate_chain'])

        response = rest.post("certificates", data=cert_data)
        status_code = response.status_code
        if status_code == 500:
            module.fail_json(
                msg=
                "Failed to upload certificates as the certificates are malformed."
            )

        resp_json = response.json
        if status_code == 201:
            results.update(changed=True, response=resp_json)
        elif status_code == 422:
            results.update(changed=False, response=resp_json)

    elif state == 'absent':
        cert_id_del = None
        for cert in resp_json['certificates']:
            if cert['name'] == name:
                cert_id_del = cert['id']

        if cert_id_del is not None:
            url = "certificates/{0}".format(cert_id_del)
            response = rest.delete(url)
            if response.status_code == 204:
                results.update(changed=True)
            else:
                results.update(changed=False)
        else:
            module.fail_json(msg="Failed to find certificate %s" % name)

    module.exit_json(**results)