]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
VMware: manage_existing for VMDK driver
authorVipin Balachandran <vbala@vmware.com>
Fri, 29 Jan 2016 12:19:17 +0000 (04:19 -0800)
committerVipin Balachandran <vbala@vmware.com>
Tue, 16 Feb 2016 06:53:54 +0000 (12:23 +0530)
This patch implements manage_existing for the VMDK driver. Manage
existing creates a volume backing from the vmdk identified by the
source-name option. The source-name format is:
                                    "vmdk_path@vm_inventory_path".

This patch detaches the disk device at vmdk_path from the VM at
vCenter inventory path given by vm_inventory_path and attaches it
to the volume backing.

Implements: blueprint vmdk-manage-existing
Change-Id: I03501bb9bcc9932211174d5a00511af347befd96

cinder/tests/unit/test_vmware_vmdk.py
cinder/tests/unit/test_vmware_volumeops.py
cinder/volume/drivers/vmware/vmdk.py
cinder/volume/drivers/vmware/volumeops.py
releasenotes/notes/vmware-vmdk-manage-existing-0edc20d9d4d19172.yaml [new file with mode: 0644]

index 2e514bbdc19a1c7d798d749e0e75773d7bc75b8d..94e338cc525108edfc9da2c19330fe72f6cf69ed 100644 (file)
@@ -2413,6 +2413,53 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                                                                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
index 056a065ff7cbf0466a3813820d64aa66e71b7ecf..154d64a1986d544dc4f75241ea9874ff0638edeb 100644 (file)
@@ -1326,6 +1326,39 @@ class VolumeOpsTestCase(test.TestCase):
                                                         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
@@ -1653,6 +1686,31 @@ class VolumeOpsTestCase(test.TestCase):
                                            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
index e5fb5abc51c1503b662563f3705578bf3c689273..dd1327ee4418172340c5554bdcda7fb2abe5e915 100644 (file)
@@ -1694,6 +1694,45 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
         (_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:
index 4454b92af6c93c25156ef772378a66a1ca09ea07..f9d8a9743ed377dd7785f11191a8874eafb4e3b6 100644 (file)
@@ -1228,6 +1228,26 @@ class VMwareVolumeOps(object):
         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.
 
@@ -1495,6 +1515,32 @@ class VMwareVolumeOps(object):
         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.
 
diff --git a/releasenotes/notes/vmware-vmdk-manage-existing-0edc20d9d4d19172.yaml b/releasenotes/notes/vmware-vmdk-manage-existing-0edc20d9d4d19172.yaml
new file mode 100644 (file)
index 0000000..f26ecb1
--- /dev/null
@@ -0,0 +1,3 @@
+---
+features:
+- Added support for manage volume in the VMware VMDK driver.
\ No newline at end of file