]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fixes cinder volume create on Windows Server 2012 R2
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Tue, 8 Apr 2014 15:57:41 +0000 (18:57 +0300)
committerPetrut Lucian <lpetrut@cloudbasesolutions.com>
Sat, 21 Jun 2014 18:02:17 +0000 (18:02 +0000)
Windows Server 2012 R2 does not support vhd images as iSCSI disks,
requiring VHDX images. For this reason, the cinder driver fails to
create volumes. For the moment, the default format is vhd.
On WSS 2012 R2, we should use vhdx images as default.

This patch introduces vhd/vhdx related methods for both v1 and v2
wmi virtualization namespaces.

Closes-Bug: #1299150
Change-Id: I272988dc5b0e3b8129c5e4d3c79bea1c292701c8

cinder/tests/windows/test_vhdutils.py [new file with mode: 0644]
cinder/tests/windows/test_vhdutilsv2.py [new file with mode: 0644]
cinder/tests/windows/test_windows.py [moved from cinder/tests/test_windows.py with 84% similarity]
cinder/tests/windows/test_windows_utils.py [moved from cinder/tests/test_windows_utils.py with 69% similarity]
cinder/volume/drivers/windows/utilsfactory.py [new file with mode: 0644]
cinder/volume/drivers/windows/vhdutils.py [new file with mode: 0644]
cinder/volume/drivers/windows/vhdutilsv2.py [new file with mode: 0644]
cinder/volume/drivers/windows/windows.py
cinder/volume/drivers/windows/windows_utils.py

diff --git a/cinder/tests/windows/test_vhdutils.py b/cinder/tests/windows/test_vhdutils.py
new file mode 100644 (file)
index 0000000..21af781
--- /dev/null
@@ -0,0 +1,70 @@
+#  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 vhdutils
+from cinder.volume.drivers.windows import windows_utils
+
+
+class VHDUtilsTestCase(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(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)
diff --git a/cinder/tests/windows/test_vhdutilsv2.py b/cinder/tests/windows/test_vhdutilsv2.py
new file mode 100644 (file)
index 0000000..667cfb1
--- /dev/null
@@ -0,0 +1,70 @@
+#  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)
similarity index 84%
rename from cinder/tests/test_windows.py
rename to cinder/tests/windows/test_windows.py
index 99cd3cbe9cdf106a25befd068fb3da466e3e9571..bd73e3ab14bc6fa2f45bb3f533d4becc7043d3a3 100644 (file)
@@ -30,9 +30,12 @@ from cinder import test
 
 from cinder.image import image_utils
 
+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
 
@@ -56,7 +59,6 @@ class TestWindowsDriver(test.TestCase):
         self._setup_stubs()
         configuration = conf.Configuration(None)
         configuration.append_config_values(windows.windows_opts)
-
         self._driver = windows.WindowsDriver(configuration=configuration)
         self._driver.do_setup({})
 
@@ -64,7 +66,13 @@ 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,
@@ -257,14 +265,17 @@ class TestWindowsDriver(test.TestCase):
 
         volume = db_fakes.get_fake_volume_info()
 
+        fake_get_supported_type = lambda x: constants.VHD_TYPE_FIXED
         self.stubs.Set(drv, 'local_path', self.fake_local_path)
+        self.stubs.Set(windows_utils.WindowsUtils, 'get_supported_vhd_type',
+                       fake_get_supported_type)
 
         self.mox.StubOutWithMock(os, 'makedirs')
         self.mox.StubOutWithMock(os, 'unlink')
         self.mox.StubOutWithMock(image_utils, 'create_temporary_file')
         self.mox.StubOutWithMock(image_utils, 'fetch_to_vhd')
-        self.mox.StubOutWithMock(windows_utils.WindowsUtils, 'convert_vhd')
-        self.mox.StubOutWithMock(windows_utils.WindowsUtils, 'resize_vhd')
+        self.mox.StubOutWithMock(vhdutils.VHDUtils, 'convert_vhd')
+        self.mox.StubOutWithMock(vhdutils.VHDUtils, 'resize_vhd')
         self.mox.StubOutWithMock(windows_utils.WindowsUtils,
                                  'change_disk_status')
 
@@ -283,11 +294,11 @@ class TestWindowsDriver(test.TestCase):
         windows_utils.WindowsUtils.change_disk_status(volume['name'],
                                                       mox.IsA(bool))
         os.unlink(mox.IsA(str))
-        windows_utils.WindowsUtils.convert_vhd(fake_temp_path,
-                                               fake_volume_path,
-                                               constants.VHD_TYPE_FIXED)
-        windows_utils.WindowsUtils.resize_vhd(fake_volume_path,
-                                              volume['size'] << 30)
+        vhdutils.VHDUtils.convert_vhd(fake_temp_path,
+                                      fake_volume_path,
+                                      constants.VHD_TYPE_FIXED)
+        vhdutils.VHDUtils.resize_vhd(fake_volume_path,
+                                     volume['size'] << 30)
         windows_utils.WindowsUtils.change_disk_status(volume['name'],
                                                       mox.IsA(bool))
         os.unlink(mox.IsA(str))
@@ -296,32 +307,49 @@ class TestWindowsDriver(test.TestCase):
 
         drv.copy_image_to_volume(None, volume, None, None)
 
-    def test_copy_volume_to_image(self):
+    def _test_copy_volume_to_image(self, supported_format):
         drv = self._driver
 
         vol = db_fakes.get_fake_volume_info()
 
         image_meta = db_fakes.get_fake_image_meta()
 
+        fake_get_supported_format = lambda x: supported_format
         self.stubs.Set(drv, 'local_path', self.fake_local_path)
+        self.stubs.Set(windows_utils.WindowsUtils, 'get_supported_format',
+                       fake_get_supported_format)
 
+        self.mox.StubOutWithMock(fileutils, 'delete_if_exists')
         self.mox.StubOutWithMock(image_utils, 'upload_volume')
+        self.mox.StubOutWithMock(windows_utils.WindowsUtils, 'copy_vhd_disk')
+        self.mox.StubOutWithMock(vhdutils.VHDUtils, 'convert_vhd')
 
         temp_vhd_path = os.path.join(CONF.image_conversion_dir,
-                                     str(image_meta['id']) + ".vhd")
-
-        image_utils.upload_volume(None, None, image_meta, temp_vhd_path, 'vpc')
-
-        self.mox.StubOutWithMock(windows_utils.WindowsUtils,
-                                 'copy_vhd_disk')
+                                     str(image_meta['id']) + "." +
+                                     supported_format)
+        upload_image = temp_vhd_path
 
         windows_utils.WindowsUtils.copy_vhd_disk(self.fake_local_path(vol),
                                                  temp_vhd_path)
+        if supported_format == 'vhdx':
+            upload_image = upload_image[:-1]
+            vhdutils.VHDUtils.convert_vhd(temp_vhd_path, upload_image)
+
+        image_utils.upload_volume(None, None, image_meta, upload_image, 'vpc')
+
+        fileutils.delete_if_exists(temp_vhd_path)
+        fileutils.delete_if_exists(upload_image)
 
         self.mox.ReplayAll()
 
         drv.copy_volume_to_image(None, vol, None, image_meta)
 
+    def test_copy_volume_to_image_using_vhd(self):
+        self._test_copy_volume_to_image('vhd')
+
+    def test_copy_volume_to_image_using_vhdx(self):
+        self._test_copy_volume_to_image('vhdx')
+
     def test_create_cloned_volume(self):
         drv = self._driver
 
@@ -331,6 +359,8 @@ class TestWindowsDriver(test.TestCase):
         self.mox.StubOutWithMock(windows_utils.WindowsUtils,
                                  'create_volume')
 
+        self.stubs.Set(drv, 'local_path', self.fake_local_path)
+
         windows_utils.WindowsUtils.create_volume(mox.IgnoreArg(),
                                                  mox.IgnoreArg(),
                                                  mox.IgnoreArg())
similarity index 69%
rename from cinder/tests/test_windows_utils.py
rename to cinder/tests/windows/test_windows_utils.py
index 7cee500afaf4d596e9025d65082367689ed7def8..e237b664cacb5d3790980fcbf8353348b6e80857 100644 (file)
@@ -22,11 +22,7 @@ from cinder.volume.drivers.windows import windows_utils
 
 class WindowsUtilsTestCase(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_RET_VAL_ERROR = 10
     _FAKE_VHD_SIZE = 1024
@@ -36,36 +32,8 @@ class WindowsUtilsTestCase(test.TestCase):
         super(WindowsUtilsTestCase, self).setUp()
         windows_utils.WindowsUtils.__init__ = lambda x: None
         self.wutils = windows_utils.WindowsUtils()
-        self.wutils._conn_virt = mock.MagicMock()
         self.wutils.time = mock.MagicMock()
 
-    def test_convert_vhd(self):
-        self.wutils.check_ret_val = mock.MagicMock()
-        mock_img_svc = self.wutils._conn_virt.Msvm_ImageManagementService()[0]
-        mock_img_svc.ConvertVirtualHardDisk.return_value = (
-            self._FAKE_JOB_PATH, self._FAKE_RET_VAL)
-
-        self.wutils.convert_vhd(self._FAKE_VHD_PATH,
-                                self._FAKE_DESTINATION_PATH,
-                                self._FAKE_TYPE)
-
-        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.wutils.check_ret_val = mock.MagicMock()
-        mock_img_svc = self.wutils._conn_virt.Msvm_ImageManagementService()[0]
-        mock_img_svc.ExpandVirtualHardDisk.return_value = (self._FAKE_JOB_PATH,
-                                                           self._FAKE_RET_VAL)
-
-        self.wutils.resize_vhd(self._FAKE_VHD_PATH,
-                               self._FAKE_VHD_SIZE)
-
-        mock_img_svc.ExpandVirtualHardDisk.assert_called_once()
-        self.wutils.check_ret_val.assert_called_once_with(self._FAKE_RET_VAL,
-                                                          self._FAKE_JOB_PATH)
-
     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:
diff --git a/cinder/volume/drivers/windows/utilsfactory.py b/cinder/volume/drivers/windows/utilsfactory.py
new file mode 100644 (file)
index 0000000..57a3692
--- /dev/null
@@ -0,0 +1,37 @@
+# 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)()
diff --git a/cinder/volume/drivers/windows/vhdutils.py b/cinder/volume/drivers/windows/vhdutils.py
new file mode 100644 (file)
index 0000000..a6b3d80
--- /dev/null
@@ -0,0 +1,57 @@
+# 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.
+
+Official VHD format specs can be retrieved at:
+http://technet.microsoft.com/en-us/library/bb676673.aspx
+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 os
+
+if os.name == 'nt':
+    import wmi
+
+from cinder.openstack.common import log as logging
+from cinder.volume.drivers.windows import windows_utils
+
+LOG = logging.getLogger(__name__)
+
+
+class VHDUtils(object):
+
+    def __init__(self):
+        self.utils = windows_utils.WindowsUtils()
+        self._conn = wmi.WMI(moniker='//./root/virtualization')
+
+    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)
+
+    def _get_resize_method(self):
+        image_man_svc = self._conn.Msvm_ImageManagementService()[0]
+        return image_man_svc.ExpandVirtualHardDisk
+
+    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)
diff --git a/cinder/volume/drivers/windows/vhdutilsv2.py b/cinder/volume/drivers/windows/vhdutilsv2.py
new file mode 100644 (file)
index 0000000..e50cca6
--- /dev/null
@@ -0,0 +1,64 @@
+# 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 fbafb85fb28d06451d4496ba112a6791eec8530b..f88a9bc78956a0eb95cc0c54a52e6088445a34ec 100644 (file)
@@ -24,9 +24,10 @@ import os
 from oslo.config import cfg
 
 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 constants
+from cinder.volume.drivers.windows import utilsfactory
 from cinder.volume.drivers.windows import windows_utils
 
 LOG = logging.getLogger(__name__)
@@ -59,6 +60,7 @@ class WindowsDriver(driver.ISCSIDriver):
         Validate the flags we care about
         """
         self.utils = windows_utils.WindowsUtils()
+        self.vhdutils = utilsfactory.get_vhdutils()
 
     def check_for_setup_error(self):
         """Check that the driver is working and can communicate."""
@@ -97,12 +99,15 @@ class WindowsDriver(driver.ISCSIDriver):
 
         self.utils.create_volume(vhd_path, vol_name, vol_size)
 
-    def local_path(self, volume):
+    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)
-        return os.path.join(base_vhd_folder, str(volume['name']) + ".vhd")
+        if not format:
+            format = self.utils.get_supported_format()
+        return os.path.join(base_vhd_folder, str(volume['name']) + "." +
+                            format)
 
     def delete_volume(self, volume):
         """Driver entry point for destroying existing volumes."""
@@ -168,6 +173,7 @@ class WindowsDriver(driver.ISCSIDriver):
     def copy_image_to_volume(self, context, volume, image_service, image_id):
         """Fetch the image from image_service and create a volume using it."""
         # Convert to VHD and file back to VHD
+        vhd_type = self.utils.get_supported_vhd_type()
         if (CONF.image_conversion_dir and not
                 os.path.exists(CONF.image_conversion_dir)):
             os.makedirs(CONF.image_conversion_dir)
@@ -179,21 +185,32 @@ class WindowsDriver(driver.ISCSIDriver):
             # the desired image.
             self.utils.change_disk_status(volume['name'], False)
             os.unlink(volume_path)
-            self.utils.convert_vhd(tmp, volume_path,
-                                   constants.VHD_TYPE_FIXED)
-            self.utils.resize_vhd(volume_path,
-                                  volume['size'] << 30)
+            self.vhdutils.convert_vhd(tmp, volume_path,
+                                      vhd_type)
+            self.vhdutils.resize_vhd(volume_path,
+                                     volume['size'] << 30)
             self.utils.change_disk_status(volume['name'], True)
 
     def copy_volume_to_image(self, context, volume, image_service, image_meta):
         """Copy the volume to the specified image."""
-
-        # Copy the volume to the image conversion dir
+        disk_format = self.utils.get_supported_format()
         temp_vhd_path = os.path.join(self.configuration.image_conversion_dir,
-                                     str(image_meta['id']) + ".vhd")
-        self.utils.copy_vhd_disk(self.local_path(volume), temp_vhd_path)
-        image_utils.upload_volume(context, image_service, image_meta,
-                                  temp_vhd_path, 'vpc')
+                                     str(image_meta['id']) + '.' + disk_format)
+        upload_image = temp_vhd_path
+
+        try:
+            self.utils.copy_vhd_disk(self.local_path(volume), temp_vhd_path)
+            # qemu-img does not yet fully support vhdx format, so we'll first
+            # 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)
+
+            image_utils.upload_volume(context, image_service, image_meta,
+                                      upload_image, 'vpc')
+        finally:
+            fileutils.delete_if_exists(temp_vhd_path)
+            fileutils.delete_if_exists(upload_image)
 
     def create_cloned_volume(self, volume, src_vref):
         """Creates a clone of the specified volume."""
index d99958384648555958a4c632b397f5deec486a0a..6b30561ee9566a4afad698880b7775bcdb7d44b2 100644 (file)
@@ -37,7 +37,6 @@ class WindowsUtils(object):
         # Set the flags
         self._conn_wmi = wmi.WMI(moniker='//./root/wmi')
         self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2')
-        self._conn_virt = wmi.WMI(moniker='//./root/virtualization')
 
     def check_for_setup_error(self):
         """Check that the driver is working and can communicate.
@@ -322,19 +321,24 @@ class WindowsUtils(object):
             LOG.error(err_msg)
             raise exception.VolumeBackendAPIException(data=err_msg)
 
-    def convert_vhd(self, src, dest, vhd_type):
-        # Due to the fact that qemu does not fully support vhdx format yet,
-        # we must use WMI make conversions between vhd and vhdx formats
-        image_man_svc = self._conn_virt.Msvm_ImageManagementService()[0]
-        (job_path, ret_val) = image_man_svc.ConvertVirtualHardDisk(
-            SourcePath=src, DestinationPath=dest, Type=vhd_type)
-        self.check_ret_val(ret_val, job_path)
-
-    def resize_vhd(self, vhd_path, new_max_size):
-        image_man_svc = self._conn_virt.Msvm_ImageManagementService()[0]
-        (job_path, ret_val) = image_man_svc.ExpandVirtualHardDisk(
-            Path=vhd_path, MaxInternalSize=new_max_size)
-        self.check_ret_val(ret_val, job_path)
+    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]
+
+    def get_windows_version(self):
+        return self._conn_cimv2.Win32_OperatingSystem()[0].Version
+
+    def get_supported_format(self):
+        if self.check_min_windows_version(6, 3):
+            return 'vhdx'
+        else:
+            return 'vhd'
+
+    def get_supported_vhd_type(self):
+        if self.check_min_windows_version(6, 3):
+            return constants.VHD_TYPE_DYNAMIC
+        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: