Merge "Fix 500 error if 'offset' is out of range"
This commit is contained in:
commit
9504f51882
@ -25,6 +25,7 @@ import webob
|
|||||||
|
|
||||||
from cinder.api.openstack import wsgi
|
from cinder.api.openstack import wsgi
|
||||||
from cinder.api import xmlutil
|
from cinder.api import xmlutil
|
||||||
|
import cinder.db
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
import cinder.policy
|
import cinder.policy
|
||||||
@ -137,17 +138,8 @@ def _get_marker_param(params):
|
|||||||
|
|
||||||
def _get_offset_param(params):
|
def _get_offset_param(params):
|
||||||
"""Extract offset id from request's dictionary (defaults to 0) or fail."""
|
"""Extract offset id from request's dictionary (defaults to 0) or fail."""
|
||||||
try:
|
offset = params.pop('offset', 0)
|
||||||
offset = int(params.pop('offset', 0))
|
return utils.validate_integer(offset, 'offset', 0, cinder.db.MAX_INT)
|
||||||
except ValueError:
|
|
||||||
msg = _('offset param must be an integer')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
if offset < 0:
|
|
||||||
msg = _('offset param must be positive')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
return offset
|
|
||||||
|
|
||||||
|
|
||||||
def limited(items, request, max_limit=None):
|
def limited(items, request, max_limit=None):
|
||||||
|
@ -22,6 +22,7 @@ from cinder import db
|
|||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
from cinder import quota
|
from cinder import quota
|
||||||
|
from cinder import utils
|
||||||
|
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
@ -80,8 +81,8 @@ class QuotaClassSetsController(wsgi.Controller):
|
|||||||
for key, value in body['quota_class_set'].items():
|
for key, value in body['quota_class_set'].items():
|
||||||
if key in QUOTAS:
|
if key in QUOTAS:
|
||||||
try:
|
try:
|
||||||
value = self.validate_integer(value, key, min_value=-1,
|
value = utils.validate_integer(value, key, min_value=-1,
|
||||||
max_value=db.MAX_INT)
|
max_value=db.MAX_INT)
|
||||||
db.quota_class_update(context, quota_class, key, value)
|
db.quota_class_update(context, quota_class, key, value)
|
||||||
except exception.QuotaClassNotFound:
|
except exception.QuotaClassNotFound:
|
||||||
db.quota_class_create(context, quota_class, key, value)
|
db.quota_class_create(context, quota_class, key, value)
|
||||||
|
@ -269,7 +269,7 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
if key in NON_QUOTA_KEYS:
|
if key in NON_QUOTA_KEYS:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value = self.validate_integer(
|
value = utils.validate_integer(
|
||||||
body['quota_set'][key], key, min_value=-1,
|
body['quota_set'][key], key, min_value=-1,
|
||||||
max_value=db.MAX_INT)
|
max_value=db.MAX_INT)
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from cinder import db
|
|||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
from cinder import rpc
|
from cinder import rpc
|
||||||
|
from cinder import utils
|
||||||
from cinder.volume import volume_types
|
from cinder.volume import volume_types
|
||||||
|
|
||||||
authorize = extensions.extension_authorizer('volume',
|
authorize = extensions.extension_authorizer('volume',
|
||||||
@ -58,7 +59,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
|
|||||||
|
|
||||||
def _check_encryption_input(self, encryption, create=True):
|
def _check_encryption_input(self, encryption, create=True):
|
||||||
if encryption.get('key_size') is not None:
|
if encryption.get('key_size') is not None:
|
||||||
encryption['key_size'] = self.validate_integer(
|
encryption['key_size'] = utils.validate_integer(
|
||||||
encryption['key_size'], 'key_size',
|
encryption['key_size'], 'key_size',
|
||||||
min_value=0, max_value=db.MAX_INT)
|
min_value=0, max_value=db.MAX_INT)
|
||||||
|
|
||||||
|
@ -1496,33 +1496,6 @@ class Controller(object):
|
|||||||
except exception.InvalidInput as error:
|
except exception.InvalidInput as error:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=error.msg)
|
raise webob.exc.HTTPBadRequest(explanation=error.msg)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_integer(value, name, min_value=None, max_value=None):
|
|
||||||
"""Make sure that value is a valid integer, potentially within range.
|
|
||||||
|
|
||||||
:param value: the value of the integer
|
|
||||||
:param name: the name of the integer
|
|
||||||
:param min_length: the min_length of the integer
|
|
||||||
:param max_length: the max_length of the integer
|
|
||||||
:returns: integer
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
value = int(value)
|
|
||||||
except (TypeError, ValueError, UnicodeEncodeError):
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=(
|
|
||||||
_('%s must be an integer.') % name))
|
|
||||||
|
|
||||||
if min_value is not None and value < min_value:
|
|
||||||
raise webob.exc.HTTPBadRequest(
|
|
||||||
explanation=(_('%(value_name)s must be >= %(min_value)d') %
|
|
||||||
{'value_name': name, 'min_value': min_value}))
|
|
||||||
if max_value is not None and value > max_value:
|
|
||||||
raise webob.exc.HTTPBadRequest(
|
|
||||||
explanation=(_('%(value_name)s must be <= %(max_value)d') %
|
|
||||||
{'value_name': name, 'max_value': max_value}))
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class Fault(webob.exc.HTTPException):
|
class Fault(webob.exc.HTTPException):
|
||||||
"""Wrap webob.exc.HTTPException to provide API friendly response."""
|
"""Wrap webob.exc.HTTPException to provide API friendly response."""
|
||||||
|
@ -250,6 +250,14 @@ class BackupsAPITestCase(test.TestCase):
|
|||||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||||
|
|
||||||
|
def test_list_backups_with_offset_out_of_range(self):
|
||||||
|
url = '/v2/fake/backups?offset=252452434242342434'
|
||||||
|
req = webob.Request.blank(url)
|
||||||
|
req.method = 'GET'
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
res = req.get_response(fakes.wsgi_app())
|
||||||
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
def test_list_backups_with_marker(self):
|
def test_list_backups_with_marker(self):
|
||||||
backup_id1 = self._create_backup()
|
backup_id1 = self._create_backup()
|
||||||
backup_id2 = self._create_backup()
|
backup_id2 = self._create_backup()
|
||||||
@ -553,6 +561,14 @@ class BackupsAPITestCase(test.TestCase):
|
|||||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||||
|
|
||||||
|
def test_list_backups_detail_with_offset_out_of_range(self):
|
||||||
|
url = '/v2/fake/backups/detail?offset=234534543657634523'
|
||||||
|
req = webob.Request.blank(url)
|
||||||
|
req.method = 'GET'
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
res = req.get_response(fakes.wsgi_app())
|
||||||
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
@mock.patch('cinder.db.service_get_all_by_topic')
|
@mock.patch('cinder.db.service_get_all_by_topic')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
||||||
|
@ -248,6 +248,17 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
|||||||
consistencygroup2.destroy()
|
consistencygroup2.destroy()
|
||||||
consistencygroup3.destroy()
|
consistencygroup3.destroy()
|
||||||
|
|
||||||
|
@ddt.data(False, True)
|
||||||
|
def test_list_consistencygroups_with_offset_out_of_range(self, is_detail):
|
||||||
|
url = '/v2/fake/consistencygroups?offset=234523423455454'
|
||||||
|
if is_detail:
|
||||||
|
url = '/v2/fake/consistencygroups/detail?offset=234523423455454'
|
||||||
|
req = webob.Request.blank(url)
|
||||||
|
req.method = 'GET'
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
res = req.get_response(fakes.wsgi_app())
|
||||||
|
self.assertEqual(400, res.status_int)
|
||||||
|
|
||||||
@ddt.data(False, True)
|
@ddt.data(False, True)
|
||||||
def test_list_consistencygroups_with_limit_and_offset(self, is_detail):
|
def test_list_consistencygroups_with_limit_and_offset(self, is_detail):
|
||||||
consistencygroup1 = self._create_consistencygroup()
|
consistencygroup1 = self._create_consistencygroup()
|
||||||
|
@ -227,6 +227,12 @@ class QoSSpecManageApiTest(test.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(3, len(res['qos_specs']))
|
self.assertEqual(3, len(res['qos_specs']))
|
||||||
|
|
||||||
|
def test_index_with_offset_out_of_range(self):
|
||||||
|
url = '/v2/fake/qos-specs?offset=356576877698707'
|
||||||
|
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index,
|
||||||
|
req)
|
||||||
|
|
||||||
def test_index_with_limit_and_offset(self):
|
def test_index_with_limit_and_offset(self):
|
||||||
url = '/v2/fake/qos-specs?limit=2&offset=1'
|
url = '/v2/fake/qos-specs?limit=2&offset=1'
|
||||||
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
req = fakes.HTTPRequest.blank(url, use_admin_context=True)
|
||||||
|
@ -224,7 +224,7 @@ class QuotaSetsControllerTest(QuotaSetsControllerTestBase):
|
|||||||
@mock.patch(
|
@mock.patch(
|
||||||
'cinder.api.openstack.wsgi.Controller.validate_string_length')
|
'cinder.api.openstack.wsgi.Controller.validate_string_length')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'cinder.api.openstack.wsgi.Controller.validate_integer')
|
'cinder.utils.validate_integer')
|
||||||
def test_update_limit(self, mock_validate_integer, mock_validate):
|
def test_update_limit(self, mock_validate_integer, mock_validate):
|
||||||
mock_validate_integer.return_value = 10
|
mock_validate_integer.return_value = 10
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ class QuotaClassSetsControllerTest(test.TestCase):
|
|||||||
self.assertDictMatch(body, result)
|
self.assertDictMatch(body, result)
|
||||||
|
|
||||||
@mock.patch('cinder.api.openstack.wsgi.Controller.validate_string_length')
|
@mock.patch('cinder.api.openstack.wsgi.Controller.validate_string_length')
|
||||||
@mock.patch('cinder.api.openstack.wsgi.Controller.validate_integer')
|
@mock.patch('cinder.utils.validate_integer')
|
||||||
def test_update_limit(self, mock_validate_integer, mock_validate):
|
def test_update_limit(self, mock_validate_integer, mock_validate):
|
||||||
mock_validate_integer.return_value = 5
|
mock_validate_integer.return_value = 5
|
||||||
volume_types.create(self.ctxt, 'fake_type')
|
volume_types.create(self.ctxt, 'fake_type')
|
||||||
|
@ -18,11 +18,11 @@ import mock
|
|||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from cinder.api.openstack import wsgi
|
|
||||||
from cinder import context
|
from cinder import context
|
||||||
from cinder import db
|
from cinder import db
|
||||||
from cinder import test
|
from cinder import test
|
||||||
from cinder.tests.unit.api import fakes
|
from cinder.tests.unit.api import fakes
|
||||||
|
from cinder import utils
|
||||||
|
|
||||||
|
|
||||||
def return_volume_type_encryption(context, volume_type_id):
|
def return_volume_type_encryption(context, volume_type_id):
|
||||||
@ -199,7 +199,7 @@ class VolumeTypeEncryptionTest(test.TestCase):
|
|||||||
db.volume_type_destroy(context.get_admin_context(), volume_type['id'])
|
db.volume_type_destroy(context.get_admin_context(), volume_type['id'])
|
||||||
|
|
||||||
def test_create_json(self):
|
def test_create_json(self):
|
||||||
with mock.patch.object(wsgi.Controller,
|
with mock.patch.object(utils,
|
||||||
'validate_integer') as mock_validate_integer:
|
'validate_integer') as mock_validate_integer:
|
||||||
mock_validate_integer.return_value = 128
|
mock_validate_integer.return_value = 128
|
||||||
self._create('fake_cipher', 'front-end', 128, 'fake_encryptor')
|
self._create('fake_cipher', 'front-end', 128, 'fake_encryptor')
|
||||||
@ -500,7 +500,7 @@ class VolumeTypeEncryptionTest(test.TestCase):
|
|||||||
self.assertEqual(expected, jsonutils.loads(res.body))
|
self.assertEqual(expected, jsonutils.loads(res.body))
|
||||||
db.volume_type_destroy(context.get_admin_context(), volume_type['id'])
|
db.volume_type_destroy(context.get_admin_context(), volume_type['id'])
|
||||||
|
|
||||||
@mock.patch('cinder.api.openstack.wsgi.Controller.validate_integer')
|
@mock.patch('cinder.utils.validate_integer')
|
||||||
def test_update_item(self, mock_validate_integer):
|
def test_update_item(self, mock_validate_integer):
|
||||||
mock_validate_integer.return_value = 512
|
mock_validate_integer.return_value = 512
|
||||||
volume_type = self._default_volume_type
|
volume_type = self._default_volume_type
|
||||||
|
@ -1060,21 +1060,3 @@ class ValidBodyTest(test.TestCase):
|
|||||||
body = {'name': 'a' * 255 + " "}
|
body = {'name': 'a' * 255 + " "}
|
||||||
self.controller.validate_name_and_description(body)
|
self.controller.validate_name_and_description(body)
|
||||||
self.assertEqual('a' * 255, body['name'])
|
self.assertEqual('a' * 255, body['name'])
|
||||||
|
|
||||||
def test_validate_integer_greater_than_max_int_limit(self):
|
|
||||||
value = (2 ** 31) + 1
|
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
|
||||||
self.controller.validate_integer,
|
|
||||||
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
|
||||||
|
|
||||||
def test_validate_integer_less_than_min_int_limit(self):
|
|
||||||
value = -12
|
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
|
||||||
self.controller.validate_integer,
|
|
||||||
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
|
||||||
|
|
||||||
def test_validate_integer_invalid_limit(self):
|
|
||||||
value = "should_be_int"
|
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
|
||||||
self.controller.validate_integer,
|
|
||||||
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
|
||||||
|
@ -80,6 +80,12 @@ class LimiterTest(test.TestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
webob.exc.HTTPBadRequest, common.limited, self.tiny, req)
|
webob.exc.HTTPBadRequest, common.limited, self.tiny, req)
|
||||||
|
|
||||||
|
def test_limiter_offset_out_of_range(self):
|
||||||
|
"""Test offset key works with a offset out of range."""
|
||||||
|
req = webob.Request.blank('/?offset=123456789012346456')
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPBadRequest, common.limited, self.tiny, req)
|
||||||
|
|
||||||
def test_limiter_offset_bad(self):
|
def test_limiter_offset_bad(self):
|
||||||
"""Test offset key works with a BAD offset."""
|
"""Test offset key works with a BAD offset."""
|
||||||
req = webob.Request.blank(u'/?offset=\u0020aa')
|
req = webob.Request.blank(u'/?offset=\u0020aa')
|
||||||
@ -135,6 +141,9 @@ class LimiterTest(test.TestCase):
|
|||||||
self.assertEqual(items[3:1003], common.limited(items, req))
|
self.assertEqual(items[3:1003], common.limited(items, req))
|
||||||
req = webob.Request.blank('/?offset=3000&limit=10')
|
req = webob.Request.blank('/?offset=3000&limit=10')
|
||||||
self.assertEqual([], common.limited(items, req))
|
self.assertEqual([], common.limited(items, req))
|
||||||
|
req = webob.Request.blank('/?offset=30034522235674530&limit=10')
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPBadRequest, common.limited, items, req)
|
||||||
|
|
||||||
def test_limiter_custom_max_limit(self):
|
def test_limiter_custom_max_limit(self):
|
||||||
"""Test a max_limit other than 1000."""
|
"""Test a max_limit other than 1000."""
|
||||||
|
@ -401,6 +401,13 @@ class SnapshotApiTest(test.TestCase):
|
|||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
|
# Test that we get an exception HTTPBadRequest(400) with an offset
|
||||||
|
# greater than the maximum offset value.
|
||||||
|
url = '/v2/snapshots?limit=1&offset=323245324356534235'
|
||||||
|
req = fakes.HTTPRequest.blank(url)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.index, req)
|
||||||
|
|
||||||
def _assert_list_next(self, expected_query=None, project='fakeproject',
|
def _assert_list_next(self, expected_query=None, project='fakeproject',
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""Check a page of snapshots list."""
|
"""Check a page of snapshots list."""
|
||||||
|
@ -153,6 +153,12 @@ class VolumeTypesApiTest(test.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(2, len(res['volume_types']))
|
self.assertEqual(2, len(res['volume_types']))
|
||||||
|
|
||||||
|
def test_volume_types_index_with_offset_out_of_range(self):
|
||||||
|
url = '/v2/fake/types?offset=424366766556787'
|
||||||
|
req = fakes.HTTPRequest.blank(url)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.index, req)
|
||||||
|
|
||||||
def test_volume_types_index_with_limit_and_offset(self):
|
def test_volume_types_index_with_limit_and_offset(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/fake/types?limit=2&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/fake/types?limit=2&offset=1')
|
||||||
req.environ['cinder.context'] = self.ctxt
|
req.environ['cinder.context'] = self.ctxt
|
||||||
|
@ -914,6 +914,14 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
|
# Test that we get an exception HTTPBadRequest(400) with an offset
|
||||||
|
# greater than the maximum offset value.
|
||||||
|
url = '/v2/volumes?limit=2&offset=43543564546567575'
|
||||||
|
req = fakes.HTTPRequest.blank(url)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.index,
|
||||||
|
req)
|
||||||
|
|
||||||
def test_volume_detail_with_marker(self):
|
def test_volume_detail_with_marker(self):
|
||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
@ -1005,6 +1013,12 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller.detail,
|
self.controller.detail,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
|
url = '/v2/volumes/detail?limit=2&offset=4536546546546467'
|
||||||
|
req = fakes.HTTPRequest.blank(url)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.detail,
|
||||||
|
req)
|
||||||
|
|
||||||
def test_volume_with_limit_zero(self):
|
def test_volume_with_limit_zero(self):
|
||||||
def stub_volume_get_all(context, marker, limit, **kwargs):
|
def stub_volume_get_all(context, marker, limit, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
@ -24,6 +24,7 @@ from oslo_config import cfg
|
|||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import six
|
import six
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
import cinder
|
import cinder
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
@ -1401,3 +1402,24 @@ class TestComparableMixin(test.TestCase):
|
|||||||
def test_compare(self):
|
def test_compare(self):
|
||||||
self.assertEqual(NotImplemented,
|
self.assertEqual(NotImplemented,
|
||||||
self.one._compare(1, self.one._cmpkey))
|
self.one._compare(1, self.one._cmpkey))
|
||||||
|
|
||||||
|
|
||||||
|
class TestValidateInteger(test.TestCase):
|
||||||
|
|
||||||
|
def test_validate_integer_greater_than_max_int_limit(self):
|
||||||
|
value = (2 ** 31) + 1
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
utils.validate_integer,
|
||||||
|
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
||||||
|
|
||||||
|
def test_validate_integer_less_than_min_int_limit(self):
|
||||||
|
value = -12
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
utils.validate_integer,
|
||||||
|
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
||||||
|
|
||||||
|
def test_validate_integer_invalid_limit(self):
|
||||||
|
value = "should_be_int"
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
utils.validate_integer,
|
||||||
|
value, 'limit', min_value=-1, max_value=(2 ** 31))
|
||||||
|
@ -53,6 +53,7 @@ from oslo_utils import strutils
|
|||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import retrying
|
import retrying
|
||||||
import six
|
import six
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _, _LE, _LW
|
from cinder.i18n import _, _LE, _LW
|
||||||
@ -1057,3 +1058,30 @@ def calculate_virtual_free_capacity(total_capacity,
|
|||||||
# account the reserved space.
|
# account the reserved space.
|
||||||
free = free_capacity - math.floor(total * reserved)
|
free = free_capacity - math.floor(total * reserved)
|
||||||
return free
|
return free
|
||||||
|
|
||||||
|
|
||||||
|
def validate_integer(value, name, min_value=None, max_value=None):
|
||||||
|
"""Make sure that value is a valid integer, potentially within range.
|
||||||
|
|
||||||
|
:param value: the value of the integer
|
||||||
|
:param name: the name of the integer
|
||||||
|
:param min_length: the min_length of the integer
|
||||||
|
:param max_length: the max_length of the integer
|
||||||
|
:returns: integer
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = int(value)
|
||||||
|
except (TypeError, ValueError, UnicodeEncodeError):
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=(
|
||||||
|
_('%s must be an integer.') % name))
|
||||||
|
|
||||||
|
if min_value is not None and value < min_value:
|
||||||
|
raise webob.exc.HTTPBadRequest(
|
||||||
|
explanation=(_('%(value_name)s must be >= %(min_value)d') %
|
||||||
|
{'value_name': name, 'min_value': min_value}))
|
||||||
|
if max_value is not None and value > max_value:
|
||||||
|
raise webob.exc.HTTPBadRequest(
|
||||||
|
explanation=(_('%(value_name)s must be <= %(max_value)d') %
|
||||||
|
{'value_name': name, 'max_value': max_value}))
|
||||||
|
|
||||||
|
return value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user