diff options
| author | alecorps <alban.lecorps@ubisoft.com> | 2021-09-13 17:01:43 +0200 |
|---|---|---|
| committer | alecorps <alban.lecorps@gmail.com> | 2022-02-15 09:44:38 +0000 |
| commit | d5faf45e9df00528e6e3aa55cd2edd184181a249 (patch) | |
| tree | b69199fb28cd1148636e3f2644cbc36e36be65d7 /nova/tests/unit/virt/vmwareapi | |
| parent | bdfab8851f2fcbce1e1d85d9b699699236f39dbe (diff) | |
| download | nova-d5faf45e9df00528e6e3aa55cd2edd184181a249.tar.gz | |
VMware: Support volumes backed by VStorageObject
vSphere 6.5 introduced APIs to manage virtual disks (volumes)
as first class objects. The new managed disk entity is called
VStorageObject aka First Class Disk (FCD). Adding support for
volumes backed by VStorageObject.
Change-Id: I4a5a9d3537dc175508f0a0fd82507c498737d1a5
Diffstat (limited to 'nova/tests/unit/virt/vmwareapi')
| -rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_vm_util.py | 61 | ||||
| -rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_volumeops.py | 173 |
2 files changed, 234 insertions, 0 deletions
diff --git a/nova/tests/unit/virt/vmwareapi/test_vm_util.py b/nova/tests/unit/virt/vmwareapi/test_vm_util.py index ea30895a4d..13383fd9f6 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vm_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_vm_util.py @@ -23,6 +23,7 @@ from oslo_utils import uuidutils from oslo_vmware import exceptions as vexc from oslo_vmware.objects import datastore as ds_obj from oslo_vmware import pbm +from oslo_vmware import vim_util as vutil from nova import exception from nova.network import model as network_model @@ -1987,6 +1988,66 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): mock_get_name.assert_called_once_with(self._instance.display_name, self._instance.uuid) + def test_create_fcd_id_obj(self): + fcd_id_obj = mock.Mock() + client_factory = mock.Mock() + client_factory.create.return_value = fcd_id_obj + fcd_id = mock.sentinel.fcd_id + ret = vm_util._create_fcd_id_obj(client_factory, fcd_id) + + self.assertEqual(fcd_id_obj, ret) + self.assertEqual(fcd_id, ret.id) + client_factory.create.assert_called_once_with('ns0:ID') + + @mock.patch.object(vm_util, '_create_fcd_id_obj') + @mock.patch.object(vutil, 'get_moref') + def test_attach_fcd(self, get_moref, create_fcd_id_obj): + disk_id = mock.sentinel.disk_id + create_fcd_id_obj.return_value = disk_id + + ds_ref = mock.sentinel.ds_ref + get_moref.return_value = ds_ref + + task = mock.sentinel.task + session = mock.Mock() + session._call_method.return_value = task + + vm_ref = mock.sentinel.vm_ref + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + controller_key = mock.sentinel.controller_key + unit_number = mock.sentinel.unit_number + vm_util.attach_fcd( + session, vm_ref, fcd_id, ds_ref_val, controller_key, unit_number) + + create_fcd_id_obj.assert_called_once_with( + session.vim.client.factory, fcd_id) + get_moref.assert_called_once_with(ds_ref_val, 'Datastore') + session._call_method.assert_called_once_with( + session.vim, "AttachDisk_Task", vm_ref, diskId=disk_id, + datastore=ds_ref, controllerKey=controller_key, + unitNumber=unit_number) + session._wait_for_task.assert_called_once_with(task) + + @mock.patch.object(vm_util, '_create_fcd_id_obj') + def test_detach_fcd(self, create_fcd_id_obj): + disk_id = mock.sentinel.disk_id + create_fcd_id_obj.return_value = disk_id + + task = mock.sentinel.task + session = mock.Mock() + session._call_method.return_value = task + + vm_ref = mock.sentinel.vm_ref + fcd_id = mock.sentinel.fcd_id + vm_util.detach_fcd(session, vm_ref, fcd_id) + + create_fcd_id_obj.assert_called_once_with( + session.vim.client.factory, fcd_id) + session._call_method.assert_called_once_with( + session.vim, "DetachDisk_Task", vm_ref, diskId=disk_id) + session._wait_for_task.assert_called_once_with(task) + @mock.patch.object(driver.VMwareAPISession, 'vim', stubs.fake_vim_prop) class VMwareVMUtilGetHostRefTestCase(test.NoDBTestCase): diff --git a/nova/tests/unit/virt/vmwareapi/test_volumeops.py b/nova/tests/unit/virt/vmwareapi/test_volumeops.py index 0a051d62f5..5b95ee9949 100644 --- a/nova/tests/unit/virt/vmwareapi/test_volumeops.py +++ b/nova/tests/unit/virt/vmwareapi/test_volumeops.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ddt import mock from oslo_utils.fixture import uuidsentinel as uuids from oslo_vmware import exceptions as oslo_vmw_exceptions @@ -31,6 +32,7 @@ from nova.virt.vmwareapi import vm_util from nova.virt.vmwareapi import volumeops +@ddt.ddt class VMwareVolumeOpsTestCase(test.NoDBTestCase): def setUp(self): @@ -406,6 +408,57 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_rdm_disk.assert_called_once_with(hardware_devices, disk_uuid) self.assertFalse(detach_disk_from_vm.called) + @mock.patch.object(vm_util, 'get_vm_ref') + @mock.patch.object(vm_util, 'get_vm_state') + @mock.patch.object(vm_util, 'detach_fcd') + def _test__detach_volume_fcd( + self, detach_fcd, get_vm_state, get_vm_ref, + adapter_type=constants.ADAPTER_TYPE_IDE, powered_off=True): + vm_ref = mock.sentinel.vm_ref + get_vm_ref.return_value = vm_ref + + if adapter_type == constants.ADAPTER_TYPE_IDE: + get_vm_state.return_value = ( + power_state.SHUTDOWN if powered_off else power_state.RUNNING) + + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + connection_info = {'data': {'id': fcd_id, + 'ds_ref_val': ds_ref_val, + 'adapter_type': adapter_type}} + instance = mock.sentinel.instance + + if adapter_type == constants.ADAPTER_TYPE_IDE and not powered_off: + self.assertRaises(exception.Invalid, + self._volumeops._detach_volume_fcd, + connection_info, + instance) + detach_fcd.assert_not_called() + else: + self._volumeops._detach_volume_fcd(connection_info, instance) + detach_fcd.assert_called_once_with( + self._volumeops._session, vm_ref, fcd_id) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_detach_volume_fcd_powered_off_instance(self, adapter_type): + self._test__detach_volume_fcd(adapter_type=adapter_type) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_detach_volume_fcd_powered_on_instance(self, adapter_type): + self._test__detach_volume_fcd(adapter_type=adapter_type, + powered_off=False) + + @mock.patch.object(volumeops.VMwareVolumeOps, '_detach_volume_fcd') + def test_detach_volume_fcd(self, detach_volume_fcd): + connection_info = {'driver_volume_type': constants.DISK_FORMAT_FCD} + instance = mock.sentinel.instance + self._volumeops.detach_volume(connection_info, instance) + detach_volume_fcd.assert_called_once_with(connection_info, instance) + def _test_attach_volume_vmdk(self, adapter_type=None): connection_info = {'driver_volume_type': constants.DISK_FORMAT_VMDK, 'serial': 'volume-fake-id', @@ -498,6 +551,126 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): constants.ADAPTER_TYPE_PARAVIRTUAL): self._test_attach_volume_vmdk(adapter_type) + @mock.patch.object(vm_util, 'allocate_controller_key_and_unit_number') + def test_get_controller_key_and_unit( + self, allocate_controller_key_and_unit_number): + key = mock.sentinel.key + unit = mock.sentinel.unit + allocate_controller_key_and_unit_number.return_value = ( + key, unit, None) + + with mock.patch.object(self._volumeops, '_session') as session: + devices = mock.sentinel.devices + session._call_method.return_value = devices + + vm_ref = mock.sentinel.vm_ref + adapter_type = mock.sentinel.adapter_type + ret = self._volumeops._get_controller_key_and_unit( + vm_ref, adapter_type) + self.assertEqual((key, unit, None), ret) + session._call_method.assert_called_once_with( + vutil, 'get_object_property', vm_ref, 'config.hardware.device') + allocate_controller_key_and_unit_number.assert_called_once_with( + session.vim.client.factory, devices, adapter_type) + + @mock.patch.object(volumeops.VMwareVolumeOps, + '_get_controller_key_and_unit') + @mock.patch.object(vm_util, 'reconfigure_vm') + @mock.patch.object(vm_util, 'attach_fcd') + def _test_attach_fcd( + self, attach_fcd, reconfigure_vm, get_controller_key_and_unit, + existing_controller=True): + key = mock.sentinel.key + unit = mock.sentinel.unit + spec = mock.sentinel.spec + if existing_controller: + get_controller_key_and_unit.return_value = (key, unit, None) + else: + get_controller_key_and_unit.side_effect = [(None, None, spec), + (key, unit, None)] + + with mock.patch.object(self._volumeops, '_session') as session: + config_spec = mock.Mock() + session.vim.client.factory.create.return_value = config_spec + + vm_ref = mock.sentinel.vm_ref + adapter_type = mock.sentinel.adapter_type + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + self._volumeops._attach_fcd( + vm_ref, adapter_type, fcd_id, ds_ref_val) + + attach_fcd.assert_called_once_with( + session, vm_ref, fcd_id, ds_ref_val, key, unit) + if existing_controller: + get_controller_key_and_unit.assert_called_once_with( + vm_ref, adapter_type) + reconfigure_vm.assert_not_called() + else: + exp_calls = [mock.call(vm_ref, adapter_type), + mock.call(vm_ref, adapter_type)] + get_controller_key_and_unit.assert_has_calls(exp_calls) + self.assertEqual([spec], config_spec.deviceChange) + reconfigure_vm.assert_called_once_with( + session, vm_ref, config_spec) + + def test_attach_fcd_using_existing_controller(self): + self._test_attach_fcd() + + def test_attach_fcd_using_new_controller(self): + self._test_attach_fcd(existing_controller=False) + + @mock.patch.object(vm_util, 'get_vm_ref') + @mock.patch.object(vm_util, 'get_vm_state') + @mock.patch.object(volumeops.VMwareVolumeOps, '_attach_fcd') + def _test__attach_volume_fcd( + self, attach_fcd, get_vm_state, get_vm_ref, + adapter_type=constants.ADAPTER_TYPE_IDE, powered_off=True): + vm_ref = mock.sentinel.vm_ref + get_vm_ref.return_value = vm_ref + + if adapter_type == constants.ADAPTER_TYPE_IDE: + get_vm_state.return_value = ( + power_state.SHUTDOWN if powered_off else power_state.RUNNING) + + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + connection_info = {'data': {'id': fcd_id, + 'ds_ref_val': ds_ref_val, + 'adapter_type': adapter_type}} + instance = mock.sentinel.instance + + if adapter_type == constants.ADAPTER_TYPE_IDE and not powered_off: + self.assertRaises(exception.Invalid, + self._volumeops._attach_volume_fcd, + connection_info, + instance) + attach_fcd.assert_not_called() + else: + self._volumeops._attach_volume_fcd(connection_info, instance) + attach_fcd.assert_called_once_with( + vm_ref, adapter_type, fcd_id, ds_ref_val) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_attach_volume_fcd_powered_off_instance(self, adapter_type): + self._test__attach_volume_fcd(adapter_type=adapter_type) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_attach_volume_fcd_powered_on_instance(self, adapter_type): + self._test__attach_volume_fcd(adapter_type=adapter_type, + powered_off=False) + + @mock.patch.object(volumeops.VMwareVolumeOps, '_attach_volume_fcd') + def test_attach_volume_fcd(self, attach_volume_fcd): + connection_info = {'driver_volume_type': constants.DISK_FORMAT_FCD} + instance = mock.sentinel.instance + self._volumeops.attach_volume(connection_info, instance) + attach_volume_fcd.assert_called_once_with(connection_info, instance) + def test_attach_volume_iscsi(self): for adapter_type in (None, constants.DEFAULT_ADAPTER_TYPE, constants.ADAPTER_TYPE_BUSLOGIC, |
