summaryrefslogtreecommitdiff
path: root/nova/tests/unit/virt/vmwareapi
diff options
context:
space:
mode:
authoralecorps <alban.lecorps@ubisoft.com>2021-09-13 17:01:43 +0200
committeralecorps <alban.lecorps@gmail.com>2022-02-15 09:44:38 +0000
commitd5faf45e9df00528e6e3aa55cd2edd184181a249 (patch)
treeb69199fb28cd1148636e3f2644cbc36e36be65d7 /nova/tests/unit/virt/vmwareapi
parentbdfab8851f2fcbce1e1d85d9b699699236f39dbe (diff)
downloadnova-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.py61
-rw-r--r--nova/tests/unit/virt/vmwareapi/test_volumeops.py173
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,