Skip to content

Commit

Permalink
Merge "set/unset volume image metadata"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins authored and openstack-gerrit committed Jul 13, 2015
2 parents d84c501 + 4196e5f commit 238df8b
Show file tree
Hide file tree
Showing 14 changed files with 561 additions and 37 deletions.
3 changes: 3 additions & 0 deletions cinder/api/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import os
import re

import enum
from oslo_config import cfg
from oslo_log import log as logging
from six.moves import urllib
Expand Down Expand Up @@ -49,6 +50,8 @@
XML_NS_V1 = 'http://docs.openstack.org/api/openstack-block-storage/1.0/content'
XML_NS_V2 = 'http://docs.openstack.org/api/openstack-block-storage/2.0/content'

METADATA_TYPES = enum.Enum('METADATA_TYPES', 'user image')


# Regex that matches alphanumeric characters, periods, hyphens,
# colons and underscores:
Expand Down
84 changes: 83 additions & 1 deletion cinder/api/contrib/volume_image_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
# under the License.

"""The Volume Image Metadata API extension."""

import logging

import six
import webob

from cinder.api import common
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder.api import xmlutil
from cinder import exception
from cinder.i18n import _
from cinder import volume


Expand All @@ -35,6 +38,15 @@ def __init__(self, *args, **kwargs):
super(VolumeImageMetadataController, self).__init__(*args, **kwargs)
self.volume_api = volume.API()

def _get_image_metadata(self, context, volume_id):
try:
volume = self.volume_api.get(context, volume_id)
meta = self.volume_api.get_volume_image_metadata(context, volume)
except exception.VolumeNotFound:
msg = _('Volume with volume id %s does not exist.') % volume_id
raise webob.exc.HTTPNotFound(explanation=msg)
return meta

def _get_all_images_metadata(self, context):
"""Returns the image metadata for all volumes."""
try:
Expand Down Expand Up @@ -81,6 +93,76 @@ def detail(self, req, resp_obj):
image_meta = all_meta.get(vol['id'], {})
self._add_image_metadata(context, vol, image_meta)

@wsgi.action("os-set_image_metadata")
@wsgi.serializers(xml=common.MetadataTemplate)
@wsgi.deserializers(xml=common.MetadataDeserializer)
def create(self, req, id, body):
context = req.environ['cinder.context']
if authorize(context):
try:
metadata = body['os-set_image_metadata']['metadata']
except (KeyError, TypeError):
msg = _("Malformed request body.")
raise webob.exc.HTTPBadRequest(explanation=msg)
new_metadata = self._update_volume_image_metadata(context,
id,
metadata,
delete=False)

return {'metadata': new_metadata}

def _update_volume_image_metadata(self, context,
volume_id,
metadata,
delete=False):
try:
volume = self.volume_api.get(context, volume_id)
return self.volume_api.update_volume_metadata(
context,
volume,
metadata,
delete=False,
meta_type=common.METADATA_TYPES.image)
except exception.VolumeNotFound:
msg = _('Volume with volume id %s does not exist.') % volume_id
raise webob.exc.HTTPNotFound(explanation=msg)
except (ValueError, AttributeError):
msg = _("Malformed request body.")
raise webob.exc.HTTPBadRequest(explanation=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)

@wsgi.action("os-unset_image_metadata")
def delete(self, req, id, body):
"""Deletes an existing image metadata."""
context = req.environ['cinder.context']
if authorize(context):
try:
key = body['os-unset_image_metadata']['key']
except (KeyError, TypeError):
msg = _("Malformed request body.")
raise webob.exc.HTTPBadRequest(explanation=msg)

if key:
metadata = self._get_image_metadata(context, id)
if key not in metadata:
msg = _("Metadata item was not found.")
raise webob.exc.HTTPNotFound(explanation=msg)

try:
volume = self.volume_api.get(context, id)
self.volume_api.delete_volume_metadata(
context,
volume,
key,
meta_type=common.METADATA_TYPES.image)
except exception.VolumeNotFound:
msg = _('Volume does not exist.')
raise webob.exc.HTTPNotFound(explanation=msg)
return webob.Response(status_int=200)


class Volume_image_metadata(extensions.ExtensionDescriptor):
"""Show image metadata associated with the volume."""
Expand Down
16 changes: 11 additions & 5 deletions cinder/api/v2/volume_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ def _update_volume_metadata(self, context,
delete=False):
try:
volume = self.volume_api.get(context, volume_id)
return self.volume_api.update_volume_metadata(context,
volume,
metadata,
delete)
return self.volume_api.update_volume_metadata(
context,
volume,
metadata,
delete,
meta_type=common.METADATA_TYPES.user)
except exception.VolumeNotFound as error:
raise webob.exc.HTTPNotFound(explanation=error.msg)

Expand Down Expand Up @@ -139,7 +141,11 @@ def delete(self, req, volume_id, id):

try:
volume = self.volume_api.get(context, volume_id)
self.volume_api.delete_volume_metadata(context, volume, id)
self.volume_api.delete_volume_metadata(
context,
volume,
id,
meta_type=common.METADATA_TYPES.user)
except exception.VolumeNotFound as error:
raise webob.exc.HTTPNotFound(explanation=error.msg)
return webob.Response(status_int=200)
Expand Down
13 changes: 9 additions & 4 deletions cinder/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from oslo_db import concurrency as db_concurrency
from oslo_db import options as db_options

from cinder.api import common

db_opts = [
cfg.BoolOpt('enable_new_services',
Expand Down Expand Up @@ -368,14 +369,18 @@ def volume_metadata_get(context, volume_id):
return IMPL.volume_metadata_get(context, volume_id)


def volume_metadata_delete(context, volume_id, key):
def volume_metadata_delete(context, volume_id, key,
meta_type=common.METADATA_TYPES.user):
"""Delete the given metadata item."""
return IMPL.volume_metadata_delete(context, volume_id, key)
return IMPL.volume_metadata_delete(context, volume_id,
key, meta_type)


def volume_metadata_update(context, volume_id, metadata, delete):
def volume_metadata_update(context, volume_id, metadata,
delete, meta_type=common.METADATA_TYPES.user):
"""Update metadata if it exists, otherwise create it."""
return IMPL.volume_metadata_update(context, volume_id, metadata, delete)
return IMPL.volume_metadata_update(context, volume_id, metadata,
delete, meta_type)


##################
Expand Down
60 changes: 51 additions & 9 deletions cinder/db/sqlalchemy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from sqlalchemy.sql.expression import true
from sqlalchemy.sql import func

from cinder.api import common
from cinder.common import sqlalchemyutils
from cinder.db.sqlalchemy import models
from cinder import exception
Expand Down Expand Up @@ -1760,7 +1761,10 @@ def _volume_x_metadata_get_item(context, volume_id, key, model, notfound_exec,
first()

if not result:
raise notfound_exec(metadata_key=key, volume_id=volume_id)
if model is models.VolumeGlanceMetadata:
raise notfound_exec(id=volume_id)
else:
raise notfound_exec(metadata_key=key, volume_id=volume_id)
return result


Expand Down Expand Up @@ -1812,6 +1816,12 @@ def _volume_user_metadata_get_query(context, volume_id, session=None):
models.VolumeMetadata, session=session)


def _volume_image_metadata_get_query(context, volume_id, session=None):
return _volume_x_metadata_get_query(context, volume_id,
models.VolumeGlanceMetadata,
session=session)


@require_context
@require_volume_exists
def _volume_user_metadata_get(context, volume_id, session=None):
Expand All @@ -1837,6 +1847,16 @@ def _volume_user_metadata_update(context, volume_id, metadata, delete,
session=session)


@require_context
@require_volume_exists
def _volume_image_metadata_update(context, volume_id, metadata, delete,
session=None):
return _volume_x_metadata_update(context, volume_id, metadata, delete,
models.VolumeGlanceMetadata,
exception.GlanceMetadataNotFound,
session=session)


@require_context
@require_volume_exists
def volume_metadata_get_item(context, volume_id, key):
Expand All @@ -1852,19 +1872,41 @@ def volume_metadata_get(context, volume_id):
@require_context
@require_volume_exists
@_retry_on_deadlock
def volume_metadata_delete(context, volume_id, key):
_volume_user_metadata_get_query(context, volume_id).\
filter_by(key=key).\
update({'deleted': True,
'deleted_at': timeutils.utcnow(),
'updated_at': literal_column('updated_at')})
def volume_metadata_delete(context, volume_id, key, meta_type):
if meta_type == common.METADATA_TYPES.user:
(_volume_user_metadata_get_query(context, volume_id).
filter_by(key=key).
update({'deleted': True,
'deleted_at': timeutils.utcnow(),
'updated_at': literal_column('updated_at')}))
elif meta_type == common.METADATA_TYPES.image:
(_volume_image_metadata_get_query(context, volume_id).
filter_by(key=key).
update({'deleted': True,
'deleted_at': timeutils.utcnow(),
'updated_at': literal_column('updated_at')}))
else:
raise exception.InvalidMetadataType(metadata_type=meta_type,
id=volume_id)


@require_context
@require_volume_exists
@_retry_on_deadlock
def volume_metadata_update(context, volume_id, metadata, delete):
return _volume_user_metadata_update(context, volume_id, metadata, delete)
def volume_metadata_update(context, volume_id, metadata, delete, meta_type):
if meta_type == common.METADATA_TYPES.user:
return _volume_user_metadata_update(context,
volume_id,
metadata,
delete)
elif meta_type == common.METADATA_TYPES.image:
return _volume_image_metadata_update(context,
volume_id,
metadata,
delete)
else:
raise exception.InvalidMetadataType(metadata_type=meta_type,
id=volume_id)


###################
Expand Down
5 changes: 5 additions & 0 deletions cinder/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,11 @@ class MetadataCopyFailure(Invalid):
message = _("Failed to copy metadata to volume: %(reason)s")


class InvalidMetadataType(Invalid):
message = _("The type of metadata: %(metadata_type)s for volume/snapshot "
"%(id)s is invalid.")


class ImageCopyFailure(Invalid):
message = _("Failed to copy image to volume: %(reason)s")

Expand Down

0 comments on commit 238df8b

Please sign in to comment.