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:
parent
06848e59c0
commit
ad755f9843
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue