From 8cd4224070299cefa922ff2251fe81770494ad46 Mon Sep 17 00:00:00 2001 From: Andrew Kerr Date: Fri, 21 Mar 2014 10:13:58 -0400 Subject: [PATCH] Implement extend volume in NFS driver This fix implements the extend_volume functionality in the NFS driver. Change-Id: I1634fef0206b4ef12684c6c7f4851d76da579942 Closes-Bug: #1295316 --- cinder/exception.py | 4 ++ cinder/tests/test_nfs.py | 72 ++++++++++++++++++++++++++++++++++++ cinder/volume/drivers/nfs.py | 22 +++++++++++ 3 files changed, 98 insertions(+) diff --git a/cinder/exception.py b/cinder/exception.py index 034df564c..b72d9d08b 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -579,6 +579,10 @@ class ManageExistingVolumeTypeMismatch(CinderException): "%(reason)s") +class ExtendVolumeError(CinderException): + message = _("Error extending volume: %(reason)s") + + # Driver specific exceptions # Coraid class CoraidException(VolumeDriverException): diff --git a/cinder/tests/test_nfs.py b/cinder/tests/test_nfs.py index 3dab3cfcf..f59a2017e 100644 --- a/cinder/tests/test_nfs.py +++ b/cinder/tests/test_nfs.py @@ -683,3 +683,75 @@ class NfsDriverTestCase(test.TestCase): total_available, total_allocated, requested_volume_size)) + + def test_extend_volume(self): + """Extend a volume by 1.""" + drv = self._driver + volume = {'id': '80ee16b6-75d2-4d54-9539-ffc1b4b0fb10', 'size': 1, + 'provider_location': 'nfs_share'} + path = 'path' + newSize = volume['size'] + 1 + + with mock.patch.object(image_utils, 'resize_image') as resize: + with mock.patch.object(drv, 'local_path', return_value=path): + with mock.patch.object(drv, '_is_share_eligible', + return_value=True): + with mock.patch.object(drv, '_is_file_size_equal', + return_value=True): + drv.extend_volume(volume, newSize) + + resize.assert_called_once_with(path, newSize) + + def test_extend_volume_failure(self): + """Error during extend operation.""" + drv = self._driver + volume = {'id': '80ee16b6-75d2-4d54-9539-ffc1b4b0fb10', 'size': 1, + 'provider_location': 'nfs_share'} + + with mock.patch.object(image_utils, 'resize_image'): + with mock.patch.object(drv, 'local_path', return_value='path'): + with mock.patch.object(drv, '_is_share_eligible', + return_value=True): + with mock.patch.object(drv, '_is_file_size_equal', + return_value=False): + self.assertRaises(exception.ExtendVolumeError, + drv.extend_volume, volume, 2) + + def test_extend_volume_insufficient_space(self): + """Insufficient space on nfs_share during extend operation.""" + drv = self._driver + volume = {'id': '80ee16b6-75d2-4d54-9539-ffc1b4b0fb10', 'size': 1, + 'provider_location': 'nfs_share'} + + with mock.patch.object(image_utils, 'resize_image'): + with mock.patch.object(drv, 'local_path', return_value='path'): + with mock.patch.object(drv, '_is_share_eligible', + return_value=False): + with mock.patch.object(drv, '_is_file_size_equal', + return_value=False): + self.assertRaises(exception.ExtendVolumeError, + drv.extend_volume, volume, 2) + + def test_is_file_size_equal(self): + """File sizes are equal.""" + drv = self._driver + path = 'fake/path' + size = 2 + data = mock.MagicMock() + data.virtual_size = size * units.GiB + + with mock.patch.object(image_utils, 'qemu_img_info', + return_value=data): + self.assertTrue(drv._is_file_size_equal(path, size)) + + def test_is_file_size_equal_false(self): + """File sizes are not equal.""" + drv = self._driver + path = 'fake/path' + size = 2 + data = mock.MagicMock() + data.virtual_size = (size + 1) * units.GiB + + with mock.patch.object(image_utils, 'qemu_img_info', + return_value=data): + self.assertFalse(drv._is_file_size_equal(path, size)) diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index 8ac5a542c..a841597ef 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -570,3 +570,25 @@ class NfsDriver(RemoteFsDriver): def _get_mount_point_base(self): return self.base + + def extend_volume(self, volume, new_size): + """Extend an existing volume to the new size.""" + LOG.info(_('Extending volume %s.'), volume['id']) + extend_by = int(new_size) - volume['size'] + if not self._is_share_eligible(volume['provider_location'], + extend_by): + raise exception.ExtendVolumeError(reason='Insufficient space to' + ' extend volume %s to %sG' + % (volume['id'], new_size)) + path = self.local_path(volume) + LOG.info(_('Resizing file to %sG...'), new_size) + image_utils.resize_image(path, new_size) + if not self._is_file_size_equal(path, new_size): + raise exception.ExtendVolumeError( + reason='Resizing image file failed.') + + def _is_file_size_equal(self, path, size): + """Checks if file size at path is equal to size.""" + data = image_utils.qemu_img_info(path) + virt_size = data.virtual_size / units.GiB + return virt_size == size -- 2.45.2