compute: improve exceptions related to disk size checks

Having read several user reports of FlavorDiskTooSmall, it is apparent
that this error is not meaningful to users. We create 3 new subclasses
of FlavorDiskTooSmall: FlavorDiskSmallerThanImage,
FlavorDiskSmallerThanMinDisk, and VolumeSmallerThanMinDisk, because
these exceptions require different actions by the user. The new
subclasses get a more detailed error message to help the user.

Change-Id: I2234c4f4f9b5ac0780b3ec2e6a168380c07ed8c1
This commit is contained in:
Matthew Booth 2015-07-24 14:34:43 +01:00
parent 9e27fc1f47
commit 30e73e001d
10 changed files with 56 additions and 28 deletions

View File

@ -705,10 +705,8 @@ class API(base.Base):
dest_size *= units.Gi
if image_min_disk > dest_size:
# TODO(mdbooth) Raise a more descriptive exception here.
# This is the exception which calling code expects, but
# it's potentially misleading to the user.
raise exception.FlavorDiskTooSmall()
raise exception.VolumeSmallerThanMinDisk(
volume_size=dest_size, image_min_disk=image_min_disk)
# Target disk is a local disk whose size is taken from the flavor
else:
@ -719,10 +717,12 @@ class API(base.Base):
# drivers. A value of 0 means don't check size.
if dest_size != 0:
if image_size > dest_size:
raise exception.FlavorDiskTooSmall()
raise exception.FlavorDiskSmallerThanImage(
flavor_size=dest_size, image_size=image_size)
if image_min_disk > dest_size:
raise exception.FlavorDiskTooSmall()
raise exception.FlavorDiskSmallerThanMinDisk(
flavor_size=dest_size, image_min_disk=image_min_disk)
def _get_image_defined_bdms(self, base_options, instance_type, image_meta,
root_device_name):

View File

@ -1255,7 +1255,24 @@ class FlavorMemoryTooSmall(NovaException):
class FlavorDiskTooSmall(NovaException):
msg_fmt = _("Flavor's disk is too small for requested image.")
msg_fmt = _("The created instance's disk would be too small.")
class FlavorDiskSmallerThanImage(FlavorDiskTooSmall):
msg_fmt = _("Flavor's disk is too small for requested image. Flavor disk "
"is %(flavor_size)i bytes, image is %(image_size)i bytes.")
class FlavorDiskSmallerThanMinDisk(FlavorDiskTooSmall):
msg_fmt = _("Flavor's disk is smaller than the minimum size specified in "
"image metadata. Flavor disk is %(flavor_size)i bytes, "
"minimum size is %(image_min_disk)i bytes.")
class VolumeSmallerThanMinDisk(FlavorDiskTooSmall):
msg_fmt = _("Volume is smaller than the minimum size specified in image "
"metadata. Volume size is %(volume_size)i bytes, minimum "
"size is %(image_min_disk)i bytes.")
class InsufficientFreeMemory(NovaException):

View File

@ -7412,7 +7412,7 @@ class ComputeAPITestCase(BaseTestCase):
self.fake_image['min_disk'] = 2
self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanMinDisk,
self.compute_api.create, self.context,
inst_type, self.fake_image['id'])
@ -7431,7 +7431,7 @@ class ComputeAPITestCase(BaseTestCase):
self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
self.compute_api.create, self.context,
inst_type, self.fake_image['id'])
@ -7873,7 +7873,7 @@ class ComputeAPITestCase(BaseTestCase):
self.fake_image['min_disk'] = 2
self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanMinDisk,
self.compute_api.rebuild, self.context,
instance, self.fake_image['id'], 'new_password')
@ -7942,7 +7942,7 @@ class ComputeAPITestCase(BaseTestCase):
self.fake_image['size'] = '1073741825'
self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
self.compute_api.rebuild, self.context,
instance, self.fake_image['id'], 'new_password')
@ -11580,7 +11580,7 @@ class CheckRequestedImageTestCase(test.TestCase):
def test_image_min_disk_check(self):
image = dict(id='123', status='active', min_disk='2')
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanMinDisk,
self.compute_api._check_requested_image, self.context,
image['id'], image, self.instance_type, None)
@ -11591,7 +11591,7 @@ class CheckRequestedImageTestCase(test.TestCase):
def test_image_too_large(self):
image = dict(id='123', status='active', size='1073741825')
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
self.compute_api._check_requested_image, self.context,
image['id'], image, self.instance_type, None)
@ -11659,7 +11659,7 @@ class CheckRequestedImageTestCase(test.TestCase):
image_id=image_uuid, volume_id=volume_uuid,
volume_size=self.instance_type.root_gb)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.VolumeSmallerThanMinDisk,
self.compute_api._check_requested_image,
self.context, image_uuid, image, self.instance_type,
root_bdm)
@ -11703,7 +11703,7 @@ class CheckRequestedImageTestCase(test.TestCase):
root_bdm = block_device_obj.BlockDeviceMapping(
source_type='image', destination_type='local', image_id=image_uuid)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
self.compute_api._check_requested_image,
self.context, image['id'],
image, self.instance_type, root_bdm)
@ -11718,7 +11718,7 @@ class CheckRequestedImageTestCase(test.TestCase):
root_bdm = block_device_obj.BlockDeviceMapping(
source_type='image', destination_type='local', image_id=image_uuid)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanMinDisk,
self.compute_api._check_requested_image,
self.context, image['id'],
image, self.instance_type, root_bdm)

View File

@ -3223,9 +3223,15 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
self._test_build_and_run_spawn_exceptions(
exception.NoMoreFixedIps("error messge"))
def test_build_and_run_flavor_disk_too_small_exception(self):
def test_build_and_run_flavor_disk_smaller_image_exception(self):
self._test_build_and_run_spawn_exceptions(
exception.FlavorDiskTooSmall())
exception.FlavorDiskSmallerThanImage(
flavor_size=0, image_size=1))
def test_build_and_run_flavor_disk_smaller_min_disk(self):
self._test_build_and_run_spawn_exceptions(
exception.FlavorDiskSmallerThanMinDisk(
flavor_size=0, image_min_disk=1))
def test_build_and_run_flavor_memory_too_small_exception(self):
self._test_build_and_run_spawn_exceptions(

View File

@ -489,7 +489,7 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
self.mox.ReplayAll()
image = self.image_class(self.INSTANCE, self.NAME)
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
image.create_image, fn, self.TEMPLATE_PATH, 1)
self.mox.VerifyAll()
@ -1362,7 +1362,7 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
driver_mock.exists.return_value = True
driver_mock.size.return_value = 2
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
image.create_image, mock.MagicMock(),
self.TEMPLATE_PATH, 1)
driver_mock.size.assert_called_once_with(image.rbd_name)

View File

@ -651,7 +651,7 @@ disk size: 4.4M
target = 'big.qcow2'
self.executes = []
expected_commands = [('rm', '-f', 'big.qcow2.part')]
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
images.fetch_to_raw,
context, image_id, target, user_id, project_id,
max_size=1)

View File

@ -387,7 +387,8 @@ class FetchVhdImageTestCase(VMUtilsTestBase):
self.mox.StubOutWithMock(vm_utils, '_check_vdi_size')
vm_utils._check_vdi_size(self.context, self.session, self.instance,
"vdi").AndRaise(exception.FlavorDiskTooSmall)
"vdi").AndRaise(exception.FlavorDiskSmallerThanImage(
flavor_size=0, image_size=1))
self.mox.StubOutWithMock(self.session, 'call_xenapi')
self.session.call_xenapi("VDI.get_by_uuid", "vdi").AndReturn("ref")
@ -398,7 +399,7 @@ class FetchVhdImageTestCase(VMUtilsTestBase):
self.mox.ReplayAll()
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
vm_utils._fetch_vhd_image, self.context, self.session,
self.instance, 'image_id')
@ -628,7 +629,7 @@ class CheckVDISizeTestCase(VMUtilsTestBase):
with mock.patch.object(self.instance, 'get_flavor') as get:
self.flavor.root_gb = 1
get.return_value = self.flavor
self.assertRaises(exception.FlavorDiskTooSmall,
self.assertRaises(exception.FlavorDiskSmallerThanImage,
vm_utils._check_vdi_size, self.context,
self.session, self.instance, self.vdi_uuid)

View File

@ -116,7 +116,8 @@ def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0):
{'base': path,
'disk_size': disk_size,
'size': max_size})
raise exception.FlavorDiskTooSmall()
raise exception.FlavorDiskSmallerThanImage(
flavor_size=max_size, image_size=disk_size)
if fmt != "raw" and CONF.force_raw_images:
staged = "%s.converted" % path

View File

@ -286,7 +286,8 @@ class Image(object):
LOG.error(msg % {'base': base,
'base_size': base_size,
'size': size})
raise exception.FlavorDiskTooSmall()
raise exception.FlavorDiskSmallerThanImage(
flavor_size=base_size, image_size=size)
def get_disk_size(self, name):
return disk.get_disk_size(name)

View File

@ -139,7 +139,7 @@ PROGRESS_INTERVAL_SECONDS = 300
# Fudge factor to allow for the VHD chain to be slightly larger than
# the partitioned space. Otherwise, legitimate images near their
# maximum allowed size can fail on build with FlavorDiskTooSmall.
# maximum allowed size can fail on build with FlavorDiskSmallerThanImage.
VHD_SIZE_CHECK_FUDGE_FACTOR_GB = 10
@ -1497,7 +1497,9 @@ def _check_vdi_size(context, session, instance, vdi_uuid):
{'size': size, 'allowed_size': allowed_size},
instance=instance)
raise exception.FlavorDiskTooSmall()
raise exception.FlavorDiskSmallerThanImage(
flavor_size=(flavor.root_gb * units.Gi),
image_size=(size * units.Gi))
def _fetch_disk_image(context, session, instance, name_label, image_id,