Fix DbError when image params are out of range
Image params such as checksum, min_ram and min_disk are validated. Checksum is expected to be not longer than 32 characters, min_ram/min_disk can't exceed pow(2, 31) - 1. A method for mysql_int validation has been moved to glance.common.utils. Change-Id: I78554ef1ba65fc11da3de56467d6d70bb7991787 Closes-Bug: #1454730 Closes-Bug: #1460060
This commit is contained in:
parent
4efb56aae9
commit
1badf69b01
|
@ -65,9 +65,12 @@ CONF.import_opt('container_formats', 'glance.common.config',
|
|||
CONF.import_opt('image_property_quota', 'glance.common.config')
|
||||
|
||||
|
||||
def validate_image_meta(req, values):
|
||||
def _validate_format(req, values):
|
||||
"""Validates disk_format and container_format fields
|
||||
|
||||
name = values.get('name')
|
||||
Introduced to split too complex validate_image_meta method.
|
||||
"""
|
||||
amazon_formats = ('aki', 'ari', 'ami')
|
||||
disk_format = values.get('disk_format')
|
||||
container_format = values.get('container_format')
|
||||
|
||||
|
@ -82,13 +85,7 @@ def validate_image_meta(req, values):
|
|||
"for image.") % container_format
|
||||
raise HTTPBadRequest(explanation=msg, request=req)
|
||||
|
||||
if name and len(name) > 255:
|
||||
msg = _('Image name too long: %d') % len(name)
|
||||
raise HTTPBadRequest(explanation=msg, request=req)
|
||||
|
||||
amazon_formats = ('aki', 'ari', 'ami')
|
||||
|
||||
if disk_format in amazon_formats or container_format in amazon_formats:
|
||||
if any(f in amazon_formats for f in [disk_format, container_format]):
|
||||
if disk_format is None:
|
||||
values['disk_format'] = container_format
|
||||
elif container_format is None:
|
||||
|
@ -100,6 +97,25 @@ def validate_image_meta(req, values):
|
|||
"and disk formats must match."))
|
||||
raise HTTPBadRequest(explanation=msg, request=req)
|
||||
|
||||
|
||||
def validate_image_meta(req, values):
|
||||
_validate_format(req, values)
|
||||
|
||||
name = values.get('name')
|
||||
checksum = values.get('checksum')
|
||||
|
||||
if name and len(name) > 255:
|
||||
msg = _('Image name too long: %d') % len(name)
|
||||
raise HTTPBadRequest(explanation=msg, request=req)
|
||||
|
||||
# check that checksum retrieved is exactly 32 characters
|
||||
# as long as we expect md5 checksum
|
||||
# https://bugs.launchpad.net/glance/+bug/1454730
|
||||
if checksum and len(checksum) > 32:
|
||||
msg = (_("Invalid checksum '%s': can't exceed 32 characters") %
|
||||
checksum)
|
||||
raise HTTPBadRequest(explanation=msg, request=req)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
|
|
|
@ -715,6 +715,31 @@ def no_4byte_params(f):
|
|||
return wrapper
|
||||
|
||||
|
||||
def validate_mysql_int(*args, **kwargs):
|
||||
"""
|
||||
Make sure that all arguments are less than 2 ** 31 - 1.
|
||||
|
||||
This limitation is introduced because mysql stores INT in 4 bytes.
|
||||
If the validation fails for some argument, exception.Invalid is raised with
|
||||
appropriate information.
|
||||
"""
|
||||
max_int = (2 ** 31) - 1
|
||||
for param in args:
|
||||
if param > max_int:
|
||||
msg = _("Value %(value)d out of range, "
|
||||
"must not exceed %(max)d") % {"value": param,
|
||||
"max": max_int}
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
for param_str in kwargs:
|
||||
param = kwargs.get(param_str)
|
||||
if param and param > max_int:
|
||||
msg = _("'%(param)s' value out of range, "
|
||||
"must not exceed %(max)d") % {"param": param_str,
|
||||
"max": max_int}
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
|
||||
def stash_conf_values():
|
||||
"""
|
||||
Make a copy of some of the current global CONF's settings.
|
||||
|
|
|
@ -675,6 +675,10 @@ def _validate_image(values):
|
|||
msg = "Invalid image status '%s' for image." % status
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
# validate integer values to eliminate DBError on save
|
||||
utils.validate_mysql_int(min_disk=values.get('min_disk'),
|
||||
min_ram=values.get('min_ram'))
|
||||
|
||||
return values
|
||||
|
||||
|
||||
|
|
|
@ -222,6 +222,25 @@ class DriverTests(object):
|
|||
self.assertRaises(exception.Invalid, self.db_api.image_create,
|
||||
self.context, fixture)
|
||||
|
||||
def test_image_create_bad_checksum(self):
|
||||
# checksum should be no longer than 32 characters
|
||||
bad_checksum = "42" * 42
|
||||
fixture = {'checksum': bad_checksum}
|
||||
self.assertRaises(exception.Invalid, self.db_api.image_create,
|
||||
self.context, fixture)
|
||||
# if checksum is not longer than 32 characters but non-ascii ->
|
||||
# still raise 400
|
||||
fixture = {'checksum': u'\u042f' * 32}
|
||||
self.assertRaises(exception.Invalid, self.db_api.image_create,
|
||||
self.context, fixture)
|
||||
|
||||
def test_image_create_bad_int_params(self):
|
||||
int_too_long = 2 ** 31 + 42
|
||||
for param in ['min_disk', 'min_ram']:
|
||||
fixture = {param: int_too_long}
|
||||
self.assertRaises(exception.Invalid, self.db_api.image_create,
|
||||
self.context, fixture)
|
||||
|
||||
def test_image_create_bad_property(self):
|
||||
# bad value
|
||||
fixture = {'status': 'queued',
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
import hashlib
|
||||
|
||||
import httplib2
|
||||
import sys
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import units
|
||||
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
|
||||
|
@ -35,6 +37,50 @@ class TestApi(functional.FunctionalTest):
|
|||
|
||||
"""Functional tests using httplib2 against the API server"""
|
||||
|
||||
def _check_image_create(self, headers, status=201,
|
||||
image_data="*" * FIVE_KB):
|
||||
# performs image_create request, checks the response and returns
|
||||
# content
|
||||
http = httplib2.Http()
|
||||
path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
|
||||
response, content = http.request(
|
||||
path, 'POST', headers=headers, body=image_data)
|
||||
self.assertEqual(status, response.status)
|
||||
return content
|
||||
|
||||
def test_checksum_32_chars_at_image_create(self):
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
headers = minimal_headers('Image1')
|
||||
image_data = "*" * FIVE_KB
|
||||
|
||||
# checksum can be no longer that 32 characters (String(32))
|
||||
headers['X-Image-Meta-Checksum'] = 'x' * 42
|
||||
content = self._check_image_create(headers, 400)
|
||||
self.assertIn("Invalid checksum", content)
|
||||
# test positive case as well
|
||||
headers['X-Image-Meta-Checksum'] = hashlib.md5(image_data).hexdigest()
|
||||
self._check_image_create(headers)
|
||||
|
||||
def test_param_int_too_large_at_create(self):
|
||||
# currently 2 params min_disk/min_ram can cause DBError on save
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
# Integer field can't be greater than max 8-byte signed integer
|
||||
for param in ['min_disk', 'min_ram']:
|
||||
headers = minimal_headers('Image1')
|
||||
# check that long numbers result in 400
|
||||
headers['X-Image-Meta-%s' % param] = str(sys.maxint + 1)
|
||||
content = self._check_image_create(headers, 400)
|
||||
self.assertIn("'%s' value out of range" % param, content)
|
||||
# check that integers over 4 byte result in 400
|
||||
headers['X-Image-Meta-%s' % param] = str(2 ** 31)
|
||||
content = self._check_image_create(headers, 400)
|
||||
self.assertIn("'%s' value out of range" % param, content)
|
||||
# verify positive case as well
|
||||
headers['X-Image-Meta-%s' % param] = str((2 ** 31) - 1)
|
||||
self._check_image_create(headers)
|
||||
|
||||
@skip_if_disabled
|
||||
def test_get_head_simple_post(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue