]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add volume metadata to v2
authorMike Perez <thingee@gmail.com>
Tue, 17 Sep 2013 09:41:59 +0000 (02:41 -0700)
committerMike Perez <thingee@gmail.com>
Fri, 20 Sep 2013 05:01:55 +0000 (22:01 -0700)
This exposes volume metadata in the Cinder v2 API. This already exists
in v1 and was originally suppose to exist back with Grizzly, but appears
to have slipped.

Closes-Bug: #1227979
Change-Id: I512dad591d7d491eca54a230d3cc290d9a349e6f

cinder/api/v2/router.py
cinder/api/v2/volume_metadata.py [new file with mode: 0644]
cinder/tests/api/v2/test_volume_metadata.py [new file with mode: 0644]

index bcd668470a6c1b20f7c19308f52abebcb538a4ff..34eb81933f6004c76ae60d0f9644409e40dc13c9 100644 (file)
@@ -27,6 +27,7 @@ from cinder.api.v2 import limits
 from cinder.api.v2 import snapshot_metadata
 from cinder.api.v2 import snapshots
 from cinder.api.v2 import types
+from cinder.api.v2 import volume_metadata
 from cinder.api.v2 import volumes
 from cinder.api import versions
 from cinder.openstack.common import log as logging
@@ -78,3 +79,17 @@ class APIRouter(cinder.api.openstack.APIRouter):
                         controller=snapshot_metadata_controller,
                         parent_resource=dict(member_name='snapshot',
                                              collection_name='snapshots'))
+        self.resources['volume_metadata'] = \
+            volume_metadata.create_resource()
+        volume_metadata_controller = self.resources['volume_metadata']
+
+        mapper.resource("volume_metadata", "metadata",
+                        controller=volume_metadata_controller,
+                        parent_resource=dict(member_name='volume',
+                                             collection_name='volumes'))
+
+        mapper.connect("metadata",
+                       "/{project_id}/volumes/{volume_id}/metadata",
+                       controller=volume_metadata_controller,
+                       action='update_all',
+                       conditions={"method": ['PUT']})
diff --git a/cinder/api/v2/volume_metadata.py b/cinder/api/v2/volume_metadata.py
new file mode 100644 (file)
index 0000000..b9449da
--- /dev/null
@@ -0,0 +1,163 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 cinder.api import common
+from cinder.api.openstack import wsgi
+from cinder import exception
+from cinder import volume
+
+
+class Controller(object):
+    """The volume metadata API controller for the OpenStack API."""
+
+    def __init__(self):
+        self.volume_api = volume.API()
+        super(Controller, self).__init__()
+
+    def _get_metadata(self, context, volume_id):
+        try:
+            volume = self.volume_api.get(context, volume_id)
+            meta = self.volume_api.get_volume_metadata(context, volume)
+        except exception.VolumeNotFound:
+            msg = _('volume does not exist')
+            raise webob.exc.HTTPNotFound(explanation=msg)
+        return meta
+
+    @wsgi.serializers(xml=common.MetadataTemplate)
+    def index(self, req, volume_id):
+        """Returns the list of metadata for a given volume."""
+        context = req.environ['cinder.context']
+        return {'metadata': self._get_metadata(context, volume_id)}
+
+    @wsgi.serializers(xml=common.MetadataTemplate)
+    @wsgi.deserializers(xml=common.MetadataDeserializer)
+    def create(self, req, volume_id, body):
+        try:
+            metadata = body['metadata']
+        except (KeyError, TypeError):
+            msg = _("Malformed request body")
+            raise webob.exc.HTTPBadRequest(explanation=msg)
+
+        context = req.environ['cinder.context']
+
+        new_metadata = self._update_volume_metadata(context,
+                                                    volume_id,
+                                                    metadata,
+                                                    delete=False)
+
+        return {'metadata': new_metadata}
+
+    @wsgi.serializers(xml=common.MetaItemTemplate)
+    @wsgi.deserializers(xml=common.MetaItemDeserializer)
+    def update(self, req, volume_id, id, body):
+        try:
+            meta_item = body['meta']
+        except (TypeError, KeyError):
+            expl = _('Malformed request body')
+            raise webob.exc.HTTPBadRequest(explanation=expl)
+
+        if id not in meta_item:
+            expl = _('Request body and URI mismatch')
+            raise webob.exc.HTTPBadRequest(explanation=expl)
+
+        if len(meta_item) > 1:
+            expl = _('Request body contains too many items')
+            raise webob.exc.HTTPBadRequest(explanation=expl)
+
+        context = req.environ['cinder.context']
+        self._update_volume_metadata(context,
+                                     volume_id,
+                                     meta_item,
+                                     delete=False)
+
+        return {'meta': meta_item}
+
+    @wsgi.serializers(xml=common.MetadataTemplate)
+    @wsgi.deserializers(xml=common.MetadataDeserializer)
+    def update_all(self, req, volume_id, body):
+        try:
+            metadata = body['metadata']
+        except (TypeError, KeyError):
+            expl = _('Malformed request body')
+            raise webob.exc.HTTPBadRequest(explanation=expl)
+
+        context = req.environ['cinder.context']
+        new_metadata = self._update_volume_metadata(context,
+                                                    volume_id,
+                                                    metadata,
+                                                    delete=True)
+
+        return {'metadata': new_metadata}
+
+    def _update_volume_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)
+        except exception.VolumeNotFound:
+            msg = _('volume does not exist')
+            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.serializers(xml=common.MetaItemTemplate)
+    def show(self, req, volume_id, id):
+        """Return a single metadata item."""
+        context = req.environ['cinder.context']
+        data = self._get_metadata(context, volume_id)
+
+        try:
+            return {'meta': {id: data[id]}}
+        except KeyError:
+            msg = _("Metadata item was not found")
+            raise webob.exc.HTTPNotFound(explanation=msg)
+
+    def delete(self, req, volume_id, id):
+        """Deletes an existing metadata."""
+        context = req.environ['cinder.context']
+
+        metadata = self._get_metadata(context, volume_id)
+
+        if id not in metadata:
+            msg = _("Metadata item was not found")
+            raise webob.exc.HTTPNotFound(explanation=msg)
+
+        try:
+            volume = self.volume_api.get(context, volume_id)
+            self.volume_api.delete_volume_metadata(context, volume, id)
+        except exception.VolumeNotFound:
+            msg = _('volume does not exist')
+            raise webob.exc.HTTPNotFound(explanation=msg)
+        return webob.Response(status_int=200)
+
+
+def create_resource():
+    return wsgi.Resource(Controller())
diff --git a/cinder/tests/api/v2/test_volume_metadata.py b/cinder/tests/api/v2/test_volume_metadata.py
new file mode 100644 (file)
index 0000000..16ea3b7
--- /dev/null
@@ -0,0 +1,451 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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 cinder.api import extensions
+from cinder.api.v2 import volume_metadata
+from cinder.api.v2 import volumes
+from cinder import db
+from cinder import exception
+from cinder.openstack.common import jsonutils
+from cinder import test
+from cinder.tests.api import fakes
+from cinder.tests.api.v2 import stubs
+from cinder.volume import api as volume_api
+
+
+CONF = cfg.CONF
+
+
+def return_create_volume_metadata_max(context, volume_id, metadata, delete):
+    return stub_max_volume_metadata()
+
+
+def return_create_volume_metadata(context, volume_id, metadata, delete):
+    return stub_volume_metadata()
+
+
+def return_volume_metadata(context, volume_id):
+    if not isinstance(volume_id, str) or not len(volume_id) == 36:
+        msg = 'id %s must be a uuid in return volume metadata' % volume_id
+        raise Exception(msg)
+    return stub_volume_metadata()
+
+
+def return_empty_volume_metadata(context, volume_id):
+    return {}
+
+
+def delete_volume_metadata(context, volume_id, key):
+    pass
+
+
+def stub_volume_metadata():
+    metadata = {
+        "key1": "value1",
+        "key2": "value2",
+        "key3": "value3",
+    }
+    return metadata
+
+
+def stub_max_volume_metadata():
+    metadata = {"metadata": {}}
+    for num in range(CONF.quota_metadata_items):
+        metadata['metadata']['key%i' % num] = "blah"
+    return metadata
+
+
+def return_volume(context, volume_id):
+    return {'id': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
+            'name': 'fake',
+            'metadata': {}}
+
+
+def return_volume_nonexistent(context, volume_id):
+    raise exception.VolumeNotFound('bogus test message')
+
+
+def fake_update_volume_metadata(self, context, volume, diff):
+    pass
+
+
+class volumeMetaDataTest(test.TestCase):
+
+    def setUp(self):
+        super(volumeMetaDataTest, self).setUp()
+        self.volume_api = volume_api.API()
+        fakes.stub_out_key_pair_funcs(self.stubs)
+        self.stubs.Set(db, 'volume_get', return_volume)
+        self.stubs.Set(db, 'volume_metadata_get',
+                       return_volume_metadata)
+        self.stubs.Set(db, 'service_get_all_by_topic',
+                       stubs.stub_service_get_all_by_topic)
+
+        self.stubs.Set(self.volume_api, 'update_volume_metadata',
+                       fake_update_volume_metadata)
+
+        self.ext_mgr = extensions.ExtensionManager()
+        self.ext_mgr.extensions = {}
+        self.volume_controller = volumes.VolumeController(self.ext_mgr)
+        self.controller = volume_metadata.Controller()
+        self.req_id = str(uuid.uuid4())
+        self.url = '/v2/fake/volumes/%s/metadata' % self.req_id
+
+        vol = {"size": 100,
+               "display_name": "Volume Test Name",
+               "display_description": "Volume Test Desc",
+               "availability_zone": "zone1:host1",
+               "metadata": {}}
+        body = {"volume": vol}
+        req = fakes.HTTPRequest.blank('/v2/volumes')
+        self.volume_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_volume(self):
+        self.stubs.Set(db, 'volume_metadata_get',
+                       return_volume_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(db, 'volume_metadata_get',
+                       return_empty_volume_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_volume(self):
+        self.stubs.Set(db, 'volume_metadata_get',
+                       return_volume_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(db, 'volume_metadata_get',
+                       return_empty_volume_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(db, 'volume_metadata_get',
+                       return_volume_metadata)
+        self.stubs.Set(db, 'volume_metadata_delete',
+                       delete_volume_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_volume(self):
+        self.stubs.Set(db, 'volume_get',
+                       return_volume_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(db, 'volume_metadata_get',
+                       return_empty_volume_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(db, 'volume_metadata_get',
+                       return_empty_volume_metadata)
+        self.stubs.Set(db, 'volume_metadata_update',
+                       return_create_volume_metadata)
+
+        req = fakes.HTTPRequest.blank('/v2/volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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_volume(self):
+        self.stubs.Set(db, 'volume_get',
+                       return_volume_nonexistent)
+        self.stubs.Set(db, 'volume_metadata_get',
+                       return_volume_metadata)
+        self.stubs.Set(db, 'volume_metadata_update',
+                       return_create_volume_metadata)
+
+        req = fakes.HTTPRequest.blank('/v2/volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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_volume(self):
+        self.stubs.Set(db, 'volume_get', return_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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_volume(self):
+        self.stubs.Set(db, 'volume_get',
+                       return_volume_nonexistent)
+        req = fakes.HTTPRequest.blank('/v2/fake/volumes/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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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.HTTPRequestEntityTooLarge,
+                          self.controller.update,
+                          req, self.req_id, ("a" * 260), body)
+
+    def test_update_item_value_too_long(self):
+        self.stubs.Set(db, 'volume_metadata_update',
+                       return_create_volume_metadata)
+        req = fakes.HTTPRequest.blank(self.url + '/key1')
+        req.method = 'PUT'
+        body = {"meta": {"key1": ("a" * 260)}}
+        req.body = jsonutils.dumps(body)
+        req.headers["content-type"] = "application/json"
+
+        self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+                          self.controller.update,
+                          req, self.req_id, "key1", body)
+
+    def test_update_item_too_many_keys(self):
+        self.stubs.Set(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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(db, 'volume_metadata_update',
+                       return_create_volume_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.HTTPRequestEntityTooLarge,
+                          self.controller.create, req, self.req_id, data)
+
+        #test for long value
+        data = {"metadata": {"key": "v" * 260}}
+        req.body = jsonutils.dumps(data)
+        self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+                          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)