From 824a3b45218d2d548486eec6dac84bea148f4de5 Mon Sep 17 00:00:00 2001 From: leseb Date: Thu, 23 May 2013 19:09:06 +0200 Subject: [PATCH] NFS drivers don't honor vm size with volume from an image The bug has been encountered with the NFS generic driver (cinder.volume.nfs.NfsDriver) and the NetApp NFS driver (cinder.volume.drivers.netapp.nfs.NetAppDirectCmodeNfsDriver). I believe that drivers based on distributed filesystem such as GlusterFS, nexenta and scality are also impacted however I didn't test it those backends. However since most of them already inherit from the RemoteFsDriver class, this should be fine. Change-Id: I14575da69a2c99c7cbcece27b40a171153371ee3 Fixes: bug #1183459 --- cinder/image/image_utils.py | 6 +++++ cinder/tests/test_image_utils.py | 45 ++++++++++++++++++++++++++++++++ cinder/tests/test_nfs.py | 33 +++++++++++++++++++++++ cinder/volume/drivers/nfs.py | 17 ++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 cinder/tests/test_image_utils.py diff --git a/cinder/image/image_utils.py b/cinder/image/image_utils.py index 417f2b90c98..f27ccd77b2a 100644 --- a/cinder/image/image_utils.py +++ b/cinder/image/image_utils.py @@ -191,6 +191,12 @@ def convert_image(source, dest, out_format): utils.execute(*cmd, run_as_root=True) +def resize_image(source, size): + """Changes the virtual size of the image.""" + cmd = ('qemu-img', 'resize', source, '%sG' % size) + utils.execute(*cmd, run_as_root=False) + + def fetch(context, image_service, image_id, path, _user_id, _project_id): # TODO(vish): Improve context handling and add owner and auth data # when it is added to glance. Right now there is no diff --git a/cinder/tests/test_image_utils.py b/cinder/tests/test_image_utils.py new file mode 100644 index 00000000000..f7d19fa663c --- /dev/null +++ b/cinder/tests/test_image_utils.py @@ -0,0 +1,45 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 eNovance , Inc. +# 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. +"""Unit tests for image utils.""" + +from cinder.image import image_utils +from cinder import test +from cinder import utils +import mox + + +class TestUtils(test.TestCase): + def setUp(self): + super(TestUtils, self).setUp() + self._mox = mox.Mox() + self.addCleanup(self._mox.UnsetStubs) + + def test_resize_image(self): + mox = self._mox + mox.StubOutWithMock(utils, 'execute') + + TEST_IMG_SOURCE = 'boobar.img' + TEST_IMG_SIZE_IN_GB = 1 + + utils.execute('qemu-img', 'resize', TEST_IMG_SOURCE, + '%sG' % TEST_IMG_SIZE_IN_GB, run_as_root=False) + + mox.ReplayAll() + + image_utils.resize_image(TEST_IMG_SOURCE, TEST_IMG_SIZE_IN_GB) + + mox.VerifyAll() diff --git a/cinder/tests/test_nfs.py b/cinder/tests/test_nfs.py index 3f967c6f2eb..89173d07175 100644 --- a/cinder/tests/test_nfs.py +++ b/cinder/tests/test_nfs.py @@ -33,6 +33,8 @@ from cinder import test from cinder import units +from cinder.image import image_utils + from cinder.volume import configuration as conf from cinder.volume.drivers import nfs @@ -167,6 +169,37 @@ def test_mount_nfs_should_mount_correctly(self): mox.VerifyAll() + def test_copy_image_to_volume(self): + """resize_image common case usage.""" + mox = self._mox + drv = self._driver + + TEST_IMG_SOURCE = 'foo.img' + + volume = {'size': self.TEST_SIZE_IN_GB, 'name': TEST_IMG_SOURCE} + + def fake_local_path(volume): + return volume['name'] + + self.stubs.Set(drv, 'local_path', fake_local_path) + + mox.StubOutWithMock(image_utils, 'fetch_to_raw') + image_utils.fetch_to_raw(None, None, None, TEST_IMG_SOURCE) + + mox.StubOutWithMock(image_utils, 'resize_image') + image_utils.resize_image(TEST_IMG_SOURCE, self.TEST_SIZE_IN_GB) + + mox.StubOutWithMock(image_utils, 'qemu_img_info') + data = mox_lib.MockAnything() + data.virtual_size = 1024 ** 3 + image_utils.qemu_img_info(TEST_IMG_SOURCE).AndReturn(data) + + mox.ReplayAll() + + drv.copy_image_to_volume(None, volume, None, None) + + mox.VerifyAll() + def test_mount_nfs_should_suppress_already_mounted_error(self): """_mount_nfs should suppress already mounted error if ensure=True """ diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index a162a7d256f..b92b713528b 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -122,6 +122,23 @@ def copy_image_to_volume(self, context, volume, image_service, image_id): image_id, self.local_path(volume)) + # NOTE (leseb): Set the virtual size of the image + # the raw conversion overwrote the destination file + # (which had the correct size) + # with the fetched glance image size, + # thus the initial 'size' parameter is not honored + # this sets the size to the one asked in the first place by the user + # and then verify the final virtual size + image_utils.resize_image(self.local_path(volume), volume['size']) + + data = image_utils.qemu_img_info(self.local_path(volume)) + virt_size = data.virtual_size / units.GiB + if virt_size != volume['size']: + raise exception.ImageUnacceptable( + image_id=image_id, + reason=(_("Expected volume size was %d") % volume['size']) + + (_(" but size is now %d") % virt_size)) + def copy_volume_to_image(self, context, volume, image_service, image_meta): """Copy the volume to the specified image.""" image_utils.upload_volume(context,