Example #1
0
    def on_post(self, req, resp, tenant_id):
        """Create volume (SL Portable storage)."""
        client = req.env['sl_client']

        try:
            body = json.loads(req.stream.read().decode())
            # required field in the create volume payload
            namestr = body['volume'].get("display_name")
            volreq = body['volume']
            # portable storage order cannot have empty name
            name = (config.CONF['volume']['volume_name_prefix'] +
                    (namestr if namestr else ""))

            # size is required option for volume create. Throw type exception
            # if it is invalid
            size = int(volreq['size'])
            # availability_zone is optional, don't throw exception if
            # it is not available
            availability_zone = (
                body['volume'].get('availability_zone')
                or config.CONF['volume']['default_availability_zone'])
            volume_type = body['volume'].get('volume_type')

        except Exception:
            return error_handling.bad_request(resp, 'Malformed request body')

        try:
            volinfo = self._create_volume(tenant_id, client, resp,
                                          size, name=name,
                                          zone=availability_zone,
                                          volume_type=volume_type)

            resp.status = HTTP.ACCEPTED

            if volinfo:
                resp.body = {'volume':
                             format_volume(tenant_id, volinfo, client)}
                resp.body['volume'].update({'status': 'creating'})
            else:
                # Cannot generate a valid response without knowning
                # the volume id when order takes too long to complete.
                # This should be a rare case, but it could break openstack
                # since the volume create caller always expect a volume id
                # uppon successful return. The approach here is to fail
                # the volume create operation and leak one portable storage
                # volume. User can always cancel from SL portal.
                return error_handling.volume_fault(
                    resp, "Portable storage order delayed")

        except SoftLayer.SoftLayerAPIError as e:
            return error_handling.error(resp,
                                        "SoftLayerAPIError",
                                        e.faultString,
                                        code=HTTP.INTERNAL_SERVER_ERROR)
        except Exception as e:
            return error_handling.volume_fault(resp, str(e))
Example #2
0
 def on_get(self, req, resp, tenant_id, volume_id='detail'):
     try:
         # if the volume id is 'detail' then we will treat it as
         # a get list with detail
         if volume_id == 'detail':
             get_volume_list_detail(req, resp, tenant_id)
         else:
             get_volume(req, resp, tenant_id, volume_id)
     except Exception as e:
         error_handling.volume_fault(resp, str(e))
Example #3
0
    def _list_volumes(self, tenant_id, client, req, resp):
        """Retrieve all the SoftLayer portable storage devices

        Generate the Cinder volume list. The swap
        device(SoftLayer Virtual_Disk_Image with typeID 246) will not be
        listed. The VSI's boot disk is also SoftLayer portable storage
        device. The VSI boot disk will always be shown as attached and
        it will be removed during VSI destory due to its ephemeral nature.

        :param tenant_id: SoftLayer tenant id
        :param client: SoftLayer Client
        :param req: Http Request body
        :param resp: Http Response body
        :param return: Http status
        """

        # Get SoftLayer getVirtualDiskImages() function
        try:
            _getVirtualDiskImages = getattr(client['Account'],
                                            'getVirtualDiskImages')
            # filter out the swap disk from the retrived portable storage
            # devices
            vols = [x for x in
                    _getVirtualDiskImages(mask=get_virt_disk_img_mask())
                    if x['typeId'] != VIRTUAL_DISK_IMAGE_TYPE['SWAP'] and
                    not x['localDiskFlag']]
            resp.body = {"volumes":
                         [format_volume(tenant_id,
                                        vol,
                                        client) for vol in vols]}
            resp.status = HTTP.OK

        except Exception as e:
            return error_handling.volume_fault(resp, str(e))
Example #4
0
    def _show_volume(self, tenant_id, volume_id, client, req, resp):
        """Show the details of a particular portable storage device.

        :param tenant_id: SoftLayer tenant id
        :param volume_id: id of the portable storage device
        :param client: SoftLayer Client
        :param req: Http Request body
        :param resp: Http Response body
        :param return: Http status
        """

        vol = client['Virtual_Disk_Image']
        volinfo = None
        try:
            volinfo = vol.getObject(id=volume_id,
                                    mask=get_virt_disk_img_mask())
        except Exception as e:
            return error_handling.volume_fault(resp, e.faultString,
                                               code=HTTP.NOT_FOUND)

        resp.status = HTTP.OK
        resp.body = {'volume':
                     format_volume(tenant_id,
                                   volinfo,
                                   client,
                                   showDetails=True)}
Example #5
0
    def on_get(self, req, resp, tenant_id, instance_id, volume_id):
        '''Shows details for the specified volume attachment.'''
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        # since detail has the same info as the input request params, we can
        # just return the values back in the response using the request params.
        # But instead we will do sanity check to ensure the volume_id belongs
        # to the instance.
        vg_client = req.sl_client['Virtual_Guest']
        try:
            blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type',
                                                   id=instance_id)
            vols = [
                x for x in blkDevices
                if x['diskImage']['type']['keyName'] != 'SWAP'
            ]
            for vol in vols:
                json_response = None
                vol_disk_id = vol['diskImage']['id']
                if str(vol_disk_id) == volume_id:
                    json_response = {
                        "volumeAttachment": {
                            "device": "",
                            "id": vol_disk_id,
                            "serverId": instance_id,
                            "volumeId": vol_disk_id
                        }
                    }
                    break
            if json_response:
                resp.body = json_response
            else:
                return error_handling.volume_fault(resp,
                                                   'Invalid volume id.',
                                                   code=HTTP.BAD_REQUEST)
        except Exception as e:
            return error_handling.volume_fault(resp, e.faultString)
Example #6
0
    def on_delete(self, req, resp, tenant_id, instance_id, volume_id):
        """Detach the requested volume from the specified instance."""
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        vdi_client = req.env['sl_client']['Virtual_Disk_Image']

        # first let's check if the volume is already attached
        try:
            volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices')
            blkDevices = volinfo['blockDevices']
            if len(blkDevices) > 0:
                guestId_list = [blkDevice['guestId'] for blkDevice
                                in blkDevices]
                for guest_id in guestId_list:
                    if guest_id == instance_id:
                        try:
                            # detach the volume here
                            vg_client = req.env['sl_client']['Virtual_Guest']
                            vg_client.detachDiskImage(volume_id,
                                                      id=instance_id)
                            break
                        except Exception as e:
                            error_handling.volume_fault(resp,
                                                        e.faultString)
                    else:
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is attached to another '
                            'guest and cannot be detached.',
                            code=HTTP.BAD_REQUEST)

        except Exception as e:
            return error_handling.volume_fault(resp, e.faultString,
                                               code=500)

        resp.status = HTTP.ACCEPTED
Example #7
0
    def on_delete(self, req, resp, tenant_id, instance_id, volume_id):
        """Detach the requested volume from the specified instance."""
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        vdi_client = req.sl_client['Virtual_Disk_Image']

        # first let's check if the volume is already attached
        try:
            volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices')
            blkDevices = volinfo['blockDevices']
            if len(blkDevices) > 0:
                guestId_list = [
                    blkDevice['guestId'] for blkDevice in blkDevices
                ]
                for guest_id in guestId_list:
                    if guest_id == instance_id:
                        try:
                            # detach the volume here
                            vg_client = req.sl_client['Virtual_Guest']
                            vg_client.detachDiskImage(volume_id,
                                                      id=instance_id)
                            break
                        except Exception as e:
                            error_handling.volume_fault(resp, e.faultString)
                    else:
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is attached to another '
                            'guest and cannot be detached.',
                            code=HTTP.BAD_REQUEST)

        except Exception as e:
            return error_handling.volume_fault(resp, e.faultString, code=500)

        resp.status = HTTP.ACCEPTED
Example #8
0
    def on_get(self, req, resp, tenant_id, instance_id):
        '''Lists volume attachments for the instance.'''
        vg_client = req.env['sl_client']['Virtual_Guest']
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        try:
            blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type',
                                                   id=instance_id)

            vols = [format_volume_attachment(vol['diskImage']['id'],
                                             instance_id,
                                             '')
                    for vol in blkDevices
                    if vol['diskImage']['type']['keyName'] != 'SWAP']
            resp.body = {"volumeAttachments": vols}
        except Exception as e:
            error_handling.volume_fault(resp, e.faultString)
Example #9
0
    def on_get(self, req, resp, tenant_id, instance_id):
        '''Lists volume attachments for the instance.'''
        vg_client = req.sl_client['Virtual_Guest']
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        try:
            blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type',
                                                   id=instance_id)

            vols = [
                format_volume_attachment(vol['diskImage']['id'], instance_id,
                                         '') for vol in blkDevices
                if vol['diskImage']['type']['keyName'] != 'SWAP'
            ]
            resp.body = {"volumeAttachments": vols}
        except Exception as e:
            error_handling.volume_fault(resp, e.faultString)
Example #10
0
    def on_get(self, req, resp, tenant_id, instance_id, volume_id):
        '''Shows details for the specified volume attachment.'''
        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        # since detail has the same info as the input request params, we can
        # just return the values back in the response using the request params.
        # But instead we will do sanity check to ensure the volume_id belongs
        # to the instance.
        vg_client = req.env['sl_client']['Virtual_Guest']
        try:
            blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type',
                                                   id=instance_id)
            vols = [x for x in blkDevices
                    if x['diskImage']['type']['keyName'] != 'SWAP']
            for vol in vols:
                json_response = None
                vol_disk_id = vol['diskImage']['id']
                if str(vol_disk_id) == volume_id:
                    json_response = {"volumeAttachment":
                                     {"device": "", "id": vol_disk_id,
                                      "serverId": instance_id,
                                      "volumeId": vol_disk_id}}
                    break
            if json_response:
                resp.body = json_response
            else:
                error_handling.volume_fault(resp, 'Invalid volume id.',
                                            code=HTTP.BAD_REQUEST)
        except Exception as e:
            error_handling.volume_fault(resp, e.faultString)
Example #11
0
    def _delete_volume(self, tenant_id, volume_id, client, req, resp):

        virtual_disk = client['Virtual_Disk_Image']

        try:
            item = virtual_disk.getObject(id=volume_id, mask='billingItem')
        except SoftLayer.SoftLayerAPIError as e:
            return error_handling.volume_fault(resp, e.faultString,
                                               code=HTTP.NOT_FOUND)

        billingItemId = item['billingItem']['id']
        billing = client['Billing_Item']
        # Reason is from this document:
        # https://sldn.softlayer.com/reference/services/
        # SoftLayer_Billing_Item/cancelItem
        reason = "No longer needed"
        billing.cancelItem(True, True, reason, id=billingItemId)
        resp.status = HTTP.ACCEPTED
Example #12
0
    def on_post(self, req, resp, tenant_id, instance_id):
        '''Attaches a specified volume to a specified server.'''
        body = json.loads(req.stream.read().decode())

        if any([len(body) == 0,
                'volumeAttachment' not in body,
                'volumeId' not in body['volumeAttachment']]):
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        vg_client = req.env['sl_client']['Virtual_Guest']

        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        volume_id = body['volumeAttachment']['volumeId']
        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed request body")

        vdi_client = req.env['sl_client']['Virtual_Disk_Image']
        volinfo = None

        # first let's check if the volume is already attached
        try:
            volinfo = vdi_client.getObject(id=volume_id,
                                           mask='blockDevices')
            blkDevices = volinfo['blockDevices']
            if (len(blkDevices) > 0):
                guestId_list = [blkDevice['guestId'] for blkDevice
                                in blkDevices]
                for guest_id in guestId_list:
                    if (guest_id == instance_id):
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is already attached to '
                            'this guest.',
                            code=HTTP.BAD_REQUEST)
                    else:
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is already attached to '
                            'another guest.',
                            code=HTTP.BAD_REQUEST)
        except Exception as e:
            return error_handling.volume_fault(resp,
                                               e.faultString,
                                               code=HTTP.NOT_FOUND)

        try:
            # providing different size doesn't seem to have any impact on the
            # outcome hence using 10 as default size.
            disk_check = vg_client.checkHostDiskAvailability(10,
                                                             id=instance_id)
        except Exception:
            disk_check = True

        try:
            if disk_check:
                sl_transaction = vg_client.attachDiskImage(volume_id,
                                                           id=instance_id)
                resp.body = {"volumeAttachment":
                             {"device": "",
                              "id": sl_transaction['id'],
                              "serverId": instance_id,
                              "volumeId": volume_id}}
                resp.status = HTTP.ACCEPTED
            else:
                return error_handling.volume_fault(
                    resp,
                    'Action causes migration to a new host. Migration is not '
                    'allowed.',
                    code=HTTP.BAD_REQUEST)
        except Exception as e:
            error_handling.volume_fault(resp, e.faultString)
Example #13
0
    def on_post(self, req, resp, tenant_id, instance_id):
        '''Attaches a specified volume to a specified server.'''
        vs_mgr = SoftLayer.VSManager(req.sl_client)

        try:
            instance_id = int(instance_id)
        except Exception:
            # If the instance ID is not valid an exception will be sent
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")
        # Get the instance to verify it exists;  also using info from
        # instance to fill out the response object
        try:
            instance = vs_mgr.get_instance(instance_id)
        except Exception as e:
            # If the instance is not found an exception will be sent
            return error_handling.volume_fault(
                resp,
                'The requested instance was not found',
                code=HTTP.NOT_FOUND)

        if not instance:
            return error_handling.volume_fault(
                resp,
                'The requested instance was not found',
                code=HTTP.NOT_FOUND)

        # Get the volume ID from the request body
        body = json.loads(req.stream.read().decode())

        CREATE_VOLUME_ATTACHMENT_VALIDATOR.validate(body)

        volume_id = body['volumeAttachment']['volumeId']

        # first let's check if the volume is already attached
        block_mgr = SoftLayer.BlockStorageManager(req.sl_client)
        blk_devices = []

        try:
            # get the volume info to see if there are any attachments
            # it also verifies that the volume exists

            items = {'id', 'allowedVirtualGuests[allowedHost[credential]]'}
            al_mask = ','.join(items)
            access_list = block_mgr.get_block_volume_access_list(volume_id,
                                                                 mask=al_mask)
            blk_devices = access_list['allowedVirtualGuests']

        except Exception as e:
            return error_handling.volume_fault(
                resp,
                'The requested volume was not found',
                code=HTTP.NOT_FOUND)

        if len(blk_devices) > 0:
            return error_handling.volume_fault(
                resp, 'The requested volume is already attached to '
                'a guest.',
                code=HTTP.BAD_REQUEST)

        try:

            # attach the volume;  authorizing the host to the volume
            block_mgr.authorize_host_to_volume(volume_id,
                                               virtual_guest_ids=[instance_id])
            volume_att = {
                'device': "",
                'serverId': instance_id,
                'volumeId': volume_id
            }
            resp.body = {"volumeAttachment": volume_att}

            resp.status = HTTP.OK

        except Exception as e:
            return error_handling.volume_fault(resp,
                                               message=str(e),
                                               code=HTTP.BAD_REQUEST)
Example #14
0
    def on_post(self, req, resp, tenant_id):
        """Create volume (SL Portable storage)

        :param req: Falcon request object
        :param resp: Falcon request object
        :param tenant_id: Softlayer tenant_id
        :param return: Falcon response object with openstack response body
        """
        client = req.env['sl_client']
        try:
            v_type_zone = None
            rounding = False
            body = json.loads(req.stream.read().decode())
            if body['volume']['volume_type'] is not None:
                if not self.volume_types['volume_types']:
                    resp.status = HTTP.INTERNAL_SERVER_ERROR
                    return error_handling.volume_fault(resp,
                                                       "Server has no"
                                                       " types to select")
                foundType = False
                for type in self.volume_types['volume_types']:
                    if type['name'] == body['volume']['volume_type']:
                        foundType = True
                        v_type_zone = (
                            type['extra_specs']['capabilities:volume_backend_name']  # noqa
                            )
                        rounding = (
                            type['extra_specs']['drivers:exact_capacity']
                            )
                if not foundType:
                    resp.status = 400
                    raise Exception('Specify a volume with a valid name')

            # required field in the create volume payload
            namestr = body['volume'].get("display_name")
            volreq = body['volume']
            # portable storage order cannot have empty name
            name = (config.CONF['volume']['volume_name_prefix'] +
                    (namestr if namestr else ""))

            # size is required option for volume create. Throw type exception
            # if it is invalid
            size = int(volreq['size'])
            # availability_zone is optional, don't throw exception if
            # it is not available
            availability_zone = (body['volume'].get('availability_zone')
                                 or
                                 v_type_zone
                                 or
                                 config.CONF['volume']['default_availability_zone'])  # noqa
            volume_type = body['volume'].get('volume_type')

        except Exception as e:
            return error_handling.bad_request(resp, str(e))

        try:
            volinfo = self._create_volume(tenant_id, client, resp,
                                          size, name=name,
                                          zone=availability_zone,
                                          volume_type=volume_type,
                                          exact_capacity=rounding)

            resp.status = HTTP.ACCEPTED

            if volinfo:
                resp.body = {'volume':
                             format_volume(tenant_id, volinfo, client)}
                resp.body['volume'].update({'status': 'creating'})
            else:
                # Cannot generate a valid response without knowning
                # the volume id when order takes too long to complete.
                # This should be a rare case, but it could break openstack
                # since the volume create caller always expect a volume id
                # uppon successful return. The approach here is to fail
                # the volume create operation and leak one portable storage
                # volume. User can always cancel from SL portal.

                return error_handling.volume_fault(resp,
                                                   "Portable storage"
                                                   " order delayed")

        except SoftLayer.SoftLayerAPIError as e:
            return error_handling.error(resp,
                                        "SoftLayerAPIError",
                                        e.faultString,
                                        code=e.faultCode)
        except Exception as e:
            return error_handling.volume_fault(resp, str(e))
Example #15
0
    def on_post(self, req, resp, tenant_id, instance_id):
        '''Attaches a specified volume to a specified server.'''
        body = json.loads(req.stream.read().decode())

        if (len(body) == 0 or 'volumeAttachment' not in body
                or 'volumeId' not in body['volumeAttachment']):
            return error_handling.bad_request(resp,
                                              message="Malformed "
                                              "request body")

        vg_client = req.env['sl_client']['Virtual_Guest']

        try:
            instance_id = int(instance_id)
        except Exception:
            return error_handling.not_found(resp,
                                            "Invalid instance ID specified.")

        volume_id = body['volumeAttachment']['volumeId']
        if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN:
            return error_handling.bad_request(resp,
                                              message="Malformed "
                                              "request body")

        vdi_client = req.env['sl_client']['Virtual_Disk_Image']
        volinfo = None

        # first let's check if the volume is already attached
        try:
            volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices')
            blkDevices = volinfo['blockDevices']
            if (len(blkDevices) > 0):
                guestId_list = [
                    blkDevice['guestId'] for blkDevice in blkDevices
                ]
                for guest_id in guestId_list:
                    if (guest_id == instance_id):
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is already attached to '
                            'this guest.',
                            code=HTTP.BAD_REQUEST)
                    else:
                        return error_handling.volume_fault(
                            resp,
                            'The requested disk image is already attached to '
                            'another guest.',
                            code=HTTP.BAD_REQUEST)
        except Exception as e:
            return error_handling.volume_fault(resp,
                                               e.faultString,
                                               code=HTTP.NOT_FOUND)

        try:
            # providing different size doesn't seem to have any impact on the
            # outcome hence using 10 as default size.
            disk_check = vg_client.checkHostDiskAvailability(10,
                                                             id=instance_id)
        except Exception:
            disk_check = True

        try:
            if disk_check:
                sl_transaction = vg_client.attachDiskImage(volume_id,
                                                           id=instance_id)
                resp.body = {
                    "volumeAttachment": {
                        "device": "",
                        "id": sl_transaction['id'],
                        "serverId": instance_id,
                        "volumeId": volume_id
                    }
                }
                resp.status = HTTP.ACCEPTED
            else:
                return error_handling.volume_fault(
                    resp,
                    'Action causes migration to a new host. Migration is not '
                    'allowed.',
                    code=HTTP.BAD_REQUEST)
        except Exception as e:
            error_handling.volume_fault(resp, e.faultString)