VMware: Add support for swap disk

Added support for specifying swap device in flavor
definition or block device mappings.  The swap disk
will be of a 'thin' disk type.

Implements: blueprint vmware-swap-support
Closes-Bug: #1257683
DocImpact: Swap disks are supported by VMware driver.

Change-Id: I9139f1d912861baff497b1a1297389fb8bb16b90
This commit is contained in:
Thang Pham 2014-12-16 00:10:45 -05:00
parent 8ad8701c1f
commit 5e5ef99e9c
5 changed files with 263 additions and 42 deletions

View File

@ -82,6 +82,9 @@ class ConfigDriveTestCase(test.NoDBTestCase):
}
self.test_instance = fake_instance.fake_instance_obj(self.context,
**instance_values)
self.test_instance.flavor = objects.Flavor(vcpus=4, memory_mb=8192,
ephemeral_gb=0, swap=0,
extra_specs={})
(image_service, image_id) = glance.get_remote_image_service(context,
image_ref)

View File

@ -1411,6 +1411,37 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
expected.extraConfig.append(extra_config)
self.assertEqual(expected, result)
def test_get_swap(self):
vm_ref = 'fake-vm-ref'
# Root disk
controller_key = 1000
root_disk = fake.VirtualDisk()
root_disk.controllerKey = controller_key
disk_backing = fake.VirtualDiskFlatVer2BackingInfo()
disk_backing.fileName = '[test_datastore] uuid/uuid.vmdk'
root_disk.capacityInBytes = 1048576
root_disk.backing = disk_backing
# Swap disk
swap_disk = fake.VirtualDisk()
swap_disk.controllerKey = controller_key
disk_backing = fake.VirtualDiskFlatVer2BackingInfo()
disk_backing.fileName = "swap"
swap_disk.capacityInBytes = 1024
swap_disk.backing = disk_backing
devices = [root_disk, swap_disk]
session = fake.FakeSession()
with mock.patch.object(session, '_call_method',
return_value=devices) as mock_call:
device = vm_util.get_swap(session, vm_ref)
mock_call.assert_called_once_with(mock.ANY,
"get_dynamic_property", vm_ref, "VirtualMachine",
"config.hardware.device")
self.assertEqual(swap_disk, device)
@mock.patch.object(driver.VMwareAPISession, 'vim', stubs.fake_vim_prop)
class VMwareVMUtilGetHostRefTestCase(test.NoDBTestCase):

View File

@ -495,13 +495,14 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def _test_finish_migration(self, power_on=True, resize_instance=False):
with contextlib.nested(
mock.patch.object(self._vmops, '_resize_create_ephemerals'),
mock.patch.object(self._vmops,
'_resize_create_ephemerals_and_swap'),
mock.patch.object(self._vmops, "_update_instance_progress"),
mock.patch.object(vm_util, "power_on_instance"),
mock.patch.object(vm_util, "get_vm_ref",
return_value='fake-ref')
) as (fake_resize_create_ephemerals, fake_update_instance_progress,
fake_power_on, fake_get_vm_ref):
) as (fake_resize_create_ephemerals_and_swap,
fake_update_instance_progress, fake_power_on, fake_get_vm_ref):
self._vmops.finish_migration(context=self._context,
migration=None,
instance=self._instance,
@ -511,9 +512,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
resize_instance=resize_instance,
image_meta=None,
power_on=power_on)
fake_resize_create_ephemerals.called_once_with('fake-ref',
self._instance,
None)
fake_resize_create_ephemerals_and_swap.called_once_with(
'fake-ref', self._instance, None)
if power_on:
fake_power_on.assert_called_once_with(self._session,
self._instance,
@ -538,8 +538,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._test_finish_migration(power_on=True, resize_instance=True)
@mock.patch.object(vmops.VMwareVMOps, '_get_extra_specs')
@mock.patch.object(vmops.VMwareVMOps, '_resize_create_ephemerals')
@mock.patch.object(vmops.VMwareVMOps, '_remove_ephemerals')
@mock.patch.object(vmops.VMwareVMOps, '_resize_create_ephemerals_and_swap')
@mock.patch.object(vmops.VMwareVMOps, '_remove_ephemerals_and_swap')
@mock.patch.object(ds_util, 'disk_delete')
@mock.patch.object(ds_util, 'disk_move')
@mock.patch.object(ds_util, 'file_exists',
@ -558,8 +558,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
fake_get_browser,
fake_original_exists, fake_disk_move,
fake_disk_delete,
fake_remove_ephemerals,
fake_resize_create_ephemerals,
fake_remove_ephemerals_and_swap,
fake_resize_create_ephemerals_and_swap,
fake_get_extra_specs,
power_on):
"""Tests the finish_revert_migration method on vmops."""
@ -641,10 +641,9 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
mock_attach_disk.assert_called_once_with(
'fake-ref', self._instance, 'fake-adapter', 'fake-disk',
'[fake] uuid/root.vmdk')
fake_remove_ephemerals.called_once_with('fake-ref')
fake_resize_create_ephemerals.called_once_with('fake-ref',
self._instance,
None)
fake_remove_ephemerals_and_swap.called_once_with('fake-ref')
fake_resize_create_ephemerals_and_swap.called_once_with(
'fake-ref', self._instance, None)
if power_on:
fake_power_on.assert_called_once_with(self._session,
self._instance)
@ -728,6 +727,21 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'fake-ref', self._instance, 'fake-adapter', 'fake-disk',
'[fake] uuid/root.vmdk')
@mock.patch.object(vm_util, 'detach_devices_from_vm')
@mock.patch.object(vm_util, 'get_swap')
@mock.patch.object(vm_util, 'get_ephemerals')
def test_remove_ephemerals_and_swap(self, get_ephemerals, get_swap,
detach_devices):
get_ephemerals.return_value = [mock.sentinel.ephemeral0,
mock.sentinel.ephemeral1]
get_swap.return_value = mock.sentinel.swap
devices = [mock.sentinel.ephemeral0, mock.sentinel.ephemeral1,
mock.sentinel.swap]
self._vmops._remove_ephemerals_and_swap(mock.sentinel.vm_ref)
detach_devices.assert_called_once_with(self._vmops._session,
mock.sentinel.vm_ref, devices)
@mock.patch.object(ds_util, 'disk_delete')
@mock.patch.object(ds_util, 'file_exists',
return_value=True)
@ -784,7 +798,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._test_migrate_disk_and_power_off,
flavor_root_gb=self._instance.root_gb - 1)
@mock.patch.object(vmops.VMwareVMOps, "_remove_ephemerals")
@mock.patch.object(vmops.VMwareVMOps, "_remove_ephemerals_and_swap")
@mock.patch.object(vm_util, 'get_vmdk_info')
@mock.patch.object(vmops.VMwareVMOps, "_resize_disk")
@mock.patch.object(vmops.VMwareVMOps, "_resize_vm")
@ -794,7 +808,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def _test_migrate_disk_and_power_off(self, fake_get_vm_ref, fake_progress,
fake_power_off, fake_resize_vm,
fake_resize_disk, fake_get_vmdk_info,
fake_remove_ephemerals,
fake_remove_ephemerals_and_swap,
flavor_root_gb):
vmdk = vm_util.VmdkInfo('[fake] uuid/root.vmdk',
'fake-adapter',
@ -1405,7 +1419,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
cookies='Fake-CookieJar')
self.assertTrue(len(_wait_for_task.mock_calls) > 0)
extras = None
if block_device_info and 'ephemerals' in block_device_info:
if block_device_info and ('ephemerals' in block_device_info or
'swap' in block_device_info):
extras = ['CreateVirtualDisk_Task']
self._verify_spawn_method_calls(_call_method, extras)
@ -1512,6 +1527,99 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'size': 1}]
self._spawn_with_block_device_info_ephemerals(ephemerals)
def test_spawn_with_block_device_info_swap(self):
block_device_info = {'swap': {'disk_bus': None,
'swap_size': 512,
'device_name': '/dev/sdb'}}
self._test_spawn(block_device_info=block_device_info)
@mock.patch('nova.virt.vmwareapi.vm_util.power_on_instance')
@mock.patch.object(vmops.VMwareVMOps, '_create_and_attach_thin_disk')
@mock.patch.object(vmops.VMwareVMOps, '_use_disk_image_as_linked_clone')
@mock.patch.object(vmops.VMwareVMOps, '_fetch_image_if_missing')
@mock.patch(
'nova.virt.vmwareapi.imagecache.ImageCacheManager.enlist_image')
@mock.patch.object(vmops.VMwareVMOps, 'build_virtual_machine')
@mock.patch.object(vmops.VMwareVMOps, '_get_vm_config_info')
@mock.patch.object(vmops.VMwareVMOps, '_get_extra_specs')
@mock.patch.object(nova.virt.vmwareapi.images.VMwareImage,
'from_image')
def test_spawn_with_ephemerals_and_swap(self, from_image,
get_extra_specs,
get_vm_config_info,
build_virtual_machine,
enlist_image,
fetch_image,
use_disk_image,
create_and_attach_thin_disk,
power_on_instance):
self._instance.flavor = objects.Flavor(vcpus=1, memory_mb=512,
name="m1.tiny", root_gb=1,
ephemeral_gb=1, swap=512,
extra_specs={})
extra_specs = self._vmops._get_extra_specs(self._instance.flavor)
ephemerals = [{'device_type': 'disk',
'disk_bus': None,
'device_name': '/dev/vdb',
'size': 1},
{'device_type': 'disk',
'disk_bus': None,
'device_name': '/dev/vdc',
'size': 1}]
swap = {'disk_bus': None, 'swap_size': 512, 'device_name': '/dev/vdd'}
bdi = {'block_device_mapping': [], 'root_device_name': '/dev/sda',
'ephemerals': ephemerals, 'swap': swap}
metadata = self._vmops._get_instance_metadata(self._context,
self._instance)
self.flags(enabled=False, group='vnc')
self.flags(flat_injected=False)
image_size = (self._instance.root_gb) * units.Gi / 2
image_info = images.VMwareImage(
image_id=self._image_id,
file_size=image_size)
vi = get_vm_config_info.return_value
from_image.return_value = image_info
build_virtual_machine.return_value = 'fake-vm-ref'
self._vmops.spawn(self._context, self._instance, {},
injected_files=None, admin_password=None,
network_info=[], block_device_info=bdi)
from_image.assert_called_once_with(self._instance.image_ref, {})
get_vm_config_info.assert_called_once_with(self._instance,
image_info, extra_specs)
build_virtual_machine.assert_called_once_with(self._instance,
image_info, vi.dc_info, vi.datastore, [], extra_specs, metadata)
enlist_image.assert_called_once_with(image_info.image_id,
vi.datastore, vi.dc_info.ref)
fetch_image.assert_called_once_with(self._context, vi)
use_disk_image.assert_called_once_with('fake-vm-ref', vi)
# _create_and_attach_thin_disk should be called for each ephemeral
# and swap disk
eph0_path = str(ds_obj.DatastorePath(vi.datastore.name, 'fake_uuid',
'ephemeral_0.vmdk'))
eph1_path = str(ds_obj.DatastorePath(vi.datastore.name, 'fake_uuid',
'ephemeral_1.vmdk'))
swap_path = str(ds_obj.DatastorePath(vi.datastore.name, 'fake_uuid',
'swap.vmdk'))
create_and_attach_thin_disk.assert_has_calls([
mock.call(self._instance, 'fake-vm-ref', vi.dc_info,
ephemerals[0]['size'] * units.Mi, vi.ii.adapter_type,
eph0_path),
mock.call(self._instance, 'fake-vm-ref', vi.dc_info,
ephemerals[1]['size'] * units.Mi, vi.ii.adapter_type,
eph1_path),
mock.call(self._instance, 'fake-vm-ref', vi.dc_info,
swap['swap_size'] * units.Ki, vi.ii.adapter_type,
swap_path)
])
power_on_instance.assert_called_once_with(self._session,
self._instance,
vm_ref='fake-vm-ref')
def _get_fake_vi(self):
image_info = images.VMwareImage(
image_id=self._image_id,
@ -1523,18 +1631,18 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
return vi
@mock.patch.object(vm_util, 'create_virtual_disk')
def test_create_and_attach_ephemeral_disk(self, mock_create):
def test_create_and_attach_thin_disk(self, mock_create):
vi = self._get_fake_vi()
self._vmops._volumeops = mock.Mock()
mock_attach_disk_to_vm = self._vmops._volumeops.attach_disk_to_vm
path = str(ds_obj.DatastorePath(vi.datastore.name, 'fake_uuid',
'fake-filename'))
self._vmops._create_and_attach_ephemeral_disk(self._instance,
'fake-vm-ref',
vi.dc_info, 1,
'fake-adapter-type',
path)
'fake-filename'))
self._vmops._create_and_attach_thin_disk(self._instance,
'fake-vm-ref',
vi.dc_info, 1,
'fake-adapter-type',
path)
mock_create.assert_called_once_with(
self._session, self._dc_info.ref, 'fake-adapter-type',
'thin', path, 1)
@ -1550,7 +1658,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
block_device_info = {'ephemerals': ephemerals}
vi = self._get_fake_vi()
with mock.patch.object(
self._vmops, '_create_and_attach_ephemeral_disk') as mock_caa:
self._vmops, '_create_and_attach_thin_disk') as mock_caa:
self._vmops._create_ephemeral(block_device_info,
self._instance,
'fake-vm-ref',
@ -1565,7 +1673,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def _test_create_ephemeral_from_instance(self, bdi):
vi = self._get_fake_vi()
with mock.patch.object(
self._vmops, '_create_and_attach_ephemeral_disk') as mock_caa:
self._vmops, '_create_and_attach_thin_disk') as mock_caa:
self._vmops._create_ephemeral(bdi,
self._instance,
'fake-vm-ref',
@ -1586,6 +1694,35 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._instance.ephemeral_gb = 1
self._test_create_ephemeral_from_instance(None)
def _test_create_swap_from_instance(self, bdi):
vi = self._get_fake_vi()
flavor = objects.Flavor(vcpus=1, memory_mb=1024, ephemeral_gb=1,
swap=1024, extra_specs={})
self._instance.flavor = flavor
with mock.patch.object(
self._vmops, '_create_and_attach_thin_disk'
) as create_and_attach:
self._vmops._create_swap(bdi, self._instance, 'fake-vm-ref',
vi.dc_info, vi.datastore, 'fake_uuid',
'lsiLogic')
size = flavor.swap * units.Ki
if bdi is not None:
swap = bdi.get('swap', {})
size = swap.get('swap_size', 0) * units.Ki
path = str(ds_obj.DatastorePath(vi.datastore.name, 'fake_uuid',
'swap.vmdk'))
create_and_attach.assert_called_once_with(self._instance,
'fake-vm-ref', vi.dc_info, size, 'lsiLogic', path)
def test_create_swap_with_bdi(self):
block_device_info = {'swap': {'disk_bus': None,
'swap_size': 512,
'device_name': '/dev/sdb'}}
self._test_create_swap_from_instance(block_device_info)
def test_create_swap_with_no_bdi(self):
self._test_create_swap_from_instance(None)
def test_build_virtual_machine(self):
image_id = nova.tests.unit.image.fake.get_valid_image_id()
image = images.VMwareImage(image_id=image_id)

View File

@ -1571,3 +1571,19 @@ def get_ephemerals(session, vm_ref):
if 'ephemeral' in device.backing.fileName:
devices.append(device)
return devices
def get_swap(session, vm_ref):
hardware_devices = session._call_method(vim_util,
"get_dynamic_property", vm_ref, "VirtualMachine",
"config.hardware.device")
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
hardware_devices = hardware_devices.VirtualDevice
for device in hardware_devices:
if (device.__class__.__name__ == "VirtualDisk" and
device.backing.__class__.__name__ ==
"VirtualDiskFlatVer2BackingInfo" and
'swap' in device.backing.fileName):
return device

View File

@ -557,8 +557,8 @@ class VMwareVMOps(object):
instance=vi.instance)
self._delete_datastore_file(str(tmp_dir_loc), vi.dc_info.ref)
def _create_and_attach_ephemeral_disk(self, instance, vm_ref, dc_info,
size, adapter_type, path):
def _create_and_attach_thin_disk(self, instance, vm_ref, dc_info, size,
adapter_type, path):
disk_type = constants.DISK_TYPE_THIN
vm_util.create_virtual_disk(
self._session, dc_info.ref,
@ -583,19 +583,42 @@ class VMwareVMOps(object):
filename = vm_util.get_ephemeral_name(idx)
path = str(ds_obj.DatastorePath(datastore.name, folder,
filename))
self._create_and_attach_ephemeral_disk(instance, vm_ref,
dc_info, size,
at, path)
self._create_and_attach_thin_disk(instance, vm_ref, dc_info,
size, at, path)
# There may be block devices defined but no ephemerals. In this case
# we need to allocate a ephemeral disk if required
if not ephemerals and instance.ephemeral_gb:
size = instance.ephemeral_gb * units.Mi
filename = vm_util.get_ephemeral_name(0)
path = str(ds_obj.DatastorePath(datastore.name, folder,
filename))
self._create_and_attach_ephemeral_disk(instance, vm_ref,
dc_info, size,
adapter_type, path)
filename))
self._create_and_attach_thin_disk(instance, vm_ref, dc_info, size,
adapter_type, path)
def _create_swap(self, bdi, instance, vm_ref, dc_info, datastore,
folder, adapter_type):
swap = None
filename = "swap.vmdk"
path = str(ds_obj.DatastorePath(datastore.name, folder, filename))
if bdi is not None:
swap = driver.block_device_info_get_swap(bdi)
if driver.swap_is_usable(swap):
size = swap['swap_size'] * units.Ki
self._create_and_attach_thin_disk(instance, vm_ref, dc_info,
size, adapter_type, path)
else:
# driver.block_device_info_get_swap returns
# {'device_name': None, 'swap_size': 0} if swap is None
# in block_device_info. If block_device_info does not contain
# a swap device, we need to reset swap to None, so we can
# extract the swap_size from the instance's flavor.
swap = None
size = instance.flavor.swap * units.Ki
if not swap and size > 0:
self._create_and_attach_thin_disk(instance, vm_ref, dc_info, size,
adapter_type, path)
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info, block_device_info=None):
@ -678,6 +701,8 @@ class VMwareVMOps(object):
self._create_ephemeral(block_device_info, instance, vm_ref,
vi.dc_info, vi.datastore, instance.uuid,
vi.ii.adapter_type)
self._create_swap(block_device_info, instance, vm_ref, vi.dc_info,
vi.datastore, instance.uuid, vi.ii.adapter_type)
if configdrive.required_by(instance):
self._configure_config_drive(
@ -1176,12 +1201,17 @@ class VMwareVMOps(object):
vmdk.adapter_type,
vmdk.disk_type, vmdk.path)
def _remove_ephemerals(self, vm_ref):
def _remove_ephemerals_and_swap(self, vm_ref):
devices = vm_util.get_ephemerals(self._session, vm_ref)
swap = vm_util.get_swap(self._session, vm_ref)
if swap is not None:
devices.append(swap)
if devices:
vm_util.detach_devices_from_vm(self._session, vm_ref, devices)
def _resize_create_ephemerals(self, vm_ref, instance, block_device_info):
def _resize_create_ephemerals_and_swap(self, vm_ref, instance,
block_device_info):
vmdk = vm_util.get_vmdk_info(self._session, vm_ref,
uuid=instance.uuid)
ds_ref = vmdk.device.backing.datastore
@ -1190,6 +1220,8 @@ class VMwareVMOps(object):
folder = ds_obj.DatastorePath.parse(vmdk.path).dirname
self._create_ephemeral(block_device_info, instance, vm_ref,
dc_info, datastore, folder, vmdk.adapter_type)
self._create_swap(block_device_info, instance, vm_ref, dc_info,
datastore, folder, vmdk.adapter_type)
def migrate_disk_and_power_off(self, context, instance, dest,
flavor):
@ -1235,8 +1267,8 @@ class VMwareVMOps(object):
step=3,
total_steps=RESIZE_TOTAL_STEPS)
# 4. Purge ephemeral disks
self._remove_ephemerals(vm_ref)
# 4. Purge ephemeral and swap disks
self._remove_ephemerals_and_swap(vm_ref)
self._update_instance_progress(context, instance,
step=4,
total_steps=RESIZE_TOTAL_STEPS)
@ -1298,8 +1330,9 @@ class VMwareVMOps(object):
vmdk.adapter_type,
vmdk.disk_type, vmdk.path)
# Reconfigure ephemerals
self._remove_ephemerals(vm_ref)
self._resize_create_ephemerals(vm_ref, instance, block_device_info)
self._remove_ephemerals_and_swap(vm_ref)
self._resize_create_ephemerals_and_swap(vm_ref, instance,
block_device_info)
if power_on:
vm_util.power_on_instance(self._session, instance)
@ -1310,7 +1343,8 @@ class VMwareVMOps(object):
vm_ref = vm_util.get_vm_ref(self._session, instance)
# 5. Update ephemerals if necessary
self._resize_create_ephemerals(vm_ref, instance, block_device_info)
self._resize_create_ephemerals_and_swap(vm_ref, instance,
block_device_info)
self._update_instance_progress(context, instance,
step=5,