VMware: Support volume adapter type change

Adding support for volume adapter type change using volume
retype.

Change-Id: I7731eaf9561965d4e0f87c0597bfdb784ef6ca7a
This commit is contained in:
Vipin Balachandran 2017-12-15 18:09:41 -08:00
parent 714139dd5c
commit 52d2ef021f
5 changed files with 154 additions and 11 deletions

View File

@ -1238,10 +1238,22 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
@mock.patch.object(VMDK_DRIVER, '_get_extra_spec_storage_profile')
@mock.patch.object(VMDK_DRIVER, 'ds_sel')
@mock.patch.object(VMDK_DRIVER, '_select_datastore')
@mock.patch.object(
VMDK_DRIVER, '_get_adapter_type', return_value='lsiLogic')
@mock.patch.object(
VMDK_DRIVER, '_get_extra_spec_adapter_type', return_value='lsiLogic')
def test_retype_with_diff_profile_and_ds_compliance(
self, select_datastore, ds_sel, get_extra_spec_storage_profile,
get_storage_profile, get_extra_spec_disk_type, get_disk_type,
vops, in_use):
self,
_get_extra_spec_adapter_type,
_get_adapter_type,
select_datastore,
ds_sel,
get_extra_spec_storage_profile,
get_storage_profile,
get_extra_spec_disk_type,
get_disk_type,
vops,
in_use):
backing = mock.sentinel.backing
vops.get_backing.return_value = backing
@ -1337,8 +1349,14 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
@mock.patch.object(VMDK_DRIVER, '_select_datastore')
@mock.patch.object(VMDK_DRIVER, '_get_dc')
@mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
@mock.patch.object(
VMDK_DRIVER, '_get_adapter_type', return_value='lsiLogic')
@mock.patch.object(
VMDK_DRIVER, '_get_extra_spec_adapter_type', return_value='lsiLogic')
def test_retype_with_diff_extra_spec_and_vol_snapshot(
self,
get_extra_spec_adapter_type,
get_adapter_type,
get_volume_group_folder,
get_dc,
select_datastore,
@ -1418,8 +1436,12 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
@mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder')
@mock.patch('oslo_utils.uuidutils.generate_uuid')
@mock.patch.object(VMDK_DRIVER, '_delete_temp_backing')
@mock.patch.object(VMDK_DRIVER, '_get_adapter_type')
@mock.patch.object(VMDK_DRIVER, '_get_extra_spec_adapter_type')
def _test_retype_with_diff_extra_spec_and_ds_compliance(
self,
get_extra_spec_adapter_type,
get_adapter_type,
delete_temp_backing,
generate_uuid,
get_volume_group_folder,
@ -1475,6 +1497,17 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
new_backing = mock.sentinel.new_backing
vops.clone_backing.return_value = new_backing
adapter_type = 'lsiLogic'
get_adapter_type.return_value = adapter_type
new_adapter_type = 'paraVirtual'
get_extra_spec_adapter_type.return_value = new_adapter_type
capacity = self.VOL_SIZE * units.Mi
filename = mock.sentinel.filename
disk_backing = mock.Mock(filename=filename)
disk_device = mock.Mock(capacityInKB=capacity, backing=disk_backing)
vops._get_disk_device.return_value = disk_device
context = mock.sentinel.context
volume = self._create_volume_dict(status='retyping')
new_type = {'id': 'f04a65e0-d10c-4db7-b4a5-f933d57aa2b5'}
@ -1510,6 +1543,11 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
vops.update_backing_disk_uuid.assert_called_once_with(
new_backing, volume['id'])
delete_temp_backing.assert_called_once_with(backing)
vops.detach_disk_from_backing.assert_called_once_with(
new_backing, disk_device)
vops.attach_disk_to_backing.assert_called_once_with(
new_backing, disk_device.capacityInKB, new_disk_type,
new_adapter_type, None, disk_device.backing.fileName)
vops.change_backing_profile.assert_called_once_with(new_backing,
new_profile_id)

View File

@ -1184,9 +1184,32 @@ class VolumeOpsTestCase(test.TestCase):
def test_clone_backing_with_empty_folder(self):
self._test_clone_backing('linked', None)
def _create_controller_device(self, controller_type):
dev = mock.Mock()
dev.__class__.__name__ = controller_type
return dev
def test_get_controller(self):
disk = self._create_disk_device('foo.vmdk')
controller1 = self._create_controller_device(
volumeops.ControllerType.LSI_LOGIC)
controller2 = self._create_controller_device(
volumeops.ControllerType.PARA_VIRTUAL)
self.session.invoke_api.return_value = [disk, controller1, controller2]
backing = mock.sentinel.backing
ret = self.vops._get_controller(
backing, volumeops.VirtualDiskAdapterType.PARA_VIRTUAL)
self.assertEqual(controller2, ret)
self.session.invoke_api.assert_called_once_with(
vim_util, 'get_object_property', self.session.vim, backing,
'config.hardware.device')
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'_get_controller', return_value=None)
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'_create_specs_for_disk_add')
def test_attach_disk_to_backing(self, create_spec):
def test_attach_disk_to_backing(self, create_spec, get_controller):
reconfig_spec = mock.Mock()
self.session.vim.client.factory.create.return_value = reconfig_spec
disk_add_config_specs = mock.Mock()
@ -1204,6 +1227,7 @@ class VolumeOpsTestCase(test.TestCase):
adapter_type, profile_id,
vmdk_ds_file_path)
get_controller.assert_called_once_with(backing, adapter_type)
self.assertEqual(disk_add_config_specs, reconfig_spec.deviceChange)
create_spec.assert_called_once_with(
size_in_kb, disk_type, adapter_type, profile_id,
@ -1214,6 +1238,43 @@ class VolumeOpsTestCase(test.TestCase):
spec=reconfig_spec)
self.session.wait_for_task.assert_called_once_with(task)
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'_get_controller')
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'_create_virtual_disk_config_spec')
def test_attach_disk_to_backing_existing_controller(
self, create_disk_spec, get_controller):
key = mock.sentinel.key
controller = mock.Mock(key=key)
get_controller.return_value = controller
reconfig_spec = mock.Mock()
self.session.vim.client.factory.create.return_value = reconfig_spec
disk_spec = mock.Mock()
create_disk_spec.return_value = disk_spec
task = mock.Mock()
self.session.invoke_api.return_value = task
backing = mock.Mock()
size_in_kb = units.Ki
disk_type = "thin"
adapter_type = "ide"
profile_id = mock.sentinel.profile_id
vmdk_ds_file_path = mock.sentinel.vmdk_ds_file_path
self.vops.attach_disk_to_backing(backing, size_in_kb, disk_type,
adapter_type, profile_id,
vmdk_ds_file_path)
get_controller.assert_called_once_with(backing, adapter_type)
self.assertEqual([disk_spec], reconfig_spec.deviceChange)
create_disk_spec.assert_called_once_with(
size_in_kb, disk_type, key, profile_id, vmdk_ds_file_path)
self.session.invoke_api.assert_called_once_with(self.session.vim,
"ReconfigVM_Task",
backing,
spec=reconfig_spec)
self.session.wait_for_task.assert_called_once_with(task)
def test_create_spec_for_disk_remove(self):
disk_spec = mock.Mock()
self.session.vim.client.factory.create.return_value = disk_spec

View File

@ -250,7 +250,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
# add storage profile ID to connection info
# support for revert-to-snapshot
# improve scalability of querying volumes in backend (bug 1600754)
VERSION = '3.0.0'
# 3.1.0 - support adapter type change using retype
VERSION = '3.1.0'
# ThirdPartySystems wiki page
CI_WIKI_NAME = "VMware_CI"
@ -1555,6 +1556,20 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
'new_name': tmp_name,
'old_name': volume['name']})
adapter_type = self._get_adapter_type(volume)
new_adapter_type = self._get_extra_spec_adapter_type(new_type['id'])
if new_adapter_type != adapter_type:
LOG.debug("Changing volume: %(name)s adapter type from "
"%(adapter_type)s to %(new_adapter_type)s.",
{'name': volume['name'],
'adapter_type': adapter_type,
'new_adapter_type': new_adapter_type})
disk_device = self.volumeops._get_disk_device(backing)
self.volumeops.detach_disk_from_backing(backing, disk_device)
self.volumeops.attach_disk_to_backing(
backing, disk_device.capacityInKB, new_disk_type,
new_adapter_type, None, disk_device.backing.fileName)
# Update the backing's storage profile if needed.
if need_profile_change:
LOG.debug("Backing: %(backing)s needs a profile change to:"

View File

@ -1235,6 +1235,18 @@ class VMwareVolumeOps(object):
reconfig_task)
self._session.wait_for_task(reconfig_task)
def _get_controller(self, backing, adapter_type):
devices = self._session.invoke_api(vim_util,
'get_object_property',
self._session.vim,
backing,
'config.hardware.device')
controller_type = ControllerType.get_controller_type(adapter_type)
for device in devices:
if device.__class__.__name__ == controller_type:
return device
def attach_disk_to_backing(self, backing, size_in_kb, disk_type,
adapter_type, profile_id, vmdk_ds_file_path):
"""Attach an existing virtual disk to the backing VM.
@ -1256,12 +1268,23 @@ class VMwareVolumeOps(object):
'adapter_type': adapter_type})
cf = self._session.vim.client.factory
reconfig_spec = cf.create('ns0:VirtualMachineConfigSpec')
specs = self._create_specs_for_disk_add(
size_in_kb,
disk_type,
adapter_type,
profile_id,
vmdk_ds_file_path=vmdk_ds_file_path)
controller = self._get_controller(backing, adapter_type)
if controller:
disk_spec = self._create_virtual_disk_config_spec(
size_in_kb,
disk_type,
controller.key,
profile_id,
vmdk_ds_file_path)
specs = [disk_spec]
else:
specs = self._create_specs_for_disk_add(
size_in_kb,
disk_type,
adapter_type,
profile_id,
vmdk_ds_file_path=vmdk_ds_file_path)
reconfig_spec.deviceChange = specs
self._reconfigure_backing(backing, reconfig_spec)
LOG.debug("Backing VM: %s reconfigured with new disk.", backing)

View File

@ -0,0 +1,6 @@
---
features:
- |
VMware VMDK driver now supports changing adpater type using retype.
To change the adapter type, set ``vmware:adapter_type`` in the
new volume type.