In some environments, volume cloning fails when using the Windows
iSCSI volume driver. This involves creating a new VHD image and
copying the data from the source volume on top.
This can raise an error, caused by the fact that the destination
path exists.
This patch slightly changes the workflow used when cloning an image.
Instead of first creating a new WT disk and then copying the cloned
image data on top, the source image is copied, resized if needed
and then imported as a WT disk.
Change-Id: I07d45fa9324a34ef45ed2aa8051338ee6a0f8a5b
Closes-Bug: #
1435865
volume = db_fakes.get_fake_volume_info()
volume_cloned = db_fakes.get_fake_volume_info_cloned()
+ new_vhd_path = self.fake_local_path(volume)
+ src_vhd_path = self.fake_local_path(volume_cloned)
self.mox.StubOutWithMock(windows_utils.WindowsUtils,
- 'create_volume')
+ 'copy_vhd_disk')
+ self.mox.StubOutWithMock(windows_utils.WindowsUtils,
+ 'import_wt_disk')
+ self.mox.StubOutWithMock(vhdutils.VHDUtils,
+ 'resize_vhd')
+ self.stubs.Set(drv.utils,
+ 'is_resize_needed',
+ lambda vhd_path, new_size, old_size: True)
self.stubs.Set(drv, 'local_path', self.fake_local_path)
- windows_utils.WindowsUtils.create_volume(mox.IgnoreArg(),
- mox.IgnoreArg(),
- mox.IgnoreArg())
-
- self.mox.StubOutWithMock(windows_utils.WindowsUtils,
- 'copy_vhd_disk')
- windows_utils.WindowsUtils.copy_vhd_disk(self.fake_local_path(
- volume_cloned), self.fake_local_path(volume))
+ windows_utils.WindowsUtils.copy_vhd_disk(src_vhd_path,
+ new_vhd_path)
+ drv.utils.is_resize_needed(new_vhd_path,
+ volume['size'],
+ volume_cloned['size'])
+ vhdutils.VHDUtils.resize_vhd(new_vhd_path, volume['size'] << 30)
+ windows_utils.WindowsUtils.import_wt_disk(new_vhd_path,
+ volume['name'])
self.mox.ReplayAll()
windows_utils.WindowsUtils.__init__ = lambda x: None
self.wutils = windows_utils.WindowsUtils()
+ self.wutils._conn_wmi = mock.Mock()
self.wutils._conn_cimv2 = mock.MagicMock()
def _test_copy_vhd_disk(self, source_exists=True, copy_failed=False):
def test_copy_vhd_disk_copy_failed(self):
self._test_copy_vhd_disk(copy_failed=True)
+
+ @mock.patch.object(windows_utils, 'wmi', create=True)
+ def test_import_wt_disk_exception(self, mock_wmi):
+ mock_wmi.x_wmi = Exception
+ mock_import_disk = self.wutils._conn_wmi.WT_Disk.ImportWTDisk
+ mock_import_disk.side_effect = mock_wmi.x_wmi
+
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.wutils.import_wt_disk,
+ mock.sentinel.vhd_path,
+ mock.sentinel.vol_name)
+ mock_import_disk.assert_called_once_with(
+ DevicePath=mock.sentinel.vhd_path,
+ Description=mock.sentinel.vol_name)
+
+ def test_check_if_resize_is_needed_bigger_requested_size(self):
+ ret_val = self.wutils.is_resize_needed(
+ mock.sentinel.vhd_path, 1, 0)
+ self.assertTrue(ret_val)
+
+ def test_check_if_resize_is_needed_equal_requested_size(self):
+ ret_val = self.wutils.is_resize_needed(
+ mock.sentinel.vhd_path, 1, 1)
+ self.assertFalse(ret_val)
+
+ def test_check_if_resize_is_needed_smaller_requested_size(self):
+ self.assertRaises(
+ exception.VolumeBackendAPIException,
+ self.wutils.is_resize_needed,
+ mock.sentinel.vhd_path, 1, 2)
def create_cloned_volume(self, volume, src_vref):
"""Creates a clone of the specified volume."""
- # Create a new volume
- # Copy VHD file of the volume to clone to the created volume
- self.create_volume(volume)
- self.utils.copy_vhd_disk(self.local_path(src_vref),
- self.local_path(volume))
+ vol_name = volume['name']
+ vol_size = volume['size']
+ src_vol_size = src_vref['size']
+
+ new_vhd_path = self.local_path(volume)
+ src_vhd_path = self.local_path(src_vref)
+
+ self.utils.copy_vhd_disk(src_vhd_path,
+ new_vhd_path)
+
+ if self.utils.is_resize_needed(new_vhd_path, vol_size, src_vol_size):
+ self.vhdutils.resize_vhd(new_vhd_path, vol_size << 30)
+
+ self.utils.import_wt_disk(new_vhd_path, vol_name)
def get_volume_stats(self, refresh=False):
"""Get volume stats.
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
+ def import_wt_disk(self, vhd_path, vol_name):
+ """Import a vhd/x image to be used by Windows iSCSI targets"""
+ try:
+ self._conn_wmi.WT_Disk.ImportWTDisk(DevicePath=vhd_path,
+ Description=vol_name)
+ except wmi.x_wmi as exc:
+ err_msg = (_("Failed to import disk: %(vhd_path)s. "
+ "WMI exception: %(exc)s") %
+ {'vhd_path': vhd_path,
+ 'exc': exc})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+
def change_disk_status(self, vol_name, enabled):
try:
cl = self._conn_wmi.WT_Disk(Description=vol_name)[0]
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
+ def is_resize_needed(self, vhd_path, new_size, old_size):
+ if new_size > old_size:
+ return True
+ elif old_size > new_size:
+ err_msg = (_("Cannot resize image %(vhd_path)s "
+ "to a smaller size. "
+ "Image size: %(old_size)s, "
+ "Requested size: %(new_size)s") %
+ {'vhd_path': vhd_path,
+ 'old_size': old_size,
+ 'new_size': new_size})
+ raise exception.VolumeBackendAPIException(data=err_msg)
+ return False
+
def extend(self, vol_name, additional_size):
"""Extend an existing volume."""
try: