From 3fe6adbe7d3e2009c09dd0b8516c501d50fdf25d Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Fri, 27 Mar 2015 15:00:28 +0200 Subject: [PATCH] Windows SMBFS: Fix image resize errors during volume creation When creating volumes from images or snapshots, the corresponding virtual disk image is resized up to the volume size. At the moment, the resize is performed even if the image already has the requested size. This can raise an excepion in some environments. This patch checks the image size and performs the resize only if it's required. Change-Id: Ib65f0e8d69f93ae21975eb46ba24e085c06e4e34 Closes-Bug: #1437306 --- cinder/tests/unit/test_smbfs.py | 4 +- cinder/tests/unit/windows/test_smbfs.py | 64 +++++++++++++++---- cinder/volume/drivers/windows/smbfs.py | 17 ++++- .../volume/drivers/windows/windows_utils.py | 3 +- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/cinder/tests/unit/test_smbfs.py b/cinder/tests/unit/test_smbfs.py index d97ce97cd..05cf1f2a7 100644 --- a/cinder/tests/unit/test_smbfs.py +++ b/cinder/tests/unit/test_smbfs.py @@ -60,8 +60,8 @@ class SmbFsTestCase(test.TestCase): def setUp(self): super(SmbFsTestCase, self).setUp() - smbfs.SmbfsDriver.__init__ = lambda x: None - self._smbfs_driver = smbfs.SmbfsDriver() + + self._smbfs_driver = smbfs.SmbfsDriver(configuration=mock.Mock()) self._smbfs_driver._remotefsclient = mock.Mock() self._smbfs_driver._local_volume_dir = mock.Mock( return_value=self._FAKE_MNT_POINT) diff --git a/cinder/tests/unit/windows/test_smbfs.py b/cinder/tests/unit/windows/test_smbfs.py index 5fa4061b9..9e28634eb 100644 --- a/cinder/tests/unit/windows/test_smbfs.py +++ b/cinder/tests/unit/windows/test_smbfs.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -import importlib import os import sys @@ -21,6 +20,7 @@ import mock from cinder import exception from cinder.image import image_utils from cinder import test +from cinder.volume.drivers.windows import smbfs class WindowsSmbFsTestCase(test.TestCase): @@ -53,25 +53,19 @@ class WindowsSmbFsTestCase(test.TestCase): super(WindowsSmbFsTestCase, self).setUp() self._mock_wmi = mock.MagicMock() - self._platform_patcher = mock.patch('sys.platform', 'win32') + mock.patch('sys.platform', 'win32').start() + mock.patch.dict(sys.modules, ctypes=mock.DEFAULT).start() + mock.patch('__builtin__.wmi', create=True).start() - mock.patch.dict(sys.modules, wmi=self._mock_wmi, - ctypes=self._mock_wmi).start() - - self._platform_patcher.start() - # self._wmi_patcher.start() self.addCleanup(mock.patch.stopall) - smbfs = importlib.import_module( - 'cinder.volume.drivers.windows.smbfs') - smbfs.WindowsSmbfsDriver.__init__ = lambda x: None - self._smbfs_driver = smbfs.WindowsSmbfsDriver() + self._smbfs_driver = smbfs.WindowsSmbfsDriver( + configuration=mock.Mock()) self._smbfs_driver._remotefsclient = mock.Mock() self._smbfs_driver._delete = mock.Mock() self._smbfs_driver.local_path = mock.Mock( return_value=self._FAKE_VOLUME_PATH) self._smbfs_driver.vhdutils = mock.Mock() - self._smbfs_driver._check_os_platform = mock.Mock() def _test_create_volume(self, volume_exists=False, volume_format='vhdx'): self._smbfs_driver.create_dynamic_vhd = mock.MagicMock() @@ -241,6 +235,7 @@ class WindowsSmbFsTestCase(test.TestCase): return_value=self._FAKE_VOLUME_PATH) drv.configuration = mock.MagicMock() drv.configuration.volume_dd_blocksize = mock.sentinel.block_size + drv._extend_vhd_if_needed = mock.Mock() with mock.patch.object(image_utils, 'fetch_to_volume_format') as fake_fetch: @@ -254,6 +249,8 @@ class WindowsSmbFsTestCase(test.TestCase): mock.sentinel.image_id, self._FAKE_VOLUME_PATH, mock.sentinel.volume_format, mock.sentinel.block_size) + drv._extend_vhd_if_needed.assert_called_once_with( + self._FAKE_VOLUME_PATH, self._FAKE_VOLUME['size']) def test_copy_volume_from_snapshot(self): drv = self._smbfs_driver @@ -272,6 +269,7 @@ class WindowsSmbFsTestCase(test.TestCase): return_value=fake_img_info) drv.local_path = mock.Mock( return_value=mock.sentinel.new_volume_path) + drv._extend_vhd_if_needed = mock.Mock() drv._copy_volume_from_snapshot( self._FAKE_SNAPSHOT, self._FAKE_VOLUME, @@ -281,8 +279,8 @@ class WindowsSmbFsTestCase(test.TestCase): drv.vhdutils.convert_vhd.assert_called_once_with( self._FAKE_VOLUME_PATH, mock.sentinel.new_volume_path) - drv.vhdutils.resize_vhd.assert_called_once_with( - mock.sentinel.new_volume_path, self._FAKE_VOLUME['size'] << 30) + drv._extend_vhd_if_needed.assert_called_once_with( + mock.sentinel.new_volume_path, self._FAKE_VOLUME['size']) def test_rebase_img(self): self._smbfs_driver._rebase_img( @@ -290,3 +288,41 @@ class WindowsSmbFsTestCase(test.TestCase): self._FAKE_VOLUME_NAME + '.vhdx', 'vhdx') self._smbfs_driver.vhdutils.reconnect_parent.assert_called_once_with( self._FAKE_SNAPSHOT_PATH, self._FAKE_VOLUME_PATH) + + def _test_extend_vhd_if_needed(self, virtual_size_gb, requested_size_gb): + drv = self._smbfs_driver + virtual_size_bytes = virtual_size_gb << 30 + requested_size_bytes = requested_size_gb << 30 + + virtual_size_dict = {'VirtualSize': virtual_size_bytes} + drv.vhdutils.get_vhd_size = mock.Mock(return_value=virtual_size_dict) + + if virtual_size_gb > requested_size_gb: + self.assertRaises(exception.VolumeBackendAPIException, + drv._extend_vhd_if_needed, + mock.sentinel.vhd_path, + requested_size_gb) + else: + drv._extend_vhd_if_needed(mock.sentinel.vhd_path, + requested_size_gb) + + if virtual_size_gb < requested_size_gb: + drv.vhdutils.resize_vhd.assert_called_once_with( + mock.sentinel.vhd_path, requested_size_bytes) + else: + self.assertFalse(drv.vhdutils.resize_vhd.called) + + drv.vhdutils.get_vhd_size.assert_called_once_with( + mock.sentinel.vhd_path) + + def test_extend_vhd_if_needed_bigger_size(self): + self._test_extend_vhd_if_needed(virtual_size_gb=1, + requested_size_gb=2) + + def test_extend_vhd_if_needed_equal_size(self): + self._test_extend_vhd_if_needed(virtual_size_gb=1, + requested_size_gb=1) + + def test_extend_vhd_if_needed_smaller_size(self): + self._test_extend_vhd_if_needed(virtual_size_gb=2, + requested_size_gb=1) diff --git a/cinder/volume/drivers/windows/smbfs.py b/cinder/volume/drivers/windows/smbfs.py index e6f98e93c..480035f2b 100644 --- a/cinder/volume/drivers/windows/smbfs.py +++ b/cinder/volume/drivers/windows/smbfs.py @@ -30,6 +30,7 @@ from cinder import utils from cinder.volume.drivers import smbfs from cinder.volume.drivers.windows import remotefs from cinder.volume.drivers.windows import vhdutils +from cinder.volume.drivers.windows import windows_utils VERSION = '1.1.0' @@ -57,6 +58,7 @@ class WindowsSmbfsDriver(smbfs.SmbfsDriver): 'cifs', root_helper=None, smbfs_mount_point_base=self.base, smbfs_mount_options=opts) self.vhdutils = vhdutils.VHDUtils() + self._windows_utils = windows_utils.WindowsUtils() def do_setup(self, context): self._check_os_platform() @@ -216,8 +218,7 @@ class WindowsSmbfsDriver(smbfs.SmbfsDriver): volume_path, volume_format, self.configuration.volume_dd_blocksize) - self.vhdutils.resize_vhd(volume_path, - volume['size'] * units.Gi) + self._extend_vhd_if_needed(self.local_path(volume), volume['size']) def _copy_volume_from_snapshot(self, snapshot, volume, volume_size): """Copy data from snapshot to destination volume.""" @@ -244,4 +245,14 @@ class WindowsSmbfsDriver(smbfs.SmbfsDriver): self._delete(volume_path) self.vhdutils.convert_vhd(snapshot_path, volume_path) - self.vhdutils.resize_vhd(volume_path, volume_size * units.Gi) + self._extend_vhd_if_needed(volume_path, volume_size) + + def _extend_vhd_if_needed(self, vhd_path, new_size_gb): + old_size_bytes = self.vhdutils.get_vhd_size(vhd_path)['VirtualSize'] + new_size_bytes = new_size_gb * units.Gi + + # This also ensures we're not attempting to shrink the image. + is_resize_needed = self._windows_utils.is_resize_needed( + vhd_path, new_size_bytes, old_size_bytes) + if is_resize_needed: + self.vhdutils.resize_vhd(vhd_path, new_size_bytes) diff --git a/cinder/volume/drivers/windows/windows_utils.py b/cinder/volume/drivers/windows/windows_utils.py index 333905570..2b22bc3ba 100644 --- a/cinder/volume/drivers/windows/windows_utils.py +++ b/cinder/volume/drivers/windows/windows_utils.py @@ -18,6 +18,7 @@ Utility class for Windows Storage Server 2012 volume related operations. import ctypes import os +import sys from oslo_config import cfg from oslo_log import log as logging @@ -27,7 +28,7 @@ from cinder.i18n import _, _LI from cinder.volume.drivers.windows import constants # Check needed for unit testing on Unix -if os.name == 'nt': +if sys.platform == 'win32': import wmi from ctypes import wintypes -- 2.45.2