VMware: Create VMwareImage object for image metadata
This change pulls the nested function _get_image_properties() out of spawn() and makes it a factory method for the new VMwareImage object. Linked clone logic is moved from a separate function in vmops.py to the new get_image_properties() function. vmware_images.get_vmdk_size_and_properties() is no longer used, and is removed. Tests have been updated and/or moved as appropriate. No tests have been removed. This change has been split out of https://review.openstack.org/#/c/87002/, which was written by Shawn Hartsock. partial blueprint vmware-spawn-refactor Co-authored-by: Shawn Hartsock <hartsocks@vmware.com> Change-Id: I580f173da798318d2675c7c70bbdd19b266259f4
This commit is contained in:
@@ -972,13 +972,6 @@ def fake_upload_image(context, image, instance, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def fake_get_vmdk_size_and_properties(context, image_id, instance):
|
||||
"""Fakes the file size and properties fetch for the image file."""
|
||||
props = {"vmware_ostype": constants.DEFAULT_OS_TYPE,
|
||||
"vmware_adaptertype": constants.DEFAULT_ADAPTER_TYPE}
|
||||
return _FAKE_FILE_SIZE, props
|
||||
|
||||
|
||||
def _get_vm_mdo(vm_ref):
|
||||
"""Gets the Virtual Machine with the ref from the db."""
|
||||
if _db_content.get("VirtualMachine", None) is None:
|
||||
|
||||
@@ -25,7 +25,6 @@ from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import network_util
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
|
||||
|
||||
def fake_get_vim_object(arg):
|
||||
@@ -65,8 +64,6 @@ def set_stubs(stubs):
|
||||
"""Set the stubs."""
|
||||
stubs.Set(network_util, 'get_network_with_the_name',
|
||||
fake.fake_get_network)
|
||||
stubs.Set(vmware_images, 'get_vmdk_size_and_properties',
|
||||
fake.fake_get_vmdk_size_and_properties)
|
||||
stubs.Set(driver.VMwareAPISession, "_get_vim_object",
|
||||
fake_get_vim_object)
|
||||
stubs.Set(driver.VMwareAPISession, "_is_vim_object",
|
||||
|
||||
@@ -54,6 +54,7 @@ from nova.tests.virt.vmwareapi import fake as vmwareapi_fake
|
||||
from nova.tests.virt.vmwareapi import stubs
|
||||
from nova import utils as nova_utils
|
||||
from nova.virt import driver as v_driver
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
@@ -693,19 +694,35 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
else:
|
||||
self.assertFalse(vmwareapi_fake.get_file(str(cache)))
|
||||
|
||||
def test_instance_dir_disk_created(self):
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_instance_dir_disk_created(self, mock_from_image):
|
||||
"""Test image file is cached when even when use_linked_clone
|
||||
is False
|
||||
"""
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
linked_clone=False)
|
||||
|
||||
mock_from_image.return_value = img_props
|
||||
self._create_vm()
|
||||
path = ds_util.DatastorePath(self.ds, self.uuid, '%s.vmdk' % self.uuid)
|
||||
self.assertTrue(vmwareapi_fake.get_file(str(path)))
|
||||
self._cached_files_exist()
|
||||
|
||||
def test_cache_dir_disk_created(self):
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_cache_dir_disk_created(self, mock_from_image):
|
||||
"""Test image disk is cached when use_linked_clone is True."""
|
||||
self.flags(use_linked_clone=True, group='vmware')
|
||||
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=1 * units.Ki,
|
||||
disk_type=constants.DISK_TYPE_SPARSE)
|
||||
|
||||
mock_from_image.return_value = img_props
|
||||
|
||||
self._create_vm()
|
||||
path = ds_util.DatastorePath(self.ds, 'vmware_base',
|
||||
self.fake_image_uuid,
|
||||
@@ -748,7 +765,18 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
self.image['disk_format'] = 'iso'
|
||||
self._create_vm()
|
||||
|
||||
def test_iso_disk_cdrom_attach_with_config_drive(self):
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_iso_disk_cdrom_attach_with_config_drive(self,
|
||||
mock_from_image):
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=80 * units.Gi,
|
||||
file_type='iso',
|
||||
linked_clone=False)
|
||||
|
||||
mock_from_image.return_value = img_props
|
||||
|
||||
self.flags(force_config_drive=True)
|
||||
iso_path = [
|
||||
ds_util.DatastorePath(self.ds, 'vmware_base',
|
||||
@@ -920,57 +948,32 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
self._check_vm_info(info, power_state.RUNNING)
|
||||
self.assertTrue(vmwareapi_fake.get_file(str(root)))
|
||||
|
||||
def test_spawn_disk_extend_sparse(self):
|
||||
self.mox.StubOutWithMock(vmware_images, 'get_vmdk_size_and_properties')
|
||||
result = [1024, {"vmware_ostype": "otherGuest",
|
||||
"vmware_adaptertype": "lsiLogic",
|
||||
"vmware_disktype": "sparse"}]
|
||||
vmware_images.get_vmdk_size_and_properties(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(result)
|
||||
self.mox.StubOutWithMock(self.conn._vmops, '_extend_virtual_disk')
|
||||
requested_size = 80 * units.Mi
|
||||
self.conn._vmops._extend_virtual_disk(mox.IgnoreArg(),
|
||||
requested_size, mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.mox.ReplayAll()
|
||||
self._create_vm()
|
||||
info = self.conn.get_info({'uuid': self.uuid,
|
||||
'node': self.instance_node})
|
||||
self._check_vm_info(info, power_state.RUNNING)
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_spawn_disk_extend_sparse(self, mock_from_image):
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=units.Ki,
|
||||
disk_type=constants.DISK_TYPE_SPARSE,
|
||||
linked_clone=True)
|
||||
|
||||
def test_spawn_disk_extend_insufficient_disk_space(self):
|
||||
self.flags(use_linked_clone=True, group='vmware')
|
||||
self.wait_task = self.conn._session._wait_for_task
|
||||
self.call_method = self.conn._session._call_method
|
||||
self.task_ref = None
|
||||
id = self.fake_image_uuid
|
||||
cached_image = '[%s] vmware_base/%s/%s.80.vmdk' % (self.ds,
|
||||
id, id)
|
||||
tmp_file = '[%s] vmware_base/%s/%s.80-flat.vmdk' % (self.ds,
|
||||
id, id)
|
||||
mock_from_image.return_value = img_props
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == self.task_ref:
|
||||
self.task_ref = None
|
||||
self.assertTrue(vmwareapi_fake.get_file(cached_image))
|
||||
self.assertTrue(vmwareapi_fake.get_file(tmp_file))
|
||||
raise exception.NovaException('No space!')
|
||||
return self.wait_task(task_ref)
|
||||
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
task_ref = self.call_method(module, method, *args, **kwargs)
|
||||
if method == "ExtendVirtualDisk_Task":
|
||||
self.task_ref = task_ref
|
||||
return task_ref
|
||||
|
||||
self.stubs.Set(self.conn._session, "_call_method", fake_call_method)
|
||||
self.stubs.Set(self.conn._session, "_wait_for_task",
|
||||
fake_wait_for_task)
|
||||
|
||||
self.assertRaises(exception.NovaException,
|
||||
self._create_vm)
|
||||
self.assertFalse(vmwareapi_fake.get_file(cached_image))
|
||||
self.assertFalse(vmwareapi_fake.get_file(tmp_file))
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.conn._vmops, '_extend_virtual_disk'),
|
||||
mock.patch.object(self.conn._vmops, 'get_datacenter_ref_and_name'),
|
||||
) as (mock_extend, mock_get_dc):
|
||||
dc_val = mock.Mock()
|
||||
dc_val.ref = "fake_dc_ref"
|
||||
dc_val.name = "dc1"
|
||||
mock_get_dc.return_value = dc_val
|
||||
self._create_vm()
|
||||
iid = img_props.image_id
|
||||
cached_image = ds_util.DatastorePath(self.ds, 'vmware_base',
|
||||
iid, '%s.80.vmdk' % iid)
|
||||
mock_extend.assert_called_once_with(
|
||||
self.instance, self.instance.root_gb * units.Mi,
|
||||
str(cached_image), "fake_dc_ref")
|
||||
|
||||
def test_spawn_disk_extend_failed_copy(self):
|
||||
# Spawn instance
|
||||
@@ -1086,28 +1089,63 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(DeleteError, self._create_vm)
|
||||
self.assertTrue(vmwareapi_fake.get_file(cached_image))
|
||||
|
||||
def test_spawn_disk_invalid_disk_size(self):
|
||||
self.mox.StubOutWithMock(vmware_images, 'get_vmdk_size_and_properties')
|
||||
result = [82 * units.Gi,
|
||||
{"vmware_ostype": "otherGuest",
|
||||
"vmware_adaptertype": "lsiLogic",
|
||||
"vmware_disktype": "sparse"}]
|
||||
vmware_images.get_vmdk_size_and_properties(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(result)
|
||||
self.mox.ReplayAll()
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_spawn_disk_invalid_disk_size(self, mock_from_image):
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=82 * units.Gi,
|
||||
disk_type=constants.DISK_TYPE_SPARSE,
|
||||
linked_clone=True)
|
||||
|
||||
mock_from_image.return_value = img_props
|
||||
|
||||
self.assertRaises(exception.InstanceUnacceptable,
|
||||
self._create_vm)
|
||||
|
||||
def test_spawn_invalid_disk_format(self):
|
||||
self._create_instance()
|
||||
self.image['disk_format'] = 'invalid'
|
||||
self.assertRaises(exception.InvalidDiskFormat,
|
||||
self.conn.spawn, self.context,
|
||||
self.instance, self.image,
|
||||
injected_files=[], admin_password=None,
|
||||
network_info=self.network_info,
|
||||
block_device_info=None)
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
def test_spawn_disk_extend_insufficient_disk_space(self, mock_from_image):
|
||||
img_props = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=1024,
|
||||
disk_type=constants.DISK_TYPE_SPARSE,
|
||||
linked_clone=True)
|
||||
|
||||
mock_from_image.return_value = img_props
|
||||
|
||||
cached_image = ds_util.DatastorePath(self.ds, 'vmware_base',
|
||||
self.fake_image_uuid,
|
||||
'%s.80.vmdk' %
|
||||
self.fake_image_uuid)
|
||||
tmp_file = ds_util.DatastorePath(self.ds, 'vmware_base',
|
||||
self.fake_image_uuid,
|
||||
'%s.80-flat.vmdk' %
|
||||
self.fake_image_uuid)
|
||||
|
||||
NoDiskSpace = error_util.get_fault_class('NoDiskSpace')
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == self.task_ref:
|
||||
self.task_ref = None
|
||||
raise NoDiskSpace()
|
||||
return self.wait_task(task_ref)
|
||||
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
task_ref = self.call_method(module, method, *args, **kwargs)
|
||||
if method == 'ExtendVirtualDisk_Task':
|
||||
self.task_ref = task_ref
|
||||
return task_ref
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.conn._session, '_wait_for_task',
|
||||
fake_wait_for_task),
|
||||
mock.patch.object(self.conn._session, '_call_method',
|
||||
fake_call_method)
|
||||
) as (mock_wait_for_task, mock_call_method):
|
||||
self.assertRaises(NoDiskSpace, self._create_vm)
|
||||
self.assertFalse(vmwareapi_fake.get_file(str(cached_image)))
|
||||
self.assertFalse(vmwareapi_fake.get_file(str(tmp_file)))
|
||||
|
||||
def test_spawn_with_move_file_exists_exception(self):
|
||||
# The test will validate that the spawn completes
|
||||
@@ -2300,28 +2338,20 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase,
|
||||
network_info=self.network_info,
|
||||
block_device_info=None)
|
||||
|
||||
def test_spawn_with_sparse_image(self):
|
||||
# Only a sparse disk image triggers the copy
|
||||
self.mox.StubOutWithMock(vmware_images, 'get_vmdk_size_and_properties')
|
||||
result = [1024, {"vmware_ostype": "otherGuest",
|
||||
"vmware_adaptertype": "lsiLogic",
|
||||
"vmware_disktype": "sparse"}]
|
||||
vmware_images.get_vmdk_size_and_properties(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(result)
|
||||
@mock.patch.object(nova.virt.vmwareapi.vmware_images.VMwareImage,
|
||||
'from_image')
|
||||
@mock.patch.object(vmops.VMwareVCVMOps, 'get_copy_virtual_disk_spec')
|
||||
def test_spawn_with_sparse_image(self, mock_get_copy_virtual_disk_spec,
|
||||
mock_from_image):
|
||||
img_info = vmware_images.VMwareImage(
|
||||
image_id=self.fake_image_uuid,
|
||||
file_size=1024,
|
||||
disk_type=constants.DISK_TYPE_SPARSE,
|
||||
linked_clone=False)
|
||||
|
||||
# Ensure VMwareVCVMOps's get_copy_virtual_disk_spec is getting called
|
||||
# two times
|
||||
self.mox.StubOutWithMock(vmops.VMwareVCVMOps,
|
||||
'get_copy_virtual_disk_spec')
|
||||
self.conn._vmops.get_copy_virtual_disk_spec(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
self.conn._vmops.get_copy_virtual_disk_spec(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
mock_from_image.return_value = img_info
|
||||
mock_get_copy_virtual_disk_spec.return_value = None
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self._create_vm()
|
||||
info = self.conn.get_info({'uuid': self.uuid,
|
||||
'node': self.instance_node})
|
||||
|
||||
@@ -29,6 +29,7 @@ from nova.tests import fake_instance
|
||||
import nova.tests.image.fake
|
||||
from nova.tests.virt.vmwareapi import fake as vmwareapi_fake
|
||||
from nova.tests.virt.vmwareapi import stubs
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
@@ -38,6 +39,39 @@ from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
|
||||
|
||||
class VMwareVMOpsSimpleTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(vm_util, 'get_res_pool_ref')
|
||||
@mock.patch.object(ds_util, 'get_datastore')
|
||||
@mock.patch.object(vmops.VMwareVMOps, 'get_datacenter_ref_and_name')
|
||||
def test_spawn_disk_invalid_disk_size(self,
|
||||
mock_get_datacenter_ref_and_name,
|
||||
mock_get_datastore,
|
||||
mock_get_res_pool_ref):
|
||||
image = {
|
||||
'id': 'c1c8ce3d-c2e0-4247-890c-ccf5cc1c004c',
|
||||
'disk_format': 'vmdk',
|
||||
'size': 999999999 * units.Gi,
|
||||
}
|
||||
self._context = context.RequestContext('fake_user', 'fake_project')
|
||||
instance = fake_instance.fake_instance_obj(self._context,
|
||||
image_ref=nova.tests.image.fake.get_valid_image_id(),
|
||||
uuid='fake_uuid',
|
||||
root_gb=1,
|
||||
node='respool-1001(MyResPoolName)'
|
||||
)
|
||||
|
||||
ops = vmops.VMwareVMOps(mock.Mock(), mock.Mock(), mock.Mock())
|
||||
self.assertRaises(exception.InstanceUnacceptable,
|
||||
ops.spawn,
|
||||
mock.Mock(),
|
||||
instance,
|
||||
image,
|
||||
injected_files=[],
|
||||
admin_password=None,
|
||||
network_info=None,
|
||||
block_device_info=None)
|
||||
|
||||
|
||||
class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(VMwareVMOpsTestCase, self).setUp()
|
||||
@@ -60,7 +94,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
'vcpus': 1,
|
||||
'memory_mb': 512,
|
||||
'image_ref': self._image_id,
|
||||
'root_gb': 1,
|
||||
'root_gb': 10,
|
||||
'node': 'respool-1001(MyResPoolName)'
|
||||
}
|
||||
self._instance = fake_instance.fake_instance_obj(
|
||||
@@ -122,21 +156,6 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
rxtx_cap=3)
|
||||
])
|
||||
|
||||
def test_get_disk_format_none(self):
|
||||
format, is_iso = self._vmops._get_disk_format({'disk_format': None})
|
||||
self.assertIsNone(format)
|
||||
self.assertFalse(is_iso)
|
||||
|
||||
def test_get_disk_format_iso(self):
|
||||
format, is_iso = self._vmops._get_disk_format({'disk_format': 'iso'})
|
||||
self.assertEqual('iso', format)
|
||||
self.assertTrue(is_iso)
|
||||
|
||||
def test_get_disk_format_bad(self):
|
||||
self.assertRaises(exception.InvalidDiskFormat,
|
||||
self._vmops._get_disk_format,
|
||||
{'disk_format': 'foo'})
|
||||
|
||||
def test_get_machine_id_str(self):
|
||||
result = vmops.VMwareVMOps._get_machine_id_str(self.network_info)
|
||||
self.assertEqual(result,
|
||||
@@ -146,33 +165,6 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
self.pure_IPv6_network_info)
|
||||
self.assertEqual('DE:AD:BE:EF:00:00;;;;;#', result)
|
||||
|
||||
def test_use_linked_clone_override_nf(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone(None, False)
|
||||
self.assertFalse(value, "No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_none_true(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone(None, True)
|
||||
self.assertTrue(value, "No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_ny(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone(None, "yes")
|
||||
self.assertTrue(value, "No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_ft(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone(False, True)
|
||||
self.assertFalse(value,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def test_use_linked_clone_override_no_true(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone("no", True)
|
||||
self.assertFalse(value,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def test_use_linked_clone_override_yf(self):
|
||||
value = vmops.VMwareVMOps.decide_linked_clone("yes", False)
|
||||
self.assertTrue(value,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def _setup_create_folder_mocks(self):
|
||||
ops = vmops.VMwareVMOps(mock.Mock(), mock.Mock(), mock.Mock())
|
||||
base_name = 'folder'
|
||||
@@ -670,8 +662,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
mock_get_res_pool_ref.assert_called_once_with(
|
||||
self._session, None, 'fake_node_mo_id')
|
||||
mock_get_vif_info.assert_called_once_with(
|
||||
self._session, None, False, network_model.VIF_MODEL_E1000,
|
||||
network_info)
|
||||
self._session, None, False,
|
||||
constants.DEFAULT_VIF_MODEL, network_info)
|
||||
mock_get_create_spec.assert_called_once_with(
|
||||
self._session._get_vim().client.factory,
|
||||
self._instance,
|
||||
|
||||
@@ -19,8 +19,11 @@ import contextlib
|
||||
|
||||
import mock
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import units
|
||||
from nova import test
|
||||
import nova.tests.image.fake
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import read_write_util
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
|
||||
@@ -81,3 +84,135 @@ class VMwareImagesTestCase(test.NoDBTestCase):
|
||||
write_file_handle=write_file_handle)
|
||||
image_download.assert_called_once_with(context, instance['image_ref'])
|
||||
image_show.assert_called_once_with(context, instance['image_ref'])
|
||||
|
||||
def _setup_mock_get_remote_image_service(self,
|
||||
mock_get_remote_image_service,
|
||||
metadata):
|
||||
mock_image_service = mock.MagicMock()
|
||||
mock_image_service.show.return_value = metadata
|
||||
mock_get_remote_image_service.return_value = [mock_image_service, 'i']
|
||||
|
||||
def test_from_image_with_image_ref(self):
|
||||
raw_disk_size_in_gb = 83
|
||||
raw_disk_size_in_bytes = raw_disk_size_in_gb * units.Gi
|
||||
image_id = nova.tests.image.fake.get_valid_image_id()
|
||||
mdata = {'size': raw_disk_size_in_bytes,
|
||||
'disk_format': 'vmdk',
|
||||
'properties': {
|
||||
"vmware_ostype": constants.DEFAULT_OS_TYPE,
|
||||
"vmware_adaptertype": constants.DEFAULT_ADAPTER_TYPE,
|
||||
"vmware_disktype": constants.DEFAULT_DISK_TYPE,
|
||||
"hw_vif_model": constants.DEFAULT_VIF_MODEL,
|
||||
vmware_images.LINKED_CLONE_PROPERTY: True}}
|
||||
|
||||
img_props = vmware_images.VMwareImage.from_image(image_id, mdata)
|
||||
|
||||
image_size_in_kb = raw_disk_size_in_bytes / units.Ki
|
||||
image_size_in_gb = raw_disk_size_in_bytes / units.Gi
|
||||
|
||||
# assert that defaults are set and no value returned is left empty
|
||||
self.assertEqual(constants.DEFAULT_OS_TYPE, img_props.os_type)
|
||||
self.assertEqual(constants.DEFAULT_ADAPTER_TYPE,
|
||||
img_props.adapter_type)
|
||||
self.assertEqual(constants.DEFAULT_DISK_TYPE, img_props.disk_type)
|
||||
self.assertEqual(constants.DEFAULT_VIF_MODEL, img_props.vif_model)
|
||||
self.assertTrue(img_props.linked_clone)
|
||||
self.assertEqual(image_size_in_kb, img_props.file_size_in_kb)
|
||||
self.assertEqual(image_size_in_gb, img_props.file_size_in_gb)
|
||||
|
||||
def _image_build(self, image_lc_setting, global_lc_setting,
|
||||
disk_format=constants.DEFAULT_DISK_FORMAT,
|
||||
os_type=constants.DEFAULT_OS_TYPE,
|
||||
adapter_type=constants.DEFAULT_ADAPTER_TYPE,
|
||||
disk_type=constants.DEFAULT_DISK_TYPE,
|
||||
vif_model=constants.DEFAULT_VIF_MODEL):
|
||||
self.flags(use_linked_clone=global_lc_setting, group='vmware')
|
||||
raw_disk_size_in_gb = 93
|
||||
raw_disk_size_in_btyes = raw_disk_size_in_gb * units.Gi
|
||||
|
||||
image_id = nova.tests.image.fake.get_valid_image_id()
|
||||
mdata = {'size': raw_disk_size_in_btyes,
|
||||
'disk_format': disk_format,
|
||||
'properties': {
|
||||
"vmware_ostype": os_type,
|
||||
"vmware_adaptertype": adapter_type,
|
||||
"vmware_disktype": disk_type,
|
||||
"hw_vif_model": vif_model}}
|
||||
|
||||
if image_lc_setting is not None:
|
||||
mdata['properties'][
|
||||
vmware_images.LINKED_CLONE_PROPERTY] = image_lc_setting
|
||||
|
||||
return vmware_images.VMwareImage.from_image(image_id, mdata)
|
||||
|
||||
def test_use_linked_clone_override_nf(self):
|
||||
image_props = self._image_build(None, False)
|
||||
self.assertFalse(image_props.linked_clone,
|
||||
"No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_nt(self):
|
||||
image_props = self._image_build(None, True)
|
||||
self.assertTrue(image_props.linked_clone,
|
||||
"No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_ny(self):
|
||||
image_props = self._image_build(None, "yes")
|
||||
self.assertTrue(image_props.linked_clone,
|
||||
"No overrides present but still overridden!")
|
||||
|
||||
def test_use_linked_clone_override_ft(self):
|
||||
image_props = self._image_build(False, True)
|
||||
self.assertFalse(image_props.linked_clone,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def test_use_linked_clone_override_string_nt(self):
|
||||
image_props = self._image_build("no", True)
|
||||
self.assertFalse(image_props.linked_clone,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def test_use_linked_clone_override_string_yf(self):
|
||||
image_props = self._image_build("yes", False)
|
||||
self.assertTrue(image_props.linked_clone,
|
||||
"image level metadata failed to override global")
|
||||
|
||||
def test_use_disk_format_none(self):
|
||||
image = self._image_build(None, True, disk_format=None)
|
||||
self.assertIsNone(image.file_type)
|
||||
self.assertFalse(image.is_iso)
|
||||
|
||||
def test_use_disk_format_iso(self):
|
||||
image = self._image_build(None, True, disk_format='iso')
|
||||
self.assertEqual('iso', image.file_type)
|
||||
self.assertTrue(image.is_iso)
|
||||
|
||||
def test_use_bad_disk_format(self):
|
||||
self.assertRaises(exception.InvalidDiskFormat,
|
||||
self._image_build,
|
||||
None,
|
||||
True,
|
||||
disk_format='bad_disk_format')
|
||||
|
||||
def test_image_no_defaults(self):
|
||||
image = self._image_build(False, False,
|
||||
disk_format='iso',
|
||||
os_type='fake-os-type',
|
||||
adapter_type='fake-adapter-type',
|
||||
disk_type='fake-disk-type',
|
||||
vif_model='fake-vif-model')
|
||||
self.assertEqual('iso', image.file_type)
|
||||
self.assertEqual('fake-os-type', image.os_type)
|
||||
self.assertEqual('fake-adapter-type', image.adapter_type)
|
||||
self.assertEqual('fake-disk-type', image.disk_type)
|
||||
self.assertEqual('fake-vif-model', image.vif_model)
|
||||
self.assertFalse(image.linked_clone)
|
||||
|
||||
def test_image_defaults(self):
|
||||
image = vmware_images.VMwareImage(image_id='fake-image-id')
|
||||
|
||||
# N.B. We intentially don't use the defined constants here. Amongst
|
||||
# other potential failures, we're interested in changes to their
|
||||
# values, which would not otherwise be picked up.
|
||||
self.assertEqual('otherGuest', image.os_type)
|
||||
self.assertEqual('lsiLogic', image.adapter_type)
|
||||
self.assertEqual('preallocated', image.disk_type)
|
||||
self.assertEqual('e1000', image.vif_model)
|
||||
|
||||
@@ -18,8 +18,15 @@ Shared constants across the VMware driver
|
||||
|
||||
from nova.network import model as network_model
|
||||
|
||||
DISK_FORMAT_ISO = 'iso'
|
||||
DISK_FORMAT_VMDK = 'vmdk'
|
||||
DISK_FORMATS_ALL = [DISK_FORMAT_ISO, DISK_FORMAT_VMDK]
|
||||
|
||||
DISK_TYPE_SPARSE = 'sparse'
|
||||
DISK_TYPE_PREALLOCATED = 'preallocated'
|
||||
|
||||
DEFAULT_VIF_MODEL = network_model.VIF_MODEL_E1000
|
||||
DEFAULT_OS_TYPE = "otherGuest"
|
||||
DEFAULT_ADAPTER_TYPE = "lsiLogic"
|
||||
DEFAULT_DISK_TYPE = "preallocated"
|
||||
DEFAULT_DISK_TYPE = DISK_TYPE_PREALLOCATED
|
||||
DEFAULT_DISK_FORMAT = DISK_FORMAT_VMDK
|
||||
|
||||
@@ -36,7 +36,6 @@ from nova.i18n import _, _LE
|
||||
from nova.openstack.common import excutils
|
||||
from nova.openstack.common import lockutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import strutils
|
||||
from nova.openstack.common import units
|
||||
from nova.openstack.common import uuidutils
|
||||
from nova import utils
|
||||
@@ -67,8 +66,6 @@ VMWARE_POWER_STATES = {
|
||||
'poweredOn': power_state.RUNNING,
|
||||
'suspended': power_state.SUSPENDED}
|
||||
|
||||
VMWARE_LINKED_CLONE = 'vmware_linked_clone'
|
||||
|
||||
RESIZE_TOTAL_STEPS = 4
|
||||
|
||||
DcInfo = collections.namedtuple('DcInfo',
|
||||
@@ -154,11 +151,13 @@ class VMwareVMOps(object):
|
||||
def _get_vmdk_path(self, ds_name, folder, name):
|
||||
return str(ds_util.DatastorePath(ds_name, folder, '%s.vmdk' % name))
|
||||
|
||||
def _get_disk_format(self, image_meta):
|
||||
disk_format = image_meta.get('disk_format')
|
||||
if disk_format not in ['iso', 'vmdk', None]:
|
||||
raise exception.InvalidDiskFormat(disk_format=disk_format)
|
||||
return (disk_format, disk_format == 'iso')
|
||||
def _extend_if_required(self, dc_info, image_info, instance,
|
||||
root_vmdk_path):
|
||||
"""Increase the size of the root vmdk if necessary."""
|
||||
if instance.root_gb > image_info.file_size_in_gb:
|
||||
size_in_kb = instance.root_gb * units.Mi
|
||||
self._extend_virtual_disk(instance, size_in_kb,
|
||||
root_vmdk_path, dc_info.ref)
|
||||
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, network_info, block_device_info=None,
|
||||
@@ -198,57 +197,16 @@ class VMwareVMOps(object):
|
||||
if block_device_mapping:
|
||||
ebs_root = True
|
||||
|
||||
(file_type, is_iso) = self._get_disk_format(image_meta)
|
||||
|
||||
client_factory = self._session._get_vim().client.factory
|
||||
datastore = ds_util.get_datastore(
|
||||
self._session, self._cluster,
|
||||
datastore_regex=self._datastore_regex)
|
||||
dc_info = self.get_datacenter_ref_and_name(datastore.ref)
|
||||
|
||||
# TODO(hartsocks): this pattern is confusing, reimplement as methods
|
||||
# The use of nested functions in this file makes for a confusing and
|
||||
# hard to maintain file. At some future date, refactor this method to
|
||||
# be a full-fledged method. This will also make unit testing easier.
|
||||
def _get_image_properties(root_size):
|
||||
"""Get the Size of the flat vmdk file that is there on the storage
|
||||
repository.
|
||||
"""
|
||||
image_ref = instance.image_ref
|
||||
if image_ref:
|
||||
_image_info = vmware_images.get_vmdk_size_and_properties(
|
||||
context, image_ref, instance)
|
||||
else:
|
||||
# The case that the image may be booted from a volume
|
||||
_image_info = (root_size, {})
|
||||
|
||||
image_size, image_properties = _image_info
|
||||
vmdk_file_size_in_kb = int(image_size) / 1024
|
||||
os_type = image_properties.get("vmware_ostype",
|
||||
constants.DEFAULT_OS_TYPE)
|
||||
adapter_type = image_properties.get("vmware_adaptertype",
|
||||
constants.DEFAULT_ADAPTER_TYPE)
|
||||
disk_type = image_properties.get("vmware_disktype",
|
||||
constants.DEFAULT_DISK_TYPE)
|
||||
# Get the network card type from the image properties.
|
||||
vif_model = image_properties.get("hw_vif_model",
|
||||
constants.DEFAULT_VIF_MODEL)
|
||||
|
||||
# Fetch the image_linked_clone data here. It is retrieved
|
||||
# with the above network based API call. To retrieve it
|
||||
# later will necessitate additional network calls using the
|
||||
# identical method. Consider this a cache.
|
||||
image_linked_clone = image_properties.get(VMWARE_LINKED_CLONE)
|
||||
|
||||
return (vmdk_file_size_in_kb, os_type, adapter_type, disk_type,
|
||||
vif_model, image_linked_clone)
|
||||
|
||||
root_gb_in_kb = instance.root_gb * units.Mi
|
||||
|
||||
(vmdk_file_size_in_kb, os_type, adapter_type, disk_type, vif_model,
|
||||
image_linked_clone) = _get_image_properties(root_gb_in_kb)
|
||||
|
||||
if root_gb_in_kb and vmdk_file_size_in_kb > root_gb_in_kb:
|
||||
image_info = vmware_images.VMwareImage.from_image(instance.image_ref,
|
||||
image_meta)
|
||||
if (instance.root_gb != 0 and
|
||||
image_info.file_size_in_gb > instance.root_gb):
|
||||
reason = _("Image disk size greater than requested disk size")
|
||||
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
||||
reason=reason)
|
||||
@@ -258,7 +216,8 @@ class VMwareVMOps(object):
|
||||
self._cluster, node_mo_id)
|
||||
|
||||
vif_infos = vmwarevif.get_vif_info(self._session, self._cluster,
|
||||
utils.is_neutron(), vif_model,
|
||||
utils.is_neutron(),
|
||||
image_info.vif_model,
|
||||
network_info)
|
||||
|
||||
# Get the instance name. In some cases this may differ from the 'uuid',
|
||||
@@ -269,7 +228,7 @@ class VMwareVMOps(object):
|
||||
# Create the VM
|
||||
config_spec = vm_util.get_vm_create_spec(
|
||||
client_factory, instance, instance_name,
|
||||
datastore.name, vif_infos, os_type)
|
||||
datastore.name, vif_infos, image_info.os_type)
|
||||
|
||||
vm_ref = vm_util.create_vm(self._session, instance, dc_info.vmFolder,
|
||||
config_spec, res_pool_ref)
|
||||
@@ -292,22 +251,19 @@ class VMwareVMOps(object):
|
||||
# this logic allows for instances or images to decide
|
||||
# for themselves which strategy is best for them.
|
||||
|
||||
linked_clone = VMwareVMOps.decide_linked_clone(
|
||||
image_linked_clone,
|
||||
CONF.vmware.use_linked_clone
|
||||
)
|
||||
upload_name = instance.image_ref
|
||||
upload_folder = '%s/%s' % (self._base_folder, upload_name)
|
||||
|
||||
# The vmdk meta-data file
|
||||
uploaded_file_path = str(datastore.build_path(
|
||||
upload_folder, "%s.%s" % (upload_name, file_type)))
|
||||
upload_folder,
|
||||
"%s.%s" % (upload_name, image_info.file_type)))
|
||||
|
||||
session_vim = self._session._get_vim()
|
||||
cookies = session_vim.client.options.transport.cookiejar
|
||||
|
||||
ds_browser = self._get_ds_browser(datastore.ref)
|
||||
upload_file_name = upload_name + ".%s" % file_type
|
||||
upload_file_name = upload_name + ".%s" % image_info.file_type
|
||||
|
||||
# Check if the timestamp file exists - if so then delete it. This
|
||||
# will ensure that the aging will not delete a cache image if it
|
||||
@@ -351,8 +307,8 @@ class VMwareVMOps(object):
|
||||
upload_path_loc = datastore.build_path(
|
||||
upload_folder, upload_file_name)
|
||||
upload_rel_path = upload_path_loc.rel_path
|
||||
if not is_iso:
|
||||
if disk_type != "sparse":
|
||||
if not image_info.is_iso:
|
||||
if not image_info.is_sparse:
|
||||
# Create a flat virtual disk and retain the metadata
|
||||
# file. This will be done in the unique temporary
|
||||
# directory.
|
||||
@@ -364,10 +320,10 @@ class VMwareVMOps(object):
|
||||
datastore.name, instance=instance)
|
||||
vm_util.create_virtual_disk(self._session,
|
||||
dc_info.ref,
|
||||
adapter_type,
|
||||
disk_type,
|
||||
image_info.adapter_type,
|
||||
image_info.disk_type,
|
||||
str(upload_path_loc),
|
||||
vmdk_file_size_in_kb)
|
||||
image_info.file_size_in_kb)
|
||||
LOG.debug("Virtual disk created on %s.",
|
||||
datastore.name, instance=instance)
|
||||
self._delete_datastore_file(instance,
|
||||
@@ -385,12 +341,13 @@ class VMwareVMOps(object):
|
||||
upload_rel_path,
|
||||
cookies=cookies)
|
||||
|
||||
if not is_iso and disk_type == "sparse":
|
||||
if not image_info.is_iso and image_info.is_sparse:
|
||||
# Copy the sparse virtual disk to a thin virtual disk.
|
||||
disk_type = "thin"
|
||||
copy_spec = self.get_copy_virtual_disk_spec(client_factory,
|
||||
adapter_type,
|
||||
disk_type)
|
||||
copy_spec = self.get_copy_virtual_disk_spec(
|
||||
client_factory,
|
||||
image_info.adapter_type,
|
||||
disk_type)
|
||||
vm_util.copy_virtual_disk(self._session, dc_info.ref,
|
||||
str(sparse_ds_loc),
|
||||
str(upload_path_loc),
|
||||
@@ -416,13 +373,9 @@ class VMwareVMOps(object):
|
||||
datastore.build_path(
|
||||
tmp_upload_folder),
|
||||
dc_info.ref)
|
||||
else:
|
||||
# linked clone base disk exists
|
||||
if disk_type == "sparse":
|
||||
disk_type = "thin"
|
||||
|
||||
if is_iso:
|
||||
if root_gb_in_kb:
|
||||
if image_info.is_iso:
|
||||
if instance.root_gb != 0:
|
||||
dest_vmdk_path = self._get_vmdk_path(datastore.name,
|
||||
instance.uuid,
|
||||
instance_name)
|
||||
@@ -431,10 +384,10 @@ class VMwareVMOps(object):
|
||||
datastore.name, instance=instance)
|
||||
vm_util.create_virtual_disk(self._session,
|
||||
dc_info.ref,
|
||||
adapter_type,
|
||||
disk_type,
|
||||
image_info.adapter_type,
|
||||
image_info.disk_type,
|
||||
dest_vmdk_path,
|
||||
root_gb_in_kb)
|
||||
image_info.file_size_in_kb)
|
||||
LOG.debug("Blank virtual disk created on %s.",
|
||||
datastore.name, instance=instance)
|
||||
root_vmdk_path = dest_vmdk_path
|
||||
@@ -442,24 +395,24 @@ class VMwareVMOps(object):
|
||||
root_vmdk_path = None
|
||||
else:
|
||||
# Extend the disk size if necessary
|
||||
if not linked_clone:
|
||||
if not image_info.linked_clone:
|
||||
# If we are not using linked_clone, copy the image from
|
||||
# the cache into the instance directory. If we are using
|
||||
# linked clone it is references from the cache directory
|
||||
dest_vmdk_path = self._get_vmdk_path(datastore.name,
|
||||
instance_name, instance_name)
|
||||
copy_spec = self.get_copy_virtual_disk_spec(client_factory,
|
||||
adapter_type,
|
||||
disk_type)
|
||||
copy_spec = self.get_copy_virtual_disk_spec(
|
||||
client_factory,
|
||||
image_info.adapter_type,
|
||||
image_info.disk_type)
|
||||
vm_util.copy_virtual_disk(self._session,
|
||||
dc_info.ref,
|
||||
uploaded_file_path,
|
||||
dest_vmdk_path, copy_spec)
|
||||
|
||||
root_vmdk_path = dest_vmdk_path
|
||||
if root_gb_in_kb > vmdk_file_size_in_kb:
|
||||
self._extend_virtual_disk(instance, root_gb_in_kb,
|
||||
root_vmdk_path, dc_info.ref)
|
||||
self._extend_if_required(dc_info, image_info, instance,
|
||||
root_vmdk_path)
|
||||
else:
|
||||
upload_folder = '%s/%s' % (self._base_folder, upload_name)
|
||||
if instance.root_gb:
|
||||
@@ -493,7 +446,9 @@ class VMwareVMOps(object):
|
||||
instance.root_gb)
|
||||
|
||||
copy_spec = self.get_copy_virtual_disk_spec(
|
||||
client_factory, adapter_type, disk_type)
|
||||
client_factory,
|
||||
image_info.adapter_type,
|
||||
image_info.disk_type)
|
||||
|
||||
# Create a copy of the base image, ensuring we
|
||||
# clean up on failure
|
||||
@@ -524,20 +479,21 @@ class VMwareVMOps(object):
|
||||
# Resize the copy to the appropriate size. No need
|
||||
# for cleanup up here, as _extend_virtual_disk
|
||||
# already does it
|
||||
if root_gb_in_kb > vmdk_file_size_in_kb:
|
||||
self._extend_virtual_disk(instance,
|
||||
root_gb_in_kb,
|
||||
root_vmdk_path,
|
||||
dc_info.ref)
|
||||
self._extend_if_required(dc_info, image_info,
|
||||
instance, root_vmdk_path)
|
||||
|
||||
# Attach the root disk to the VM.
|
||||
if root_vmdk_path:
|
||||
if root_vmdk_path is not None:
|
||||
self._volumeops.attach_disk_to_vm(
|
||||
vm_ref, instance,
|
||||
adapter_type, disk_type, root_vmdk_path,
|
||||
root_gb_in_kb, linked_clone)
|
||||
vm_ref,
|
||||
instance,
|
||||
image_info.adapter_type,
|
||||
image_info.disk_type,
|
||||
root_vmdk_path,
|
||||
instance.root_gb * units.Mi,
|
||||
image_info.linked_clone)
|
||||
|
||||
if is_iso:
|
||||
if image_info.is_iso:
|
||||
self._attach_cdrom_to_vm(
|
||||
vm_ref, instance,
|
||||
datastore.ref,
|
||||
@@ -627,52 +583,6 @@ class VMwareVMOps(object):
|
||||
LOG.debug("Reconfigured VM instance to attach cdrom %s",
|
||||
file_path, instance=instance)
|
||||
|
||||
@staticmethod
|
||||
def decide_linked_clone(image_linked_clone, global_linked_clone):
|
||||
"""Explicit decision logic: whether to use linked clone on a vmdk.
|
||||
|
||||
This is *override* logic not boolean logic.
|
||||
|
||||
1. let the image over-ride if set at all
|
||||
2. default to the global setting
|
||||
|
||||
In math terms, I need to allow:
|
||||
glance image to override global config.
|
||||
|
||||
That is g vs c. "g" for glance. "c" for Config.
|
||||
|
||||
So, I need g=True vs c=False to be True.
|
||||
And, I need g=False vs c=True to be False.
|
||||
And, I need g=None vs c=True to be True.
|
||||
|
||||
Some images maybe independently best tuned for use_linked_clone=True
|
||||
saving datastorage space. Alternatively a whole OpenStack install may
|
||||
be tuned to performance use_linked_clone=False but a single image
|
||||
in this environment may be best configured to save storage space and
|
||||
set use_linked_clone=True only for itself.
|
||||
|
||||
The point is: let each layer of control override the layer beneath it.
|
||||
|
||||
rationale:
|
||||
For technical discussion on the clone strategies and their trade-offs
|
||||
see: https://www.vmware.com/support/ws5/doc/ws_clone_typeofclone.html
|
||||
|
||||
:param image_linked_clone: boolean or string or None
|
||||
:param global_linked_clone: boolean or string or None
|
||||
:return: Boolean
|
||||
"""
|
||||
|
||||
value = None
|
||||
|
||||
# Consider the values in order of override.
|
||||
if image_linked_clone is not None:
|
||||
value = image_linked_clone
|
||||
else:
|
||||
# this will never be not-set by this point.
|
||||
value = global_linked_clone
|
||||
|
||||
return strutils.bool_from_string(value)
|
||||
|
||||
def get_copy_virtual_disk_spec(self, client_factory, adapter_type,
|
||||
disk_type):
|
||||
return vm_util.get_copy_virtual_disk_spec(client_factory,
|
||||
|
||||
@@ -14,22 +14,136 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Utility functions for Image transfer.
|
||||
Utility functions for Image transfer and manipulation.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova import image
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import strutils
|
||||
from nova.openstack.common import units
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import io_util
|
||||
from nova.virt.vmwareapi import read_write_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
|
||||
# possible to import it here, as nova.virt.vmwareapi.driver calls
|
||||
# CONF.register_opts() after the import chain which imports this module. This
|
||||
# is not a problem as long as the import order doesn't change.
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
IMAGE_API = image.API()
|
||||
|
||||
QUEUE_BUFFER_SIZE = 10
|
||||
|
||||
LINKED_CLONE_PROPERTY = 'vmware_linked_clone'
|
||||
|
||||
|
||||
class VMwareImage(object):
|
||||
def __init__(self, image_id,
|
||||
file_size=0,
|
||||
os_type=constants.DEFAULT_OS_TYPE,
|
||||
adapter_type=constants.DEFAULT_ADAPTER_TYPE,
|
||||
disk_type=constants.DEFAULT_DISK_TYPE,
|
||||
file_type=constants.DEFAULT_DISK_FORMAT,
|
||||
linked_clone=None,
|
||||
vif_model=constants.DEFAULT_VIF_MODEL):
|
||||
"""VMwareImage holds values for use in building VMs.
|
||||
|
||||
image_id (str): uuid of the image
|
||||
file_size (int): size of file in bytes
|
||||
os_type (str): name of guest os (use vSphere names only)
|
||||
adapter_type (str): name of the adapter's type
|
||||
disk_type (str): type of disk in thin, thick, etc
|
||||
file_type (str): vmdk or iso
|
||||
linked_clone(bool): use linked clone, or don't
|
||||
"""
|
||||
self.image_id = image_id
|
||||
self.file_size = file_size
|
||||
self.os_type = os_type
|
||||
self.adapter_type = adapter_type
|
||||
self.disk_type = disk_type
|
||||
self.file_type = file_type
|
||||
|
||||
# NOTE(vui): This should be removed when we restore the
|
||||
# descriptor-based validation.
|
||||
if (self.file_type is not None and
|
||||
self.file_type not in constants.DISK_FORMATS_ALL):
|
||||
raise exception.InvalidDiskFormat(disk_format=self.file_type)
|
||||
|
||||
if linked_clone is not None:
|
||||
self.linked_clone = linked_clone
|
||||
else:
|
||||
self.linked_clone = CONF.vmware.use_linked_clone
|
||||
self.vif_model = vif_model
|
||||
|
||||
@property
|
||||
def file_size_in_kb(self):
|
||||
return self.file_size / units.Ki
|
||||
|
||||
@property
|
||||
def file_size_in_gb(self):
|
||||
return self.file_size / units.Gi
|
||||
|
||||
@property
|
||||
def is_sparse(self):
|
||||
return self.disk_type == constants.DISK_TYPE_SPARSE
|
||||
|
||||
@property
|
||||
def is_iso(self):
|
||||
return self.file_type == constants.DISK_FORMAT_ISO
|
||||
|
||||
@classmethod
|
||||
def from_image(cls, image_id, image_meta=None):
|
||||
"""Returns VMwareImage, the subset of properties the driver uses.
|
||||
|
||||
:param image_id - image id of image
|
||||
:param image_meta - image metadata we are working with
|
||||
:return: vmware image object
|
||||
:rtype: nova.virt.vmwareapi.vmware_images.VmwareImage
|
||||
"""
|
||||
if image_meta is None:
|
||||
image_meta = {}
|
||||
|
||||
properties = image_meta.get("properties", {})
|
||||
|
||||
# calculate linked_clone flag, allow image properties to override the
|
||||
# global property set in the configurations.
|
||||
image_linked_clone = properties.get(LINKED_CLONE_PROPERTY,
|
||||
CONF.vmware.use_linked_clone)
|
||||
|
||||
# catch any string values that need to be interpreted as boolean values
|
||||
linked_clone = strutils.bool_from_string(image_linked_clone)
|
||||
|
||||
props = {
|
||||
'image_id': image_id,
|
||||
'linked_clone': linked_clone
|
||||
}
|
||||
|
||||
if 'size' in image_meta:
|
||||
props['file_size'] = image_meta['size']
|
||||
if 'disk_format' in image_meta:
|
||||
props['file_type'] = image_meta['disk_format']
|
||||
|
||||
props_map = {
|
||||
'vmware_ostype': 'os_type',
|
||||
'vmware_adaptertype': 'adapter_type',
|
||||
'vmware_disktype': 'disk_type',
|
||||
'hw_vif_model': 'vif_model'
|
||||
}
|
||||
|
||||
for k, v in props_map.iteritems():
|
||||
if k in properties:
|
||||
props[v] = properties[k]
|
||||
|
||||
return cls(**props)
|
||||
|
||||
|
||||
def start_transfer(context, read_file_handle, data_size,
|
||||
write_file_handle=None, image_id=None, image_meta=None):
|
||||
@@ -171,18 +285,3 @@ def upload_image(context, image, instance, **kwargs):
|
||||
image_id=metadata['id'], image_meta=image_metadata)
|
||||
LOG.debug("Uploaded image %s to the Glance image server", image,
|
||||
instance=instance)
|
||||
|
||||
|
||||
def get_vmdk_size_and_properties(context, image, instance):
|
||||
"""Get size of the vmdk file that is to be downloaded for attach in spawn.
|
||||
Need this to create the dummy virtual disk for the meta-data file. The
|
||||
geometry of the disk created depends on the size.
|
||||
"""
|
||||
|
||||
LOG.debug("Getting image size for the image %s", image,
|
||||
instance=instance)
|
||||
meta_data = IMAGE_API.get(context, image)
|
||||
size, properties = meta_data["size"], meta_data["properties"]
|
||||
LOG.debug("Got image size of %(size)s for the image %(image)s",
|
||||
{'size': size, 'image': image}, instance=instance)
|
||||
return size, properties
|
||||
|
||||
Reference in New Issue
Block a user