V3 jsonschema validation: volume_transfer
This patch adds jsonschema validation for below volume transfer API's * POST /v3/{project_id}/os-volume-transfer * POST /v3/{project_id}/os-volume-transfer/{transfer_id}/accept Modified existing unit test cases which were mistakenly passed 'transer_id' in request body which is supposed to pass in path. Partial-Implements: bp json-schema-validation Change-Id: Idcbfd37d49584ed6cb438cfdc0e7403504977525
This commit is contained in:
parent
9586112848
commit
43ae14cc17
|
@ -21,9 +21,10 @@ from webob import exc
|
|||
from cinder.api import common
|
||||
from cinder.api import extensions
|
||||
from cinder.api.openstack import wsgi
|
||||
from cinder.api.schemas import volume_transfer
|
||||
from cinder.api import validation
|
||||
from cinder.api.views import transfers as transfer_view
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import transfer as transferAPI
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -74,25 +75,18 @@ class VolumeTransferController(wsgi.Controller):
|
|||
return transfers
|
||||
|
||||
@wsgi.response(http_client.ACCEPTED)
|
||||
@validation.schema(volume_transfer.create)
|
||||
def create(self, req, body):
|
||||
"""Create a new volume transfer."""
|
||||
LOG.debug('Creating new volume transfer %s', body)
|
||||
self.assert_valid_body(body, 'transfer')
|
||||
|
||||
context = req.environ['cinder.context']
|
||||
transfer = body['transfer']
|
||||
|
||||
try:
|
||||
volume_id = transfer['volume_id']
|
||||
except KeyError:
|
||||
msg = _("Incorrect request body format")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
volume_id = transfer['volume_id']
|
||||
|
||||
name = transfer.get('name', None)
|
||||
if name is not None:
|
||||
self.validate_string_length(name, 'Transfer name',
|
||||
min_length=1, max_length=255,
|
||||
remove_whitespaces=True)
|
||||
name = name.strip()
|
||||
|
||||
LOG.info("Creating transfer of volume %s",
|
||||
|
@ -109,20 +103,15 @@ class VolumeTransferController(wsgi.Controller):
|
|||
return transfer
|
||||
|
||||
@wsgi.response(http_client.ACCEPTED)
|
||||
@validation.schema(volume_transfer.accept)
|
||||
def accept(self, req, id, body):
|
||||
"""Accept a new volume transfer."""
|
||||
transfer_id = id
|
||||
LOG.debug('Accepting volume transfer %s', transfer_id)
|
||||
self.assert_valid_body(body, 'accept')
|
||||
|
||||
context = req.environ['cinder.context']
|
||||
accept = body['accept']
|
||||
|
||||
try:
|
||||
auth_key = accept['auth_key']
|
||||
except KeyError:
|
||||
msg = _("Incorrect request body format")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
auth_key = accept['auth_key']
|
||||
|
||||
LOG.info("Accepting transfer %s", transfer_id)
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
# Copyright (C) 2018 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 transfer API.
|
||||
|
||||
"""
|
||||
|
||||
from cinder.api.validation import parameter_types
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'transfer': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'volume_id': parameter_types.uuid,
|
||||
'name': {'oneOf': [{'type': 'string',
|
||||
'format':
|
||||
"name_skip_leading_trailing_spaces"},
|
||||
{'type': 'null'}]}
|
||||
},
|
||||
'required': ['volume_id'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['transfer'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
|
||||
accept = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'accept': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'auth_key': {'type': ['string', 'integer']},
|
||||
},
|
||||
'required': ['auth_key'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['accept'],
|
||||
'additionalProperties': False,
|
||||
}
|
|
@ -17,8 +17,6 @@
|
|||
Tests for volume transfer code.
|
||||
"""
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from six.moves import http_client
|
||||
import webob
|
||||
|
@ -186,9 +184,7 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
db.transfer_destroy(context.get_admin_context(), transfer1['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id_1)
|
||||
|
||||
@mock.patch(
|
||||
'cinder.api.openstack.wsgi.Controller.validate_string_length')
|
||||
def test_create_transfer_json(self, mock_validate):
|
||||
def test_create_transfer_json(self):
|
||||
volume_id = self._create_volume(status='available', size=5)
|
||||
body = {"transfer": {"name": "transfer1",
|
||||
"volume_id": volume_id}}
|
||||
|
@ -209,7 +205,6 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertIn('created_at', res_dict['transfer'])
|
||||
self.assertIn('name', res_dict['transfer'])
|
||||
self.assertIn('volume_id', res_dict['transfer'])
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
|
@ -227,9 +222,6 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
self.assertEqual("Missing required element 'transfer' in "
|
||||
"request body.",
|
||||
res_dict['badRequest']['message'])
|
||||
|
||||
def test_create_transfer_with_body_KeyError(self):
|
||||
body = {"transfer": {"name": "transfer1"}}
|
||||
|
@ -245,10 +237,8 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
self.assertEqual('Incorrect request body format',
|
||||
res_dict['badRequest']['message'])
|
||||
|
||||
def test_create_transfer_with_VolumeNotFound(self):
|
||||
def test_create_transfer_with_invalid_volume_id_value(self):
|
||||
body = {"transfer": {"name": "transfer1",
|
||||
"volume_id": 1234}}
|
||||
|
||||
|
@ -261,11 +251,9 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
fake_auth_context=self.user_ctxt))
|
||||
res_dict = jsonutils.loads(res.body)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, res.status_int)
|
||||
self.assertEqual(http_client.NOT_FOUND,
|
||||
res_dict['itemNotFound']['code'])
|
||||
self.assertEqual('Volume 1234 could not be found.',
|
||||
res_dict['itemNotFound']['message'])
|
||||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
|
||||
def test_create_transfer_with_InvalidVolume(self):
|
||||
volume_id = self._create_volume(status='attached')
|
||||
|
@ -288,6 +276,26 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_create_transfer_with_leading_trailing_spaces_for_name(self):
|
||||
volume_id = self._create_volume(status='available', size=5)
|
||||
body = {"transfer": {"name": " transfer1 ",
|
||||
"volume_id": volume_id}}
|
||||
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer' %
|
||||
fake.PROJECT_ID)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
res = req.get_response(fakes.wsgi_app(
|
||||
fake_auth_context=self.user_ctxt))
|
||||
|
||||
res_dict = jsonutils.loads(res.body)
|
||||
|
||||
self.assertEqual(http_client.ACCEPTED, res.status_int)
|
||||
self.assertEqual(body['transfer']['name'].strip(),
|
||||
res_dict['transfer']['name'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_delete_transfer_awaiting_transfer(self):
|
||||
volume_id = self._create_volume()
|
||||
transfer = self._create_transfer(volume_id)
|
||||
|
@ -340,8 +348,7 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
transfer = self._create_transfer(volume_id)
|
||||
|
||||
svc = self.start_service('volume', host='fake_host')
|
||||
body = {"accept": {"id": transfer['id'],
|
||||
"auth_key": transfer['auth_key']}}
|
||||
body = {"accept": {"auth_key": transfer['auth_key']}}
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, transfer['id']))
|
||||
req.method = 'POST'
|
||||
|
@ -374,9 +381,8 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
self.assertEqual("Missing required element 'accept' in request body.",
|
||||
res_dict['badRequest']['message'])
|
||||
|
||||
db.transfer_destroy(context.get_admin_context(), transfer['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_accept_transfer_with_body_KeyError(self):
|
||||
|
@ -398,15 +404,15 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
self.assertEqual("Missing required element 'accept' in request body.",
|
||||
res_dict['badRequest']['message'])
|
||||
|
||||
db.transfer_destroy(context.get_admin_context(), transfer['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_accept_transfer_invalid_id_auth_key(self):
|
||||
volume_id = self._create_volume()
|
||||
transfer = self._create_transfer(volume_id)
|
||||
|
||||
body = {"accept": {"id": transfer['id'],
|
||||
"auth_key": 1}}
|
||||
body = {"accept": {"auth_key": 1}}
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, transfer['id']))
|
||||
req.method = 'POST'
|
||||
|
@ -430,8 +436,7 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
volume_id = self._create_volume()
|
||||
transfer = self._create_transfer(volume_id)
|
||||
|
||||
body = {"accept": {"id": transfer['id'],
|
||||
"auth_key": 1}}
|
||||
body = {"accept": {"auth_key": 1}}
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID))
|
||||
req.method = 'POST'
|
||||
|
@ -467,8 +472,7 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
volume_id = self._create_volume()
|
||||
transfer = self._create_transfer(volume_id)
|
||||
|
||||
body = {"accept": {"id": transfer['id'],
|
||||
"auth_key": transfer['auth_key']}}
|
||||
body = {"accept": {"auth_key": transfer['auth_key']}}
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, transfer['id']))
|
||||
|
||||
|
@ -486,6 +490,9 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
'2G has been consumed.',
|
||||
res_dict['overLimit']['message'])
|
||||
|
||||
db.transfer_destroy(context.get_admin_context(), transfer['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_accept_transfer_with_VolumeLimitExceeded(self):
|
||||
|
||||
def fake_transfer_api_accept_throwing_VolumeLimitExceeded(cls,
|
||||
|
@ -500,8 +507,7 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
volume_id = self._create_volume()
|
||||
transfer = self._create_transfer(volume_id)
|
||||
|
||||
body = {"accept": {"id": transfer['id'],
|
||||
"auth_key": transfer['auth_key']}}
|
||||
body = {"accept": {"auth_key": transfer['auth_key']}}
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, transfer['id']))
|
||||
|
||||
|
@ -517,3 +523,28 @@ class VolumeTransferAPITestCase(test.TestCase):
|
|||
self.assertEqual("VolumeLimitExceeded: Maximum number of volumes "
|
||||
"allowed (1) exceeded for quota 'volumes'.",
|
||||
res_dict['overLimit']['message'])
|
||||
|
||||
db.transfer_destroy(context.get_admin_context(), transfer['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_accept_transfer_with_auth_key_null(self):
|
||||
volume_id = self._create_volume(size=5)
|
||||
transfer = self._create_transfer(volume_id)
|
||||
body = {"accept": {"auth_key": None}}
|
||||
|
||||
req = webob.Request.blank('/v2/%s/os-volume-transfer/%s/accept' % (
|
||||
fake.PROJECT_ID, transfer['id']))
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.headers['Accept'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app(
|
||||
fake_auth_context=self.user_ctxt))
|
||||
res_dict = jsonutils.loads(res.body)
|
||||
|
||||
self.assertEqual(http_client.BAD_REQUEST,
|
||||
res_dict['badRequest']['code'])
|
||||
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
|
||||
|
||||
db.transfer_destroy(context.get_admin_context(), transfer['id'])
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
|
Loading…
Reference in New Issue