Merge "Implement query param schema for volume, snapshot API"

This commit is contained in:
Zuul 2017-11-23 16:58:50 +00:00 committed by Gerrit Code Review
commit 47ad4a9fa1
4 changed files with 232 additions and 0 deletions

View File

@ -90,3 +90,20 @@ create_volume_attachment_v249['properties']['volumeAttachment'][
update_volume_attachment = copy.deepcopy(create_volume_attachment)
del update_volume_attachment['properties']['volumeAttachment'][
'properties']['device']
index_query = {
'type': 'object',
'properties': {
'limit': parameter_types.multi_params(
parameter_types.non_negative_integer),
'offset': parameter_types.multi_params(
parameter_types.non_negative_integer)
},
# NOTE(gmann): This is kept True to keep backward compatibility.
# As of now Schema validation stripped out the additional parameters and
# does not raise 400. In the future, we may block the additional parameters
# by bump in Microversion.
'additionalProperties': True
}
detail_query = index_query

View File

@ -132,12 +132,14 @@ class VolumeController(wsgi.Controller):
raise exc.HTTPNotFound(explanation=e.format_message())
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@validation.query_schema(volumes_schema.index_query)
@extensions.expected_errors(())
def index(self, req):
"""Returns a summary list of volumes."""
return self._items(req, entity_maker=_translate_volume_summary_view)
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@validation.query_schema(volumes_schema.detail_query)
@extensions.expected_errors(())
def detail(self, req):
"""Returns a detailed list of volumes."""
@ -262,6 +264,7 @@ class VolumeAttachmentController(wsgi.Controller):
super(VolumeAttachmentController, self).__init__()
@extensions.expected_errors(404)
@validation.query_schema(volumes_schema.index_query)
def index(self, req, server_id):
"""Returns the list of volume attachments for a given instance."""
context = req.environ['nova.context']
@ -501,12 +504,14 @@ class SnapshotController(wsgi.Controller):
raise exc.HTTPNotFound(explanation=e.format_message())
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@validation.query_schema(volumes_schema.index_query)
@extensions.expected_errors(())
def index(self, req):
"""Returns a summary list of snapshots."""
return self._items(req, entity_maker=_translate_snapshot_summary_view)
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@validation.query_schema(volumes_schema.detail_query)
@extensions.expected_errors(())
def detail(self, req):
"""Returns a detailed list of snapshots."""

View File

@ -139,6 +139,86 @@ class SnapshotApiTestV21(test.NoDBTestCase):
resp_snapshots = resp_dict['snapshots']
self.assertEqual(1, len(resp_snapshots))
def _test_list_with_invalid_filter(self, url):
prefix = '/os-snapshots'
req = fakes.HTTPRequest.blank(prefix + url)
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
self.assertRaises(exception.ValidationError,
controller_list, req)
def test_list_with_invalid_non_int_limit(self):
self._test_list_with_invalid_filter('?limit=-9')
def test_list_with_invalid_string_limit(self):
self._test_list_with_invalid_filter('?limit=abc')
def test_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_with_invalid_filter(
'?limit=1&limit=abc')
def test_detail_list_with_invalid_non_int_limit(self):
self._test_list_with_invalid_filter('/detail?limit=-9')
def test_detail_list_with_invalid_string_limit(self):
self._test_list_with_invalid_filter('/detail?limit=abc')
def test_detail_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_with_invalid_filter(
'/detail?limit=1&limit=abc')
def test_list_with_invalid_non_int_offset(self):
self._test_list_with_invalid_filter('?offset=-9')
def test_list_with_invalid_string_offset(self):
self._test_list_with_invalid_filter('?offset=abc')
def test_list_duplicate_query_with_invalid_string_offset(self):
self._test_list_with_invalid_filter(
'?offset=1&offset=abc')
def test_detail_list_with_invalid_non_int_offset(self):
self._test_list_with_invalid_filter('/detail?offset=-9')
def test_detail_list_with_invalid_string_offset(self):
self._test_list_with_invalid_filter('/detail?offset=abc')
def test_detail_list_duplicate_query_with_invalid_string_offset(self):
self._test_list_with_invalid_filter(
'/detail?offset=1&offset=abc')
def _test_list_duplicate_query_parameters_validation(self, url):
params = {
'limit': 1,
'offset': 1
}
controller_list = self.controller.index
if 'detail' in url:
controller_list = self.controller.detail
for param, value in params.items():
req = fakes.HTTPRequest.blank(
url + '?%s=%s&%s=%s' %
(param, value, param, value))
controller_list(req)
def test_list_duplicate_query_parameters_validation(self):
self._test_list_duplicate_query_parameters_validation('/os-snapshots')
def test_detail_list_duplicate_query_parameters_validation(self):
self._test_list_duplicate_query_parameters_validation(
'/os-snapshots/detail')
def test_list_with_additional_filter(self):
req = fakes.HTTPRequest.blank(
'/os-snapshots?limit=1&offset=1&additional=something')
self.controller.index(req)
def test_detail_list_with_additional_filter(self):
req = fakes.HTTPRequest.blank(
'/os-snapshots/detail?limit=1&offset=1&additional=something')
self.controller.detail(req)
class TestSnapshotAPIDeprecation(test.NoDBTestCase):

View File

@ -338,6 +338,84 @@ class VolumeApiTestV21(test.NoDBTestCase):
self.assertIn('Volume 456 could not be found.',
encodeutils.safe_decode(resp.body))
def _test_list_with_invalid_filter(self, url):
prefix = '/os-volumes'
req = fakes.HTTPRequest.blank(prefix + url)
self.assertRaises(exception.ValidationError,
volumes_v21.VolumeController().index,
req)
def test_list_with_invalid_non_int_limit(self):
self._test_list_with_invalid_filter('?limit=-9')
def test_list_with_invalid_string_limit(self):
self._test_list_with_invalid_filter('?limit=abc')
def test_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_with_invalid_filter(
'?limit=1&limit=abc')
def test_detail_list_with_invalid_non_int_limit(self):
self._test_list_with_invalid_filter('/detail?limit=-9')
def test_detail_list_with_invalid_string_limit(self):
self._test_list_with_invalid_filter('/detail?limit=abc')
def test_detail_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_with_invalid_filter(
'/detail?limit=1&limit=abc')
def test_list_with_invalid_non_int_offset(self):
self._test_list_with_invalid_filter('?offset=-9')
def test_list_with_invalid_string_offset(self):
self._test_list_with_invalid_filter('?offset=abc')
def test_list_duplicate_query_with_invalid_string_offset(self):
self._test_list_with_invalid_filter(
'?offset=1&offset=abc')
def test_detail_list_with_invalid_non_int_offset(self):
self._test_list_with_invalid_filter('/detail?offset=-9')
def test_detail_list_with_invalid_string_offset(self):
self._test_list_with_invalid_filter('/detail?offset=abc')
def test_detail_list_duplicate_query_with_invalid_string_offset(self):
self._test_list_with_invalid_filter(
'/detail?offset=1&offset=abc')
def _test_list_duplicate_query_parameters_validation(self, url):
params = {
'limit': 1,
'offset': 1
}
for param, value in params.items():
req = fakes.HTTPRequest.blank(
self.url_prefix + url + '?%s=%s&%s=%s' %
(param, value, param, value))
resp = req.get_response(self.app)
self.assertEqual(200, resp.status_int)
def test_list_duplicate_query_parameters_validation(self):
self._test_list_duplicate_query_parameters_validation('/os-volumes')
def test_detail_list_duplicate_query_parameters_validation(self):
self._test_list_duplicate_query_parameters_validation(
'/os-volumes/detail')
def test_list_with_additional_filter(self):
req = fakes.HTTPRequest.blank(self.url_prefix +
'/os-volumes?limit=1&offset=1&additional=something')
resp = req.get_response(self.app)
self.assertEqual(200, resp.status_int)
def test_detail_list_with_additional_filter(self):
req = fakes.HTTPRequest.blank(self.url_prefix +
'/os-volumes/detail?limit=1&offset=1&additional=something')
resp = req.get_response(self.app)
self.assertEqual(200, resp.status_int)
class VolumeAttachTestsV21(test.NoDBTestCase):
validation_error = exception.ValidationError
@ -700,6 +778,58 @@ class VolumeAttachTestsV21(test.NoDBTestCase):
self.attachments,
fake_func=fake_swap_volume_for_bdm_not_found)
def _test_list_with_invalid_filter(self, url):
prefix = '/servers/id/os-volume_attachments'
req = fakes.HTTPRequest.blank(prefix + url)
self.assertRaises(exception.ValidationError,
self.attachments.index,
req,
FAKE_UUID)
def test_list_with_invalid_non_int_limit(self):
self._test_list_with_invalid_filter('?limit=-9')
def test_list_with_invalid_string_limit(self):
self._test_list_with_invalid_filter('?limit=abc')
def test_list_duplicate_query_with_invalid_string_limit(self):
self._test_list_with_invalid_filter(
'?limit=1&limit=abc')
def test_list_with_invalid_non_int_offset(self):
self._test_list_with_invalid_filter('?offset=-9')
def test_list_with_invalid_string_offset(self):
self._test_list_with_invalid_filter('?offset=abc')
def test_list_duplicate_query_with_invalid_string_offset(self):
self._test_list_with_invalid_filter(
'?offset=1&offset=abc')
@mock.patch.object(objects.BlockDeviceMappingList,
'get_by_instance_uuid')
def test_list_duplicate_query_parameters_validation(self, mock_get):
fake_bdms = objects.BlockDeviceMappingList()
mock_get.return_value = fake_bdms
params = {
'limit': 1,
'offset': 1
}
for param, value in params.items():
req = fakes.HTTPRequest.blank(
'/servers/id/os-volume_attachments' + '?%s=%s&%s=%s' %
(param, value, param, value))
self.attachments.index(req, FAKE_UUID)
@mock.patch.object(objects.BlockDeviceMappingList,
'get_by_instance_uuid')
def test_list_with_additional_filter(self, mock_get):
fake_bdms = objects.BlockDeviceMappingList()
mock_get.return_value = fake_bdms
req = fakes.HTTPRequest.blank(
'/servers/id/os-volume_attachments?limit=1&additional=something')
self.attachments.index(req, FAKE_UUID)
class VolumeAttachTestsV249(test.NoDBTestCase):
validation_error = exception.ValidationError