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
|
||||
|
||||
|
||||
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()
|
||||
@ -771,6 +738,59 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
dest_file,
|
||||
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):
|
||||
self._test_spawn()
|
||||
|
||||
|
@ -179,3 +179,7 @@ class ImageCacheManager(imagecache.ImageCacheManager):
|
||||
images = self._list_datastore_images(ds_path, datastore)
|
||||
self.originals = images['originals']
|
||||
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'])
|
||||
|
||||
|
||||
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):
|
||||
"""Management class for VM-related tasks."""
|
||||
|
||||
@ -220,60 +257,47 @@ class VMwareVMOps(object):
|
||||
allocations[key] = type(value)
|
||||
return allocations
|
||||
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, network_info, block_device_info=None,
|
||||
instance_name=None, power_on=True):
|
||||
"""Creates a VM instance.
|
||||
def _get_vm_config_info(self, instance, image_info, instance_name=None):
|
||||
"""Captures all relevant information from the spawn parameters."""
|
||||
|
||||
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
|
||||
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)
|
||||
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
|
||||
# is unique within Virtual Center.
|
||||
vm_ref = self.build_virtual_machine(instance,
|
||||
instance_name,
|
||||
vi.instance_name,
|
||||
image_info,
|
||||
dc_info,
|
||||
datastore,
|
||||
vi.dc_info,
|
||||
vi.datastore,
|
||||
network_info)
|
||||
|
||||
# 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):
|
||||
self._configure_config_drive(
|
||||
instance, vm_ref, dc_info, datastore, injected_files,
|
||||
admin_password)
|
||||
instance, vm_ref, vi.dc_info, vi.datastore,
|
||||
injected_files, admin_password)
|
||||
|
||||
if power_on:
|
||||
vm_util.power_on_instance(self._session, instance, vm_ref=vm_ref)
|
||||
|
Loading…
Reference in New Issue
Block a user