]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fixes cinder volume from snapshot on Windows
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Thu, 10 Apr 2014 01:16:07 +0000 (04:16 +0300)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Sat, 21 Jun 2014 18:17:01 +0000 (21:17 +0300)
When creating a volume from a snapshot on Windows, a shadow copy
volume is exported as an iSCSI disk. The issue is that this export
is readonly and cannot be mounted to instances.

As this export cannot be modified, to make the new volume usable,
the according image must be moved to a new path and imported .

Closes-Bug: #1306032

Change-Id: I3a936d30fdd7875059dc56c2681f453757c2605f

cinder/tests/windows/test_windows.py
cinder/volume/drivers/windows/windows.py
cinder/volume/drivers/windows/windows_utils.py

index bd73e3ab14bc6fa2f45bb3f533d4becc7043d3a3..dfae30c342fda1bcc051983c1f4beee2e4c0d5ad 100644 (file)
@@ -146,7 +146,7 @@ class TestWindowsDriver(test.TestCase):
         self.mox.StubOutWithMock(windows_utils.WindowsUtils,
                                  'create_volume_from_snapshot')
         windows_utils.WindowsUtils.\
-            create_volume_from_snapshot(volume['name'], snapshot['name'])
+            create_volume_from_snapshot(volume, snapshot['name'])
 
         self.mox.ReplayAll()
 
index f88a9bc78956a0eb95cc0c54a52e6088445a34ec..e4d8e204825a8f289d1aaa031d3a78d9b590c570 100644 (file)
@@ -100,14 +100,7 @@ class WindowsDriver(driver.ISCSIDriver):
         self.utils.create_volume(vhd_path, vol_name, vol_size)
 
     def local_path(self, volume, format=None):
-        base_vhd_folder = self.configuration.windows_iscsi_lun_path
-        if not os.path.exists(base_vhd_folder):
-            LOG.debug('Creating folder %s ', base_vhd_folder)
-            os.makedirs(base_vhd_folder)
-        if not format:
-            format = self.utils.get_supported_format()
-        return os.path.join(base_vhd_folder, str(volume['name']) + "." +
-                            format)
+        return self.utils.local_path(volume, format)
 
     def delete_volume(self, volume):
         """Driver entry point for destroying existing volumes."""
@@ -127,8 +120,7 @@ class WindowsDriver(driver.ISCSIDriver):
     def create_volume_from_snapshot(self, volume, snapshot):
         """Driver entry point for exporting snapshots as volumes."""
         snapshot_name = snapshot['name']
-        vol_name = volume['name']
-        self.utils.create_volume_from_snapshot(vol_name, snapshot_name)
+        self.utils.create_volume_from_snapshot(volume, snapshot_name)
 
     def delete_snapshot(self, snapshot):
         """Driver entry point for deleting a snapshot."""
index 6b30561ee9566a4afad698880b7775bcdb7d44b2..0d2fbbac0a1e5cc9d49702aaf2aa339f527a2853 100644 (file)
 Utility class for Windows Storage Server 2012 volume related operations.
 """
 
+import ctypes
 import os
 import time
 
+from oslo.config import cfg
+
 from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder.volume.drivers.windows import constants
@@ -27,6 +30,10 @@ from cinder.volume.drivers.windows import constants
 if os.name == 'nt':
     import wmi
 
+    from ctypes import wintypes
+
+CONF = cfg.CONF
+
 LOG = logging.getLogger(__name__)
 
 
@@ -132,13 +139,17 @@ class WindowsUtils(object):
             LOG.error(err_msg)
             raise exception.VolumeBackendAPIException(data=err_msg)
 
-    def create_volume(self, vhd_path, vol_name, vol_size):
+    def create_volume(self, vhd_path, vol_name, vol_size=None):
         """Creates a volume."""
         try:
             cl = self._conn_wmi.__getattr__("WT_Disk")
+            if vol_size:
+                size_mb = vol_size * 1024
+            else:
+                size_mb = None
             cl.NewWTDisk(DevicePath=vhd_path,
                          Description=vol_name,
-                         SizeInMB=vol_size * 1024)
+                         SizeInMB=size_mb)
         except wmi.x_wmi as exc:
             err_msg = (_(
                 'create_volume: error when creating the volume name: '
@@ -203,14 +214,24 @@ class WindowsUtils(object):
             LOG.error(err_msg)
             raise exception.VolumeBackendAPIException(data=err_msg)
 
-    def create_volume_from_snapshot(self, vol_name, snap_name):
+    def create_volume_from_snapshot(self, volume, snap_name):
         """Driver entry point for exporting snapshots as volumes."""
         try:
+            vol_name = volume['name']
+            vol_path = self.local_path(volume)
+
             wt_snapshot = self._conn_wmi.WT_Snapshot(Description=snap_name)[0]
             disk_id = wt_snapshot.Export()[0]
+            # This export is read-only, so it needs to be copied
+            # to another disk.
             wt_disk = self._conn_wmi.WT_Disk(WTD=disk_id)[0]
-            wt_disk.Description = vol_name
+            wt_disk.Description = '%s-temp' % vol_name
             wt_disk.put()
+            src_path = wt_disk.DevicePath
+
+            self.copy(src_path, vol_path)
+            self.create_volume(vol_path, vol_name)
+            wt_disk.Delete_()
         except wmi.x_wmi as exc:
             err_msg = (_(
                 'create_volume_from_snapshot: error when creating the volume '
@@ -321,6 +342,16 @@ class WindowsUtils(object):
             LOG.error(err_msg)
             raise exception.VolumeBackendAPIException(data=err_msg)
 
+    def local_path(self, volume, format=None):
+        base_vhd_folder = CONF.windows_iscsi_lun_path
+        if not os.path.exists(base_vhd_folder):
+            LOG.debug('Creating folder: %s' % base_vhd_folder)
+            os.makedirs(base_vhd_folder)
+        if not format:
+            format = self.get_supported_format()
+        return os.path.join(base_vhd_folder, str(volume['name']) + "." +
+                            format)
+
     def check_min_windows_version(self, major, minor, build=0):
         version_str = self.get_windows_version()
         return map(int, version_str.split('.')) >= [major, minor, build]
@@ -347,6 +378,19 @@ class WindowsUtils(object):
             raise exception.VolumeBackendAPIException(
                 _('Operation failed with return value: %s') % ret_val)
 
+    def copy(self, src, dest):
+        # With large files this is 2x-3x faster than shutil.copy(src, dest),
+        # especially with UNC targets.
+        kernel32 = ctypes.windll.kernel32
+        kernel32.CopyFileW.restype = wintypes.BOOL
+
+        retcode = kernel32.CopyFileW(ctypes.c_wchar_p(src),
+                                     ctypes.c_wchar_p(dest),
+                                     wintypes.BOOL(True))
+        if not retcode:
+            raise IOError(_('The file copy from %(src)s to %(dest)s failed.')
+                          % {'src': src, 'dest': dest})
+
     def _wait_for_job(self, job_path):
         """Poll WMI job state and wait for completion."""
         job = self._get_wmi_obj(job_path)