Adds an ability to manage share metadata
Now on share creating, we can specify metadata and it will be saved in database. Also support of setting, deleting and updating metadata info was added. Adds model for share metadata Adds migration Adds api extension for share metadata Adds relative api functions Partially implements bp manila-client-enhancements Change-Id: Id421323dd9e947d536b577092f1875f178d9aba5
This commit is contained in:
parent
cce07d59e1
commit
2e1f4e191b
|
@ -26,6 +26,7 @@ import manila.api.openstack
|
|||
from manila.api.v1 import limits
|
||||
from manila.api import versions
|
||||
|
||||
from manila.api.v1 import share_metadata
|
||||
from manila.api.v1 import share_snapshots
|
||||
from manila.api.v1 import shares
|
||||
|
||||
|
@ -62,6 +63,20 @@ class APIRouter(manila.api.openstack.APIRouter):
|
|||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
self.resources['share_metadata'] = share_metadata.create_resource()
|
||||
share_metadata_controller = self.resources['share_metadata']
|
||||
|
||||
mapper.resource("share_metadata", "metadata",
|
||||
controller=share_metadata_controller,
|
||||
parent_resource=dict(member_name='share',
|
||||
collection_name='shares'))
|
||||
|
||||
mapper.connect("metadata",
|
||||
"/{project_id}/shares/{share_id}/metadata",
|
||||
controller=share_metadata_controller,
|
||||
action='update_all',
|
||||
conditions={"method": ['PUT']})
|
||||
|
||||
self.resources['limits'] = limits.create_resource()
|
||||
mapper.resource("limit", "limits",
|
||||
controller=self.resources['limits'])
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import webob
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import wsgi
|
||||
from manila import exception
|
||||
from manila import share
|
||||
from webob import exc
|
||||
|
||||
|
||||
class ShareMetadataController(object):
|
||||
"""The share metadata API controller for the OpenStack API."""
|
||||
|
||||
def __init__(self):
|
||||
self.share_api = share.API()
|
||||
super(ShareMetadataController, self).__init__()
|
||||
|
||||
def _get_metadata(self, context, share_id):
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
meta = self.share_api.get_share_metadata(context, share)
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
return meta
|
||||
|
||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
||||
def index(self, req, share_id):
|
||||
"""Returns the list of metadata for a given share."""
|
||||
context = req.environ['manila.context']
|
||||
return {'metadata': self._get_metadata(context, share_id)}
|
||||
|
||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
||||
def create(self, req, share_id, body):
|
||||
try:
|
||||
metadata = body['metadata']
|
||||
except (KeyError, TypeError):
|
||||
msg = _("Malformed request body")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
|
||||
new_metadata = self._update_share_metadata(context,
|
||||
share_id,
|
||||
metadata,
|
||||
delete=False)
|
||||
|
||||
return {'metadata': new_metadata}
|
||||
|
||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
||||
@wsgi.deserializers(xml=common.MetaItemDeserializer)
|
||||
def update(self, req, share_id, id, body):
|
||||
try:
|
||||
meta_item = body['meta']
|
||||
except (TypeError, KeyError):
|
||||
expl = _('Malformed request body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
if id not in meta_item:
|
||||
expl = _('Request body and URI mismatch')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
if len(meta_item) > 1:
|
||||
expl = _('Request body contains too many items')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
self._update_share_metadata(context,
|
||||
share_id,
|
||||
meta_item,
|
||||
delete=False)
|
||||
|
||||
return {'meta': meta_item}
|
||||
|
||||
@wsgi.serializers(xml=common.MetadataTemplate)
|
||||
@wsgi.deserializers(xml=common.MetadataDeserializer)
|
||||
def update_all(self, req, share_id, body):
|
||||
try:
|
||||
metadata = body['metadata']
|
||||
except (TypeError, KeyError):
|
||||
expl = _('Malformed request body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
new_metadata = self._update_share_metadata(context, share_id,
|
||||
metadata, delete=True)
|
||||
return {'metadata': new_metadata}
|
||||
|
||||
def _update_share_metadata(self, context,
|
||||
share_id, metadata,
|
||||
delete=False):
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
return self.share_api.update_share_metadata(context,
|
||||
share,
|
||||
metadata,
|
||||
delete)
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
except (ValueError, AttributeError):
|
||||
msg = _("Malformed request body")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
except exception.InvalidShareMetadata as error:
|
||||
raise exc.HTTPBadRequest(explanation=error.msg)
|
||||
|
||||
except exception.InvalidShareMetadataSize as error:
|
||||
raise exc.HTTPBadRequest(explanation=error.msg)
|
||||
|
||||
@wsgi.serializers(xml=common.MetaItemTemplate)
|
||||
def show(self, req, share_id, id):
|
||||
"""Return a single metadata item."""
|
||||
context = req.environ['manila.context']
|
||||
data = self._get_metadata(context, share_id)
|
||||
|
||||
try:
|
||||
return {'meta': {id: data[id]}}
|
||||
except KeyError:
|
||||
msg = _("Metadata item was not found")
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
def delete(self, req, share_id, id):
|
||||
"""Deletes an existing metadata."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
metadata = self._get_metadata(context, share_id)
|
||||
|
||||
if id not in metadata:
|
||||
msg = _("Metadata item was not found")
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
self.share_api.delete_share_metadata(context, share, id)
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
return webob.Response(status_int=200)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareMetadataController())
|
|
@ -205,6 +205,8 @@ class ShareController(wsgi.Controller):
|
|||
kwargs = {}
|
||||
kwargs['availability_zone'] = share.get('availability_zone')
|
||||
|
||||
kwargs['metadata'] = share.get('metadata', None)
|
||||
|
||||
snapshot_id = share.get('snapshot_id')
|
||||
if snapshot_id:
|
||||
kwargs['snapshot'] = self.share_api.get_snapshot(context,
|
||||
|
|
|
@ -44,6 +44,12 @@ class ViewBuilder(common.ViewBuilder):
|
|||
|
||||
def detail(self, request, share):
|
||||
"""Detailed view of a single share."""
|
||||
metadata = share.get('share_metadata')
|
||||
if metadata:
|
||||
metadata = dict((item['key'], item['value']) for item in metadata)
|
||||
else:
|
||||
metadata = {}
|
||||
|
||||
return {
|
||||
'share': {
|
||||
'id': share.get('id'),
|
||||
|
@ -56,6 +62,7 @@ class ViewBuilder(common.ViewBuilder):
|
|||
'snapshot_id': share.get('snapshot_id'),
|
||||
'share_proto': share.get('share_proto'),
|
||||
'export_location': share.get('export_location'),
|
||||
'metadata': metadata,
|
||||
'links': self._get_links(request, share['id'])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,3 +430,18 @@ def share_snapshot_data_get_for_project(context, project_id, session=None):
|
|||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
def share_metadata_get(context, share_id):
|
||||
"""Get all metadata for a share."""
|
||||
return IMPL.share_metadata_get(context, share_id)
|
||||
|
||||
|
||||
def share_metadata_delete(context, share_id, key):
|
||||
"""Delete the given metadata item."""
|
||||
IMPL.share_metadata_delete(context, share_id, key)
|
||||
|
||||
|
||||
def share_metadata_update(context, share, metadata, delete):
|
||||
"""Update metadata if it exists, otherwise create it."""
|
||||
IMPL.share_metadata_update(context, share, metadata, delete)
|
||||
|
|
|
@ -128,6 +128,20 @@ def require_context(f):
|
|||
return wrapper
|
||||
|
||||
|
||||
def require_share_exists(f):
|
||||
"""Decorator to require the specified share to exist.
|
||||
|
||||
Requires the wrapped function to use context and share_id as
|
||||
their first two arguments.
|
||||
"""
|
||||
|
||||
def wrapper(context, share_id, *args, **kwargs):
|
||||
db.share_get(context, share_id)
|
||||
return f(context, share_id, *args, **kwargs)
|
||||
wrapper.__name__ = f.__name__
|
||||
return wrapper
|
||||
|
||||
|
||||
def model_query(context, *args, **kwargs):
|
||||
"""Query helper that accounts for context's `read_deleted` field.
|
||||
|
||||
|
@ -1037,11 +1051,25 @@ def reservation_expire(context):
|
|||
def _share_get_query(context, session=None):
|
||||
if session is None:
|
||||
session = get_session()
|
||||
return model_query(context, models.Share, session=session)
|
||||
return model_query(context, models.Share, session=session).\
|
||||
options(joinedload('share_metadata'))
|
||||
|
||||
|
||||
def _metadata_refs(metadata_dict, meta_class):
|
||||
metadata_refs = []
|
||||
if metadata_dict:
|
||||
for k, v in metadata_dict.iteritems():
|
||||
metadata_ref = meta_class()
|
||||
metadata_ref['key'] = k
|
||||
metadata_ref['value'] = v
|
||||
metadata_refs.append(metadata_ref)
|
||||
return metadata_refs
|
||||
|
||||
|
||||
@require_context
|
||||
def share_create(context, values):
|
||||
values['share_metadata'] = _metadata_refs(values.get('metadata'),
|
||||
models.ShareMetadata)
|
||||
share_ref = models.Share()
|
||||
if not values.get('id'):
|
||||
values['id'] = str(uuid.uuid4())
|
||||
|
@ -1112,6 +1140,12 @@ def share_delete(context, share_id):
|
|||
'deleted_at': timeutils.utcnow(),
|
||||
'updated_at': literal_column('updated_at'),
|
||||
'status': 'deleted'})
|
||||
session.query(models.ShareMetadata).\
|
||||
filter_by(share_id=share_id).\
|
||||
update({'deleted': True,
|
||||
'deleted_at': timeutils.utcnow(),
|
||||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
share_ref.save(session)
|
||||
|
||||
|
||||
|
@ -1284,3 +1318,99 @@ def share_snapshot_update(context, snapshot_id, values):
|
|||
snapshot_ref.update(values)
|
||||
snapshot_ref.save(session=session)
|
||||
return snapshot_ref
|
||||
|
||||
|
||||
#################################
|
||||
|
||||
|
||||
@require_context
|
||||
@require_share_exists
|
||||
def share_metadata_get(context, share_id):
|
||||
return _share_metadata_get(context, share_id)
|
||||
|
||||
|
||||
@require_context
|
||||
@require_share_exists
|
||||
def share_metadata_delete(context, share_id, key):
|
||||
_share_metadata_get_query(context, share_id).\
|
||||
filter_by(key=key).\
|
||||
update({'deleted': True,
|
||||
'deleted_at': timeutils.utcnow(),
|
||||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
|
||||
@require_context
|
||||
@require_share_exists
|
||||
def share_metadata_update(context, share_id, metadata, delete):
|
||||
return _share_metadata_update(context, share_id, metadata, delete)
|
||||
|
||||
|
||||
def _share_metadata_get_query(context, share_id, session=None):
|
||||
return model_query(context, models.ShareMetadata, session=session,
|
||||
read_deleted="no").\
|
||||
filter_by(share_id=share_id)
|
||||
|
||||
|
||||
@require_context
|
||||
@require_share_exists
|
||||
def _share_metadata_get(context, share_id, session=None):
|
||||
rows = _share_metadata_get_query(context, share_id,
|
||||
session=session).all()
|
||||
result = {}
|
||||
for row in rows:
|
||||
result[row['key']] = row['value']
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@require_context
|
||||
@require_share_exists
|
||||
def _share_metadata_update(context, share_id, metadata, delete, session=None):
|
||||
if not session:
|
||||
session = get_session()
|
||||
|
||||
with session.begin():
|
||||
# Set existing metadata to deleted if delete argument is True
|
||||
if delete:
|
||||
original_metadata = _share_metadata_get(context, share_id,
|
||||
session=session)
|
||||
for meta_key, meta_value in original_metadata.iteritems():
|
||||
if meta_key not in metadata:
|
||||
meta_ref = _share_metadata_get_item(context, share_id,
|
||||
meta_key,
|
||||
session=session)
|
||||
meta_ref.update({'deleted': True})
|
||||
meta_ref.save(session=session)
|
||||
|
||||
meta_ref = None
|
||||
|
||||
# Now update all existing items with new values, or create new meta
|
||||
# objects
|
||||
for meta_key, meta_value in metadata.items():
|
||||
|
||||
# update the value whether it exists or not
|
||||
item = {"value": meta_value}
|
||||
|
||||
try:
|
||||
meta_ref = _share_metadata_get_item(context, share_id,
|
||||
meta_key,
|
||||
session=session)
|
||||
except exception.ShareMetadataNotFound:
|
||||
meta_ref = models.ShareMetadata()
|
||||
item.update({"key": meta_key, "share_id": share_id})
|
||||
|
||||
meta_ref.update(item)
|
||||
meta_ref.save(session=session)
|
||||
|
||||
return metadata
|
||||
|
||||
|
||||
def _share_metadata_get_item(context, share_id, key, session=None):
|
||||
result = _share_metadata_get_query(context, share_id, session=session).\
|
||||
filter_by(key=key).\
|
||||
first()
|
||||
|
||||
if not result:
|
||||
raise exception.ShareMetadataNotFound(metadata_key=key,
|
||||
share_id=share_id)
|
||||
return result
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
|
||||
from sqlalchemy import MetaData, String, Table
|
||||
|
||||
from manila.openstack.common.gettextutils import _
|
||||
from manila.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
shares = Table('shares', meta, autoload=True)
|
||||
|
||||
share_metadata = Table('share_metadata', meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('deleted', Boolean),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('share_id', String(length=36), ForeignKey('shares.id'),
|
||||
nullable=False),
|
||||
Column('key', String(length=255), nullable=False),
|
||||
Column('value', String(length=1024), nullable=False),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
try:
|
||||
share_metadata.create()
|
||||
except Exception:
|
||||
LOG.exception("Exception while creating table 'share_metadata'")
|
||||
meta.drop_all(tables=[share_metadata])
|
||||
raise
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
share_metadata = Table('share_metadata', meta, autoload=True)
|
||||
try:
|
||||
share_metadata.drop()
|
||||
except Exception:
|
||||
LOG.error(_("share_metadata table not dropped"))
|
||||
raise
|
|
@ -21,7 +21,7 @@
|
|||
SQLAlchemy models for Manila data.
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Integer, String, Text, schema
|
||||
from sqlalchemy import Column, Index, Integer, String, Text, schema
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import ForeignKey, DateTime, Boolean, Enum
|
||||
|
@ -257,6 +257,20 @@ class Share(BASE, ManilaBase):
|
|||
export_location = Column(String(255))
|
||||
|
||||
|
||||
class ShareMetadata(BASE, ManilaBase):
|
||||
"""Represents a metadata key/value pair for a share."""
|
||||
__tablename__ = 'share_metadata'
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255), nullable=False)
|
||||
value = Column(String(1024), nullable=False)
|
||||
share_id = Column(String(36), ForeignKey('shares.id'), nullable=False)
|
||||
share = relationship(Share, backref="share_metadata",
|
||||
foreign_keys=share_id,
|
||||
primaryjoin='and_('
|
||||
'ShareMetadata.share_id == Share.id,'
|
||||
'ShareMetadata.deleted == False)')
|
||||
|
||||
|
||||
class ShareAccessMapping(BASE, ManilaBase):
|
||||
"""Represents access to NFS."""
|
||||
STATE_NEW = 'new'
|
||||
|
|
|
@ -131,6 +131,7 @@ class ManilaException(Exception):
|
|||
# at least get the core message out if something happened
|
||||
message = self.message
|
||||
|
||||
self.msg = message
|
||||
super(ManilaException, self).__init__(message)
|
||||
|
||||
|
||||
|
@ -478,3 +479,15 @@ class InvalidShareSnapshot(ManilaException):
|
|||
|
||||
class SwiftConnectionFailed(ManilaException):
|
||||
message = _("Connection to swift failed") + ": %(reason)s"
|
||||
|
||||
|
||||
class ShareMetadataNotFound(NotFound):
|
||||
message = _("Metadata item is not found")
|
||||
|
||||
|
||||
class InvalidShareMetadata(Invalid):
|
||||
message = _("Invalid metadata")
|
||||
|
||||
|
||||
class InvalidShareMetadataSize(Invalid):
|
||||
message = _("Invalid metadata size")
|
||||
|
|
|
@ -77,9 +77,12 @@ class API(base.Base):
|
|||
super(API, self).__init__(db_driver)
|
||||
|
||||
def create(self, context, share_proto, size, name, description,
|
||||
snapshot=None, availability_zone=None):
|
||||
snapshot=None, availability_zone=None, metadata=None):
|
||||
"""Create new share."""
|
||||
check_policy(context, 'create')
|
||||
|
||||
self._check_metadata_properties(context, metadata)
|
||||
|
||||
if snapshot is not None:
|
||||
if snapshot['status'] != 'available':
|
||||
msg = _('status must be available')
|
||||
|
@ -151,6 +154,7 @@ class API(base.Base):
|
|||
'project_id': context.project_id,
|
||||
'snapshot_id': snapshot_id,
|
||||
'availability_zone': availability_zone,
|
||||
'metadata': metadata,
|
||||
'status': "creating",
|
||||
'scheduled_at': timeutils.utcnow(),
|
||||
'display_name': name,
|
||||
|
@ -423,3 +427,54 @@ class API(base.Base):
|
|||
check_policy(context, 'access_get')
|
||||
rule = self.db.share_access_get(context, access_id)
|
||||
return rule
|
||||
|
||||
@wrap_check_policy
|
||||
def get_share_metadata(self, context, share):
|
||||
"""Get all metadata associated with a share."""
|
||||
rv = self.db.share_metadata_get(context, share['id'])
|
||||
return dict(rv.iteritems())
|
||||
|
||||
@wrap_check_policy
|
||||
def delete_share_metadata(self, context, share, key):
|
||||
"""Delete the given metadata item from a share."""
|
||||
self.db.share_metadata_delete(context, share['id'], key)
|
||||
|
||||
def _check_metadata_properties(self, context, metadata=None):
|
||||
if not metadata:
|
||||
metadata = {}
|
||||
|
||||
for k, v in metadata.iteritems():
|
||||
if len(k) == 0:
|
||||
msg = _("Metadata property key is blank")
|
||||
LOG.warn(msg)
|
||||
raise exception.InvalidShareMetadata(message=msg)
|
||||
if len(k) > 255:
|
||||
msg = _("Metadata property key is greater than 255 characters")
|
||||
LOG.warn(msg)
|
||||
raise exception.InvalidShareMetadataSize(message=msg)
|
||||
if len(v) > 1024:
|
||||
msg = _("Metadata property value is "
|
||||
"greater than 1024 characters")
|
||||
LOG.warn(msg)
|
||||
raise exception.InvalidShareMetadataSize(message=msg)
|
||||
|
||||
@wrap_check_policy
|
||||
def update_share_metadata(self, context, share, metadata, delete=False):
|
||||
"""Updates or creates share metadata.
|
||||
|
||||
If delete is True, metadata items that are not specified in the
|
||||
`metadata` argument will be deleted.
|
||||
|
||||
"""
|
||||
orig_meta = self.get_share_metadata(context, share)
|
||||
if delete:
|
||||
_metadata = metadata
|
||||
else:
|
||||
_metadata = orig_meta.copy()
|
||||
_metadata.update(metadata)
|
||||
|
||||
self._check_metadata_properties(context, _metadata)
|
||||
self.db.share_metadata_update(context, share['id'],
|
||||
_metadata, delete)
|
||||
|
||||
return _metadata
|
||||
|
|
|
@ -284,3 +284,12 @@ class TestCase(unittest.TestCase):
|
|||
self.assertFalse(a in b, *args, **kwargs)
|
||||
else:
|
||||
f(a, b, *args, **kwargs)
|
||||
|
||||
def assertIsInstance(self, a, b, *args, **kwargs):
|
||||
"""Python < v2.7 compatibility."""
|
||||
try:
|
||||
f = super(TestCase, self).assertIsInstance
|
||||
except AttributeError:
|
||||
self.assertTrue(isinstance(a, b))
|
||||
else:
|
||||
f(a, b, *args, **kwargs)
|
||||
|
|
|
@ -0,0 +1,446 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo.config import cfg
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v1 import share_metadata
|
||||
from manila.api.v1 import shares
|
||||
import manila.db
|
||||
from manila import exception
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def return_create_share_metadata_max(context, share_id, metadata, delete):
|
||||
return stub_max_share_metadata()
|
||||
|
||||
|
||||
def return_create_share_metadata(context, share_id, metadata, delete):
|
||||
return stub_share_metadata()
|
||||
|
||||
|
||||
def return_share_metadata(context, share_id):
|
||||
if not isinstance(share_id, str) or not len(share_id) == 36:
|
||||
msg = 'id %s must be a uuid in return share metadata' % share_id
|
||||
raise Exception(msg)
|
||||
return stub_share_metadata()
|
||||
|
||||
|
||||
def return_empty_share_metadata(context, share_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_share_metadata(context, share_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_share_metadata():
|
||||
metadata = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
return metadata
|
||||
|
||||
|
||||
def stub_max_share_metadata():
|
||||
metadata = {"metadata": {}}
|
||||
for num in range(CONF.quota_metadata_items):
|
||||
metadata['metadata']['key%i' % num] = "blah"
|
||||
return metadata
|
||||
|
||||
|
||||
def return_share(context, share_id):
|
||||
return {'id': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
|
||||
'name': 'fake',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_share_nonexistent(context, share_id):
|
||||
raise exception.NotFound('bogus test message')
|
||||
|
||||
|
||||
def fake_update_share_metadata(self, context, share, diff):
|
||||
pass
|
||||
|
||||
|
||||
class ShareMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ShareMetaDataTest, self).setUp()
|
||||
self.share_api = manila.share.api.API()
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.stubs.Set(manila.db, 'share_get', return_share)
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_share_metadata)
|
||||
|
||||
self.stubs.Set(self.share_api, 'update_share_metadata',
|
||||
fake_update_share_metadata)
|
||||
|
||||
self.share_controller = shares.ShareController()
|
||||
self.controller = share_metadata.ShareMetadataController()
|
||||
self.req_id = str(uuid.uuid4())
|
||||
self.url = '/shares/%s/metadata' % self.req_id
|
||||
|
||||
sh = {"size": 1,
|
||||
"name": "Share Test Name",
|
||||
"share_proto": "nfs",
|
||||
"display_name": "Updated Desc",
|
||||
"display_description": "Share Test Desc",
|
||||
"metadata": {}}
|
||||
body = {"share": sh}
|
||||
req = fakes.HTTPRequest.blank('/shares')
|
||||
self.share_controller.create(req, body)
|
||||
|
||||
def test_index(self):
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.req_id)
|
||||
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
'key3': 'value3',
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_index_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_share_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.index, req, self.url)
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_empty_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.req_id)
|
||||
expected = {'metadata': {}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show(self):
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
res_dict = self.controller.show(req, self.req_id, 'key2')
|
||||
expected = {'meta': {'key2': 'value2'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_share_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.req_id, 'key2')
|
||||
|
||||
def test_show_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_empty_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.req_id, 'key6')
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_share_metadata)
|
||||
self.stubs.Set(manila.db, 'share_metadata_delete',
|
||||
delete_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
req.method = 'DELETE'
|
||||
res = self.controller.delete(req, self.req_id, 'key2')
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_delete_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_get',
|
||||
return_share_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.req_id, 'key1')
|
||||
|
||||
def test_delete_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_empty_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.req_id, 'key6')
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_empty_share_metadata)
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/share_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
res_dict = self.controller.create(req, self.req_id, body)
|
||||
self.assertEqual(body, res_dict)
|
||||
|
||||
def test_create_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.req_id, None)
|
||||
|
||||
def test_create_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.req_id, body)
|
||||
|
||||
def test_create_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req, self.req_id, body)
|
||||
|
||||
def test_create_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_get',
|
||||
return_share_nonexistent)
|
||||
self.stubs.Set(manila.db, 'share_metadata_get',
|
||||
return_share_metadata)
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/share_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.create, req, self.req_id, body)
|
||||
|
||||
def test_update_all(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key10': 'value10',
|
||||
'key99': 'value99',
|
||||
},
|
||||
}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.req_id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_empty_container(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.req_id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_malformed_container(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'meta': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.req_id,
|
||||
expected)
|
||||
|
||||
def test_update_all_malformed_data(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': ['asdf']}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.req_id,
|
||||
expected)
|
||||
|
||||
def test_update_all_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_get', return_share_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
body = {'metadata': {'key10': 'value10'}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update_all, req, '100', body)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res_dict = self.controller.update(req, self.req_id, 'key1', body)
|
||||
expected = {'meta': {'key1': 'value1'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_item_nonexistent_share(self):
|
||||
self.stubs.Set(manila.db, 'share_get',
|
||||
return_share_nonexistent)
|
||||
req = fakes.HTTPRequest.blank('/v1.1/fake/shares/asdf/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update, req, self.req_id, 'key1',
|
||||
body)
|
||||
|
||||
def test_update_item_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.req_id, 'key1',
|
||||
None)
|
||||
|
||||
def test_update_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.req_id, '', body)
|
||||
|
||||
def test_update_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, self.req_id, ("a" * 260), body)
|
||||
|
||||
def test_update_item_value_too_long(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": ("a" * 1025)}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, self.req_id, "key1", body)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1", "key2": "value2"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.req_id, 'key1',
|
||||
body)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/bad')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.req_id, 'bad',
|
||||
body)
|
||||
|
||||
def test_invalid_metadata_items_on_create(self):
|
||||
self.stubs.Set(manila.db, 'share_metadata_update',
|
||||
return_create_share_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
#test for long key
|
||||
data = {"metadata": {"a" * 260: "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.req_id, data)
|
||||
|
||||
#test for long value
|
||||
data = {"metadata": {"key": "v" * 1025}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.req_id, data)
|
||||
|
||||
#test for empty key.
|
||||
data = {"metadata": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.req_id, data)
|
|
@ -141,6 +141,7 @@ class ShareApiTest(test.TestCase):
|
|||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'share_proto': 'fakeproto',
|
||||
'metadata': {},
|
||||
'size': 1,
|
||||
'snapshot_id': '2',
|
||||
'status': 'fakestatus',
|
||||
|
@ -241,6 +242,7 @@ class ShareApiTest(test.TestCase):
|
|||
'availability_zone': 'fakeaz',
|
||||
'name': 'displayname',
|
||||
'share_proto': 'fakeproto',
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'snapshot_id': '2',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
|
|
|
@ -11,5 +11,9 @@
|
|||
"share:create_snapshot": [],
|
||||
"share:delete_snapshot": [],
|
||||
"share:get_snapshot": [],
|
||||
"share:get_all_snapshots": []
|
||||
"share:get_all_snapshots": [],
|
||||
|
||||
"share:get_share_metadata": [],
|
||||
"share:delete_share_metadata": [],
|
||||
"share:update_share_metadata": []
|
||||
}
|
||||
|
|
|
@ -369,3 +369,43 @@ class TestMigrations(test.TestCase):
|
|||
LOG.error("Failed to migrate to version %s on engine %s" %
|
||||
(version, engine))
|
||||
raise
|
||||
|
||||
def test_migration_006(self):
|
||||
"""Test adding share_metadata table works correctly."""
|
||||
for (key, engine) in self.engines.items():
|
||||
migration_api.version_control(engine,
|
||||
TestMigrations.REPOSITORY,
|
||||
migration.INIT_VERSION)
|
||||
migration_api.upgrade(engine, TestMigrations.REPOSITORY, 5)
|
||||
metadata = sqlalchemy.schema.MetaData()
|
||||
metadata.bind = engine
|
||||
|
||||
migration_api.upgrade(engine, TestMigrations.REPOSITORY, 6)
|
||||
|
||||
self.assertTrue(engine.dialect.has_table(engine.connect(),
|
||||
"share_metadata"))
|
||||
share_metadata = sqlalchemy.Table('share_metadata',
|
||||
metadata,
|
||||
autoload=True)
|
||||
|
||||
self.assertIsInstance(share_metadata.c.created_at.type,
|
||||
sqlalchemy.types.DATETIME)
|
||||
self.assertIsInstance(share_metadata.c.updated_at.type,
|
||||
sqlalchemy.types.DATETIME)
|
||||
self.assertIsInstance(share_metadata.c.deleted_at.type,
|
||||
sqlalchemy.types.DATETIME)
|
||||
self.assertIsInstance(share_metadata.c.deleted.type,
|
||||
sqlalchemy.types.BOOLEAN)
|
||||
self.assertIsInstance(share_metadata.c.id.type,
|
||||
sqlalchemy.types.INTEGER)
|
||||
self.assertIsInstance(share_metadata.c.share_id.type,
|
||||
sqlalchemy.types.VARCHAR)
|
||||
self.assertIsInstance(share_metadata.c.key.type,
|
||||
sqlalchemy.types.VARCHAR)
|
||||
self.assertIsInstance(share_metadata.c.value.type,
|
||||
sqlalchemy.types.VARCHAR)
|
||||
|
||||
migration_api.downgrade(engine, TestMigrations.REPOSITORY, 5)
|
||||
|
||||
self.assertFalse(engine.dialect.has_table(engine.connect(),
|
||||
"share_metadata"))
|
||||
|
|
|
@ -113,6 +113,7 @@ class ShareTestCase(test.TestCase):
|
|||
share['snapshot_id'] = snapshot_id
|
||||
share['user_id'] = 'fake'
|
||||
share['project_id'] = 'fake'
|
||||
share['metadata'] = {'fake_key': 'fake_value'}
|
||||
share['availability_zone'] = CONF.storage_availability_zone
|
||||
share['status'] = status
|
||||
share['host'] = CONF.host
|
||||
|
@ -367,3 +368,32 @@ class ShareTestCase(test.TestCase):
|
|||
|
||||
acs = db.share_access_get(self.context, access_id)
|
||||
self.assertEquals(acs['state'], 'error')
|
||||
|
||||
def test_create_delete_share_with_metadata(self):
|
||||
"""Test share can be created with metadata and deleted."""
|
||||
test_meta = {'fake_key': 'fake_value'}
|
||||
share = self._create_share()
|
||||
share_id = share['id']
|
||||
self.share.create_share(self.context, share_id)
|
||||
result_meta = {
|
||||
share.share_metadata[0].key: share.share_metadata[0].value}
|
||||
self.assertEqual(result_meta, test_meta)
|
||||
|
||||
self.share.delete_share(self.context, share_id)
|
||||
self.assertRaises(exception.NotFound,
|
||||
db.share_get,
|
||||
self.context,
|
||||
share_id)
|
||||
|
||||
def test_create_share_with_invalid_metadata(self):
|
||||
"""Test share create with too much metadata fails."""
|
||||
share_api = manila.share.api.API()
|
||||
test_meta = {'fake_key': 'fake_value' * 1025}
|
||||
self.assertRaises(exception.InvalidShareMetadataSize,
|
||||
share_api.create,
|
||||
self.context,
|
||||
'nfs',
|
||||
1,
|
||||
'name',
|
||||
'description',
|
||||
metadata=test_meta)
|
||||
|
|
|
@ -43,6 +43,7 @@ def fake_share(id, **kwargs):
|
|||
'availability_zone': 'fakeaz',
|
||||
'status': 'fakestatus',
|
||||
'display_name': 'fakename',
|
||||
'metadata': None,
|
||||
'display_description': 'fakedesc',
|
||||
'share_proto': 'nfs',
|
||||
'export_location': 'fake_location',
|
||||
|
@ -527,3 +528,34 @@ class ShareAPITestCase(test.TestCase):
|
|||
'access_type': 'fakeacctype',
|
||||
'access_to': 'fakeaccto',
|
||||
'state': 'fakeerror'}])
|
||||
|
||||
def test_share_metadata_get(self):
|
||||
metadata = {'a': 'b', 'c': 'd'}
|
||||
db_driver.share_create(self.context, {'id': '1', 'metadata': metadata})
|
||||
|
||||
self.assertEqual(metadata,
|
||||
db_driver.share_metadata_get(self.context, '1'))
|
||||
|
||||
def test_share_metadata_update(self):
|
||||
metadata1 = {'a': '1', 'c': '2'}
|
||||
metadata2 = {'a': '3', 'd': '5'}
|
||||
should_be = {'a': '3', 'c': '2', 'd': '5'}
|
||||
|
||||
db_driver.share_create(self.context, {'id': '1',
|
||||
'metadata': metadata1})
|
||||
db_driver.share_metadata_update(self.context, '1', metadata2, False)
|
||||
|
||||
self.assertEqual(should_be,
|
||||
db_driver.share_metadata_get(self.context, '1'))
|
||||
|
||||
def test_share_metadata_update_delete(self):
|
||||
metadata1 = {'a': '1', 'c': '2'}
|
||||
metadata2 = {'a': '3', 'd': '4'}
|
||||
should_be = metadata2
|
||||
|
||||
db_driver.share_create(self.context, {'id': '1',
|
||||
'metadata': metadata1})
|
||||
db_driver.share_metadata_update(self.context, '1', metadata2, True)
|
||||
|
||||
self.assertEqual(should_be,
|
||||
db_driver.share_metadata_get(self.context, '1'))
|
||||
|
|
Loading…
Reference in New Issue