Merge "VMware: spawn refactor add VirtualMachineConfigInfo"
This commit is contained in:
commit
10a6c6cb0b
@ -40,39 +40,6 @@ from nova.virt.vmwareapi import vmops
|
|||||||
from nova.virt.vmwareapi import vmware_images
|
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):
|
class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(VMwareVMOpsTestCase, self).setUp()
|
super(VMwareVMOpsTestCase, self).setUp()
|
||||||
@ -771,6 +738,59 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
|||||||
dest_file,
|
dest_file,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.VMwareVCVMOps, 'get_copy_virtual_disk_spec',
|
||||||
|
return_value=None)
|
||||||
|
@mock.patch.object(ds_util, 'get_datastore')
|
||||||
|
@mock.patch.object(vmops.VMwareVCVMOps, 'get_datacenter_ref_and_name')
|
||||||
|
def _test_get_spawn_vm_config_info(self,
|
||||||
|
mock_get_datacenter_ref_and_name,
|
||||||
|
mock_get_datastore,
|
||||||
|
mock_get_copy_virtual_disk_spec,
|
||||||
|
image_size_bytes=0,
|
||||||
|
instance_name=None):
|
||||||
|
image_info = vmware_images.VMwareImage(
|
||||||
|
image_id=self._image_id,
|
||||||
|
file_size=image_size_bytes,
|
||||||
|
linked_clone=True)
|
||||||
|
|
||||||
|
mock_get_datastore.return_value = self._ds
|
||||||
|
mock_get_datacenter_ref_and_name.return_value = self._dc_info
|
||||||
|
|
||||||
|
vi = self._vmops._get_vm_config_info(
|
||||||
|
self._instance, image_info, instance_name=instance_name)
|
||||||
|
self.assertEqual(image_info, vi.ii)
|
||||||
|
self.assertEqual(self._ds, vi.datastore)
|
||||||
|
self.assertEqual(vi.root_gb, self._instance.root_gb)
|
||||||
|
self.assertEqual(vi.instance, self._instance)
|
||||||
|
if instance_name:
|
||||||
|
self.assertEqual(vi.instance_name, instance_name)
|
||||||
|
else:
|
||||||
|
self.assertEqual(vi.instance_name, self._instance['uuid'])
|
||||||
|
|
||||||
|
cache_image_path = '[%s] vmware_base/%s/%s.vmdk' % (
|
||||||
|
self._ds.name, self._image_id, self._image_id)
|
||||||
|
self.assertEqual(str(vi.cache_image_path), cache_image_path)
|
||||||
|
|
||||||
|
cache_image_folder = '[%s] vmware_base/%s' % (
|
||||||
|
self._ds.name, self._image_id)
|
||||||
|
self.assertEqual(str(vi.cache_image_folder), cache_image_folder)
|
||||||
|
|
||||||
|
def test_get_spawn_vm_config_info(self):
|
||||||
|
image_size = (self._instance.root_gb) * units.Gi / 2
|
||||||
|
self._test_get_spawn_vm_config_info(image_size_bytes=image_size)
|
||||||
|
|
||||||
|
def test_get_spawn_vm_config_info_image_too_big(self):
|
||||||
|
image_size = (self._instance.root_gb + 1) * units.Gi
|
||||||
|
self.assertRaises(exception.InstanceUnacceptable,
|
||||||
|
self._test_get_spawn_vm_config_info,
|
||||||
|
image_size_bytes=image_size)
|
||||||
|
|
||||||
|
def test_get_spawn_vm_config_info_with_instance_name(self):
|
||||||
|
image_size = (self._instance.root_gb) * units.Gi / 2
|
||||||
|
self._test_get_spawn_vm_config_info(
|
||||||
|
image_size_bytes=image_size,
|
||||||
|
instance_name="foo_instance_name")
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
self._test_spawn()
|
self._test_spawn()
|
||||||
|
|
||||||
|
@ -179,3 +179,7 @@ class ImageCacheManager(imagecache.ImageCacheManager):
|
|||||||
images = self._list_datastore_images(ds_path, datastore)
|
images = self._list_datastore_images(ds_path, datastore)
|
||||||
self.originals = images['originals']
|
self.originals = images['originals']
|
||||||
self._age_cached_images(context, datastore, dc_info, ds_path)
|
self._age_cached_images(context, datastore, dc_info, ds_path)
|
||||||
|
|
||||||
|
def get_image_cache_folder(self, datastore, image_id):
|
||||||
|
"""Returns datastore path of folder containing the image."""
|
||||||
|
return datastore.build_path(self._base_folder, image_id)
|
||||||
|
@ -73,6 +73,43 @@ DcInfo = collections.namedtuple('DcInfo',
|
|||||||
['ref', 'name', 'vmFolder'])
|
['ref', 'name', 'vmFolder'])
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachineInstanceConfigInfo(object):
|
||||||
|
"""Parameters needed to create and configure a new instance."""
|
||||||
|
|
||||||
|
def __init__(self, instance, instance_name, image_info,
|
||||||
|
datastore, dc_info, image_cache):
|
||||||
|
|
||||||
|
# Some methods called during spawn take the instance parameter purely
|
||||||
|
# for logging purposes.
|
||||||
|
# TODO(vui) Clean them up, so we no longer need to keep this variable
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
# Get the instance name. In some cases this may differ from the 'uuid',
|
||||||
|
# for example when the spawn of a rescue instance takes place.
|
||||||
|
self.instance_name = instance_name or instance.uuid
|
||||||
|
|
||||||
|
self.ii = image_info
|
||||||
|
self.root_gb = instance.root_gb
|
||||||
|
self.datastore = datastore
|
||||||
|
self.dc_info = dc_info
|
||||||
|
self._image_cache = image_cache
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cache_image_folder(self):
|
||||||
|
if self.ii.image_id is None:
|
||||||
|
return
|
||||||
|
return self._image_cache.get_image_cache_folder(
|
||||||
|
self.datastore, self.ii.image_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cache_image_path(self):
|
||||||
|
if self.ii.image_id is None:
|
||||||
|
return
|
||||||
|
cached_image_file_name = "%s.%s" % (self.ii.image_id,
|
||||||
|
self.ii.file_type)
|
||||||
|
return self.cache_image_folder.join(cached_image_file_name)
|
||||||
|
|
||||||
|
|
||||||
class VMwareVMOps(object):
|
class VMwareVMOps(object):
|
||||||
"""Management class for VM-related tasks."""
|
"""Management class for VM-related tasks."""
|
||||||
|
|
||||||
@ -220,60 +257,47 @@ class VMwareVMOps(object):
|
|||||||
allocations[key] = type(value)
|
allocations[key] = type(value)
|
||||||
return allocations
|
return allocations
|
||||||
|
|
||||||
def spawn(self, context, instance, image_meta, injected_files,
|
def _get_vm_config_info(self, instance, image_info, instance_name=None):
|
||||||
admin_password, network_info, block_device_info=None,
|
"""Captures all relevant information from the spawn parameters."""
|
||||||
instance_name=None, power_on=True):
|
|
||||||
"""Creates a VM instance.
|
|
||||||
|
|
||||||
Steps followed are:
|
|
||||||
|
|
||||||
#. Create a VM with no disk and the specifics in the instance object
|
|
||||||
like RAM size.
|
|
||||||
#. For flat disk
|
|
||||||
|
|
||||||
#. Create a dummy vmdk of the size of the disk file that is to be
|
|
||||||
uploaded. This is required just to create the metadata file.
|
|
||||||
#. Delete the -flat.vmdk file created in the above step and retain
|
|
||||||
the metadata .vmdk file.
|
|
||||||
#. Upload the disk file.
|
|
||||||
|
|
||||||
#. For sparse disk
|
|
||||||
|
|
||||||
#. Upload the disk file to a -sparse.vmdk file.
|
|
||||||
#. Copy/Clone the -sparse.vmdk file to a thin vmdk.
|
|
||||||
#. Delete the -sparse.vmdk file.
|
|
||||||
|
|
||||||
#. Attach the disk to the VM by reconfiguring the same.
|
|
||||||
#. Power on the VM.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE(hartsocks): some of the logic below relies on instance_name
|
|
||||||
# even when it is not set by the caller.
|
|
||||||
if instance_name is None:
|
|
||||||
instance_name = instance.uuid
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
image_info = vmware_images.VMwareImage.from_image(instance.image_ref,
|
|
||||||
image_meta)
|
|
||||||
if (instance.root_gb != 0 and
|
if (instance.root_gb != 0 and
|
||||||
image_info.file_size_in_gb > instance.root_gb):
|
image_info.file_size_in_gb > instance.root_gb):
|
||||||
reason = _("Image disk size greater than requested disk size")
|
reason = _("Image disk size greater than requested disk size")
|
||||||
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
||||||
reason=reason)
|
reason=reason)
|
||||||
|
datastore = ds_util.get_datastore(
|
||||||
|
self._session, self._cluster, None, self._datastore_regex)
|
||||||
|
dc_info = self.get_datacenter_ref_and_name(datastore.ref)
|
||||||
|
|
||||||
|
return VirtualMachineInstanceConfigInfo(instance,
|
||||||
|
instance_name,
|
||||||
|
image_info,
|
||||||
|
datastore,
|
||||||
|
dc_info,
|
||||||
|
self._imagecache)
|
||||||
|
|
||||||
|
def spawn(self, context, instance, image_meta, injected_files,
|
||||||
|
admin_password, network_info, block_device_info=None,
|
||||||
|
instance_name=None, power_on=True):
|
||||||
|
|
||||||
|
client_factory = self._session._get_vim().client.factory
|
||||||
|
image_info = vmware_images.VMwareImage.from_image(instance.image_ref,
|
||||||
|
image_meta)
|
||||||
|
vi = self._get_vm_config_info(instance, image_info, instance_name)
|
||||||
|
|
||||||
|
# NOTE(vui): temporarily retaining some local variables until
|
||||||
|
# the spawn refactoring is complete.
|
||||||
|
instance_name = vi.instance_name
|
||||||
|
datastore = vi.datastore
|
||||||
|
dc_info = vi.dc_info
|
||||||
|
|
||||||
# Creates the virtual machine. The virtual machine reference returned
|
# Creates the virtual machine. The virtual machine reference returned
|
||||||
# is unique within Virtual Center.
|
# is unique within Virtual Center.
|
||||||
vm_ref = self.build_virtual_machine(instance,
|
vm_ref = self.build_virtual_machine(instance,
|
||||||
instance_name,
|
vi.instance_name,
|
||||||
image_info,
|
image_info,
|
||||||
dc_info,
|
vi.dc_info,
|
||||||
datastore,
|
vi.datastore,
|
||||||
network_info)
|
network_info)
|
||||||
|
|
||||||
# Cache the vm_ref. This saves a remote call to the VC. This uses the
|
# Cache the vm_ref. This saves a remote call to the VC. This uses the
|
||||||
@ -569,8 +593,8 @@ class VMwareVMOps(object):
|
|||||||
|
|
||||||
if configdrive.required_by(instance):
|
if configdrive.required_by(instance):
|
||||||
self._configure_config_drive(
|
self._configure_config_drive(
|
||||||
instance, vm_ref, dc_info, datastore, injected_files,
|
instance, vm_ref, vi.dc_info, vi.datastore,
|
||||||
admin_password)
|
injected_files, admin_password)
|
||||||
|
|
||||||
if power_on:
|
if power_on:
|
||||||
vm_util.power_on_instance(self._session, instance, vm_ref=vm_ref)
|
vm_util.power_on_instance(self._session, instance, vm_ref=vm_ref)
|
||||||
|
Loading…
Reference in New Issue
Block a user