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)
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'] }
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")
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")
def _notify_reset_status(self, context, id, message): volume = objects.Volume.get_by_id(context, id) volume_utils.notify_about_volume_usage(context, volume, message)
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']}