Ejemplo n.º 1
0
    def _get_encryption_key_id(
            self, key_manager, context: context.RequestContext,
            volume_type_id: str, snapshot: Optional[objects.Snapshot],
            source_volume: Optional[objects.Volume],
            image_metadata: Optional[dict[str, Any]]) -> Optional[str]:
        if volume_types.is_encrypted(context, volume_type_id):
            encryption_key_id = None

            if snapshot is not None:  # creating from snapshot
                encryption_key_id = snapshot['encryption_key_id']
            elif source_volume is not None:  # cloning volume
                encryption_key_id = source_volume['encryption_key_id']
            elif image_metadata is not None:
                # creating from image
                encryption_key_id = image_metadata.get(
                    'cinder_encryption_key_id')

            # NOTE(joel-coffman): References to the encryption key should *not*
            # be copied because the key is deleted when the volume is deleted.
            # Clone the existing key and associate a separate -- but
            # identical -- key with each volume.
            new_encryption_key_id: Optional[str]
            if encryption_key_id is not None:
                new_encryption_key_id = volume_utils.clone_encryption_key(
                    context, key_manager, encryption_key_id)
            else:
                new_encryption_key_id = volume_utils.create_encryption_key(
                    context, key_manager, volume_type_id)

            return new_encryption_key_id
        else:
            return None
Ejemplo n.º 2
0
    def _get_encryption_key_id(self, key_manager, context, volume_type_id,
                               snapshot, source_volume, image_metadata):
        encryption_key_id = None
        if volume_types.is_encrypted(context, volume_type_id):
            if snapshot is not None:  # creating from snapshot
                encryption_key_id = snapshot['encryption_key_id']
            elif source_volume is not None:  # cloning volume
                encryption_key_id = source_volume['encryption_key_id']
            elif image_metadata is not None:
                # creating from image
                encryption_key_id = image_metadata.get(
                    'cinder_encryption_key_id')

            # NOTE(joel-coffman): References to the encryption key should *not*
            # be copied because the key is deleted when the volume is deleted.
            # Clone the existing key and associate a separate -- but
            # identical -- key with each volume.
            if encryption_key_id is not None:
                encryption_key_id = volume_utils.clone_encryption_key(
                    context, key_manager, encryption_key_id)
            else:
                encryption_key_id = volume_utils.create_encryption_key(
                    context, key_manager, volume_type_id)

        return encryption_key_id
Ejemplo n.º 3
0
    def _volume_upload_image(self, req, id, body):
        """Uploads the specified volume to image service."""
        context = req.environ['cinder.context']
        params = body['os-volume_upload_image']
        req_version = req.api_version_request

        force = params.get('force', 'False')
        force = strutils.bool_from_string(force, strict=True)

        # Not found exception will be handled at the wsgi level
        volume = self.volume_api.get(context, id)

        context.authorize(policy.UPLOAD_IMAGE_POLICY)

        disk_format = params.get("disk_format", "raw")
        image_metadata = {
            "container_format": params.get("container_format", "bare"),
            "disk_format": disk_format,
            "name": params["image_name"]
        }

        if volume.encryption_key_id:
            # Clone volume encryption key: the current key cannot
            # be reused because it will be deleted when the volume is
            # deleted.
            encryption_key_id = volume_utils.clone_encryption_key(
                context, self._key_manager, volume.encryption_key_id)

            image_metadata['cinder_encryption_key_id'] = encryption_key_id
            image_metadata['cinder_encryption_key_deletion_policy'] = \
                'on_image_deletion'

        if req_version >= mv.get_api_version(mv.UPLOAD_IMAGE_PARAMS):

            image_metadata['visibility'] = params.get('visibility', 'private')
            image_metadata['protected'] = strutils.bool_from_string(
                params.get('protected', 'False'), strict=True)

            if image_metadata['visibility'] == 'public':
                context.authorize(policy.UPLOAD_PUBLIC_POLICY)

        try:
            response = self.volume_api.copy_volume_to_image(
                context, volume, image_metadata, force)
        except exception.InvalidVolume as error:
            raise webob.exc.HTTPBadRequest(explanation=error.msg)
        except ValueError as error:
            raise webob.exc.HTTPBadRequest(explanation=str(error))
        except messaging.RemoteError as error:
            msg = "%(err_type)s: %(err_msg)s" % {
                'err_type': error.exc_type,
                'err_msg': error.value
            }
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except Exception as error:
            raise webob.exc.HTTPBadRequest(explanation=str(error))
        return {'os-volume_upload_image': response}
Ejemplo n.º 4
0
    def _run_backup(self, context, backup, volume):
        # Save a copy of the encryption key ID in case the volume is deleted.
        if (volume.encryption_key_id is not None
                and backup.encryption_key_id is None):
            backup.encryption_key_id = volume_utils.clone_encryption_key(
                context, key_manager.API(CONF), volume.encryption_key_id)
            backup.save()

        backup_service = self.service(context)

        properties = volume_utils.brick_get_connector_properties()

        # NOTE(geguileo): Not all I/O disk operations properly do greenthread
        # context switching and may end up blocking the greenthread, so we go
        # with native threads proxy-wrapping the device file object.
        try:
            backup_device = self.volume_rpcapi.get_backup_device(
                context, backup, volume)
            attach_info = self._attach_device(context,
                                              backup_device.device_obj,
                                              properties,
                                              backup_device.is_snapshot)
            try:
                device_path = attach_info['device']['path']
                if (isinstance(device_path, str)
                        and not os.path.isdir(device_path)):
                    if backup_device.secure_enabled:
                        with open(device_path, 'rb') as device_file:
                            updates = backup_service.backup(
                                backup, tpool.Proxy(device_file))
                    else:
                        with utils.temporary_chown(device_path):
                            with open(device_path, 'rb') as device_file:
                                updates = backup_service.backup(
                                    backup, tpool.Proxy(device_file))
                # device_path is already file-like so no need to open it
                else:
                    updates = backup_service.backup(backup,
                                                    tpool.Proxy(device_path))

            finally:
                self._detach_device(context,
                                    attach_info,
                                    backup_device.device_obj,
                                    properties,
                                    backup_device.is_snapshot,
                                    force=True,
                                    ignore_errors=True)
        finally:
            with backup.as_read_deleted():
                backup.refresh()
            self._cleanup_temp_volumes_snapshots_when_backup_created(
                context, backup)
        return updates
Ejemplo n.º 5
0
    def _run_restore(self, context, backup, volume):
        orig_key_id = volume.encryption_key_id
        backup_service = self.service(context)

        properties = volume_utils.brick_get_connector_properties()
        secure_enabled = (self.volume_rpcapi.secure_file_operations_enabled(
            context, volume))
        attach_info = self._attach_device(context, volume, properties)

        # NOTE(geguileo): Not all I/O disk operations properly do greenthread
        # context switching and may end up blocking the greenthread, so we go
        # with native threads proxy-wrapping the device file object.
        try:
            device_path = attach_info['device']['path']
            open_mode = 'rb+' if os.name == 'nt' else 'wb'
            if (isinstance(device_path, str)
                    and not os.path.isdir(device_path)):
                if secure_enabled:
                    with open(device_path, open_mode) as device_file:
                        backup_service.restore(backup, volume.id,
                                               tpool.Proxy(device_file))
                else:
                    with utils.temporary_chown(device_path):
                        with open(device_path, open_mode) as device_file:
                            backup_service.restore(backup, volume.id,
                                                   tpool.Proxy(device_file))
            # device_path is already file-like so no need to open it
            else:
                backup_service.restore(backup, volume.id,
                                       tpool.Proxy(device_path))
        except exception.BackupRestoreCancel:
            raise
        except Exception:
            LOG.exception(
                'Restoring backup %(backup_id)s to volume '
                '%(volume_id)s failed.', {
                    'backup_id': backup.id,
                    'volume_id': volume.id
                })
            raise
        finally:
            self._detach_device(context,
                                attach_info,
                                volume,
                                properties,
                                force=True)

        # Regardless of whether the restore was successful, do some
        # housekeeping to ensure the restored volume's encryption key ID is
        # unique, and any previous key ID is deleted. Start by fetching fresh
        # info on the restored volume.
        restored_volume = objects.Volume.get_by_id(context, volume.id)
        restored_key_id = restored_volume.encryption_key_id
        if restored_key_id != orig_key_id:
            LOG.info(
                'Updating encryption key ID for volume %(volume_id)s '
                'from backup %(backup_id)s.', {
                    'volume_id': volume.id,
                    'backup_id': backup.id
                })

            key_mgr = key_manager.API(CONF)
            if orig_key_id is not None:
                LOG.debug('Deleting original volume encryption key ID.')
                volume_utils.delete_encryption_key(context, key_mgr,
                                                   orig_key_id)

            if backup.encryption_key_id is None:
                # This backup predates the current code that stores the cloned
                # key ID in the backup database. Fortunately, the key ID
                # restored from the backup data _is_ a clone of the original
                # volume's key ID, so grab it.
                LOG.debug('Gleaning backup encryption key ID from metadata.')
                backup.encryption_key_id = restored_key_id
                backup.save()

            # Clone the key ID again to ensure every restored volume has
            # a unique key ID. The volume's key ID should not be the same
            # as the backup.encryption_key_id (the copy made when the backup
            # was first created).
            new_key_id = volume_utils.clone_encryption_key(
                context, key_mgr, backup.encryption_key_id)
            restored_volume.encryption_key_id = new_key_id
            restored_volume.save()
        else:
            LOG.debug(
                'Encryption key ID for volume %(volume_id)s already '
                'matches encryption key ID in backup %(backup_id)s.', {
                    'volume_id': volume.id,
                    'backup_id': backup.id
                })