]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Implement extend volume in NFS driver
authorAndrew Kerr <andrew.kerr@netapp.com>
Fri, 21 Mar 2014 14:13:58 +0000 (10:13 -0400)
committerAndrew Kerr <andrew.kerr@netapp.com>
Wed, 18 Jun 2014 13:06:23 +0000 (09:06 -0400)
This fix implements the extend_volume functionality in the NFS
driver.

Change-Id: I1634fef0206b4ef12684c6c7f4851d76da579942
Closes-Bug: #1295316

cinder/exception.py
cinder/tests/test_nfs.py
cinder/volume/drivers/nfs.py

index 034df564ca7fd96d8c5b37b0e00a4bcedae6af29..b72d9d08bd0195b443fb8f511a0ac7a24155e7c2 100644 (file)
@@ -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):
index 3dab3cfcf3f75b7f58d299d717be40198eebbfbd..f59a2017ef0ecf5480cfb65619f56704c9d8d73c 100644 (file)
@@ -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))
index 8ac5a542c7a87d2ed4839753d721c65bb4d99a70..a841597ef3c0b3bf89fa54756ca722aecf6cd30e 100644 (file)
@@ -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