]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Remove Hyper-V dependency in the Windows Cinder Volume Driver
authorJenkins <jenkins@review.openstack.org>
Sat, 19 Jul 2014 21:35:17 +0000 (21:35 +0000)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Fri, 25 Jul 2014 17:14:25 +0000 (20:14 +0300)
Currently, the Windows Cinder Volume driver depends on the Hyper-V
feature in order to be able to access the WMI namespaces that
are used for some of the volume related operations.

Those WMI calls must be urgently replaced with Win32 calls in order
to provide CI tests performed on Windows Storage Server edition.

Change-Id: Ib4b8bccee3c7ee86e6cc44e1b16d20d455e61612
Closes-Bug: #1346496

cinder/tests/windows/test_vhdutils.py
cinder/tests/windows/test_vhdutilsv2.py [deleted file]
cinder/tests/windows/test_windows.py
cinder/tests/windows/test_windows_utils.py [deleted file]
cinder/volume/drivers/windows/constants.py
cinder/volume/drivers/windows/utilsfactory.py [deleted file]
cinder/volume/drivers/windows/vhdutils.py
cinder/volume/drivers/windows/vhdutilsv2.py [deleted file]
cinder/volume/drivers/windows/windows.py
cinder/volume/drivers/windows/windows_utils.py

index 21af7816531f2926449ffc6fa863cf7e29e6986f..2caa03f9e0f4e998f03e3e6c3ed3721f567ec4d9 100644 (file)
 
 import mock
 
+from cinder import exception
 from cinder import test
+from cinder.volume.drivers.windows import constants
 from cinder.volume.drivers.windows import vhdutils
-from cinder.volume.drivers.windows import windows_utils
 
 
 class VHDUtilsTestCase(test.TestCase):
 
     _FAKE_FORMAT = 2
-    _FAKE_TYPE = 3
+    _FAKE_TYPE = constants.VHD_TYPE_DYNAMIC
     _FAKE_JOB_PATH = 'fake_job_path'
     _FAKE_VHD_PATH = r'C:\fake\vhd.vhd'
-    _FAKE_DESTINATION_PATH = r'C:\fake\destination.vhd'
+    _FAKE_DEST_PATH = r'C:\fake\destination.vhdx'
     _FAKE_RET_VAL = 0
     _FAKE_VHD_SIZE = 1024
+    _FAKE_DEVICE_ID = 'fake_device_id'
 
     def setUp(self):
         super(VHDUtilsTestCase, self).setUp()
-        windows_utils.WindowsUtils.__init__ = lambda x: None
-        vhdutils.VHDUtils.__init__ = lambda x: None
-        self.wutils = windows_utils.WindowsUtils()
-        self.wutils.check_ret_val = mock.MagicMock()
-        self.vhdutils = vhdutils.VHDUtils()
-        self.vhdutils._conn = mock.MagicMock()
-        self.vhdutils.utils = self.wutils
-        self.mock_img_svc = (
-            self.vhdutils._conn.Msvm_ImageManagementService()[0])
-        self.vhdutils._get_resize_method = mock.Mock(
-            return_value=self.mock_img_svc.ExpandVirtualHardDisk)
-
-    def test_convert_vhd(self):
-        self.mock_img_svc.ConvertVirtualHardDisk.return_value = (
-            self._FAKE_JOB_PATH, self._FAKE_RET_VAL)
-
-        self.vhdutils.convert_vhd(self._FAKE_VHD_PATH,
-                                  self._FAKE_DESTINATION_PATH,
-                                  self._FAKE_TYPE)
-
-        self.mock_img_svc.ConvertVirtualHardDisk.assert_called_once()
-        self.wutils.check_ret_val.assert_called_once_with(
-            self._FAKE_RET_VAL, self._FAKE_JOB_PATH)
-
-    def test_resize_vhd(self):
-        self.mock_img_svc.ExpandVirtualHardDisk.return_value = (
-            self._FAKE_JOB_PATH, self._FAKE_RET_VAL)
-
-        self.vhdutils.resize_vhd(self._FAKE_VHD_PATH,
-                                 self._FAKE_VHD_SIZE)
-
-        self.mock_img_svc.ExpandVirtualHardDisk.assert_called_once()
-        self.wutils.check_ret_val.assert_called_once_with(self._FAKE_RET_VAL,
-                                                          self._FAKE_JOB_PATH)
-        self.vhdutils._get_resize_method.assert_called_once()
-        self.mock_img_svc.ExpandVirtualHardDisk.assert_called_once_with(
-            Path=self._FAKE_VHD_PATH, MaxInternalSize=self._FAKE_VHD_SIZE)
+        self._setup_mocks()
+        self._vhdutils = vhdutils.VHDUtils()
+        self._vhdutils._msft_vendor_id = 'fake_vendor_id'
+
+        self.addCleanup(mock.patch.stopall)
+
+    def _setup_mocks(self):
+        fake_ctypes = mock.Mock()
+        # Use this in order to make assertions on the variables parsed by
+        # references.
+        fake_ctypes.byref = lambda x: x
+        fake_ctypes.c_wchar_p = lambda x: x
+
+        mock.patch.multiple(
+            'cinder.volume.drivers.windows.vhdutils', ctypes=fake_ctypes,
+            windll=mock.DEFAULT, wintypes=mock.DEFAULT, kernel32=mock.DEFAULT,
+            virtdisk=mock.DEFAULT, Win32_GUID=mock.DEFAULT,
+            Win32_RESIZE_VIRTUAL_DISK_PARAMETERS=mock.DEFAULT,
+            Win32_CREATE_VIRTUAL_DISK_PARAMETERS=mock.DEFAULT,
+            Win32_VIRTUAL_STORAGE_TYPE=mock.DEFAULT,
+            create=True).start()
+
+    def _test_convert_vhd(self, convertion_failed=False):
+        self._vhdutils._get_device_id_by_path = mock.Mock(
+            side_effect=(vhdutils.VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
+                         vhdutils.VIRTUAL_STORAGE_TYPE_DEVICE_VHDX))
+        self._vhdutils._close = mock.Mock()
+
+        fake_params = (
+            vhdutils.Win32_CREATE_VIRTUAL_DISK_PARAMETERS.return_value)
+        fake_vst = mock.Mock()
+        fake_source_vst = mock.Mock()
+
+        vhdutils.Win32_VIRTUAL_STORAGE_TYPE = mock.Mock(
+            side_effect=[fake_vst, None, fake_source_vst])
+        vhdutils.virtdisk.CreateVirtualDisk.return_value = int(
+            convertion_failed)
+
+        if convertion_failed:
+            self.assertRaises(exception.VolumeBackendAPIException,
+                              self._vhdutils.convert_vhd,
+                              self._FAKE_VHD_PATH, self._FAKE_DEST_PATH,
+                              self._FAKE_TYPE)
+        else:
+            self._vhdutils.convert_vhd(self._FAKE_VHD_PATH,
+                                       self._FAKE_DEST_PATH,
+                                       self._FAKE_TYPE)
+
+        self.assertEqual(vhdutils.VIRTUAL_STORAGE_TYPE_DEVICE_VHDX,
+                         fake_vst.DeviceId)
+        self.assertEqual(vhdutils.VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
+                         fake_source_vst.DeviceId)
+
+        vhdutils.virtdisk.CreateVirtualDisk.assert_called_with(
+            vhdutils.ctypes.byref(fake_vst),
+            vhdutils.ctypes.c_wchar_p(self._FAKE_DEST_PATH),
+            vhdutils.VIRTUAL_DISK_ACCESS_NONE, None,
+            vhdutils.CREATE_VIRTUAL_DISK_FLAG_NONE, 0,
+            vhdutils.ctypes.byref(fake_params), None,
+            vhdutils.ctypes.byref(vhdutils.wintypes.HANDLE()))
+        self.assertTrue(self._vhdutils._close.called)
+
+    def test_convert_vhd_successfully(self):
+        self._test_convert_vhd()
+
+    def test_convert_vhd_exception(self):
+        self._test_convert_vhd(True)
+
+    def _test_open(self, open_failed=False):
+        vhdutils.virtdisk.OpenVirtualDisk.return_value = int(open_failed)
+
+        fake_vst = vhdutils.Win32_VIRTUAL_STORAGE_TYPE.return_value
+
+        if open_failed:
+            self.assertRaises(exception.VolumeBackendAPIException,
+                              self._vhdutils._open,
+                              self._FAKE_DEVICE_ID, self._FAKE_VHD_PATH)
+        else:
+            self._vhdutils._open(self._FAKE_DEVICE_ID,
+                                 self._FAKE_VHD_PATH)
+
+        vhdutils.virtdisk.OpenVirtualDisk.assert_called_with(
+            vhdutils.ctypes.byref(fake_vst),
+            vhdutils.ctypes.c_wchar_p(self._FAKE_VHD_PATH),
+            vhdutils.VIRTUAL_DISK_ACCESS_ALL,
+            vhdutils.CREATE_VIRTUAL_DISK_FLAG_NONE, 0,
+            vhdutils.ctypes.byref(vhdutils.wintypes.HANDLE()))
+        self.assertEqual(self._FAKE_DEVICE_ID, fake_vst.DeviceId)
+
+    def test_open_success(self):
+        self._test_open()
+
+    def test_open_failed(self):
+        self._test_open(open_failed=True)
+
+    def _test_get_device_id_by_path(self,
+                                    get_device_failed=False):
+        if get_device_failed:
+            self.assertRaises(exception.VolumeBackendAPIException,
+                              self._vhdutils._get_device_id_by_path,
+                              self._FAKE_VHD_PATH[:-4])
+        else:
+            ret_val = self._vhdutils._get_device_id_by_path(
+                self._FAKE_VHD_PATH)
+
+            self.assertEqual(
+                ret_val,
+                vhdutils.VIRTUAL_STORAGE_TYPE_DEVICE_VHD)
+
+    def test_get_device_id_by_path_success(self):
+        self._test_get_device_id_by_path()
+
+    def test_get_device_id_by_path_failed(self):
+        self._test_get_device_id_by_path(get_device_failed=True)
+
+    def _test_resize_vhd(self, resize_failed=False):
+        fake_params = (
+            vhdutils.Win32_RESIZE_VIRTUAL_DISK_PARAMETERS.return_value)
+
+        self._vhdutils._open = mock.Mock(
+            return_value=vhdutils.ctypes.byref(
+                vhdutils.wintypes.HANDLE()))
+        self._vhdutils._close = mock.Mock()
+        self._vhdutils._get_device_id_by_path = mock.Mock(return_value=2)
+
+        vhdutils.virtdisk.ResizeVirtualDisk.return_value = int(
+            resize_failed)
+
+        if resize_failed:
+            self.assertRaises(exception.VolumeBackendAPIException,
+                              self._vhdutils.resize_vhd,
+                              self._FAKE_VHD_PATH,
+                              self._FAKE_VHD_SIZE)
+        else:
+            self._vhdutils.resize_vhd(self._FAKE_VHD_PATH,
+                                      self._FAKE_VHD_SIZE)
+
+        vhdutils.virtdisk.ResizeVirtualDisk.assert_called_with(
+            vhdutils.ctypes.byref(vhdutils.wintypes.HANDLE()),
+            vhdutils.RESIZE_VIRTUAL_DISK_FLAG_NONE,
+            vhdutils.ctypes.byref(fake_params),
+            None)
+        self.assertTrue(self._vhdutils._close.called)
+
+    def test_resize_vhd_success(self):
+        self._test_resize_vhd()
+
+    def test_resize_vhd_failed(self):
+        self._test_resize_vhd(resize_failed=True)
diff --git a/cinder/tests/windows/test_vhdutilsv2.py b/cinder/tests/windows/test_vhdutilsv2.py
deleted file mode 100644 (file)
index 667cfb1..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#  Copyright 2014 Cloudbase Solutions Srl
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import mock
-
-from cinder import test
-from cinder.volume.drivers.windows import vhdutilsv2
-from cinder.volume.drivers.windows import windows_utils
-
-
-class VHDUtilsV2TestCase(test.TestCase):
-
-    _FAKE_FORMAT = 2
-    _FAKE_TYPE = 3
-    _FAKE_JOB_PATH = 'fake_job_path'
-    _FAKE_VHD_PATH = r'C:\fake\vhd.vhd'
-    _FAKE_DESTINATION_PATH = r'C:\fake\destination.vhd'
-    _FAKE_RET_VAL = 0
-    _FAKE_VHD_SIZE = 1024
-
-    def setUp(self):
-        super(VHDUtilsV2TestCase, self).setUp()
-        windows_utils.WindowsUtils.__init__ = lambda x: None
-        vhdutilsv2.VHDUtilsV2.__init__ = lambda x: None
-        self.wutils = windows_utils.WindowsUtils()
-        self.wutils.check_ret_val = mock.MagicMock()
-        self.vhdutilsv2 = vhdutilsv2.VHDUtilsV2()
-        self.vhdutilsv2._conn = mock.MagicMock()
-        self.vhdutilsv2.utils = self.wutils
-        self.mock_img_svc = (
-            self.vhdutilsv2._conn.Msvm_ImageManagementService()[0])
-        self.vhdutilsv2._get_resize_method = mock.Mock(
-            return_value=self.mock_img_svc.ResizeVirtualHardDisk)
-
-    def test_convert_vhd(self):
-        self.mock_img_svc.ConvertVirtualHardDisk.return_value = (
-            self._FAKE_JOB_PATH, self._FAKE_RET_VAL)
-
-        self.vhdutilsv2.convert_vhd(self._FAKE_VHD_PATH,
-                                    self._FAKE_DESTINATION_PATH,
-                                    self._FAKE_TYPE)
-
-        self.mock_img_svc.ConvertVirtualHardDisk.assert_called_once()
-        self.wutils.check_ret_val.assert_called_once_with(
-            self._FAKE_RET_VAL, self._FAKE_JOB_PATH)
-
-    def test_resize_vhd(self):
-        self.mock_img_svc.ResizeVirtualHardDisk.return_value = (
-            self._FAKE_JOB_PATH, self._FAKE_RET_VAL)
-
-        self.vhdutilsv2.resize_vhd(self._FAKE_VHD_PATH,
-                                   self._FAKE_VHD_SIZE)
-
-        self.mock_img_svc.ResizeVirtualHardDisk.assert_called_once()
-        self.wutils.check_ret_val.assert_called_once_with(self._FAKE_RET_VAL,
-                                                          self._FAKE_JOB_PATH)
-        self.vhdutilsv2._get_resize_method.assert_called_once()
-        self.mock_img_svc.ResizeVirtualHardDisk.assert_called_once_with(
-            Path=self._FAKE_VHD_PATH, MaxInternalSize=self._FAKE_VHD_SIZE)
index dfae30c342fda1bcc051983c1f4beee2e4c0d5ad..c92e77cbb2c2da4322bcb88263bea0d812c96949 100644 (file)
@@ -34,7 +34,6 @@ from cinder.openstack.common import fileutils
 from cinder.tests.windows import db_fakes
 from cinder.volume import configuration as conf
 from cinder.volume.drivers.windows import constants
-from cinder.volume.drivers.windows import utilsfactory
 from cinder.volume.drivers.windows import vhdutils
 from cinder.volume.drivers.windows import windows
 from cinder.volume.drivers.windows import windows_utils
@@ -67,12 +66,7 @@ class TestWindowsDriver(test.TestCase):
         def fake_wutils__init__(self):
             pass
 
-        def fake_get_vhdutils():
-            return vhdutils.VHDUtils()
-
         windows_utils.WindowsUtils.__init__ = fake_wutils__init__
-        vhdutils.VHDUtils.__init__ = lambda x: None
-        utilsfactory.get_vhdutils = fake_get_vhdutils
 
     def fake_local_path(self, volume):
         return os.path.join(CONF.windows_iscsi_lun_path,
@@ -333,7 +327,8 @@ class TestWindowsDriver(test.TestCase):
                                                  temp_vhd_path)
         if supported_format == 'vhdx':
             upload_image = upload_image[:-1]
-            vhdutils.VHDUtils.convert_vhd(temp_vhd_path, upload_image)
+            vhdutils.VHDUtils.convert_vhd(temp_vhd_path, upload_image,
+                                          constants.VHD_TYPE_DYNAMIC)
 
         image_utils.upload_volume(None, None, image_meta, upload_image, 'vpc')
 
diff --git a/cinder/tests/windows/test_windows_utils.py b/cinder/tests/windows/test_windows_utils.py
deleted file mode 100644 (file)
index e237b66..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#  Copyright 2014 Cloudbase Solutions Srl
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import mock
-
-from cinder import exception
-from cinder import test
-from cinder.volume.drivers.windows import constants
-from cinder.volume.drivers.windows import windows_utils
-
-
-class WindowsUtilsTestCase(test.TestCase):
-
-    _FAKE_JOB_PATH = 'fake_job_path'
-    _FAKE_RET_VAL = 0
-    _FAKE_RET_VAL_ERROR = 10
-    _FAKE_VHD_SIZE = 1024
-    _FAKE_JOB = 'fake_job'
-
-    def setUp(self):
-        super(WindowsUtilsTestCase, self).setUp()
-        windows_utils.WindowsUtils.__init__ = lambda x: None
-        self.wutils = windows_utils.WindowsUtils()
-        self.wutils.time = mock.MagicMock()
-
-    def _test_check_ret_val(self, job_started, job_failed):
-        self.wutils._wait_for_job = mock.Mock(return_value=self._FAKE_JOB)
-        if job_started:
-            ret_val = self.wutils.check_ret_val(
-                constants.WMI_JOB_STATUS_STARTED, self._FAKE_JOB_PATH)
-            self.assertEqual(ret_val, self._FAKE_JOB)
-            self.wutils._wait_for_job.assert_called_once_with(
-                self._FAKE_JOB_PATH)
-
-        elif job_failed:
-            self.assertRaises(exception.VolumeBackendAPIException,
-                              self.wutils.check_ret_val,
-                              10, self._FAKE_JOB_PATH)
-
-    def test_check_ret_val_failed_job(self):
-        self._test_check_ret_val(False, True)
-
-    def test_check_ret_val_job_started(self):
-        self._test_check_ret_val(True, False)
-
-    def _test_wait_for_job(self, job_running, job_failed):
-        fake_job = mock.MagicMock()
-        fake_job2 = mock.MagicMock()
-        fake_job2.JobState = constants.WMI_JOB_STATE_COMPLETED
-
-        if job_running:
-            fake_job.JobState = constants.WMI_JOB_STATE_RUNNING
-        elif job_failed:
-            fake_job.JobState = self._FAKE_RET_VAL_ERROR
-            fake_job.GetError = mock.Mock(return_value=(
-                1, self._FAKE_RET_VAL_ERROR))
-        else:
-            fake_job.JobState = constants.WMI_JOB_STATE_COMPLETED
-
-        self.wutils._get_wmi_obj = mock.Mock(side_effect=[fake_job, fake_job2])
-
-        if job_failed:
-            self.assertRaises(exception.VolumeBackendAPIException,
-                              self.wutils._wait_for_job,
-                              self._FAKE_JOB_PATH)
-        else:
-            self.wutils._wait_for_job(self._FAKE_JOB_PATH)
-            if job_running:
-                call_count = 2
-            else:
-                call_count = 1
-            self.assertEqual(call_count, self.wutils._get_wmi_obj.call_count)
-
-    def test_wait_for_running_job(self):
-        self._test_wait_for_job(True, False)
-
-    def test_wait_for_failed_job(self):
-        self._test_wait_for_job(False, True)
index 4c964d93de35861a2795267a83ad48b1c2120cf0..6616d3fabeed3fb7ceb7c4b6c1e8ababda0bddbf 100644 (file)
@@ -12,9 +12,5 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-WMI_JOB_STATUS_STARTED = 4096
-WMI_JOB_STATE_RUNNING = 4
-WMI_JOB_STATE_COMPLETED = 7
-
 VHD_TYPE_FIXED = 2
 VHD_TYPE_DYNAMIC = 3
diff --git a/cinder/volume/drivers/windows/utilsfactory.py b/cinder/volume/drivers/windows/utilsfactory.py
deleted file mode 100644 (file)
index 57a3692..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2014 Cloudbase Solutions Srl
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from cinder.openstack.common import log as logging
-from cinder.volume.drivers.windows import vhdutils
-from cinder.volume.drivers.windows import vhdutilsv2
-from cinder.volume.drivers.windows import windows_utils
-
-LOG = logging.getLogger(__name__)
-
-
-def _get_class(v1_class, v2_class):
-    # V2 classes are supported starting from Hyper-V Server 2012 and
-    # Windows Server 2012 (kernel version 6.2)
-    if not windows_utils.WindowsUtils().check_min_windows_version(6, 2):
-        cls = v2_class
-    else:
-        cls = v1_class
-    LOG.debug("Loading class: %(module_name)s.%(class_name)s",
-              {'module_name': cls.__module__, 'class_name': cls.__name__})
-    return cls
-
-
-def get_vhdutils():
-    return _get_class(vhdutils.VHDUtils, vhdutilsv2.VHDUtilsV2)()
index a6b3d80c1aa3dbde4446bea0ce2027eac17e0aaf..385bb4f53638ac01328f85d7e316774eff04a1c9 100644 (file)
@@ -23,35 +23,184 @@ See "Download the Specifications Without Registering"
 Official VHDX format specs can be retrieved at:
 http://www.microsoft.com/en-us/download/details.aspx?id=34750
 """
+import ctypes
 import os
 
 if os.name == 'nt':
-    import wmi
+    from ctypes import windll
+    from ctypes import wintypes
 
+    kernel32 = windll.kernel32
+    virtdisk = windll.virtdisk
+
+from cinder import exception
+from cinder.openstack.common.gettextutils import _
 from cinder.openstack.common import log as logging
-from cinder.volume.drivers.windows import windows_utils
+from cinder.volume.drivers.windows import constants
 
 LOG = logging.getLogger(__name__)
 
+if os.name == 'nt':
+    class Win32_GUID(ctypes.Structure):
+        _fields_ = [("Data1", wintypes.DWORD),
+                    ("Data2", wintypes.WORD),
+                    ("Data3", wintypes.WORD),
+                    ("Data4", wintypes.BYTE * 8)]
+
+    class Win32_VIRTUAL_STORAGE_TYPE(ctypes.Structure):
+        _fields_ = [
+            ('DeviceId', wintypes.DWORD),
+            ('VendorId', Win32_GUID)
+        ]
+
+    class Win32_RESIZE_VIRTUAL_DISK_PARAMETERS(ctypes.Structure):
+        _fields_ = [
+            ('Version', wintypes.DWORD),
+            ('NewSize', ctypes.c_ulonglong)
+        ]
+
+    class Win32_CREATE_VIRTUAL_DISK_PARAMETERS(ctypes.Structure):
+        _fields_ = [
+            ('Version', wintypes.DWORD),
+            ('UniqueId', Win32_GUID),
+            ('MaximumSize', ctypes.c_ulonglong),
+            ('BlockSizeInBytes', wintypes.ULONG),
+            ('SectorSizeInBytes', wintypes.ULONG),
+            ('PhysicalSectorSizeInBytes', wintypes.ULONG),
+            ('ParentPath', wintypes.LPCWSTR),
+            ('SourcePath', wintypes.LPCWSTR),
+            ('OpenFlags', wintypes.DWORD),
+            ('ParentVirtualStorageType', Win32_VIRTUAL_STORAGE_TYPE),
+            ('SourceVirtualStorageType', Win32_VIRTUAL_STORAGE_TYPE),
+            ('ResiliencyGuid', Win32_GUID)
+        ]
+
+VIRTUAL_STORAGE_TYPE_DEVICE_ISO = 1
+VIRTUAL_STORAGE_TYPE_DEVICE_VHD = 2
+VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 3
+VIRTUAL_DISK_ACCESS_NONE = 0
+VIRTUAL_DISK_ACCESS_ALL = 0x003f0000
+VIRTUAL_DISK_ACCESS_CREATE = 0x00100000
+OPEN_VIRTUAL_DISK_FLAG_NONE = 0
+RESIZE_VIRTUAL_DISK_FLAG_NONE = 0
+RESIZE_VIRTUAL_DISK_VERSION_1 = 1
+CREATE_VIRTUAL_DISK_VERSION_2 = 2
+CREATE_VHD_PARAMS_DEFAULT_BLOCK_SIZE = 0
+CREATE_VIRTUAL_DISK_FLAG_NONE = 0
+CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION = 1
+
 
 class VHDUtils(object):
 
     def __init__(self):
-        self.utils = windows_utils.WindowsUtils()
-        self._conn = wmi.WMI(moniker='//./root/virtualization')
+        self._ext_device_id_map = {
+            'vhd': VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
+            'vhdx': VIRTUAL_STORAGE_TYPE_DEVICE_VHDX}
+        self.create_virtual_disk_flags = {
+            constants.VHD_TYPE_FIXED: (
+                CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION),
+            constants.VHD_TYPE_DYNAMIC: CREATE_VIRTUAL_DISK_FLAG_NONE
+        }
+
+        if os.name == 'nt':
+            self._msft_vendor_id = (
+                self.get_WIN32_VIRTUAL_STORAGE_TYPE_VENDOR_MSFT())
+
+    @staticmethod
+    def get_WIN32_VIRTUAL_STORAGE_TYPE_VENDOR_MSFT():
+        guid = Win32_GUID()
+        guid.Data1 = 0xec984aec
+        guid.Data2 = 0xa0f9
+        guid.Data3 = 0x47e9
+        ByteArray8 = wintypes.BYTE * 8
+        guid.Data4 = ByteArray8(0x90, 0x1f, 0x71, 0x41, 0x5a, 0x66, 0x34, 0x5b)
+        return guid
+
+    def _open(self, device_id, vhd_path):
+        vst = Win32_VIRTUAL_STORAGE_TYPE()
+        vst.DeviceId = device_id
+        vst.VendorId = self._msft_vendor_id
 
-    def convert_vhd(self, src, dest, vhd_type=None):
-        image_man_svc = self._conn.Msvm_ImageManagementService()[0]
-        (job_path, ret_val) = image_man_svc.ConvertVirtualHardDisk(
-            SourcePath=src, DestinationPath=dest, Type=vhd_type)
-        self.utils.check_ret_val(ret_val, job_path)
+        handle = wintypes.HANDLE()
+        ret_val = virtdisk.OpenVirtualDisk(ctypes.byref(vst),
+                                           ctypes.c_wchar_p(vhd_path),
+                                           VIRTUAL_DISK_ACCESS_ALL,
+                                           OPEN_VIRTUAL_DISK_FLAG_NONE,
+                                           0, ctypes.byref(handle))
+        if ret_val:
+            raise exception.VolumeBackendAPIException(
+                _("Opening virtual disk failed with error: %s") % ret_val)
+        return handle
 
-    def _get_resize_method(self):
-        image_man_svc = self._conn.Msvm_ImageManagementService()[0]
-        return image_man_svc.ExpandVirtualHardDisk
+    def _close(self, handle):
+        kernel32.CloseHandle(handle)
+
+    def _get_device_id_by_path(self, vhd_path):
+        ext = os.path.splitext(vhd_path)[1][1:].lower()
+        device_id = self._ext_device_id_map.get(ext)
+        if not device_id:
+            raise exception.VolumeBackendAPIException(
+                _("Unsupported virtual disk extension: %s") % ext)
+        return device_id
 
     def resize_vhd(self, vhd_path, new_max_size):
-        resize = self._get_resize_method()
-        (job_path, ret_val) = resize(Path=vhd_path,
-                                     MaxInternalSize=new_max_size)
-        self.utils.check_ret_val(ret_val, job_path)
+        device_id = self._get_device_id_by_path(vhd_path)
+        handle = self._open(device_id, vhd_path)
+
+        params = Win32_RESIZE_VIRTUAL_DISK_PARAMETERS()
+        params.Version = RESIZE_VIRTUAL_DISK_VERSION_1
+        params.NewSize = new_max_size
+
+        ret_val = virtdisk.ResizeVirtualDisk(
+            handle,
+            RESIZE_VIRTUAL_DISK_FLAG_NONE,
+            ctypes.byref(params),
+            None)
+        self._close(handle)
+
+        if ret_val:
+            raise exception.VolumeBackendAPIException(
+                _("Virtual disk resize failed with error: %s") % ret_val)
+
+    def convert_vhd(self, src, dest, vhd_type):
+        src_device_id = self._get_device_id_by_path(src)
+        dest_device_id = self._get_device_id_by_path(dest)
+
+        vst = Win32_VIRTUAL_STORAGE_TYPE()
+        vst.DeviceId = dest_device_id
+        vst.VendorId = self._msft_vendor_id
+
+        params = Win32_CREATE_VIRTUAL_DISK_PARAMETERS()
+        params.Version = CREATE_VIRTUAL_DISK_VERSION_2
+        params.UniqueId = Win32_GUID()
+        params.MaximumSize = 0
+        params.BlockSizeInBytes = CREATE_VHD_PARAMS_DEFAULT_BLOCK_SIZE
+        params.SectorSizeInBytes = 0x200
+        params.PhysicalSectorSizeInBytes = 0x200
+        params.ParentPath = None
+        params.SourcePath = src
+        params.OpenFlags = OPEN_VIRTUAL_DISK_FLAG_NONE
+        params.ParentVirtualStorageType = Win32_VIRTUAL_STORAGE_TYPE()
+        params.SourceVirtualStorageType = Win32_VIRTUAL_STORAGE_TYPE()
+        params.SourceVirtualStorageType.DeviceId = src_device_id
+        params.SourceVirtualStorageType.VendorId = self._msft_vendor_id
+        params.ResiliencyGuid = Win32_GUID()
+
+        handle = wintypes.HANDLE()
+        create_virtual_disk_flag = self.create_virtual_disk_flags.get(vhd_type)
+
+        ret_val = virtdisk.CreateVirtualDisk(
+            ctypes.byref(vst),
+            ctypes.c_wchar_p(dest),
+            VIRTUAL_DISK_ACCESS_NONE,
+            None,
+            create_virtual_disk_flag,
+            0,
+            ctypes.byref(params),
+            None,
+            ctypes.byref(handle))
+        self._close(handle)
+
+        if ret_val:
+            raise exception.VolumeBackendAPIException(
+                _("Virtual disk conversion failed with error: %s") % ret_val)
diff --git a/cinder/volume/drivers/windows/vhdutilsv2.py b/cinder/volume/drivers/windows/vhdutilsv2.py
deleted file mode 100644 (file)
index e50cca6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2014 Cloudbase Solutions Srl
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-"""
-Utility class for VHD related operations.
-Based on the "root/virtualization/v2" namespace available starting with
-Hyper-V Server / Windows Server 2012.
-"""
-import os
-
-if os.name == 'nt':
-    import wmi
-
-from cinder.openstack.common import log as logging
-from cinder.volume.drivers.windows import constants
-from cinder.volume.drivers.windows import vhdutils
-from cinder.volume.drivers.windows import windows_utils
-
-LOG = logging.getLogger(__name__)
-
-
-class VHDUtilsV2(vhdutils.VHDUtils):
-
-    _vhd_format_map = {
-        'vhd': 2,
-        'vhdx': 3,
-    }
-
-    def __init__(self):
-        self.utils = windows_utils.WindowsUtils()
-        self._conn = wmi.WMI(moniker='//./root/virtualization/v2')
-
-    def _get_resize_method(self):
-        image_man_svc = self._conn.Msvm_ImageManagementService()[0]
-        return image_man_svc.ResizeVirtualHardDisk
-
-    def convert_vhd(self, src, dest, vhd_type=constants.VHD_TYPE_DYNAMIC):
-        vhd_info = self._conn.Msvm_VirtualHardDiskSettingData.new()
-        ext = os.path.splitext(dest)[1][1:]
-        format = self._vhd_format_map.get(ext)
-
-        vhd_info.Type = vhd_type
-        vhd_info.Path = dest
-        vhd_info.Format = format
-        vhd_info.BlockSize = 0
-        vhd_info.LogicalSectorSize = 0
-        vhd_info.ParentPath = None
-
-        image_man_svc = self._conn.Msvm_ImageManagementService()[0]
-        (job_path, ret_val) = image_man_svc.ConvertVirtualHardDisk(
-            SourcePath=src, VirtualDiskSettingData=vhd_info.GetText_(1))
-        self.utils.check_ret_val(ret_val, job_path)
index e4d8e204825a8f289d1aaa031d3a78d9b590c570..72044c062f96bd32526ddf38d56304d442513c20 100644 (file)
@@ -27,7 +27,8 @@ from cinder.image import image_utils
 from cinder.openstack.common import fileutils
 from cinder.openstack.common import log as logging
 from cinder.volume import driver
-from cinder.volume.drivers.windows import utilsfactory
+from cinder.volume.drivers.windows import constants
+from cinder.volume.drivers.windows import vhdutils
 from cinder.volume.drivers.windows import windows_utils
 
 LOG = logging.getLogger(__name__)
@@ -60,7 +61,7 @@ class WindowsDriver(driver.ISCSIDriver):
         Validate the flags we care about
         """
         self.utils = windows_utils.WindowsUtils()
-        self.vhdutils = utilsfactory.get_vhdutils()
+        self.vhdutils = vhdutils.VHDUtils()
 
     def check_for_setup_error(self):
         """Check that the driver is working and can communicate."""
@@ -196,7 +197,8 @@ class WindowsDriver(driver.ISCSIDriver):
             # convert the image to vhd before attempting upload
             if disk_format == 'vhdx':
                 upload_image = upload_image[:-1]
-                self.vhdutils.convert_vhd(temp_vhd_path, upload_image)
+                self.vhdutils.convert_vhd(temp_vhd_path, upload_image,
+                                          constants.VHD_TYPE_DYNAMIC)
 
             image_utils.upload_volume(context, image_service, image_meta,
                                       upload_image, 'vpc')
index 63dd9418720aafd1e4168a73d7cd0c590b59475a..437b0d90397cadbd0067b5f0422b639ed37acad4 100644 (file)
@@ -18,7 +18,6 @@ Utility class for Windows Storage Server 2012 volume related operations.
 
 import ctypes
 import os
-import time
 
 from oslo.config import cfg
 
@@ -372,13 +371,6 @@ class WindowsUtils(object):
         else:
             return constants.VHD_TYPE_FIXED
 
-    def check_ret_val(self, ret_val, job_path, success_values=[0]):
-        if ret_val == constants.WMI_JOB_STATUS_STARTED:
-            return self._wait_for_job(job_path)
-        elif ret_val not in success_values:
-            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.
@@ -391,47 +383,3 @@ class WindowsUtils(object):
         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)
-
-        while job.JobState == constants.WMI_JOB_STATE_RUNNING:
-            time.sleep(0.1)
-            job = self._get_wmi_obj(job_path)
-        if job.JobState != constants.WMI_JOB_STATE_COMPLETED:
-            job_state = job.JobState
-            if job.path().Class == "Msvm_ConcreteJob":
-                err_sum_desc = job.ErrorSummaryDescription
-                err_desc = job.ErrorDescription
-                err_code = job.ErrorCode
-                raise exception.VolumeBackendAPIException(
-                    _("WMI job failed with status "
-                      "%(job_state)d. Error details: "
-                      "%(err_sum_desc)s - %(err_desc)s - "
-                      "Error code: %(err_code)d") %
-                    {'job_state': job_state,
-                     'err_sum_desc': err_sum_desc,
-                     'err_desc': err_desc,
-                     'err_code': err_code})
-            else:
-                (error, ret_val) = job.GetError()
-                if not ret_val and error:
-                    raise exception.VolumeBackendAPIException(
-                        _("WMI job failed with status %(job_state)d. "
-                          "Job path: %(job_path)s Error details: "
-                          "%(error)s") % {'job_state': job_state,
-                                          'error': error,
-                                          'job_path': job_path})
-                else:
-                    raise exception.VolumeBackendAPIException(
-                        _("WMI job failed with status %d. No error "
-                          "description available") % job_state)
-        desc = job.Description
-        elap = job.ElapsedTime
-        LOG.debug("WMI job succeeded: %(desc)s, Elapsed=%(elap)s" %
-                  {'desc': desc, 'elap': elap})
-        return job
-
-    def _get_wmi_obj(self, path):
-        return wmi.WMI(moniker=path.replace('\\', '/'))