Merge "V3 jsonschema validation: Volume metadata"

This commit is contained in:
Zuul 2017-12-14 14:26:22 +00:00 committed by Gerrit Code Review
commit d5147cb8e5
5 changed files with 117 additions and 58 deletions

View File

@ -0,0 +1,49 @@
# Copyright (C) 2017 NTT DATA
# 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.
"""
Schema for V3 Volume metadata API.
"""
import copy
from cinder.api.validation import parameter_types
metadata_restricted_properties = copy.deepcopy(parameter_types.extra_specs)
metadata_restricted_properties.update({
'minProperties': 1,
'maxProperties': 1
})
create = {
'type': 'object',
'properties': {
'type': 'object',
'metadata': parameter_types.extra_specs,
},
'required': ['metadata'],
'additionalProperties': False,
}
update = {
'type': 'object',
'properties': {
'type': 'object',
'meta': metadata_restricted_properties,
},
'required': ['meta'],
'additionalProperties': False,
}

View File

@ -18,6 +18,8 @@ import webob
from cinder.api import common
from cinder.api.openstack import wsgi
from cinder.api.schemas import volume_metadata as metadata
from cinder.api import validation
from cinder import exception
from cinder.i18n import _
from cinder import volume
@ -46,8 +48,8 @@ class Controller(wsgi.Controller):
context = req.environ['cinder.context']
return {'metadata': self._get_metadata(context, volume_id)}
@validation.schema(metadata.create)
def create(self, req, volume_id, body):
self.assert_valid_body(body, 'metadata')
context = req.environ['cinder.context']
metadata = body['metadata']
@ -56,8 +58,8 @@ class Controller(wsgi.Controller):
use_create=True)
return {'metadata': new_metadata}
@validation.schema(metadata.update)
def update(self, req, volume_id, id, body):
self.assert_valid_body(body, 'meta')
meta_item = body['meta']
if id not in meta_item:
@ -76,8 +78,8 @@ class Controller(wsgi.Controller):
return {'meta': meta_item}
@validation.schema(metadata.create)
def update_all(self, req, volume_id, body):
self.assert_valid_body(body, 'metadata')
metadata = body['metadata']
context = req.environ['cinder.context']

View File

@ -24,7 +24,9 @@ import webob
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi
from cinder.api.schemas import volume_metadata as metadata
from cinder.api.v2 import volume_metadata as volume_meta_v2
from cinder.api import validation
class Controller(volume_meta_v2.Controller):
@ -55,6 +57,7 @@ class Controller(volume_meta_v2.Controller):
return metadata
@wsgi.extends
@validation.schema(metadata.update)
def update(self, req, volume_id, id, body):
req_version = req.api_version_request
if req_version.matches(mv.ETAGS):
@ -62,9 +65,10 @@ class Controller(volume_meta_v2.Controller):
return webob.Response(
status_int=http_client.PRECONDITION_FAILED)
return super(Controller, self).update(req, volume_id,
id, body)
id, body=body)
@wsgi.extends
@validation.schema(metadata.create)
def update_all(self, req, volume_id, body):
req_version = req.api_version_request
if req_version.matches(mv.ETAGS):
@ -72,7 +76,7 @@ class Controller(volume_meta_v2.Controller):
return webob.Response(
status_int=http_client.PRECONDITION_FAILED)
return super(Controller, self).update_all(req, volume_id,
body)
body=body)
def create_resource():

View File

@ -268,7 +268,7 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.create(req, self.req_id, body)
res_dict = self.controller.create(req, self.req_id, body=body)
self.assertEqual(body, res_dict)
@mock.patch.object(db, 'volume_metadata_update')
@ -292,7 +292,7 @@ class VolumeMetaDataTest(test.TestCase):
get_volume.return_value = fake_volume
self.assertRaises(exception.InvalidVolume,
self.controller.create,
req, self.req_id, body)
req, self.req_id, body=body)
@mock.patch.object(db, 'volume_metadata_update')
@mock.patch.object(db, 'volume_metadata_get')
@ -324,7 +324,7 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.create(req, self.req_id, body)
res_dict = self.controller.create(req, self.req_id, body=body)
self.assertEqual(expected, res_dict)
def test_create_empty_body(self):
@ -334,8 +334,8 @@ class VolumeMetaDataTest(test.TestCase):
req.method = 'POST'
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, None)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id, body=None)
def test_create_metadata_keys_value_none(self):
self.mock_object(db, 'volume_metadata_update',
@ -344,8 +344,8 @@ class VolumeMetaDataTest(test.TestCase):
req.method = 'POST'
req.headers["content-type"] = "application/json"
body = {"meta": {"key": None}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, body)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id, body=body)
def test_create_item_empty_key(self):
self.mock_object(db, 'volume_metadata_update',
@ -356,8 +356,8 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, body)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id, body=body)
def test_create_item_key_too_long(self):
self.mock_object(db, 'volume_metadata_update',
@ -368,9 +368,9 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.create,
req, self.req_id, body)
req, self.req_id, body=body)
def test_create_nonexistent_volume(self):
self.mock_object(volume.api.API, 'get',
@ -386,7 +386,7 @@ class VolumeMetaDataTest(test.TestCase):
body = {"metadata": {"key9": "value9"}}
req.body = jsonutils.dump_as_bytes(body)
self.assertRaises(exception.VolumeNotFound,
self.controller.create, req, self.req_id, body)
self.controller.create, req, self.req_id, body=body)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_all(self, metadata_update):
@ -409,7 +409,8 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update_all(req, self.req_id, expected)
res_dict = self.controller.update_all(req, self.req_id,
body=expected)
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -436,7 +437,7 @@ class VolumeMetaDataTest(test.TestCase):
get_volume.return_value = fake_volume
self.assertRaises(exception.InvalidVolume,
self.controller.update_all, req,
self.req_id, expected)
self.req_id, body=expected)
self.assertFalse(metadata_update.called)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -473,7 +474,7 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update_all(req, self.req_id, body)
res_dict = self.controller.update_all(req, self.req_id, body=body)
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -492,7 +493,8 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update_all(req, self.req_id, expected)
res_dict = self.controller.update_all(req, self.req_id,
body=expected)
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -505,9 +507,9 @@ class VolumeMetaDataTest(test.TestCase):
expected = {'meta': {}}
req.body = jsonutils.dump_as_bytes(expected)
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update_all, req, self.req_id,
expected)
body=expected)
def test_update_all_malformed_data(self):
self.mock_object(db, 'volume_metadata_update',
@ -518,9 +520,9 @@ class VolumeMetaDataTest(test.TestCase):
expected = {'metadata': ['asdf']}
req.body = jsonutils.dump_as_bytes(expected)
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update_all, req, self.req_id,
expected)
body=expected)
def test_update_all_nonexistent_volume(self):
self.mock_object(db, 'volume_get', return_volume_nonexistent)
@ -531,7 +533,7 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
self.assertRaises(exception.VolumeNotFound,
self.controller.update_all, req, '100', body)
self.controller.update_all, req, '100', body=body)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_item(self, metadata_update):
@ -548,7 +550,8 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update(req, self.req_id, 'key1', body)
res_dict = self.controller.update(req, self.req_id, 'key1',
body=body)
expected = {'meta': {'key1': 'value1'}}
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -562,9 +565,9 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update,
req, self.req_id, 'key1', body)
req, self.req_id, 'key1', body=body)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_item_volume_maintenance(self, metadata_update):
@ -583,7 +586,7 @@ class VolumeMetaDataTest(test.TestCase):
get_volume.return_value = fake_volume
self.assertRaises(exception.InvalidVolume,
self.controller.update, req,
self.req_id, 'key1', body)
self.req_id, 'key1', body=body)
self.assertFalse(metadata_update.called)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -599,7 +602,7 @@ class VolumeMetaDataTest(test.TestCase):
self.assertRaises(exception.VolumeNotFound,
self.controller.update, req, self.req_id, 'key1',
body)
body=body)
def test_update_item_empty_body(self):
self.mock_object(db, 'volume_metadata_update',
@ -608,9 +611,9 @@ class VolumeMetaDataTest(test.TestCase):
req.method = 'PUT'
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update, req, self.req_id, 'key1',
None)
body=None)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_item_empty_key(self, metadata_update):
@ -627,11 +630,10 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update, req, self.req_id,
'', body)
'', body=body)
self.assertFalse(metadata_update.called)
get_volume.assert_called_once_with(fake_context, self.req_id)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_item_key_too_long(self, metadata_update):
@ -648,11 +650,10 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
self.assertRaises(exception.ValidationError,
self.controller.update,
req, self.req_id, ("a" * 260), body)
req, self.req_id, ("a" * 260), body=body)
self.assertFalse(metadata_update.called)
get_volume.assert_called_once_with(fake_context, self.req_id)
@mock.patch.object(db, 'volume_metadata_update')
def test_update_item_value_too_long(self, metadata_update):
@ -669,11 +670,10 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
self.assertRaises(exception.ValidationError,
self.controller.update,
req, self.req_id, "key1", body)
req, self.req_id, "key1", body=body)
self.assertFalse(metadata_update.called)
get_volume.assert_called_once_with(fake_context, self.req_id)
def test_update_item_too_many_keys(self):
self.mock_object(db, 'volume_metadata_update',
@ -684,9 +684,9 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.assertRaises(exception.ValidationError,
self.controller.update, req, self.req_id, 'key1',
body)
body=body)
def test_update_item_body_uri_mismatch(self):
self.mock_object(db, 'volume_metadata_update',
@ -699,7 +699,7 @@ class VolumeMetaDataTest(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update, req, self.req_id, 'bad',
body)
body=body)
@mock.patch.object(db, 'volume_metadata_update')
def test_invalid_metadata_items_on_create(self, metadata_update):
@ -718,8 +718,9 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
self.controller.create, req, self.req_id, data)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id,
body=data)
# test for long value
data = {"metadata": {"key": "v" * 260}}
@ -729,8 +730,9 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
self.controller.create, req, self.req_id, data)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id,
body=data)
# test for empty key.
data = {"metadata": {"": "value1"}}
@ -740,5 +742,6 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, data)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id,
body=data)

View File

@ -15,7 +15,6 @@ import uuid
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import webob
from cinder.api import extensions
from cinder.api import microversions as mv
@ -225,7 +224,8 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update_all(req, self.req_id, expected)
res_dict = self.controller.update_all(req, self.req_id,
body=expected)
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -244,7 +244,8 @@ class VolumeMetaDataTest(test.TestCase):
with mock.patch.object(self.controller.volume_api,
'get') as get_volume:
get_volume.return_value = fake_volume
res_dict = self.controller.update(req, self.req_id, 'key1', body)
res_dict = self.controller.update(req, self.req_id, 'key1',
body=body)
expected = {'meta': {'key1': 'value1'}}
self.assertEqual(expected, res_dict)
get_volume.assert_called_once_with(fake_context, self.req_id)
@ -256,8 +257,8 @@ class VolumeMetaDataTest(test.TestCase):
req.method = 'POST'
req.headers["content-type"] = "application/json"
body = {"meta": {"key": None}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, body)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id, body=body)
def test_update_items_value_none(self):
self.mock_object(db, 'volume_metadata_update',
@ -268,8 +269,8 @@ class VolumeMetaDataTest(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, self.req_id, body)
self.assertRaises(exception.ValidationError,
self.controller.create, req, self.req_id, body=body)
class VolumeMetaDataTestNoMicroversion(v2_test.VolumeMetaDataTest):