From bfc5edc405138e3412ae1af01abbf098c319c77c Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Thu, 26 Mar 2015 16:54:05 +0200 Subject: [PATCH] VMware: Handle image size correctly for OVA and streamOptimized images The image size is different from the virtual disk size when the image is streamOptimized or OVA. In this case we need to use the size of the virtual disk which belongs to the temporary VM created by ImportVApp. This works for both vSAN and VMFS datastores. Related-Bug: #1240373 Related-Bug: #1472955 Change-Id: Ie7df9a78632ff01453b07ee9fed3dffdd8b4a0c7 --- nova/tests/unit/virt/vmwareapi/test_images.py | 18 ++++++++++++----- nova/tests/unit/virt/vmwareapi/test_vmops.py | 20 ++++++++++++++++++- nova/virt/vmwareapi/images.py | 8 +++++++- nova/virt/vmwareapi/vmops.py | 12 +++++++++-- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/nova/tests/unit/virt/vmwareapi/test_images.py b/nova/tests/unit/virt/vmwareapi/test_images.py index 9e3a5b1b2bc1..4a5424e7dd08 100644 --- a/nova/tests/unit/virt/vmwareapi/test_images.py +++ b/nova/tests/unit/virt/vmwareapi/test_images.py @@ -28,6 +28,7 @@ from nova import test import nova.tests.unit.image.fake from nova.virt.vmwareapi import constants from nova.virt.vmwareapi import images +from nova.virt.vmwareapi import vm_util class VMwareImagesTestCase(test.NoDBTestCase): @@ -118,12 +119,14 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock.patch.object(images.IMAGE_API, 'download'), mock.patch.object(images, 'start_transfer'), mock.patch.object(images, '_build_shadow_vm_config_spec'), - mock.patch.object(session, '_call_method') + mock.patch.object(session, '_call_method'), + mock.patch.object(vm_util, 'get_vmdk_info') ) as (mock_image_api_get, mock_image_api_download, mock_start_transfer, mock_build_shadow_vm_config_spec, - mock_call_method): + mock_call_method, + mock_get_vmdk_info): image_data = {'id': 'fake-id', 'disk_format': 'vmdk', 'size': 512} @@ -169,7 +172,8 @@ class VMwareImagesTestCase(test.NoDBTestCase): fileobj=mock_read_handle) mock_start_transfer.assert_called_once_with(context, mock_read_handle, 512, write_file_handle=mock_write_handle) - + mock_get_vmdk_info.assert_called_once_with( + session, mock.sentinel.vm_ref, 'fake-vm') mock_call_method.assert_called_once_with( session.vim, "UnregisterVM", mock.sentinel.vm_ref) @@ -186,12 +190,14 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock.patch.object(images.IMAGE_API, 'download'), mock.patch.object(images, 'start_transfer'), mock.patch.object(images, '_build_shadow_vm_config_spec'), - mock.patch.object(session, '_call_method') + mock.patch.object(session, '_call_method'), + mock.patch.object(vm_util, 'get_vmdk_info') ) as (mock_image_api_get, mock_image_api_download, mock_start_transfer, mock_build_shadow_vm_config_spec, - mock_call_method): + mock_call_method, + mock_get_vmdk_info): image_data = {'id': 'fake-id', 'disk_format': 'vmdk', 'size': 512} @@ -219,6 +225,8 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock_call_method.assert_called_once_with( session.vim, "UnregisterVM", mock.sentinel.vm_ref) + mock_get_vmdk_info.assert_called_once_with( + session, mock.sentinel.vm_ref, 'fake-vm') def test_from_image_with_image_ref(self): raw_disk_size_in_gb = 83 diff --git a/nova/tests/unit/virt/vmwareapi/test_vmops.py b/nova/tests/unit/virt/vmwareapi/test_vmops.py index 1ec50049302b..228dad8c0cb3 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vmops.py +++ b/nova/tests/unit/virt/vmwareapi/test_vmops.py @@ -2110,7 +2110,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): image_ds_loc.rel_path, cookies='Fake-CookieJar') - @mock.patch.object(images, 'fetch_image_stream_optimized') + @mock.patch.object(images, 'fetch_image_stream_optimized', + return_value=123) def test_fetch_image_as_vapp(self, mock_fetch_image): vi = self._make_vm_config_info() image_ds_loc = mock.Mock() @@ -2124,6 +2125,23 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): self._ds.name, vi.dc_info.vmFolder, self._vmops._root_resource_pool) + self.assertEqual(vi.ii.file_size, 123) + + @mock.patch.object(images, 'fetch_image_ova', return_value=123) + def test_fetch_image_as_ova(self, mock_fetch_image): + vi = self._make_vm_config_info() + image_ds_loc = mock.Mock() + image_ds_loc.parent.basename = 'fake-name' + self._vmops._fetch_image_as_ova(self._context, vi, image_ds_loc) + mock_fetch_image.assert_called_once_with( + self._context, + vi.instance, + self._session, + 'fake-name', + self._ds.name, + vi.dc_info.vmFolder, + self._vmops._root_resource_pool) + self.assertEqual(vi.ii.file_size, 123) @mock.patch.object(uuidutils, 'generate_uuid', return_value='tmp-uuid') def test_prepare_iso_image(self, mock_generate_uuid): diff --git a/nova/virt/vmwareapi/images.py b/nova/virt/vmwareapi/images.py index 524b1787dcfe..81e4609d2f47 100644 --- a/nova/virt/vmwareapi/images.py +++ b/nova/virt/vmwareapi/images.py @@ -34,6 +34,7 @@ from nova import image from nova.objects import fields from nova.virt.vmwareapi import constants from nova.virt.vmwareapi import io_util +from nova.virt.vmwareapi import vm_util # NOTE(mdbooth): We use use_linked_clone below, but don't have to import it # because nova.virt.vmwareapi.driver is imported first. In fact, it is not @@ -379,8 +380,10 @@ def fetch_image_stream_optimized(context, instance, session, vm_name, LOG.info(_LI("Downloaded image file data %(image_ref)s"), {'image_ref': instance.image_ref}, instance=instance) + vmdk = vm_util.get_vmdk_info(session, imported_vm_ref, vm_name) session._call_method(session.vim, "UnregisterVM", imported_vm_ref) LOG.info(_LI("The imported VM was unregistered"), instance=instance) + return vmdk.capacity_in_bytes def get_vmdk_name_from_ovf(xmlstr): @@ -444,11 +447,14 @@ def fetch_image_ova(context, instance, session, vm_name, ds_name, LOG.info(_LI("Downloaded OVA image file %(image_ref)s"), {'image_ref': instance.image_ref}, instance=instance) imported_vm_ref = write_handle.get_imported_vm() + vmdk = vm_util.get_vmdk_info(session, + imported_vm_ref, + vm_name) session._call_method(session.vim, "UnregisterVM", imported_vm_ref) LOG.info(_LI("The imported VM was unregistered"), instance=instance) - return + return vmdk.capacity_in_bytes raise exception.ImageUnacceptable( reason=_("Extracting vmdk from OVA failed."), image_id=image_ref) diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 82ef0500de8e..f6620bb68147 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -419,7 +419,7 @@ class VMwareVMOps(object): 'datastore_name': vi.datastore.name}, instance=vi.instance) - images.fetch_image_stream_optimized( + image_size = images.fetch_image_stream_optimized( context, vi.instance, self._session, @@ -427,6 +427,10 @@ class VMwareVMOps(object): vi.datastore.name, vi.dc_info.vmFolder, self._root_resource_pool) + # The size of the image is different from the size of the virtual disk. + # We want to use the latter. On vSAN this is the only way to get this + # size because there is no VMDK descriptor. + vi.ii.file_size = image_size def _fetch_image_as_ova(self, context, vi, image_ds_loc): """Download root disk of an OVA image as streamOptimized.""" @@ -435,13 +439,17 @@ class VMwareVMOps(object): # of the VM use to import it with. vm_name = image_ds_loc.parent.basename - images.fetch_image_ova(context, + image_size = images.fetch_image_ova(context, vi.instance, self._session, vm_name, vi.datastore.name, vi.dc_info.vmFolder, self._root_resource_pool) + # The size of the image is different from the size of the virtual disk. + # We want to use the latter. On vSAN this is the only way to get this + # size because there is no VMDK descriptor. + vi.ii.file_size = image_size def _prepare_sparse_image(self, vi): tmp_dir_loc = vi.datastore.build_path(