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)
Example #2
0
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/{}'.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={}&region={}'.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):
        block_size = self.get_key_or_fail('block_size')
        volume_name = self.get_key_or_fail('volume_name')
        region = self.get_key_or_fail('region')
        description = self.module.params['description']
        data = {
            'size_gigabytes': block_size,
            'name': volume_name,
            'description': description,
            'region': region
        }
        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'] == 'already_exists':
            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={}&region={}'.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']

    rest = DigitalOceanHelper(module)

    results = dict(changed=False)

    # Check if oauth_token is valid or not
    response = rest.get('account')
    if response.status_code == 401:
        module.fail_json(
            msg=
            'Failed to login using oauth_token, please verify validity of oauth_token'
        )

    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)
Example #4
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)
        response = self.rest.post('droplets', data=self.module.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')
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)

    # Check if api_token is valid or not
    response = rest.get('account')
    if response.status_code == 401:
        module.fail_json(msg='Failed to login using api_token, please verify '
                         'validity of api_token')
    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)
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):


    def get_attached_droplet_ID(self, volume_name, region):
        url = 'volumes?name={}&region={}'.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.rest.poll_action_for_status(json['action']['id'], status='completed')
        elif status == 200:
            return True
        elif status == 422:
            return False
        else:
            raise DOBlockStorageException(json['message'])

    def create_block_storage(self):
        block_size = self.get_key_or_fail('block_size')
        volume_name = self.get_key_or_fail('volume_name')
        region = self.get_key_or_fail('region')
        description = self.module.params['description']
        data = {
            'size_gigabytes': block_size,
            'name': volume_name,
            'description': description,
            'region': region
        }
        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'] == 'already_exists':
            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={}&region={}'.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 handle_request(module):
    block_storage = DOBlockStorage(module)
    command = module.params['command']
    state = module.params['state']
    if command == 'create':
        if state == 'present':
            block_storage.create_block_storage()
        elif state == 'absent':
            block_storage.delete_block_storage()
    elif command == 'attach':
        if state == 'present':
            block_storage.attach_block_storage()
        elif state == 'absent':
            block_storage.detach_block_storage()


def main():
    module = AnsibleModule(
        argument_spec=dict(
            state=dict(choices=['present', 'absent'], required=True),
            command=dict(choices=['create', 'attach'], required=True),
            api_token=dict(aliases=['API_TOKEN'], no_log=True),
            block_size=dict(type='int'),
            volume_name=dict(type='str', required=True),
            description=dict(type='str'),
            region=dict(type='str', required=True),
            droplet_id=dict(type='int'),
            timeout=dict(type='int', default=10),
        ),
    )
    try:
        handle_request(module)
    except DOBlockStorageException as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc())
    except KeyError as e:
        module.fail_json(msg='Unable to load %s' % e.message, exception=traceback.format_exc())

if __name__ == '__main__':
    main()
Example #7
0
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)

    # Check if api_token is valid or not
    response = rest.get('account')
    if response.status_code == 401:
        module.fail_json(msg='Failed to login using api_token, please verify '
                             'validity of api_token')
    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)
class DOFloatingIP(object):
    def __init__(self, module):
        self.module = module
        self.rest = DigitalOceanHelper(module)

    def create(self):
        payload = {}
        if self.module.params['region'] is not None:
            payload["region"] = self.module.params['region']
        if self.module.params['droplet_id'] is not None:
            payload["droplet_id"] = self.module.params['droplet_id']

        response = self.rest.post("floating_ips", data=payload)
        status_code = response.status_code
        json_data = response.json
        if status_code == 202:
            self.module.exit_json(changed=True, data=json_data)
        else:
            self.module.fail_json(
                msg="Error creating floating ip [{0}: {1}]".format(
                    status_code, json_data["message"]),
                region=self.module.params['region'])

    def delete(self):
        ip = self.module.params['ip']
        # TODO: test what happens for unassigned IP
        # self.unassign()
        response = self.rest.delete("floating_ips/{0}".format(ip))
        status_code = response.status_code
        json_data = response.json
        if status_code == 204:
            self.module.exit_json(changed=True)
        elif status_code == 404:
            self.module.exit_json(changed=False)
        else:
            self.module.exit_json(changed=False, data=json_data)

    def retrieve(self):
        ip = self.module.params['ip']
        response = self.rest.get("floating_ips/{0}".format(ip))
        status_code = response.status_code
        json_data = response.json
        if status_code == 200:
            return json_data['floating_ip']
        else:
            self.module.fail_json(
                msg="Error retrieving floating ip [{0}: {1}]".format(
                    status_code, json_data["message"]))

    def do_action(self, payload):
        ip = self.module.params['ip']
        response = self.rest.post("floating_ips/{0}/actions".format(ip),
                                  data=payload)
        status_code = response.status_code
        json_data = response.json
        self.module.exit_json(msg='%s' % (status_code, ))
        if status_code == 201:
            self.rest.poll_action_for_status(json_data['action']['id'],
                                             status='completed')
            self.module.exit_json(changed=True, data=json_data)
        else:
            self.module.fail_json(
                msg="Error completing floating ip action [{0}: {1}]".format(
                    payload['type'], json_data["message"]))

    def assign_to_droplet(self):
        details = self.retrieve()
        droplet = details['droplet']
        if droplet is not None and str(
                droplet['id']) in [self.module.params['droplet_id']]:
            self.module.exit_json(changed=False)
        payload = {
            "type": "assign",
            "droplet_id": self.module.params['droplet_id']
        }
        self.do_action(payload)

    def unassign(self):
        payload = {"type": "unassign"}
        self.do_action(payload)
Example #9
0
class DODroplet(object):
    def __init__(self, module):
        self.ops = []
        if module._verbosity >= 1:
            self.ops.append('{0} module start'.format(time.time()))
        self.module = module
        self._id = self.module.params['id']
        self._name = self.module.params['name']
        self._droplet = None  # == _id if found by _id, _name otherwise. For diagnostic messages.
        self.rest = DigitalOceanHelper(module)
        # pop all the parameters which we never POST as data
        self.rebuild = self.module.params.pop('rebuild')
        self.wait = self.module.params.pop('wait')
        self.wait_timeout = self.module.params.pop('wait_timeout')
        self.wait_step = self.module.params.pop('wait_step')
        self.wait_build = self.module.params.pop('wait_build')
        self.module.params.pop('timeout')
        self.module.params.pop('validate_certs')
        self.module.params.pop('oauth_token')
        if self.module.params.pop('unique_name', None) is not None:
            self.module.warn(
                "Parameter `unique_name` is deprecated. Consider it always True."
            )
        if self._id and self._name:
            self.module.warn(
                "Both id {0} and name {1} supplied. Your play may turn unexpectedly!"
                .format(self._id, self._name))

    def get_by_id(self, droplet_id):
        if not droplet_id:
            return None
        response = self.rest.get('droplets/{0}'.format(droplet_id))
        if response.status_code == 200:
            self._droplet = droplet_id
            if 'name' in response.json:
                self._name = response.json['name']
            return response.json
        return None

    def find_by_name(self, droplet_name):
        if not droplet_name:
            return None
        if not self._droplet:
            self._droplet = droplet_name
        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:
                        self._id = droplet['id']
                        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 expose_addresses(self, data):
        """
         Expose IP addresses as their own property the same way M(digital_ocean) did.
         Append run log (if exists)
        """
        networks = data['droplet']['networks']
        for network in networks.get('v4', []):
            if network['type'] == 'public':
                data['ip_address'] = network['ip_address']
            elif network['type'] == 'private':
                data['private_ipv4_address'] = network['ip_address']
        for network in networks.get('v6', []):
            if network['type'] == 'public':
                data['ipv6_address'] = network['ip_address']
        if self.ops:
            data['ops'] = self.ops
        return data

    def find_droplet(self):
        json_data = self.get_by_id(self._id)
        if not json_data:
            json_data = self.find_by_name(self._name)
        return json_data

    def _params_ok(self, name, image, region):
        """
        When creating droplet we need at least name, image, and region.
        :return:
        True if all the parameters were supplied.
        """
        return name and image and region

    @staticmethod
    def same_dc(j, dc):
        if (j and dc and 'droplet' in j and 'region' in j['droplet']
                and 'slug' in j['droplet']['region']):
            return j['droplet']['region']['slug'] == dc
        return None

    @staticmethod
    def same_size(j, size):
        if (j and size and 'droplet' in j and 'size_slug' in j['droplet']):
            return j['droplet']['size_slug'] == size
        return None

    @staticmethod
    def same_image(j, image):
        if (j and image and 'droplet' in j and 'image' in j['droplet']
                and 'id' in j['droplet']['image']
                and 'slug' in j['droplet']['image']):
            return (j['droplet']['image']['slug'] == image
                    or str(j['droplet']['image']['id']) == image)
        return None

    @staticmethod
    def same_tags(j, tags):
        pt = tags if tags else []
        if (j and 'droplet' in j and 'tags' in j['droplet']
                and len(j['droplet']['tags']) == len(pt)):
            return set(j['droplet']['tags']) - set(pt) == set([])
        return None

    def get(self):
        """
        Find the droplet (either by id or name), rebuild if requested so, build if not found.
        """
        json_data = self.find_droplet()
        if json_data and self.module.params['region'] and not self.same_dc(
                json_data, self.module.params['region']):
            self.module.warn("Droplet found but 'region' differs!")
        if json_data and self.module.params['size'] and not self.same_size(
                json_data, self.module.params['size']):
            self.module.warn("Droplet found but size is not '{0}'.".format(
                self.module.params['size']))
        if not self.rebuild and json_data and not self.same_image(
                json_data, self.module.params['image']):
            self.module.warn(
                "Droplet found but 'image' differs! To re-image the droplet add 'rebuild=yes'"
            )
        if json_data and self.module.params['tags'] and not self.same_tags(
                json_data, self.module.params['tags']):
            self.module.warn("Droplet found but 'tags' differ!")

        if json_data and not self.rebuild:
            self.module.exit_json(changed=False,
                                  data=self.expose_addresses(json_data))
        elif self.rebuild and not json_data:
            self.module.fail_json(
                changed=False,
                msg='droplet {0} not found. Rebuild failed.'.format(
                    self._droplet))
        elif self.rebuild and json_data:
            self._rebuild(json_data)
        else:
            self._build()

    def _build(self):
        _size = self.module.params['size']
        _image = self.module.params['image']
        _region = self.module.params['region']
        # we are going to build a new droplet now. Final checks.
        if self._id:
            self.module.warn(
                "Trying to build {0}. Parameter id found, it makes no sense here!"
                .format(self._name))
        if not _size:
            _default_size = 's-1vcpu-1gb'
            self.module.warn("Missing 'size' parameter. Using size={0}".format(
                _default_size))
            _size = _default_size
        if not self._params_ok(self._name, _image, _region):
            self.module.fail_json(
                changed=False,
                msg=
                'Droplet not created. Not enough parameters: name={0} region={1}'
                ' image={2} size={3}'.format(self._name, _region, _image,
                                             _size))
        if self.module.check_mode:
            self.module.exit_json(changed=True)
        if self.ops:
            self.ops.append("{0} posting create droplet command.".format(
                time.time()))
        response = self.rest.post('droplets', data=self.module.params)
        if self.ops:
            if response.json and 'droplet' in response.json:
                dbg1 = response.json['droplet']
            elif response.json:
                dbg1 = response.json
            else:
                dbg1 = "! no JSON !"
            self.ops.append("{0} create droplet response: {1}".format(
                time.time(), dbg1))
        if response.status_code >= 400:
            self.module.fail_json(changed=False, msg=response.json['message'])
        jr = self.ready(response.json['droplet']['id'], action="create", build=True) if self.wait \
            else response.json
        self.module.exit_json(changed=True, data=self.expose_addresses(jr))

    def _rebuild(self, json_data):
        if self._droplet is self._id and self._name:
            self.module.warn(
                "Trying to rebuild droplet id={0}. Parameter name is found too,"
                " it makes no sense here!".format(self._id))
        if self.module.check_mode:
            self.module.exit_json(changed=True)
        droplet_id = json_data['droplet']['id']
        curr_image = json_data['droplet']['image']['id']
        do_image = self.module.params['image'] if 'image' in self.module.params and self.module.params['image'] \
            else curr_image
        cmd_data = {'type': "rebuild", 'image': do_image}
        response = self.rest.post('droplets/{0}/actions'.format(droplet_id),
                                  data=cmd_data)
        if response.status_code >= 400:
            self.module.fail_json(changed=False, msg=response.json['message'])
        self.module.exit_json(  # wait for rebuild to finish, enrich received JSON, then return it.
            changed=True,
            data=self.expose_addresses(
                self.ready(droplet_id, action="rebuild", build=True)))

    @staticmethod
    def ids(name, networks):
        ip_list = [name]
        for k, v in networks.items():
            for net in v:
                if "ip_address" in net:
                    ip_list.append(net["ip_address"])
        return ip_list

    def delete(self):
        json_data = self.find_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']))
            if response.status_code == 204:
                host_ids = self.ids(self._name,
                                    json_data['droplet']['networks'])
                self.module.exit_json(changed=True,
                                      msg="Droplet '{0}' deleted.".format(
                                          self._droplet),
                                      host_ids=host_ids)
            self.module.fail_json(changed=False,
                                  msg='Failed to delete droplet {0}'.format(
                                      self._droplet))
        else:
            self.module.exit_json(changed=False,
                                  msg='Droplet {0} not found'.format(
                                      self._droplet))

    def ready(self, droplet_id, action, build=False):
        if build and self.wait_build > self.wait_timeout:
            timeout = self.wait_build
        else:
            timeout = self.wait_timeout
        end_time = time.time(
        ) + timeout + 0.2  # compensate for time.sleep() inaccuracy
        if build:
            time.sleep(self.wait_build)
        locked = True
        while time.time() < end_time:
            response = self.rest.get('droplets/{0}'.format(droplet_id))
            if self.ops:
                rj = response.json
                if 'droplet' in rj and 'locked' in rj['droplet']:
                    if 'v4' in rj['droplet']['networks'] and len(
                            rj['droplet']['networks']['v4']):
                        net = rj['droplet']['networks']['v4'][0]
                    else:
                        net = rj['droplet']['networks']
                    lck = rj['droplet']['locked']
                    sta = rj['droplet']['status']
                self.ops.append("{0} status {1} locked {2} net {3}".format(
                    time.time(), sta, lck, net))
            locked = response.json['droplet']['locked']
            has_net = len(response.json['droplet']['networks']['v4'])
            if not locked and has_net:
                return response.json
            time.sleep(min(self.wait_step, end_time - time.time()))
        if action == 'create' and not locked:  # but not has_net
            self.module.warn(
                'Droplet created but no IPv4 net found in {0} seconds.'.format(
                    timeout))
            return response.json
        self.module.fail_json(
            msg='Droplet {0} action "{1}" not finished in {2} seconds.'.format(
                self._droplet, action, timeout))