V3 jsonschema validation: Group Snapshots
This patch adds jsonschema validation for below group snapshots API's * POST /v3/{project_id}/group_snapshots * POST /v3/{project_id}/group_snapshots/{group_snapshot_id}/action Made changes to unit tests to pass body as keyword argument as wsgi calls action method [1] and passes body as keyword argument. [1] https://github.com/openstack/cinder/blob/master/cinder/api/openstack/wsgi.py#L997 Change-Id: Ie3b8ffb209b30edf2a26a935aab840441b43adfa Partial-Implements: bp json-schema-validation
This commit is contained in:
parent
9a31b4c8f5
commit
65d57cf4b1
|
@ -0,0 +1,61 @@
|
|||
# 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 Group Snapshots API.
|
||||
|
||||
"""
|
||||
|
||||
from cinder.api.validation import parameter_types
|
||||
|
||||
|
||||
name_optional = parameter_types.name
|
||||
name_optional['minLength'] = 0
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'group_snapshot': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'group_id': parameter_types.uuid,
|
||||
'name': name_optional,
|
||||
'description': parameter_types.description,
|
||||
},
|
||||
'required': ['group_id'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['group_snapshot'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
reset_status = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'reset_status': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'status': parameter_types.group_snapshot_status,
|
||||
},
|
||||
'required': ['status'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['reset_status'],
|
||||
'additionalProperties': False,
|
||||
}
|
|
@ -24,7 +24,9 @@ from webob import exc
|
|||
from cinder.api import common
|
||||
from cinder.api import microversions as mv
|
||||
from cinder.api.openstack import wsgi
|
||||
from cinder.api.schemas import group_snapshots as snapshot
|
||||
from cinder.api.v3.views import group_snapshots as group_snapshot_views
|
||||
from cinder.api import validation
|
||||
from cinder import exception
|
||||
from cinder import group as group_api
|
||||
from cinder.i18n import _
|
||||
|
@ -146,20 +148,15 @@ class GroupSnapshotsController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
|
||||
@wsgi.response(http_client.ACCEPTED)
|
||||
@validation.schema(snapshot.create)
|
||||
def create(self, req, body):
|
||||
"""Create a new group_snapshot."""
|
||||
LOG.debug('Creating new group_snapshot %s', body)
|
||||
self.assert_valid_body(body, 'group_snapshot')
|
||||
|
||||
context = req.environ['cinder.context']
|
||||
group_snapshot = body['group_snapshot']
|
||||
self.validate_name_and_description(group_snapshot)
|
||||
|
||||
try:
|
||||
group_id = group_snapshot['group_id']
|
||||
except KeyError:
|
||||
msg = _("'group_id' must be specified")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
group_id = group_snapshot['group_id']
|
||||
|
||||
group = self.group_snapshot_api.get(context, group_id)
|
||||
self._check_default_cgsnapshot_type(group.group_type_id)
|
||||
|
@ -184,6 +181,7 @@ class GroupSnapshotsController(wsgi.Controller):
|
|||
|
||||
@wsgi.Controller.api_version(mv.GROUP_SNAPSHOT_RESET_STATUS)
|
||||
@wsgi.action("reset_status")
|
||||
@validation.schema(snapshot.reset_status)
|
||||
def reset_status(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
|
@ -191,10 +189,7 @@ class GroupSnapshotsController(wsgi.Controller):
|
|||
"""Reset status on group snapshots"""
|
||||
|
||||
context = req.environ['cinder.context']
|
||||
try:
|
||||
status = body['reset_status']['status'].lower()
|
||||
except (TypeError, KeyError):
|
||||
raise exc.HTTPBadRequest(explanation=_("Must specify 'status'"))
|
||||
status = body['reset_status']['status'].lower()
|
||||
|
||||
LOG.debug("Updating group '%(id)s' with "
|
||||
"'%(update)s'", {'id': id,
|
||||
|
|
|
@ -150,3 +150,7 @@ extra_specs = {
|
|||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
group_snapshot_status = {
|
||||
'type': 'string', 'format': 'group_snapshot_status'
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import six
|
|||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.objects import fields as c_fields
|
||||
|
||||
|
||||
def _soft_validate_additional_properties(
|
||||
|
@ -96,10 +97,10 @@ def _validate_datetime_format(param_value):
|
|||
@jsonschema.FormatChecker.cls_checks('name', exception.InvalidName)
|
||||
def _validate_name(param_value):
|
||||
if not param_value:
|
||||
msg = "The 'name' can not be None."
|
||||
msg = _("The 'name' can not be None.")
|
||||
raise exception.InvalidName(reason=msg)
|
||||
elif len(param_value.strip()) == 0:
|
||||
msg = "The 'name' can not be empty."
|
||||
msg = _("The 'name' can not be empty.")
|
||||
raise exception.InvalidName(reason=msg)
|
||||
return True
|
||||
|
||||
|
@ -109,6 +110,20 @@ def _validate_uuid_format(instance):
|
|||
return uuidutils.is_uuid_like(instance)
|
||||
|
||||
|
||||
@jsonschema.FormatChecker.cls_checks('group_snapshot_status')
|
||||
def _validate_status(param_value):
|
||||
if len(param_value.strip()) == 0:
|
||||
msg = _("The 'status' can not be empty.")
|
||||
raise exception.InvalidGroupSnapshotStatus(reason=msg)
|
||||
elif param_value.lower() not in c_fields.GroupSnapshotStatus.ALL:
|
||||
msg = _("Group snapshot status: %(status)s is invalid, "
|
||||
"valid statuses are: "
|
||||
"%(valid)s.") % {'status': param_value,
|
||||
'valid': c_fields.GroupSnapshotStatus.ALL}
|
||||
raise exception.InvalidGroupSnapshotStatus(reason=msg)
|
||||
return True
|
||||
|
||||
|
||||
class FormatChecker(jsonschema.FormatChecker):
|
||||
"""A FormatChecker can output the message from cause exception
|
||||
|
||||
|
|
|
@ -925,12 +925,6 @@ class API(base.Base):
|
|||
"""Reset status of group snapshot"""
|
||||
|
||||
context.authorize(gsnap_action_policy.RESET_STATUS)
|
||||
if status not in c_fields.GroupSnapshotStatus.ALL:
|
||||
msg = _("Group snapshot status: %(status)s is invalid, "
|
||||
"valid statuses are: "
|
||||
"%(valid)s.") % {'status': status,
|
||||
'valid': c_fields.GroupSnapshotStatus.ALL}
|
||||
raise exception.InvalidGroupSnapshotStatus(reason=msg)
|
||||
field = {'updated_at': timeutils.utcnow(),
|
||||
'status': status}
|
||||
gsnapshot.update(field)
|
||||
|
|
|
@ -320,12 +320,9 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
self.assertNotIn('description',
|
||||
res_dict['group_snapshots'][2 - index].keys())
|
||||
|
||||
@mock.patch(
|
||||
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
||||
@mock.patch('cinder.db.volume_type_get')
|
||||
@mock.patch('cinder.quota.VolumeTypeQuotaEngine.reserve')
|
||||
def test_create_group_snapshot_json(self, mock_quota, mock_vol_type,
|
||||
mock_validate):
|
||||
def test_create_group_snapshot_json(self, mock_quota, mock_vol_type):
|
||||
body = {"group_snapshot": {"name": "group_snapshot1",
|
||||
"description":
|
||||
"Group Snapshot 1",
|
||||
|
@ -333,20 +330,17 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
|
||||
fake.PROJECT_ID,
|
||||
version=mv.GROUP_SNAPSHOTS)
|
||||
res_dict = self.controller.create(req, body)
|
||||
res_dict = self.controller.create(req, body=body)
|
||||
|
||||
self.assertEqual(1, len(res_dict))
|
||||
self.assertIn('id', res_dict['group_snapshot'])
|
||||
self.assertTrue(mock_validate.called)
|
||||
group_snapshot = objects.GroupSnapshot.get_by_id(
|
||||
context.get_admin_context(), res_dict['group_snapshot']['id'])
|
||||
group_snapshot.destroy()
|
||||
|
||||
@mock.patch(
|
||||
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
||||
@mock.patch('cinder.db.volume_type_get')
|
||||
def test_create_group_snapshot_when_volume_in_error_status(
|
||||
self, mock_vol_type, mock_validate):
|
||||
self, mock_vol_type):
|
||||
group = utils.create_group(
|
||||
self.context,
|
||||
group_type_id=fake.GROUP_TYPE_ID,
|
||||
|
@ -364,8 +358,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
fake.PROJECT_ID,
|
||||
version=mv.GROUP_SNAPSHOTS)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, body)
|
||||
self.assertTrue(mock_validate.called)
|
||||
req, body=body)
|
||||
|
||||
group.destroy()
|
||||
db.volume_destroy(context.get_admin_context(),
|
||||
|
@ -376,8 +369,17 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
|
||||
fake.PROJECT_ID,
|
||||
version=mv.GROUP_SNAPSHOTS)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, None)
|
||||
self.assertRaises(exception.ValidationError, self.controller.create,
|
||||
req, body=None)
|
||||
|
||||
def test_create_group_snapshot_with_empty_body(self):
|
||||
# empty body in the request
|
||||
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
|
||||
fake.PROJECT_ID,
|
||||
version=mv.GROUP_SNAPSHOTS)
|
||||
body = {"group_snapshot": {}}
|
||||
self.assertRaises(exception.ValidationError, self.controller.create,
|
||||
req, body=body)
|
||||
|
||||
@mock.patch.object(group_api.API, 'create_group_snapshot',
|
||||
side_effect=exception.InvalidGroupSnapshot(
|
||||
|
@ -391,7 +393,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
fake.PROJECT_ID,
|
||||
version=mv.GROUP_SNAPSHOTS)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, body)
|
||||
req, body=body)
|
||||
|
||||
@mock.patch.object(group_api.API, 'create_group_snapshot',
|
||||
side_effect=exception.GroupSnapshotNotFound(
|
||||
|
@ -406,7 +408,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
version=mv.GROUP_SNAPSHOTS)
|
||||
self.assertRaises(exception.GroupSnapshotNotFound,
|
||||
self.controller.create,
|
||||
req, body)
|
||||
req, body=body)
|
||||
|
||||
def test_create_group_snapshot_from_empty_group(self):
|
||||
empty_group = utils.create_group(
|
||||
|
@ -422,7 +424,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
version=mv.GROUP_SNAPSHOTS)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, body)
|
||||
req, body=body)
|
||||
empty_group.destroy()
|
||||
|
||||
def test_delete_group_snapshot_available(self):
|
||||
|
@ -507,7 +509,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
}}
|
||||
self.assertRaises(exceptions,
|
||||
self.controller.reset_status,
|
||||
req, group_snapshot_id, body)
|
||||
req, group_snapshot_id, body=body)
|
||||
|
||||
def test_reset_group_snapshot_status_invalid_status(self):
|
||||
group_snapshot = utils.create_group_snapshot(
|
||||
|
@ -520,9 +522,9 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
body = {"reset_status": {
|
||||
"status": "invalid_test_status"
|
||||
}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.assertRaises(exception.InvalidGroupSnapshotStatus,
|
||||
self.controller.reset_status,
|
||||
req, group_snapshot.id, body)
|
||||
req, group_snapshot.id, body=body)
|
||||
group_snapshot.destroy()
|
||||
|
||||
def test_reset_group_snapshot_status(self):
|
||||
|
@ -537,7 +539,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
|
|||
"status": fields.GroupSnapshotStatus.AVAILABLE
|
||||
}}
|
||||
response = self.controller.reset_status(req, group_snapshot.id,
|
||||
body)
|
||||
body=body)
|
||||
|
||||
g_snapshot = objects.GroupSnapshot.get_by_id(self.context,
|
||||
group_snapshot.id)
|
||||
|
|
Loading…
Reference in New Issue