Image virtual size doesn't fit to volume size

Glance gives images' sizes in bytes. But virtual size of image can have
different value when image created in another format (e.g. qcow2) then
raw. Raise exception when trying create volume from an image bigger size
then volume size.

This commit uses fake value image_id from cinder/tests/unit/fake_constants.py
to clean up this test module consistent with similar cleanups in the codebase.

Closes-Bug: #1599147
Change-Id: Ia8f644ac59c4ee05742ec8ce5affa682e9a90a78
This commit is contained in:
Yuriy Nesenenko 2016-04-29 18:32:31 +03:00
parent eccc58f7af
commit e6fdc05e28
6 changed files with 178 additions and 55 deletions

View File

@ -409,6 +409,20 @@ def upload_volume(context, image_service, image_meta, volume_path,
image_service.update(context, image_id, {}, image_file) image_service.update(context, image_id, {}, image_file)
def check_virtual_size(virtual_size, volume_size, image_id):
virtual_size = int(math.ceil(float(virtual_size) / units.Gi))
if virtual_size > volume_size:
params = {'image_size': virtual_size,
'volume_size': volume_size}
reason = _("Image virtual size is %(image_size)dGB"
" and doesn't fit in a volume of size"
" %(volume_size)dGB.") % params
raise exception.ImageUnacceptable(image_id=image_id,
reason=reason)
return virtual_size
def is_xenserver_image(context, image_service, image_id): def is_xenserver_image(context, image_service, image_id):
image_meta = image_service.show(context, image_id) image_meta = image_service.show(context, image_id)
return is_xenserver_format(image_meta) return is_xenserver_format(image_meta)

View File

@ -24,6 +24,7 @@ from oslo_utils import units
from cinder import exception from cinder import exception
from cinder.image import image_utils from cinder.image import image_utils
from cinder import test from cinder import test
from cinder.tests.unit import fake_constants as fake
from cinder.volume import throttling from cinder.volume import throttling
@ -1390,3 +1391,24 @@ class TestTemporaryFileContextManager(test.TestCase):
self.assertEqual(mock.sentinel.temporary_file, tmp_file) self.assertEqual(mock.sentinel.temporary_file, tmp_file)
self.assertFalse(mock_delete.called) self.assertFalse(mock_delete.called)
mock_delete.assert_called_once_with(mock.sentinel.temporary_file) mock_delete.assert_called_once_with(mock.sentinel.temporary_file)
class TestImageUtils(test.TestCase):
def test_get_virtual_size(self):
image_id = fake.IMAGE_ID
virtual_size = 1073741824
volume_size = 2
virt_size = image_utils.check_virtual_size(virtual_size,
volume_size,
image_id)
self.assertEqual(1, virt_size)
def test_get_bigger_virtual_size(self):
image_id = fake.IMAGE_ID
virtual_size = 3221225472
volume_size = 2
self.assertRaises(exception.ImageUnacceptable,
image_utils.check_virtual_size,
virtual_size,
volume_size,
image_id)

View File

@ -21,6 +21,7 @@ import os
import tempfile import tempfile
import mock import mock
from oslo_utils import imageutils
from oslo_utils import units from oslo_utils import units
from cinder import context from cinder import context
@ -1223,14 +1224,19 @@ class ManagedRBDTestCase(test_volume.DriverTestCase):
@mock.patch.object(cinder.image.glance, 'get_default_image_service') @mock.patch.object(cinder.image.glance, 'get_default_image_service')
@mock.patch('cinder.image.image_utils.TemporaryImages.fetch') @mock.patch('cinder.image.image_utils.TemporaryImages.fetch')
def test_create_vol_from_non_raw_image_status_available(self, mock_fetch, @mock.patch('cinder.image.image_utils.qemu_img_info')
mock_gdis): def test_create_vol_from_non_raw_image_status_available(
self, mock_qemu_info, mock_fetch, mock_gdis):
"""Clone non-raw image then verify volume is in available state.""" """Clone non-raw image then verify volume is in available state."""
def _mock_clone_image(context, volume, image_location, def _mock_clone_image(context, volume, image_location,
image_meta, image_service): image_meta, image_service):
return {'provider_location': None}, False return {'provider_location': None}, False
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
mock_fetch.return_value = mock.MagicMock(spec=utils.get_file_spec()) mock_fetch.return_value = mock.MagicMock(spec=utils.get_file_spec())
with mock.patch.object(self.volume.driver, 'clone_image') as \ with mock.patch.object(self.volume.driver, 'clone_image') as \
mock_clone_image: mock_clone_image:

View File

@ -32,6 +32,7 @@ import os_brick
from oslo_concurrency import processutils from oslo_concurrency import processutils
from oslo_config import cfg from oslo_config import cfg
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import imageutils
from oslo_utils import importutils from oslo_utils import importutils
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import units from oslo_utils import units
@ -3342,7 +3343,8 @@ class VolumeTestCase(BaseVolumeTestCase):
snapshot_ref.destroy() snapshot_ref.destroy()
db.volume_destroy(self.context, volume['id']) db.volume_destroy(self.context, volume['id'])
def test_create_snapshot_from_bootable_volume(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_snapshot_from_bootable_volume(self, mock_qemu_info):
"""Test create snapshot from bootable volume.""" """Test create snapshot from bootable volume."""
# create bootable volume from image # create bootable volume from image
volume = self._create_volume_from_image() volume = self._create_volume_from_image()
@ -3350,6 +3352,10 @@ class VolumeTestCase(BaseVolumeTestCase):
self.assertEqual('available', volume['status']) self.assertEqual('available', volume['status'])
self.assertTrue(volume['bootable']) self.assertTrue(volume['bootable'])
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
# get volume's volume_glance_metadata # get volume's volume_glance_metadata
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
vol_glance_meta = db.volume_glance_metadata_get(ctxt, volume_id) vol_glance_meta = db.volume_glance_metadata_get(ctxt, volume_id)
@ -3378,7 +3384,8 @@ class VolumeTestCase(BaseVolumeTestCase):
snap.destroy() snap.destroy()
db.volume_destroy(ctxt, volume_id) db.volume_destroy(ctxt, volume_id)
def test_create_snapshot_from_bootable_volume_fail(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_snapshot_from_bootable_volume_fail(self, mock_qemu_info):
"""Test create snapshot from bootable volume. """Test create snapshot from bootable volume.
But it fails to volume_glance_metadata_copy_to_snapshot. But it fails to volume_glance_metadata_copy_to_snapshot.
@ -3390,6 +3397,10 @@ class VolumeTestCase(BaseVolumeTestCase):
self.assertEqual('available', volume['status']) self.assertEqual('available', volume['status'])
self.assertTrue(volume['bootable']) self.assertTrue(volume['bootable'])
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
# get volume's volume_glance_metadata # get volume's volume_glance_metadata
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
vol_glance_meta = db.volume_glance_metadata_get(ctxt, volume_id) vol_glance_meta = db.volume_glance_metadata_get(ctxt, volume_id)
@ -3543,23 +3554,35 @@ class VolumeTestCase(BaseVolumeTestCase):
gigabytes=vol.size) gigabytes=vol.size)
mock_rollback.assert_called_once_with(self.context, ["RESERVATION"]) mock_rollback.assert_called_once_with(self.context, ["RESERVATION"])
def test_create_volume_from_image_cloned_status_available(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_volume_from_image_cloned_status_available(
self, mock_qemu_info):
"""Test create volume from image via cloning. """Test create volume from image via cloning.
Verify that after cloning image to volume, it is in available Verify that after cloning image to volume, it is in available
state and is bootable. state and is bootable.
""" """
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
volume = self._create_volume_from_image() volume = self._create_volume_from_image()
self.assertEqual('available', volume['status']) self.assertEqual('available', volume['status'])
self.assertTrue(volume['bootable']) self.assertTrue(volume['bootable'])
self.volume.delete_volume(self.context, volume.id, volume=volume) self.volume.delete_volume(self.context, volume.id, volume=volume)
def test_create_volume_from_image_not_cloned_status_available(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_volume_from_image_not_cloned_status_available(
self, mock_qemu_info):
"""Test create volume from image via full copy. """Test create volume from image via full copy.
Verify that after copying image to volume, it is in available Verify that after copying image to volume, it is in available
state and is bootable. state and is bootable.
""" """
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
volume = self._create_volume_from_image(fakeout_clone_image=True) volume = self._create_volume_from_image(fakeout_clone_image=True)
self.assertEqual('available', volume['status']) self.assertEqual('available', volume['status'])
self.assertTrue(volume['bootable']) self.assertTrue(volume['bootable'])
@ -3599,12 +3622,18 @@ class VolumeTestCase(BaseVolumeTestCase):
volume.destroy() volume.destroy()
os.unlink(dst_path) os.unlink(dst_path)
def test_create_volume_from_image_copy_exception_rescheduling(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_volume_from_image_copy_exception_rescheduling(
self, mock_qemu_info):
"""Test create volume with ImageCopyFailure """Test create volume with ImageCopyFailure
This exception should not trigger rescheduling and allocated_capacity This exception should not trigger rescheduling and allocated_capacity
should be incremented so we're having assert for that here. should be incremented so we're having assert for that here.
""" """
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
def fake_copy_image_to_volume(context, volume, image_service, def fake_copy_image_to_volume(context, volume, image_service,
image_id): image_id):
raise exception.ImageCopyFailure() raise exception.ImageCopyFailure()
@ -3622,14 +3651,18 @@ class VolumeTestCase(BaseVolumeTestCase):
@mock.patch('cinder.utils.brick_get_connector') @mock.patch('cinder.utils.brick_get_connector')
@mock.patch('cinder.volume.driver.BaseVD.secure_file_operations_enabled') @mock.patch('cinder.volume.driver.BaseVD.secure_file_operations_enabled')
@mock.patch('cinder.volume.driver.BaseVD._detach_volume') @mock.patch('cinder.volume.driver.BaseVD._detach_volume')
def test_create_volume_from_image_unavailable(self, mock_detach, @mock.patch('cinder.image.image_utils.qemu_img_info')
mock_secure, *args): def test_create_volume_from_image_unavailable(
self, mock_qemu_info, mock_detach, mock_secure, *args):
"""Test create volume with ImageCopyFailure """Test create volume with ImageCopyFailure
We'll raise an exception inside _connect_device after volume has We'll raise an exception inside _connect_device after volume has
already been attached to confirm that it detaches the volume. already been attached to confirm that it detaches the volume.
""" """
mock_secure.side_effect = NameError mock_secure.side_effect = NameError
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
# We want to test BaseVD copy_image_to_volume and since FakeISCSIDriver # We want to test BaseVD copy_image_to_volume and since FakeISCSIDriver
# inherits from LVM it overwrites it, so we'll mock it to use the # inherits from LVM it overwrites it, so we'll mock it to use the
@ -3644,12 +3677,17 @@ class VolumeTestCase(BaseVolumeTestCase):
# We must have called detach method. # We must have called detach method.
self.assertEqual(1, mock_detach.call_count) self.assertEqual(1, mock_detach.call_count)
def test_create_volume_from_image_clone_image_volume(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_volume_from_image_clone_image_volume(self, mock_qemu_info):
"""Test create volume from image via image volume. """Test create volume from image via image volume.
Verify that after cloning image to volume, it is in available Verify that after cloning image to volume, it is in available
state and is bootable. state and is bootable.
""" """
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
volume = self._create_volume_from_image(clone_image_volume=True) volume = self._create_volume_from_image(clone_image_volume=True)
self.assertEqual('available', volume['status']) self.assertEqual('available', volume['status'])
self.assertTrue(volume['bootable']) self.assertTrue(volume['bootable'])
@ -4108,13 +4146,19 @@ class VolumeTestCase(BaseVolumeTestCase):
source_volume=volume_src, source_volume=volume_src,
availability_zone='nova') availability_zone='nova')
def test_create_volume_from_sourcevol_with_glance_metadata(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_volume_from_sourcevol_with_glance_metadata(
self, mock_qemu_info):
"""Test glance metadata can be correctly copied to new volume.""" """Test glance metadata can be correctly copied to new volume."""
def fake_create_cloned_volume(volume, src_vref): def fake_create_cloned_volume(volume, src_vref):
pass pass
self.stubs.Set(self.volume.driver, 'create_cloned_volume', self.stubs.Set(self.volume.driver, 'create_cloned_volume',
fake_create_cloned_volume) fake_create_cloned_volume)
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
volume_src = self._create_volume_from_image() volume_src = self._create_volume_from_image()
self.volume.create_volume(self.context, volume_src.id, self.volume.create_volume(self.context, volume_src.id,
volume=volume_src) volume=volume_src)
@ -4755,7 +4799,8 @@ class VolumeMigrationTestCase(BaseVolumeTestCase):
self.assertEqual('error', volume.migration_status) self.assertEqual('error', volume.migration_status)
self.assertEqual('available', volume.status) self.assertEqual('available', volume.status)
def test_migrate_volume_with_glance_metadata(self): @mock.patch('cinder.image.image_utils.qemu_img_info')
def test_migrate_volume_with_glance_metadata(self, mock_qemu_info):
volume = self._create_volume_from_image(clone_image_volume=True) volume = self._create_volume_from_image(clone_image_volume=True)
glance_metadata = volume.glance_metadata glance_metadata = volume.glance_metadata
@ -4765,6 +4810,10 @@ class VolumeMigrationTestCase(BaseVolumeTestCase):
serialized_volume = serializer.serialize_entity(self.context, volume) serialized_volume = serializer.serialize_entity(self.context, volume)
volume = serializer.deserialize_entity(self.context, serialized_volume) volume = serializer.deserialize_entity(self.context, serialized_volume)
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
host_obj = {'host': 'newhost', 'capabilities': {}} host_obj = {'host': 'newhost', 'capabilities': {}}
with mock.patch.object(self.volume.driver, with mock.patch.object(self.volume.driver,
'migrate_volume') as mock_migrate_volume: 'migrate_volume') as mock_migrate_volume:

View File

@ -23,6 +23,7 @@ from cinder import context
from cinder import exception from cinder import exception
from cinder import test from cinder import test
from cinder.tests.unit.consistencygroup import fake_consistencygroup from cinder.tests.unit.consistencygroup import fake_consistencygroup
from cinder.tests.unit import fake_constants as fakes
from cinder.tests.unit import fake_snapshot from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume from cinder.tests.unit import fake_volume
from cinder.tests.unit.image import fake as fake_image from cinder.tests.unit.image import fake as fake_image
@ -632,8 +633,9 @@ class CreateVolumeFlowManagerGlanceCinderBackendCase(test.TestCase):
@mock.patch('cinder.volume.flows.manager.create_volume.' @mock.patch('cinder.volume.flows.manager.create_volume.'
'CreateVolumeFromSpecTask.' 'CreateVolumeFromSpecTask.'
'_handle_bootable_volume_glance_meta') '_handle_bootable_volume_glance_meta')
def test_create_from_image_volume(self, handle_bootable, mock_fetch_img, @mock.patch('cinder.image.image_utils.qemu_img_info')
format='raw', owner=None, def test_create_from_image_volume(self, mock_qemu_info, handle_bootable,
mock_fetch_img, format='raw', owner=None,
location=True): location=True):
self.flags(allowed_direct_url_schemes=['cinder']) self.flags(allowed_direct_url_schemes=['cinder'])
mock_fetch_img.return_value = mock.MagicMock( mock_fetch_img.return_value = mock.MagicMock(
@ -646,7 +648,11 @@ class CreateVolumeFlowManagerGlanceCinderBackendCase(test.TestCase):
volume = fake_volume.fake_volume_obj(self.ctxt) volume = fake_volume.fake_volume_obj(self.ctxt)
image_volume = fake_volume.fake_volume_obj(self.ctxt, image_volume = fake_volume.fake_volume_obj(self.ctxt,
volume_metadata={}) volume_metadata={})
image_id = '34e54c31-3bc8-5c1d-9fff-2225bcce4b59' image_id = fakes.IMAGE_ID
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
url = 'cinder://%s' % image_volume['id'] url = 'cinder://%s' % image_volume['id']
image_location = None image_location = None
if location: if location:
@ -654,7 +660,9 @@ class CreateVolumeFlowManagerGlanceCinderBackendCase(test.TestCase):
image_meta = {'id': image_id, image_meta = {'id': image_id,
'container_format': 'bare', 'container_format': 'bare',
'disk_format': format, 'disk_format': format,
'owner': owner or self.ctxt.project_id} 'owner': owner or self.ctxt.project_id,
'virtual_size': None}
fake_driver.clone_image.return_value = (None, False) fake_driver.clone_image.return_value = (None, False)
fake_db.volume_get_all_by_host.return_value = [image_volume] fake_db.volume_get_all_by_host.return_value = [image_volume]
@ -716,8 +724,8 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
volume = fake_volume.fake_volume_obj(self.ctxt) volume = fake_volume.fake_volume_obj(self.ctxt)
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = mock.Mock() image_meta = {'virtual_size': '1073741824'}
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
self.mock_volume_manager, self.mock_volume_manager,
@ -749,20 +757,21 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
image_meta=image_meta image_meta=image_meta
) )
@mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_from_image_cannot_use_cache( def test_create_from_image_cannot_use_cache(
self, mock_get_internal_context, mock_create_from_img_dl, self, mock_qemu_info, mock_get_internal_context,
mock_create_from_src, mock_handle_bootable, mock_fetch_img): mock_create_from_img_dl, mock_create_from_src,
mock_handle_bootable, mock_fetch_img):
mock_get_internal_context.return_value = None mock_get_internal_context.return_value = None
self.mock_driver.clone_image.return_value = (None, False) self.mock_driver.clone_image.return_value = (None, False)
volume = fake_volume.fake_volume_obj(self.ctxt) volume = fake_volume.fake_volume_obj(self.ctxt)
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = { image_meta = {'virtual_size': '1073741824'}
'properties': {
'virtual_size': '2147483648'
}
}
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
self.mock_volume_manager, self.mock_volume_manager,
@ -808,6 +817,33 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
image_meta=image_meta image_meta=image_meta
) )
def test_create_from_image_bigger_size(
self, mock_get_internal_context,
mock_create_from_img_dl, mock_create_from_src,
mock_handle_bootable, mock_fetch_img):
volume = fake_volume.fake_volume_obj(self.ctxt)
image_location = 'someImageLocationStr'
image_id = fakes.IMAGE_ID
image_meta = {'virtual_size': '2147483648'}
manager = create_volume_manager.CreateVolumeFromSpecTask(
self.mock_volume_manager,
self.mock_db,
self.mock_driver,
image_volume_cache=self.mock_cache
)
self.assertRaises(
exception.ImageUnacceptable,
manager._create_from_image,
self.ctxt,
volume,
image_location,
image_id,
image_meta,
self.mock_image_service)
def test_create_from_image_cache_hit( def test_create_from_image_cache_hit(
self, mock_get_internal_context, mock_create_from_img_dl, self, mock_get_internal_context, mock_create_from_img_dl,
mock_create_from_src, mock_handle_bootable, mock_fetch_img): mock_create_from_src, mock_handle_bootable, mock_fetch_img):
@ -820,8 +856,8 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
volume = fake_volume.fake_volume_obj(self.ctxt) volume = fake_volume.fake_volume_obj(self.ctxt)
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = mock.Mock() image_meta = {'virtual_size': None}
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
self.mock_volume_manager, self.mock_volume_manager,
@ -876,7 +912,7 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
mock_volume_get.return_value = volume mock_volume_get.return_value = volume
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = mock.MagicMock() image_meta = mock.MagicMock()
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
@ -944,7 +980,7 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
mock_create_from_img_dl.side_effect = exception.CinderException() mock_create_from_img_dl.side_effect = exception.CinderException()
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = mock.MagicMock() image_meta = mock.MagicMock()
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
@ -988,20 +1024,21 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
self.assertFalse(self.mock_cache.ensure_space.called) self.assertFalse(self.mock_cache.ensure_space.called)
self.assertFalse(self.mock_cache.create_cache_entry.called) self.assertFalse(self.mock_cache.create_cache_entry.called)
@mock.patch('cinder.image.image_utils.qemu_img_info')
def test_create_from_image_no_internal_context( def test_create_from_image_no_internal_context(
self, mock_get_internal_context, mock_create_from_img_dl, self, mock_qemu_info, mock_get_internal_context,
mock_create_from_src, mock_handle_bootable, mock_fetch_img): mock_create_from_img_dl, mock_create_from_src,
mock_handle_bootable, mock_fetch_img):
self.mock_driver.clone_image.return_value = (None, False) self.mock_driver.clone_image.return_value = (None, False)
mock_get_internal_context.return_value = None mock_get_internal_context.return_value = None
volume = fake_volume.fake_volume_obj(self.ctxt) volume = fake_volume.fake_volume_obj(self.ctxt)
image_info = imageutils.QemuImgInfo()
image_info.virtual_size = '1073741824'
mock_qemu_info.return_value = image_info
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = { image_meta = {'virtual_size': '1073741824'}
'properties': {
'virtual_size': '2147483648'
}
}
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(
self.mock_volume_manager, self.mock_volume_manager,
@ -1065,7 +1102,7 @@ class CreateVolumeFlowManagerImageCacheTestCase(test.TestCase):
self.mock_db.volume_create.return_value = image_volume self.mock_db.volume_create.return_value = image_volume
image_location = 'someImageLocationStr' image_location = 'someImageLocationStr'
image_id = 'c7a8b8d4-e519-46c7-a0df-ddf1b9b9fff2' image_id = fakes.IMAGE_ID
image_meta = mock.MagicMock() image_meta = mock.MagicMock()
manager = create_volume_manager.CreateVolumeFromSpecTask( manager = create_volume_manager.CreateVolumeFromSpecTask(

View File

@ -10,14 +10,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import math
import traceback import traceback
from oslo_concurrency import processutils from oslo_concurrency import processutils
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import units
import taskflow.engines import taskflow.engines
from taskflow.patterns import linear_flow from taskflow.patterns import linear_flow
from taskflow.types import failure as ft from taskflow.types import failure as ft
@ -695,6 +693,12 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
{'volume_id': volume_ref['id'], {'volume_id': volume_ref['id'],
'image_location': image_location, 'image_id': image_id}) 'image_location': image_location, 'image_id': image_id})
virtual_size = image_meta.get('virtual_size')
if virtual_size:
virtual_size = image_utils.check_virtual_size(virtual_size,
volume_ref.size,
image_id)
# Create the volume from an image. # Create the volume from an image.
# #
# First see if the driver can clone the image directly. # First see if the driver can clone the image directly.
@ -741,21 +745,12 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
image_service, context, image_id) as tmp_image: image_service, context, image_id) as tmp_image:
# Try to create the volume as the minimal size, then we can # Try to create the volume as the minimal size, then we can
# extend once the image has been downloaded. # extend once the image has been downloaded.
data = image_utils.qemu_img_info(tmp_image)
virtual_size = image_utils.check_virtual_size(
data.virtual_size, volume_ref.size, image_id)
if should_create_cache_entry: if should_create_cache_entry:
data = image_utils.qemu_img_info(tmp_image)
virtual_size = int(
math.ceil(float(data.virtual_size) / units.Gi))
if virtual_size > volume_ref.size:
params = {'image_size': virtual_size,
'volume_size': volume_ref.size}
reason = _("Image virtual size is %(image_size)dGB"
" and doesn't fit in a volume of size"
" %(volume_size)dGB.") % params
raise exception.ImageUnacceptable(
image_id=image_id, reason=reason)
if virtual_size and virtual_size != original_size: if virtual_size and virtual_size != original_size:
volume_ref.size = virtual_size volume_ref.size = virtual_size
volume_ref.save() volume_ref.save()