Fixes HTTP 500 when updating image with locations for V2

Based on current implement, user will run into 500 error during
updating locations for a queued image. The root cause is we're
trying to calculate the overall size with current image size and
the number of locations. This fix will get the correct image size
from backend store if current image size is not available.

Fixes bug 1257507

Change-Id: Ia4ac73e36211d475752ee9f4995fc3768fb2981a
This commit is contained in:
Fei Long Wang 2013-12-09 16:40:48 +08:00
parent 06848e59c0
commit ad755f9843
2 changed files with 95 additions and 8 deletions

View File

@ -45,6 +45,20 @@ def _enforce_image_tag_quota(tags):
maximum=CONF.image_tag_quota)
def _calc_required_size(context, image, locations):
required_size = None
if image.size:
required_size = image.size * len(locations)
else:
for location in locations:
size_from_backend = glance.store.get_size_from_backend(
context, location['url'])
if size_from_backend:
required_size = size_from_backend * len(locations)
break
return required_size
class ImageRepoProxy(glance.domain.proxy.Repo):
def __init__(self, image_repo, context, db_api):
@ -177,7 +191,7 @@ class QuotaImageLocationsProxy(object):
def __iadd__(self, other):
if not hasattr(other, '__iter__'):
raise TypeError()
self._check_quota(len(list(other)))
self._check_quota(other)
return self.locations.__iadd__(other)
def __iter__(self, *args, **kwargs):
@ -204,20 +218,24 @@ class QuotaImageLocationsProxy(object):
def reverse(self, *args, **kwargs):
return self.locations.reverse(*args, **kwargs)
def _check_quota(self, count):
glance.api.common.check_quota(
self.context, self.image.size * count, self.db_api)
def _check_quota(self, locations):
required_size = _calc_required_size(self.context,
self.image,
locations)
glance.api.common.check_quota(self.context,
required_size,
self.db_api)
def append(self, object):
self._check_quota(1)
self._check_quota([object])
return self.locations.append(object)
def insert(self, index, object):
self._check_quota(1)
self._check_quota([object])
return self.locations.insert(index, object)
def extend(self, iter):
self._check_quota(len(list(iter)))
self._check_quota(iter)
return self.locations.extend(iter)
@ -277,7 +295,12 @@ class ImageProxy(glance.domain.proxy.Image):
def locations(self, value):
if not isinstance(value, (list, QuotaImageLocationsProxy)):
raise exception.Invalid(_('Invalid locations: %s') % value)
required_size = _calc_required_size(self.context,
self.image,
value)
glance.api.common.check_quota(
self.context, self.image.size * len(value), self.db_api,
self.context, required_size, self.db_api,
image_id=self.image.image_id)
self.image.locations = value

View File

@ -15,6 +15,7 @@
import mock
import mox
import uuid
from glance.common import exception
import glance.quota
@ -75,6 +76,10 @@ class FakeImage(object):
self.size = self. size + len(d)
def fake_get_size_from_backend(context, uri):
return 1
class TestImageQuota(test_utils.BaseTestCase):
def setUp(self):
super(TestImageQuota, self).setUp()
@ -236,6 +241,65 @@ class TestImageQuota(test_utils.BaseTestCase):
except exception.StorageQuotaFull:
pass
def test_append_location_for_queued_image(self):
context = FakeContext()
db_api = unit_test_utils.FakeDB()
base_image = FakeImage()
base_image.image_id = str(uuid.uuid4())
image = glance.quota.ImageProxy(base_image, context, db_api)
self.assertIsNone(image.size)
self.stubs.Set(glance.store, 'get_size_from_backend',
fake_get_size_from_backend)
image.locations.append({'url': 'file:///fake.img.tar.gz',
'metadata': {}})
self.assertIn({'url': 'file:///fake.img.tar.gz', 'metadata': {}},
image.locations)
def test_insert_location_for_queued_image(self):
context = FakeContext()
db_api = unit_test_utils.FakeDB()
base_image = FakeImage()
base_image.image_id = str(uuid.uuid4())
image = glance.quota.ImageProxy(base_image, context, db_api)
self.assertIsNone(image.size)
self.stubs.Set(glance.store, 'get_size_from_backend',
fake_get_size_from_backend)
image.locations.insert(0,
{'url': 'file:///fake.img.tar.gz',
'metadata': {}})
self.assertIn({'url': 'file:///fake.img.tar.gz', 'metadata': {}},
image.locations)
def test_set_location_for_queued_image(self):
context = FakeContext()
db_api = unit_test_utils.FakeDB()
base_image = FakeImage()
base_image.image_id = str(uuid.uuid4())
image = glance.quota.ImageProxy(base_image, context, db_api)
self.assertIsNone(image.size)
self.stubs.Set(glance.store, 'get_size_from_backend',
fake_get_size_from_backend)
image.locations = [{'url': 'file:///fake.img.tar.gz', 'metadata': {}}]
self.assertEqual([{'url': 'file:///fake.img.tar.gz', 'metadata': {}}],
image.locations)
def test_iadd_location_for_queued_image(self):
context = FakeContext()
db_api = unit_test_utils.FakeDB()
base_image = FakeImage()
base_image.image_id = str(uuid.uuid4())
image = glance.quota.ImageProxy(base_image, context, db_api)
self.assertIsNone(image.size)
self.stubs.Set(glance.store, 'get_size_from_backend',
fake_get_size_from_backend)
image.locations += [{'url': 'file:///fake.img.tar.gz', 'metadata': {}}]
self.assertIn({'url': 'file:///fake.img.tar.gz', 'metadata': {}},
image.locations)
class TestImagePropertyQuotas(test_utils.BaseTestCase):
def setUp(self):