existing_ref))
get_existing.assert_called_once_with(existing_ref)
+ @mock.patch.object(VMDK_DRIVER, '_get_existing')
+ @mock.patch.object(VMDK_DRIVER, '_create_backing')
+ @mock.patch.object(VMDK_DRIVER, 'volumeops')
+ @mock.patch.object(VMDK_DRIVER, '_get_ds_name_folder_path')
+ @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
+ '_get_disk_type')
+ def test_manage_existing(
+ self, get_disk_type, get_ds_name_folder_path, vops,
+ create_backing, get_existing):
+
+ vm = mock.sentinel.vm
+ src_path = mock.sentinel.src_path
+ disk_backing = mock.Mock(fileName=src_path)
+ disk_device = mock.Mock(backing=disk_backing, capacityInKB=1048576)
+ get_existing.return_value = (vm, disk_device)
+
+ backing = mock.sentinel.backing
+ create_backing.return_value = backing
+
+ src_dc = mock.sentinel.src_dc
+ dest_dc = mock.sentinel.dest_dc
+ vops.get_dc.side_effect = [src_dc, dest_dc]
+
+ volume = self._create_volume_dict()
+ ds_name = "ds1"
+ folder_path = "%s/" % volume['name']
+ get_ds_name_folder_path.return_value = (ds_name, folder_path)
+
+ disk_type = mock.sentinel.disk_type
+ get_disk_type.return_value = disk_type
+
+ existing_ref = mock.sentinel.existing_ref
+ self._driver.manage_existing(volume, existing_ref)
+
+ get_existing.assert_called_once_with(existing_ref)
+ create_backing.assert_called_once_with(
+ volume, create_params={vmdk.CREATE_PARAM_DISK_LESS: True})
+ vops.detach_disk_from_backing.assert_called_once_with(vm, disk_device)
+ dest_path = "[%s] %s%s.vmdk" % (ds_name, folder_path, volume['name'])
+ vops.move_vmdk_file.assert_called_once_with(
+ src_dc, src_path, dest_path, dest_dc_ref=dest_dc)
+ vops.attach_disk_to_backing.assert_called_once_with(
+ backing, disk_device.capacityInKB, disk_type, 'lsiLogic',
+ dest_path)
+ vops.update_backing_disk_uuid.assert_called_once_with(backing,
+ volume['id'])
+
@mock.patch('oslo_vmware.api.VMwareAPISession')
def test_session(self, apiSession):
self._session = None
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
+
+ disk_device = mock.sentinel.disk_device
+ self.vops._create_spec_for_disk_remove(disk_device)
+
+ self.session.vim.client.factory.create.assert_called_once_with(
+ 'ns0:VirtualDeviceConfigSpec')
+ self.assertEqual('remove', disk_spec.operation)
+ self.assertEqual(disk_device, disk_spec.device)
+
+ @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
+ '_create_spec_for_disk_remove')
+ @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
+ '_reconfigure_backing')
+ def test_detach_disk_from_backing(self, reconfigure_backing, create_spec):
+ disk_spec = mock.sentinel.disk_spec
+ create_spec.return_value = disk_spec
+
+ reconfig_spec = mock.Mock()
+ self.session.vim.client.factory.create.return_value = reconfig_spec
+
+ backing = mock.sentinel.backing
+ disk_device = mock.sentinel.disk_device
+ self.vops.detach_disk_from_backing(backing, disk_device)
+
+ create_spec.assert_called_once_with(disk_device)
+ self.session.vim.client.factory.create.assert_called_once_with(
+ 'ns0:VirtualMachineConfigSpec')
+ self.assertEqual([disk_spec], reconfig_spec.deviceChange)
+ reconfigure_backing.assert_called_once_with(backing, reconfig_spec)
+
def test_rename_backing(self):
task = mock.sentinel.task
self.session.invoke_api.return_value = task
force=True)
self.session.wait_for_task.assert_called_once_with(task)
+ def test_move_vmdk_file(self):
+ task = mock.sentinel.task
+ invoke_api = self.session.invoke_api
+ invoke_api.return_value = task
+
+ disk_mgr = self.session.vim.service_content.virtualDiskManager
+ src_dc_ref = mock.sentinel.src_dc_ref
+ src_vmdk_file_path = mock.sentinel.src_vmdk_file_path
+ dest_dc_ref = mock.sentinel.dest_dc_ref
+ dest_vmdk_file_path = mock.sentinel.dest_vmdk_file_path
+ self.vops.move_vmdk_file(src_dc_ref,
+ src_vmdk_file_path,
+ dest_vmdk_file_path,
+ dest_dc_ref=dest_dc_ref)
+
+ invoke_api.assert_called_once_with(self.session.vim,
+ 'MoveVirtualDisk_Task',
+ disk_mgr,
+ sourceName=src_vmdk_file_path,
+ sourceDatacenter=src_dc_ref,
+ destName=dest_vmdk_file_path,
+ destDatacenter=dest_dc_ref,
+ force=True)
+ self.session.wait_for_task.assert_called_once_with(task)
+
def test_delete_vmdk_file(self):
task = mock.sentinel.task
invoke_api = self.session.invoke_api
(_vm, disk) = self._get_existing(existing_ref)
return int(math.ceil(disk.capacityInKB * units.Ki / float(units.Gi)))
+ def manage_existing(self, volume, existing_ref):
+ """Brings an existing virtual disk under Cinder management.
+
+ Detaches the virtual disk identified by existing_ref and attaches
+ it to a volume backing.
+
+ :param volume: Cinder volume to manage
+ :param existing_ref: Driver-specific information used to identify a
+ volume
+ """
+ (vm, disk) = self._get_existing(existing_ref)
+
+ # Create a backing for the volume.
+ create_params = {CREATE_PARAM_DISK_LESS: True}
+ backing = self._create_backing(volume, create_params=create_params)
+
+ # Detach the disk to be managed from the source VM.
+ self.volumeops.detach_disk_from_backing(vm, disk)
+
+ # Move the disk to the datastore folder of volume backing.
+ src_dc = self.volumeops.get_dc(vm)
+ dest_dc = self.volumeops.get_dc(backing)
+ (ds_name, folder_path) = self._get_ds_name_folder_path(backing)
+ dest_path = volumeops.VirtualDiskPath(
+ ds_name, folder_path, volume['name'])
+ self.volumeops.move_vmdk_file(src_dc,
+ disk.backing.fileName,
+ dest_path.get_descriptor_ds_file_path(),
+ dest_dc_ref=dest_dc)
+
+ # Attach the disk to be managed to volume backing.
+ self.volumeops.attach_disk_to_backing(
+ backing,
+ disk.capacityInKB,
+ VMwareVcVmdkDriver._get_disk_type(volume),
+ 'lsiLogic',
+ dest_path.get_descriptor_ds_file_path())
+ self.volumeops.update_backing_disk_uuid(backing, volume['id'])
+
@property
def session(self):
if not self._session:
self._reconfigure_backing(backing, reconfig_spec)
LOG.debug("Backing VM: %s reconfigured with new disk.", backing)
+ def _create_spec_for_disk_remove(self, disk_device):
+ cf = self._session.vim.client.factory
+ disk_spec = cf.create('ns0:VirtualDeviceConfigSpec')
+ disk_spec.operation = 'remove'
+ disk_spec.device = disk_device
+ return disk_spec
+
+ def detach_disk_from_backing(self, backing, disk_device):
+ """Detach the given disk from backing."""
+
+ LOG.debug("Reconfiguring backing VM: %(backing)s to remove disk: "
+ "%(disk_device)s.",
+ {'backing': backing, 'disk_device': disk_device})
+
+ cf = self._session.vim.client.factory
+ reconfig_spec = cf.create('ns0:VirtualMachineConfigSpec')
+ spec = self._create_spec_for_disk_remove(disk_device)
+ reconfig_spec.deviceChange = [spec]
+ self._reconfigure_backing(backing, reconfig_spec)
+
def rename_backing(self, backing, new_name):
"""Rename backing VM.
LOG.info(_LI("Successfully copied disk at: %(src)s to: %(dest)s."),
{'src': src_vmdk_file_path, 'dest': dest_vmdk_file_path})
+ def move_vmdk_file(self, src_dc_ref, src_vmdk_file_path,
+ dest_vmdk_file_path, dest_dc_ref=None):
+ """Move the given vmdk file to another datastore location.
+
+ :param src_dc_ref: Reference to datacenter containing src datastore
+ :param src_vmdk_file_path: Source vmdk file path
+ :param dest_vmdk_file_path: Destination vmdk file path
+ :param dest_dc_ref: Reference to datacenter of dest datastore.
+ If unspecified, source datacenter is used.
+ """
+ LOG.debug('Moving disk: %(src)s to %(dest)s.',
+ {'src': src_vmdk_file_path, 'dest': dest_vmdk_file_path})
+
+ dest_dc_ref = dest_dc_ref or src_dc_ref
+ diskMgr = self._session.vim.service_content.virtualDiskManager
+ task = self._session.invoke_api(self._session.vim,
+ 'MoveVirtualDisk_Task',
+ diskMgr,
+ sourceName=src_vmdk_file_path,
+ sourceDatacenter=src_dc_ref,
+ destName=dest_vmdk_file_path,
+ destDatacenter=dest_dc_ref,
+ force=True)
+
+ self._session.wait_for_task(task)
+
def delete_vmdk_file(self, vmdk_file_path, dc_ref):
"""Delete given vmdk files.
--- /dev/null
+---
+features:
+- Added support for manage volume in the VMware VMDK driver.
\ No newline at end of file