Пример #1
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']
        update_dict = body['volume']

        self.validate_name_and_description(update_dict, check_length=False)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict.pop('name')

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict.pop('description')

        # Not found and Invalid exceptions will be handled at the wsgi level
        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.InvalidVolumeMetadataSize as error:
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=error.msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #2
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            raise exc.HTTPUnprocessableEntity()

        if 'volume' not in body:
            raise exc.HTTPUnprocessableEntity()

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id, viewable_admin_meta=True)
        volume_utils.notify_about_volume_usage(context, volume, 'update.start')
        self.volume_api.update(context, volume, update_dict)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume, 'update.end')

        return {'volume': _translate_volume_detail_view(context, volume)}
Пример #3
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']
        update_dict = body['volume']

        self.validate_name_and_description(update_dict, check_length=False)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict.pop('name')

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict.pop('description')

        # Not found and Invalid exceptions will be handled at the wsgi level
        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.InvalidVolumeMetadataSize as error:
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=error.msg)

        volume.update(update_dict)

        api_utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #4
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ["cinder.context"]

        if not body:
            raise exc.HTTPUnprocessableEntity()

        if "volume" not in body:
            raise exc.HTTPUnprocessableEntity()

        volume = body["volume"]
        update_dict = {}

        valid_update_keys = ("display_name", "display_description", "metadata")

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume, "update.start")
            self.volume_api.update(context, volume, update_dict)
        except exception.NotFound:
            raise exc.HTTPNotFound()

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume, "update.end")

        return {"volume": _translate_volume_detail_view(context, volume)}
Пример #5
0
    def create(self, context, volume_id, display_name):
        """Creates an entry in the transfers table."""
        volume_api.check_policy(context, 'create_transfer')
        LOG.info(_LI("Generating transfer record for volume %s"), volume_id)
        volume_ref = self.db.volume_get(context, volume_id)
        if volume_ref['status'] != "available":
            raise exception.InvalidVolume(reason=_("status must be available"))

        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.start")
        # The salt is just a short random string.
        salt = self._get_random_string(CONF.volume_transfer_salt_length)
        auth_key = self._get_random_string(CONF.volume_transfer_key_length)
        crypt_hash = self._get_crypt_hash(salt, auth_key)

        # TODO(ollie): Transfer expiry needs to be implemented.
        transfer_rec = {'volume_id': volume_id,
                        'display_name': display_name,
                        'salt': salt,
                        'crypt_hash': crypt_hash,
                        'expires_at': None}

        try:
            transfer = self.db.transfer_create(context, transfer_rec)
        except Exception:
            LOG.error(_LE("Failed to create transfer record "
                          "for %s"), volume_id)
            raise
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.end")
        return {'id': transfer['id'],
                'volume_id': transfer['volume_id'],
                'display_name': transfer['display_name'],
                'auth_key': auth_key,
                'created_at': transfer['created_at']}
Пример #6
0
    def create(self, context, volume_id, display_name):
        """Creates an entry in the transfers table."""
        volume_api.check_policy(context, 'create_transfer')
        LOG.info(_LI("Generating transfer record for volume %s"), volume_id)
        volume_ref = self.db.volume_get(context, volume_id)
        if volume_ref['status'] != "available":
            raise exception.InvalidVolume(reason=_("status must be available"))

        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.start")
        # The salt is just a short random string.
        salt = self._get_random_string(CONF.volume_transfer_salt_length)
        auth_key = self._get_random_string(CONF.volume_transfer_key_length)
        crypt_hash = self._get_crypt_hash(salt, auth_key)

        # TODO(ollie): Transfer expiry needs to be implemented.
        transfer_rec = {'volume_id': volume_id,
                        'display_name': display_name,
                        'salt': salt,
                        'crypt_hash': crypt_hash,
                        'expires_at': None}

        try:
            transfer = self.db.transfer_create(context, transfer_rec)
        except Exception:
            LOG.error(_LE("Failed to create transfer record "
                          "for %s"), volume_id)
            raise
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.end")
        return {'id': transfer['id'],
                'volume_id': transfer['volume_id'],
                'display_name': transfer['display_name'],
                'auth_key': auth_key,
                'created_at': transfer['created_at']}
Пример #7
0
    def create(self, context, volume_id, display_name, no_snapshots=False):
        """Creates an entry in the transfers table."""
        LOG.info("Generating transfer record for volume %s", volume_id)
        volume_ref = objects.Volume.get_by_id(context, volume_id)
        context.authorize(policy.CREATE_POLICY, target_obj=volume_ref)
        if volume_ref['status'] != "available":
            raise exception.InvalidVolume(reason=_("status must be available"))
        if volume_ref['encryption_key_id'] is not None:
            raise exception.InvalidVolume(
                reason=_("transferring encrypted volume is not supported"))

        if not no_snapshots:
            snapshots = self.db.snapshot_get_all_for_volume(context, volume_id)
            for snapshot in snapshots:
                if snapshot['status'] != "available":
                    msg = _("snapshot: %s status must be "
                            "available") % snapshot['id']
                    raise exception.InvalidSnapshot(reason=msg)
                if snapshot.get('encryption_key_id'):
                    msg = _("snapshot: %s encrypted snapshots cannot be "
                            "transferred") % snapshot['id']
                    raise exception.InvalidSnapshot(reason=msg)

        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.start")
        # The salt is just a short random string.
        salt = self._get_random_string(CONF.volume_transfer_salt_length)
        auth_key = self._get_random_string(CONF.volume_transfer_key_length)
        crypt_hash = self._get_crypt_hash(salt, auth_key)

        # TODO(ollie): Transfer expiry needs to be implemented.
        transfer_rec = {
            'volume_id': volume_id,
            'display_name': display_name,
            'salt': salt,
            'crypt_hash': crypt_hash,
            'expires_at': None,
            'no_snapshots': no_snapshots,
            'source_project_id': volume_ref['project_id']
        }

        try:
            transfer = self.db.transfer_create(context, transfer_rec)
        except Exception:
            LOG.error("Failed to create transfer record for %s", volume_id)
            raise
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.end")
        return {
            'id': transfer['id'],
            'volume_id': transfer['volume_id'],
            'display_name': transfer['display_name'],
            'auth_key': auth_key,
            'created_at': transfer['created_at'],
            'no_snapshots': transfer['no_snapshots'],
            'source_project_id': transfer['source_project_id'],
            'destination_project_id': transfer['destination_project_id'],
            'accepted': transfer['accepted']
        }
Пример #8
0
 def _notify_about_volume_usage(self,
                                context,
                                volume,
                                event_suffix,
                                extra_usage_info=None):
     volume_utils.notify_about_volume_usage(
         context, volume, event_suffix,
         extra_usage_info=extra_usage_info, host=self.host)
Пример #9
0
 def _notify_about_volume_usage(self,
                                context,
                                volume,
                                event_suffix,
                                extra_usage_info=None):
     volume_utils.notify_about_volume_usage(
         context, volume, event_suffix,
         extra_usage_info=extra_usage_info, host=self.host)
Пример #10
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            msg = _("Missing request body")
            raise exc.HTTPBadRequest(explanation=msg)

        if 'volume' not in body:
            msg = _("Missing required element '%s' in request body") % 'volume'
            raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'name',
            'description',
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        self.validate_name_and_description(update_dict)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict.pop('name')

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict.pop('description')

        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)
        except exception.InvalidVolumeMetadata as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except exception.InvalidVolumeMetadataSize as error:
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=error.msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #11
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, "accept_transfer")
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer["salt"], auth_key)
        if crypt_hash != transfer["crypt_hash"]:
            msg = _("Attempt to transfer %s with invalid auth key.") % transfer_id
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer["volume_id"]
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref["consistencygroup_id"]:
            msg = _("Volume %s must not be part of a consistency " "group.") % vol_ref["id"]
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.start")

        try:
            reserve_opts = {"volumes": 1, "gigabytes": vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e, resource="volumes", size=vol_ref.size)
        try:
            donor_id = vol_ref["project_id"]
            reserve_opts = {"volumes": -1, "gigabytes": -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(), project_id=donor_id, **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume" " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id, context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id, context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations, project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.end")
        return {"id": transfer_id, "display_name": transfer["display_name"], "volume_id": vol_ref["id"]}
Пример #12
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            msg = _("Missing request body")
            raise exc.HTTPBadRequest(explanation=msg)

        if 'volume' not in body:
            msg = _("Missing required element '%s' in request body") % 'volume'
            raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'name',
            'description',
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        self.validate_name_and_description(update_dict)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict.pop('name')

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict.pop('description')

        # Not found and Invalid exceptions will be handled at the wsgi level
        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.InvalidVolumeMetadataSize as error:
            raise webob.exc.HTTPRequestEntityTooLarge(explanation=error.msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #13
0
    def create(self, context, volume_id, display_name, no_snapshots=False):
        """Creates an entry in the transfers table."""
        LOG.info("Generating transfer record for volume %s", volume_id)
        volume_ref = objects.Volume.get_by_id(context, volume_id)
        context.authorize(policy.CREATE_POLICY, target_obj=volume_ref)
        if volume_ref['status'] != "available":
            raise exception.InvalidVolume(reason=_("status must be available"))
        if volume_ref['encryption_key_id'] is not None:
            raise exception.InvalidVolume(
                reason=_("transferring encrypted volume is not supported"))

        if not no_snapshots:
            snapshots = self.db.snapshot_get_all_for_volume(context, volume_id)
            for snapshot in snapshots:
                if snapshot['status'] != "available":
                    msg = _("snapshot: %s status must be "
                            "available") % snapshot['id']
                    raise exception.InvalidSnapshot(reason=msg)
                if snapshot.get('encryption_key_id'):
                    msg = _("snapshot: %s encrypted snapshots cannot be "
                            "transferred") % snapshot['id']
                    raise exception.InvalidSnapshot(reason=msg)

        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.start")
        # The salt is just a short random string.
        salt = self._get_random_string(CONF.volume_transfer_salt_length)
        auth_key = self._get_random_string(CONF.volume_transfer_key_length)
        crypt_hash = self._get_crypt_hash(salt, auth_key)

        # TODO(ollie): Transfer expiry needs to be implemented.
        transfer_rec = {'volume_id': volume_id,
                        'display_name': display_name,
                        'salt': salt,
                        'crypt_hash': crypt_hash,
                        'expires_at': None,
                        'no_snapshots': no_snapshots,
                        'source_project_id': volume_ref['project_id']}

        try:
            transfer = self.db.transfer_create(context, transfer_rec)
        except Exception:
            LOG.error("Failed to create transfer record for %s", volume_id)
            raise
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.create.end")
        return {'id': transfer['id'],
                'volume_id': transfer['volume_id'],
                'display_name': transfer['display_name'],
                'auth_key': auth_key,
                'created_at': transfer['created_at'],
                'no_snapshots': transfer['no_snapshots'],
                'source_project_id': transfer['source_project_id'],
                'destination_project_id': transfer['destination_project_id'],
                'accepted': transfer['accepted']}
Пример #14
0
    def delete(self, context, transfer_id):
        """Make the RPC call to delete a volume transfer."""
        volume_api.check_policy(context, "delete_transfer")
        transfer = self.db.transfer_get(context, transfer_id)

        volume_ref = self.db.volume_get(context, transfer.volume_id)
        volume_utils.notify_about_volume_usage(context, volume_ref, "transfer.delete.start")
        if volume_ref["status"] != "awaiting-transfer":
            LOG.error(_LE("Volume in unexpected state"))
        self.db.transfer_destroy(context, transfer_id)
        volume_utils.notify_about_volume_usage(context, volume_ref, "transfer.delete.end")
Пример #15
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            msg = _("Missing request body")
            raise exc.HTTPBadRequest(explanation=msg)

        if 'volume' not in body:
            msg = _("Missing required element '%s' in request body") % 'volume'
            raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'name',
            'description',
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict['name']
            del update_dict['name']

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict['description']
            del update_dict['description']

        try:
            volume = self.volume_api.get(context, id)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.NotFound:
            msg = _("Volume could not be found")
            raise exc.HTTPNotFound(explanation=msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(context, volume, self.volume_api)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #16
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            msg = _("Missing request body")
            raise exc.HTTPBadRequest(explanation=msg)

        if 'volume' not in body:
            msg = _("Missing required element '%s' in request body") % 'volume'
            raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'name',
            'description',
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in update_dict:
            update_dict['display_name'] = update_dict['name']
            del update_dict['name']

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'description' in update_dict:
            update_dict['display_description'] = update_dict['description']
            del update_dict['description']

        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.NotFound:
            msg = _("Volume could not be found")
            raise exc.HTTPNotFound(explanation=msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return self._view_builder.detail(req, volume)
Пример #17
0
    def delete(self, context, volume, force=False, unmanage_only=False):
        if context.is_admin and context.project_id != volume["project_id"]:
            project_id = volume["project_id"]
        else:
            project_id = context.project_id

        volume_id = volume["id"]
        if not volume["host"]:
            volume_utils.notify_about_volume_usage(context, volume, "delete.start")
            # NOTE(vish): scheduling failed, so delete it
            # Note(zhiteng): update volume quota reservation
            try:
                reserve_opts = {"volumes": -1, "gigabytes": -volume["size"]}
                QUOTAS.add_volume_type_opts(context, reserve_opts, volume["volume_type_id"])
                reservations = QUOTAS.reserve(context, project_id=project_id, **reserve_opts)
            except Exception:
                reservations = None
                LOG.exception(_("Failed to update quota for deleting volume"))
            self.db.volume_destroy(context.elevated(), volume_id)

            if reservations:
                QUOTAS.commit(context, reservations, project_id=project_id)

            volume_utils.notify_about_volume_usage(context, volume, "delete.end")
            return
        if not force and volume["status"] not in ["available", "error", "error_restoring", "error_extending"]:
            msg = _("Volume status must be available or error, " "but current status is: %s") % volume["status"]
            raise exception.InvalidVolume(reason=msg)

        if volume["attach_status"] == "attached":
            # Volume is still attached, need to detach first
            raise exception.VolumeAttached(volume_id=volume_id)

        if volume["migration_status"] is not None:
            # Volume is migrating, wait until done
            msg = _("Volume cannot be deleted while migrating")
            raise exception.InvalidVolume(reason=msg)

        snapshots = self.db.snapshot_get_all_for_volume(context, volume_id)
        if len(snapshots):
            msg = _("Volume still has %d dependent snapshots") % len(snapshots)
            raise exception.InvalidVolume(reason=msg)

        # If the volume is encrypted, delete its encryption key from the key
        # manager. This operation makes volume deletion an irreversible process
        # because the volume cannot be decrypted without its key.
        encryption_key_id = volume.get("encryption_key_id", None)
        if encryption_key_id is not None:
            self.key_manager.delete_key(context, encryption_key_id)

        now = timeutils.utcnow()
        self.db.volume_update(context, volume_id, {"status": "deleting", "terminated_at": now})

        self.volume_rpcapi.delete_volume(context, volume, unmanage_only)
Пример #18
0
 def execute(self, context, volume):
     try:
         volume_utils.notify_about_volume_usage(context, volume,
                                                self.event_suffix,
                                                host=volume.host)
     except exception.CinderException:
         # If notification sending of volume database entry reading fails
         # then we shouldn't error out the whole workflow since this is
         # not always information that must be sent for volumes to operate
         LOG.exception("Failed notifying about the volume"
                       " action %(event)s for volume %(volume_id)s",
                       {'event': self.event_suffix, 'volume_id': volume.id})
Пример #19
0
 def execute(self, context, volume):
     try:
         volume_utils.notify_about_volume_usage(context, volume,
                                                self.event_suffix,
                                                host=volume.host)
     except exception.CinderException:
         # If notification sending of volume database entry reading fails
         # then we shouldn't error out the whole workflow since this is
         # not always information that must be sent for volumes to operate
         LOG.exception("Failed notifying about the volume"
                       " action %(event)s for volume %(volume_id)s",
                       {'event': self.event_suffix, 'volume_id': volume.id})
Пример #20
0
 def execute(self, context, volume_ref):
     volume_id = volume_ref["id"]
     try:
         volume_utils.notify_about_volume_usage(context, volume_ref, self.event_suffix, host=volume_ref["host"])
     except exception.CinderException:
         # If notification sending of volume database entry reading fails
         # then we shouldn't error out the whole workflow since this is
         # not always information that must be sent for volumes to operate
         LOG.exception(
             _LE("Failed notifying about the volume" " action %(event)s for volume %(volume_id)s"),
             {"event": self.event_suffix, "volume_id": volume_id},
         )
Пример #21
0
    def delete(self, context, transfer_id):
        """Make the RPC call to delete a volume transfer."""
        transfer = self.db.transfer_get(context, transfer_id)

        volume_ref = objects.Volume.get_by_id(context, transfer.volume_id)
        context.authorize(policy.DELETE_POLICY, target_obj=volume_ref)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.start")
        if volume_ref['status'] != 'awaiting-transfer':
            LOG.error("Volume in unexpected state")
        self.db.transfer_destroy(context, transfer_id)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.end")
Пример #22
0
    def delete(self, context, transfer_id):
        """Make the RPC call to delete a volume transfer."""
        volume_api.check_policy(context, 'delete_transfer')
        transfer = self.db.transfer_get(context, transfer_id)

        volume_ref = self.db.volume_get(context, transfer.volume_id)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.start")
        if volume_ref['status'] != 'awaiting-transfer':
            LOG.error(_LE("Volume in unexpected state"))
        self.db.transfer_destroy(context, transfer_id)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.end")
Пример #23
0
    def delete(self, context, transfer_id):
        """Make the RPC call to delete a volume transfer."""
        transfer = self.db.transfer_get(context, transfer_id)

        volume_ref = self.db.volume_get(context, transfer.volume_id)
        context.authorize(policy.DELETE_POLICY, target_obj=volume_ref)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.start")
        if volume_ref['status'] != 'awaiting-transfer':
            LOG.error("Volume in unexpected state")
        self.db.transfer_destroy(context, transfer_id)
        volume_utils.notify_about_volume_usage(context, volume_ref,
                                               "transfer.delete.end")
Пример #24
0
 def complete(self, req, id, body):
     """Mark a volume attachment process as completed (in-use)."""
     context = req.environ['cinder.context']
     attachment_ref = (objects.VolumeAttachment.get_by_id(context, id))
     volume_ref = objects.Volume.get_by_id(context,
                                           attachment_ref.volume_id)
     context.authorize(attachment_policy.COMPLETE_POLICY,
                       target_obj=attachment_ref)
     attachment_ref.update(
         {'attach_status': fields.VolumeAttachStatus.ATTACHED})
     attachment_ref.save()
     volume_ref.update({'status': 'in-use', 'attach_status': 'attached'})
     volume_ref.save()
     volume_utils.notify_about_volume_usage(context, volume_ref,
                                            "attach.end")
Пример #25
0
 def complete(self, req, id, body):
     """Mark a volume attachment process as completed (in-use)."""
     context = req.environ['cinder.context']
     attachment_ref = (
         objects.VolumeAttachment.get_by_id(context, id))
     volume_ref = objects.Volume.get_by_id(
         context,
         attachment_ref.volume_id)
     context.authorize(attachment_policy.COMPLETE_POLICY,
                       target_obj=attachment_ref)
     attachment_ref.update(
         {'attach_status': fields.VolumeAttachStatus.ATTACHED})
     attachment_ref.save()
     volume_ref.update({'status': 'in-use', 'attach_status': 'attached'})
     volume_ref.save()
     volume_utils.notify_about_volume_usage(context, volume_ref,
                                            "attach.end")
Пример #26
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ["cinder.context"]

        if not body:
            msg = _("Missing request body")
            raise exc.HTTPBadRequest(explanation=msg)

        if "volume" not in body:
            msg = _("Missing required element '%s' in request body") % "volume"
            raise exc.HTTPBadRequest(explanation=msg)

        volume = body["volume"]
        update_dict = {}

        valid_update_keys = ("name", "description", "display_name", "display_description", "metadata")

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        self.validate_name_and_description(update_dict)

        # NOTE(thingee): v2 API allows name instead of display_name
        if "name" in update_dict:
            update_dict["display_name"] = update_dict.pop("name")

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if "description" in update_dict:
            update_dict["display_description"] = update_dict.pop("description")

        try:
            volume = self.volume_api.get(context, id, viewable_admin_meta=True)
            volume_utils.notify_about_volume_usage(context, volume, "update.start")
            self.volume_api.update(context, volume, update_dict)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)

        volume.update(update_dict)

        utils.add_visible_admin_metadata(volume)

        volume_utils.notify_about_volume_usage(context, volume, "update.end")

        return self._view_builder.detail(req, volume)
Пример #27
0
 def test_notify_about_volume_usage(self, mock_rpc, mock_conf, mock_usage):
     mock_conf.host = "host1"
     output = volume_utils.notify_about_volume_usage(mock.sentinel.context, mock.sentinel.volume, "test_suffix")
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.context, mock.sentinel.volume)
     mock_rpc.get_notifier.assert_called_once_with("volume", "host1")
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context, "volume.test_suffix", mock_usage.return_value
     )
Пример #28
0
 def test_notify_about_volume_usage(self, mock_rpc, mock_conf, mock_usage):
     mock_conf.host = 'host1'
     output = volume_utils.notify_about_volume_usage(
         mock.sentinel.context, mock.sentinel.volume, 'test_suffix')
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.context,
                                        mock.sentinel.volume)
     mock_rpc.get_notifier.assert_called_once_with('volume', 'host1')
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context, 'volume.test_suffix',
         mock_usage.return_value)
Пример #29
0
 def test_notify_about_volume_usage(self, mock_rpc, mock_conf, mock_usage):
     mock_conf.host = 'host1'
     output = volume_utils.notify_about_volume_usage(mock.sentinel.context,
                                                     mock.sentinel.volume,
                                                     'test_suffix')
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.volume)
     mock_rpc.get_notifier.assert_called_once_with('volume', 'host1')
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context,
         'volume.test_suffix',
         mock_usage.return_value)
Пример #30
0
    def update(self, req, id, body):
        """Update a volume."""
        context = req.environ['cinder.context']

        if not body:
            raise exc.HTTPUnprocessableEntity()

        if 'volume' not in body:
            raise exc.HTTPUnprocessableEntity()

        volume = body['volume']
        update_dict = {}

        valid_update_keys = (
            'display_name',
            'display_description',
            'metadata',
        )

        for key in valid_update_keys:
            if key in volume:
                update_dict[key] = volume[key]

        try:
            volume = self.volume_api.get(context, id)
            volume_utils.notify_about_volume_usage(context, volume,
                                                   'update.start')
            self.volume_api.update(context, volume, update_dict)
        except exception.NotFound:
            raise exc.HTTPNotFound()

        volume.update(update_dict)

        self._add_visible_admin_metadata(context, volume)

        volume_utils.notify_about_volume_usage(context, volume,
                                               'update.end')

        return {'volume': _translate_volume_detail_view(context, volume)}
Пример #31
0
    def create(self, context, volume_id, display_name):
        """Creates an entry in the transfers table."""
        volume_api.check_policy(context, "create_transfer")
        LOG.info(_LI("Generating transfer record for volume %s"), volume_id)
        volume_ref = self.db.volume_get(context, volume_id)
        if volume_ref["status"] != "available":
            raise exception.InvalidVolume(reason=_("status must be available"))

        volume_utils.notify_about_volume_usage(context, volume_ref, "transfer.create.start")
        # The salt is just a short random string.
        salt = self._get_random_string(CONF.volume_transfer_salt_length)
        auth_key = self._get_random_string(CONF.volume_transfer_key_length)
        crypt_hash = self._get_crypt_hash(salt, auth_key)

        # TODO(ollie): Transfer expiry needs to be implemented.
        transfer_rec = {
            "volume_id": volume_id,
            "display_name": display_name,
            "salt": salt,
            "crypt_hash": crypt_hash,
            "expires_at": None,
        }

        try:
            transfer = self.db.transfer_create(context, transfer_rec)
        except Exception:
            LOG.error(_LE("Failed to create transfer record " "for %s"), volume_id)
            raise
        volume_utils.notify_about_volume_usage(context, volume_ref, "transfer.create.end")
        return {
            "id": transfer["id"],
            "volume_id": transfer["volume_id"],
            "display_name": transfer["display_name"],
            "auth_key": auth_key,
            "created_at": transfer["created_at"],
        }
Пример #32
0
 def test_notify_about_volume_usage_with_kwargs(self, mock_rpc, mock_conf, mock_usage):
     mock_conf.host = "host1"
     output = volume_utils.notify_about_volume_usage(
         mock.sentinel.context,
         mock.sentinel.volume,
         "test_suffix",
         extra_usage_info={"a": "b", "c": "d"},
         host="host2",
     )
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.context, mock.sentinel.volume, a="b", c="d")
     mock_rpc.get_notifier.assert_called_once_with("volume", "host2")
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context, "volume.test_suffix", mock_usage.return_value
     )
Пример #33
0
 def test_notify_about_volume_usage_with_kwargs(self, mock_rpc, mock_conf,
                                                mock_usage):
     mock_conf.host = 'host1'
     output = volume_utils.notify_about_volume_usage(
         mock.sentinel.context,
         mock.sentinel.volume,
         'test_suffix',
         extra_usage_info={'a': 'b', 'c': 'd'},
         host='host2')
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.volume, a='b', c='d')
     mock_rpc.get_notifier.assert_called_once_with('volume', 'host2')
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context,
         'volume.test_suffix',
         mock_usage.return_value)
Пример #34
0
 def test_notify_about_volume_usage_with_kwargs(self, mock_rpc, mock_conf,
                                                mock_usage):
     mock_conf.host = 'host1'
     output = volume_utils.notify_about_volume_usage(
         mock.sentinel.context,
         mock.sentinel.volume,
         'test_suffix',
         extra_usage_info={'a': 'b', 'c': 'd'},
         host='host2')
     self.assertIsNone(output)
     mock_usage.assert_called_once_with(mock.sentinel.context,
                                        mock.sentinel.volume, a='b', c='d')
     mock_rpc.get_notifier.assert_called_once_with('volume', 'host2')
     mock_rpc.get_notifier.return_value.info.assert_called_once_with(
         mock.sentinel.context,
         'volume.test_suffix',
         mock_usage.return_value)
Пример #35
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        try:
            values = {'per_volume_gigabytes': vol_ref.size}
            QUOTAS.limit_check(context, project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=vol_ref.size, limit=quotas['per_volume_gigabytes'])

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume"
                              " transfer id %s"), transfer_id)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")
        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context,
                                            vol_ref,
                                            context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(),
                                    transfer_id,
                                    context.user_id,
                                    context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {'id': transfer_id,
                'display_name': transfer['display_name'],
                'volume_id': vol_ref['id']}
Пример #36
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(
                _LE("Failed to update quota donating volume"
                    " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id,
                                    context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context,
                                    donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {
            'id': transfer_id,
            'display_name': transfer['display_name'],
            'volume_id': vol_ref['id']
        }
Пример #37
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        context.authorize(policy.ACCEPT_POLICY)
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = objects.Volume.get_by_id(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        try:
            values = {'per_volume_gigabytes': vol_ref.size}
            QUOTAS.limit_check(context,
                               project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=vol_ref.size, limit=quotas['per_volume_gigabytes'])

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(
                "Failed to update quota donating volume"
                " transfer id %s", transfer_id)

        snap_res = None
        snap_donor_res = None
        if transfer['no_snapshots'] is False:
            snapshots = objects.SnapshotList.get_all_for_volume(
                context.elevated(), volume_id)
            volume_type_id = vol_ref.volume_type_id
            snap_res, snap_donor_res = self._handle_snapshot_quota(
                context, snapshots, volume_type_id, vol_ref['project_id'])

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")
        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id,
                                            context.project_id,
                                            transfer['no_snapshots'])
            self.db.transfer_accept(context.elevated(), transfer_id,
                                    context.user_id, context.project_id,
                                    transfer['no_snapshots'])
            QUOTAS.commit(context, reservations)
            if snap_res:
                QUOTAS.commit(context, snap_res)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            if snap_donor_res:
                QUOTAS.commit(context, snap_donor_res, project_id=donor_id)
            LOG.info("Volume %s has been transferred.", volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if snap_res:
                    QUOTAS.rollback(context, snap_res)
                if donor_reservations:
                    QUOTAS.rollback(context,
                                    donor_reservations,
                                    project_id=donor_id)
                if snap_donor_res:
                    QUOTAS.rollback(context,
                                    snap_donor_res,
                                    project_id=donor_id)

        vol_ref = objects.Volume.get_by_id(context.elevated(), volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {
            'id': transfer_id,
            'display_name': transfer['display_name'],
            'volume_id': vol_ref['id']
        }
Пример #38
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            for over in overs:
                if 'gigabytes' in over:
                    msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
                              "%(s_size)sG volume (%(d_consumed)dG of "
                              "%(d_quota)dG already consumed)")
                    LOG.warning(
                        msg, {
                            's_pid': context.project_id,
                            's_size': vol_ref['size'],
                            'd_consumed': _consumed(over),
                            'd_quota': quotas[over]
                        })
                    raise exception.VolumeSizeExceedsAvailableQuota(
                        requested=vol_ref['size'],
                        consumed=_consumed(over),
                        quota=quotas[over])
                elif 'volumes' in over:
                    msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
                              "volume (%(d_consumed)d volumes "
                              "already consumed)")
                    LOG.warning(msg, {
                        's_pid': context.project_id,
                        'd_consumed': _consumed(over)
                    })
                    raise exception.VolumeLimitExceeded(allowed=quotas[over],
                                                        name=over)

        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(
                _LE("Failed to update quota donating volume"
                    " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id,
                                    context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context,
                                    donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {
            'id': transfer_id,
            'display_name': transfer['display_name'],
            'volume_id': vol_ref['id']
        }
Пример #39
0
    def delete(self, context, volume, force=False, unmanage_only=False):
        if context.is_admin and context.project_id != volume['project_id']:
            project_id = volume['project_id']
        else:
            project_id = context.project_id

        volume_id = volume['id']
        if not volume['host']:
            volume_utils.notify_about_volume_usage(context, volume,
                                                   "delete.start")
            # NOTE(vish): scheduling failed, so delete it
            # Note(zhiteng): update volume quota reservation
            try:
                reserve_opts = {'volumes': -1, 'gigabytes': -volume['size']}
                QUOTAS.add_volume_type_opts(context, reserve_opts,
                                            volume['volume_type_id'])
                reservations = QUOTAS.reserve(context,
                                              project_id=project_id,
                                              **reserve_opts)
            except Exception:
                reservations = None
                LOG.exception(_("Failed to update quota for deleting volume"))
            self.db.volume_destroy(context.elevated(), volume_id)

            if reservations:
                QUOTAS.commit(context, reservations, project_id=project_id)

            volume_utils.notify_about_volume_usage(context, volume,
                                                   "delete.end")
            return
        if not force and volume['status'] not in [
                "available", "error", "error_restoring", "error_extending"
        ]:
            msg = _("Volume status must be available or error, "
                    "but current status is: %s") % volume['status']
            raise exception.InvalidVolume(reason=msg)

        if volume['attach_status'] == "attached":
            # Volume is still attached, need to detach first
            raise exception.VolumeAttached(volume_id=volume_id)

        if volume['migration_status'] is not None:
            # Volume is migrating, wait until done
            msg = _("Volume cannot be deleted while migrating")
            raise exception.InvalidVolume(reason=msg)

        snapshots = self.db.snapshot_get_all_for_volume(context, volume_id)
        if len(snapshots):
            msg = _("Volume still has %d dependent snapshots") % len(snapshots)
            raise exception.InvalidVolume(reason=msg)

        # If the volume is encrypted, delete its encryption key from the key
        # manager. This operation makes volume deletion an irreversible process
        # because the volume cannot be decrypted without its key.
        encryption_key_id = volume.get('encryption_key_id', None)
        if encryption_key_id is not None:
            self.key_manager.delete_key(context, encryption_key_id)

        now = timeutils.utcnow()
        self.db.volume_update(context, volume_id, {
            'status': 'deleting',
            'terminated_at': now
        })

        self.volume_rpcapi.delete_volume(context, volume, unmanage_only)
Пример #40
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            for over in overs:
                if 'gigabytes' in over:
                    msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
                              "%(s_size)sG volume (%(d_consumed)dG of "
                              "%(d_quota)dG already consumed)")
                    LOG.warning(msg, {'s_pid': context.project_id,
                                      's_size': vol_ref['size'],
                                      'd_consumed': _consumed(over),
                                      'd_quota': quotas[over]})
                    raise exception.VolumeSizeExceedsAvailableQuota(
                        requested=vol_ref['size'],
                        consumed=_consumed(over),
                        quota=quotas[over])
                elif 'volumes' in over:
                    msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
                              "volume (%(d_consumed)d volumes "
                              "already consumed)")
                    LOG.warning(msg, {'s_pid': context.project_id,
                                      'd_consumed': _consumed(over)})
                    raise exception.VolumeLimitExceeded(allowed=quotas[over],
                                                        name=over)

        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume"
                              " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context,
                                            vol_ref,
                                            context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(),
                                    transfer_id,
                                    context.user_id,
                                    context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {'id': transfer_id,
                'display_name': transfer['display_name'],
                'volume_id': vol_ref['id']}
Пример #41
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, "accept_transfer")
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer["salt"], auth_key)
        if crypt_hash != transfer["crypt_hash"]:
            msg = _("Attempt to transfer %s with invalid auth key.") % transfer_id
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer["volume_id"]
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.start")

        try:
            reserve_opts = {"volumes": 1, "gigabytes": vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            overs = e.kwargs["overs"]
            usages = e.kwargs["usages"]
            quotas = e.kwargs["quotas"]

            def _consumed(name):
                return usages[name]["reserved"] + usages[name]["in_use"]

            if "gigabytes" in overs:
                msg = _LW(
                    "Quota exceeded for %(s_pid)s, tried to create "
                    "%(s_size)sG volume (%(d_consumed)dG of "
                    "%(d_quota)dG already consumed)"
                )
                LOG.warning(
                    msg,
                    {
                        "s_pid": context.project_id,
                        "s_size": vol_ref["size"],
                        "d_consumed": _consumed("gigabytes"),
                        "d_quota": quotas["gigabytes"],
                    },
                )
                raise exception.VolumeSizeExceedsAvailableQuota(
                    requested=vol_ref["size"], consumed=_consumed("gigabytes"), quota=quotas["gigabytes"]
                )
            elif "volumes" in overs:
                msg = _LW(
                    "Quota exceeded for %(s_pid)s, tried to create "
                    "volume (%(d_consumed)d volumes "
                    "already consumed)"
                )
                LOG.warning(msg, {"s_pid": context.project_id, "d_consumed": _consumed("volumes")})
                raise exception.VolumeLimitExceeded(allowed=quotas["volumes"])
        try:
            donor_id = vol_ref["project_id"]
            reserve_opts = {"volumes": -1, "gigabytes": -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(), project_id=donor_id, **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume" " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id, context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id, context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations, project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.end")
        return {"id": transfer_id, "display_name": transfer["display_name"], "volume_id": vol_ref["id"]}