Return 400 HTTP error for invalid flavor attributes
Currently create flavor API raises 500 error if you pass large value to ram, disk, vcpu, swap, ephemeral, rxtx_factor flavor properties. All integral flavor properties are validated against db.MAX_INT(2147483647) for maximum limit and raised exception.InvalidInput for invalid input. Added validation of maximum limit for flavor properties in schema. Kept the validation of flavor properties as it is in nova.compute.flavors as it is used by legacy flavor create. Moved the SQL_SP_FLOAT_MAX constant to nova.db.api so that it can be used in schema as well as in test files. APIImpact: Return 400 status code for invalid flavor properties. Closes-Bug: #1577727 Change-Id: I4e50534d67ee90c585b6679644e06ee3569c8c97
This commit is contained in:
parent
14d6a424ff
commit
9e00323621
@ -15,6 +15,7 @@
|
||||
import copy
|
||||
|
||||
from nova.api.validation import parameter_types
|
||||
from nova import db
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
@ -30,17 +31,18 @@ create = {
|
||||
'minLength': 1, 'maxLength': 255,
|
||||
'pattern': '^(?! )[a-zA-Z0-9. _-]+(?<! )$'
|
||||
},
|
||||
'ram': parameter_types.positive_integer,
|
||||
'vcpus': parameter_types.positive_integer,
|
||||
'disk': parameter_types.non_negative_integer,
|
||||
'ram': parameter_types.flavor_param_positive,
|
||||
'vcpus': parameter_types.flavor_param_positive,
|
||||
'disk': parameter_types.flavor_param_non_negative,
|
||||
'OS-FLV-EXT-DATA:ephemeral':
|
||||
parameter_types.non_negative_integer,
|
||||
'swap': parameter_types.non_negative_integer,
|
||||
parameter_types.flavor_param_non_negative,
|
||||
'swap': parameter_types.flavor_param_non_negative,
|
||||
# positive ( > 0) float
|
||||
'rxtx_factor': {
|
||||
'type': ['number', 'string'],
|
||||
'pattern': '^[0-9]+(\.[0-9]+)?$',
|
||||
'minimum': 0, 'exclusiveMinimum': True
|
||||
'minimum': 0, 'exclusiveMinimum': True,
|
||||
'maximum': db.SQL_SP_FLOAT_MAX
|
||||
},
|
||||
'os-flavor-access:is_public': parameter_types.boolean,
|
||||
},
|
||||
|
@ -356,3 +356,10 @@ volume_size = {
|
||||
'minimum': 1,
|
||||
'maximum': db.MAX_INT
|
||||
}
|
||||
|
||||
|
||||
flavor_param_positive = copy.deepcopy(volume_size)
|
||||
|
||||
|
||||
flavor_param_non_negative = copy.deepcopy(volume_size)
|
||||
flavor_param_non_negative['minimum'] = 0
|
||||
|
@ -40,15 +40,6 @@ CONF = nova.conf.CONF
|
||||
# to ascii characters.
|
||||
VALID_ID_REGEX = re.compile("^[\w\.\- ]*$")
|
||||
|
||||
# NOTE(dosaboy): This is supposed to represent the maximum value that we can
|
||||
# place into a SQL single precision float so that we can check whether values
|
||||
# are oversize. Postgres and MySQL both define this as their max whereas Sqlite
|
||||
# uses dynamic typing so this would not apply. Different dbs react in different
|
||||
# ways to oversize values e.g. postgres will raise an exception while mysql
|
||||
# will round off the value. Nevertheless we may still want to know prior to
|
||||
# insert whether the value is oversize.
|
||||
SQL_SP_FLOAT_MAX = 3.40282e+38
|
||||
|
||||
# Validate extra specs key names.
|
||||
VALID_EXTRASPEC_NAME_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE)
|
||||
|
||||
@ -143,11 +134,11 @@ def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
|
||||
try:
|
||||
kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
|
||||
if (kwargs['rxtx_factor'] <= 0 or
|
||||
kwargs['rxtx_factor'] > SQL_SP_FLOAT_MAX):
|
||||
kwargs['rxtx_factor'] > db.SQL_SP_FLOAT_MAX):
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
msg = (_("'rxtx_factor' argument must be a float between 0 and %g") %
|
||||
SQL_SP_FLOAT_MAX)
|
||||
db.SQL_SP_FLOAT_MAX)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
kwargs['name'] = name
|
||||
|
@ -47,6 +47,15 @@ LOG = logging.getLogger(__name__)
|
||||
# The maximum value a signed INT type may have
|
||||
MAX_INT = 0x7FFFFFFF
|
||||
|
||||
# NOTE(dosaboy): This is supposed to represent the maximum value that we can
|
||||
# place into a SQL single precision float so that we can check whether values
|
||||
# are oversize. Postgres and MySQL both define this as their max whereas Sqlite
|
||||
# uses dynamic typing so this would not apply. Different dbs react in different
|
||||
# ways to oversize values e.g. postgres will raise an exception while mysql
|
||||
# will round off the value. Nevertheless we may still want to know prior to
|
||||
# insert whether the value is oversize or not.
|
||||
SQL_SP_FLOAT_MAX = 3.40282e+38
|
||||
|
||||
###################
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@ import webob
|
||||
from nova.api.openstack.compute import flavor_access as flavor_access_v21
|
||||
from nova.api.openstack.compute import flavor_manage as flavormanage_v21
|
||||
from nova.compute import flavors
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
@ -250,6 +251,10 @@ class FlavorManageTestV21(test.NoDBTestCase):
|
||||
self.request_body['flavor']['ram'] = 0
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_ram_exceed_max_limit(self):
|
||||
self.request_body['flavor']['ram'] = db.MAX_INT + 1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_without_vcpus(self):
|
||||
del self.request_body['flavor']['vcpus']
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
@ -258,6 +263,10 @@ class FlavorManageTestV21(test.NoDBTestCase):
|
||||
self.request_body['flavor']['vcpus'] = 0
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_vcpus_exceed_max_limit(self):
|
||||
self.request_body['flavor']['vcpus'] = db.MAX_INT + 1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_without_disk(self):
|
||||
del self.request_body['flavor']['disk']
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
@ -266,18 +275,35 @@ class FlavorManageTestV21(test.NoDBTestCase):
|
||||
self.request_body['flavor']['disk'] = -1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_disk_exceed_max_limit(self):
|
||||
self.request_body['flavor']['disk'] = db.MAX_INT + 1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_minus_ephemeral(self):
|
||||
self.request_body['flavor']['OS-FLV-EXT-DATA:ephemeral'] = -1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_ephemeral_exceed_max_limit(self):
|
||||
self.request_body['flavor'][
|
||||
'OS-FLV-EXT-DATA:ephemeral'] = db.MAX_INT + 1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_minus_swap(self):
|
||||
self.request_body['flavor']['swap'] = -1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_swap_exceed_max_limit(self):
|
||||
self.request_body['flavor']['swap'] = db.MAX_INT + 1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_minus_rxtx_factor(self):
|
||||
self.request_body['flavor']['rxtx_factor'] = -1
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_rxtx_factor_exceed_max_limit(self):
|
||||
self.request_body['flavor']['rxtx_factor'] = db.SQL_SP_FLOAT_MAX * 2
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
||||
def test_create_with_non_boolean_is_public(self):
|
||||
self.request_body['flavor']['os-flavor-access:is_public'] = 123
|
||||
self._create_flavor_bad_request_case(self.request_body)
|
||||
|
@ -353,14 +353,14 @@ class CreateInstanceTypeTest(test.TestCase):
|
||||
db.flavor_get_all(_context)
|
||||
# We do * 10 since this is an approximation and we need to make sure
|
||||
# the difference is noticeble.
|
||||
over_rxtx_factor = flavors.SQL_SP_FLOAT_MAX * 10
|
||||
over_rxtx_factor = db.SQL_SP_FLOAT_MAX * 10
|
||||
|
||||
self.assertInvalidInput('flavor1', 64, 1, 120,
|
||||
rxtx_factor=over_rxtx_factor)
|
||||
|
||||
flavor = flavors.create('flavor2', 64, 1, 120,
|
||||
rxtx_factor=flavors.SQL_SP_FLOAT_MAX)
|
||||
self.assertEqual(flavors.SQL_SP_FLOAT_MAX, flavor.rxtx_factor)
|
||||
rxtx_factor=db.SQL_SP_FLOAT_MAX)
|
||||
self.assertEqual(db.SQL_SP_FLOAT_MAX, flavor.rxtx_factor)
|
||||
|
||||
def test_is_public_must_be_valid_bool_string(self):
|
||||
self.assertInvalidInput('flavor1', 64, 1, 120, is_public='foo')
|
||||
|
Loading…
Reference in New Issue
Block a user