]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
GlusterFS: Using 'fallocate' instead of 'dd'
authorBharat Kumar Kobagana <bharat.kobagana@redhat.com>
Wed, 1 Apr 2015 10:20:26 +0000 (15:50 +0530)
committerBharat Kumar Kobagana <bharat.kobagana@redhat.com>
Wed, 29 Jul 2015 03:36:59 +0000 (09:06 +0530)
As 'fallocate' preallocates space to a volume, we can use it to
create raw formatted volume instead of 'dd' command.

This patch also introduces a new configuration parameter
'nas_volume_prov_type', which can be set to either 'thin' or 'thick'.

DocImpact
Co-Authored-By: Deepak C Shetty <deepakcs@redhat.com>
Change-Id: If9691b7b9fcb90254d8c108f633aeea4ed4f956d

cinder/tests/unit/test_glusterfs.py
cinder/volume/drivers/glusterfs.py
cinder/volume/drivers/remotefs.py
etc/cinder/rootwrap.d/volume.filters

index b0043f7a3b854feb6133ca19d06447ccaf36a86d..ce559441ffe75d055849c5b6ed206606319d1f40 100644 (file)
@@ -90,8 +90,6 @@ class GlusterFsDriverTestCase(test.TestCase):
             self.TEST_SHARES_CONFIG_FILE
         self._configuration.glusterfs_mount_point_base = \
             self.TEST_MNT_POINT_BASE
-        self._configuration.glusterfs_sparsed_volumes = True
-        self._configuration.glusterfs_qcow2_volumes = False
         self._configuration.nas_secure_file_permissions = 'false'
         self._configuration.nas_secure_file_operations = 'false'
         self._configuration.nas_ip = None
@@ -228,8 +226,8 @@ class GlusterFsDriverTestCase(test.TestCase):
 
             self.assertEqual(3.0, provisioned_capacity)
 
-    def test_update_volume_stats_sparse(self):
-        """_update_volume_stats_sparse with sparse files."""
+    def test_update_volume_stats_thin(self):
+        """_update_volume_stats_thin with qcow2 files."""
         drv = self._driver
         rfsdriver = remotefs_drv.RemoteFSSnapDriver
 
@@ -240,33 +238,7 @@ class GlusterFsDriverTestCase(test.TestCase):
             data = {'total_capacity_gb': 10.0,
                     'free_capacity_gb': 2.0}
             drv._stats = data
-            drv.configuration.glusterfs_sparsed_volumes = True
-            drv.configuration.glusterfs_qcow2_volumes = False
-            drv.configuration.max_over_subscription_ratio = 20.0
-            mock_get_provisioned_capacity.return_value = 8.0
-            drv._update_volume_stats()
-            data['max_over_subscription_ratio'] = 20.0
-            data['thick_provisioning_support'] = False
-            data['thin_provisioning_support'] = True
-
-            self.assertEqual(data, drv._stats)
-            self.assertTrue(mock_get_provisioned_capacity.called)
-            self.assertTrue(mock_update_volume_stats.called)
-
-    def test_update_volume_stats_qcow2(self):
-        """_update_volume_stats_sparse with qcow2 files."""
-        drv = self._driver
-        rfsdriver = remotefs_drv.RemoteFSSnapDriver
-
-        with mock.patch.object(rfsdriver, '_update_volume_stats') as \
-                mock_update_volume_stats,\
-                mock.patch.object(drv, '_get_provisioned_capacity') as \
-                mock_get_provisioned_capacity:
-            data = {'total_capacity_gb': 10.0,
-                    'free_capacity_gb': 2.0}
-            drv._stats = data
-            drv.configuration.glusterfs_sparsed_volumes = False
-            drv.configuration.glusterfs_qcow2_volumes = True
+            drv.configuration.nas_volume_prov_type = 'thin'
             drv.configuration.max_over_subscription_ratio = 20.0
             mock_get_provisioned_capacity.return_value = 8.0
             drv._update_volume_stats()
@@ -279,7 +251,7 @@ class GlusterFsDriverTestCase(test.TestCase):
             self.assertTrue(mock_update_volume_stats.called)
 
     def test_update_volume_stats_thick(self):
-        """_update_volume_stats_sparse with raw files."""
+        """_update_volume_stats_thick with raw files."""
         drv = self._driver
         rfsdriver = remotefs_drv.RemoteFSSnapDriver
 
@@ -288,8 +260,7 @@ class GlusterFsDriverTestCase(test.TestCase):
             data = {'total_capacity_gb': 10.0,
                     'free_capacity_gb': 2.0}
             drv._stats = data
-            drv.configuration.glusterfs_sparsed_volumes = False
-            drv.configuration.glusterfs_qcow2_volumes = False
+            drv.configuration.nas_volume_prov_type = 'thick'
             drv.configuration.max_over_subscription_ratio = 20.0
             drv._update_volume_stats()
             data['provisioned_capacity_gb'] = 8.0
@@ -526,74 +497,68 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         return volume
 
-    def test_create_sparsed_volume(self):
+    def test_create_thin_volume(self):
         drv = self._driver
         volume = self._simple_volume()
 
-        self.override_config('glusterfs_sparsed_volumes', True)
+        self._configuration.nas_volume_prov_type = 'thin'
 
-        with mock.patch.object(drv, '_create_sparsed_file') as \
-                mock_create_sparsed_file,\
+        with mock.patch.object(drv, '_create_qcow2_file') as \
+                mock_create_qcow2_file,\
                 mock.patch.object(drv, '_set_rw_permissions_for_all') as \
                 mock_set_rw_permissions_for_all:
             drv._do_create_volume(volume)
 
             volume_path = drv.local_path(volume)
             volume_size = volume['size']
-            mock_create_sparsed_file.assert_called_once_with(volume_path,
-                                                             volume_size)
+            mock_create_qcow2_file.assert_called_once_with(volume_path,
+                                                           volume_size)
             mock_set_rw_permissions_for_all.\
                 assert_called_once_with(volume_path)
 
-    def test_create_nonsparsed_volume(self):
+    def test_create_thick_fallocate_volume(self):
         drv = self._driver
         volume = self._simple_volume()
 
-        old_value = self._configuration.glusterfs_sparsed_volumes
-        self._configuration.glusterfs_sparsed_volumes = False
+        self._configuration.nas_volume_prov_type = 'thick'
 
-        with mock.patch.object(drv, '_create_regular_file') as \
-                mock_create_regular_file,\
+        with mock.patch.object(drv, '_fallocate') as \
+                mock_fallocate,\
                 mock.patch.object(drv, '_set_rw_permissions_for_all') as \
                 mock_set_rw_permissions_for_all:
             drv._do_create_volume(volume)
 
             volume_path = drv.local_path(volume)
             volume_size = volume['size']
-            mock_create_regular_file.assert_called_once_with(volume_path,
-                                                             volume_size)
+            mock_fallocate.assert_called_once_with(volume_path,
+                                                   volume_size)
             mock_set_rw_permissions_for_all.\
                 assert_called_once_with(volume_path)
-        self._configuration.glusterfs_sparsed_volumes = old_value
 
-    def test_create_qcow2_volume(self):
+    def test_create_thick_dd_volume(self):
         drv = self._driver
         volume = self._simple_volume()
 
-        old_value = self._configuration.glusterfs_qcow2_volumes
-        self._configuration.glusterfs_qcow2_volumes = True
+        self._configuration.nas_volume_prov_type = 'thick'
 
-        with mock.patch.object(drv, '_execute') as mock_execute,\
+        with mock.patch.object(drv, '_fallocate') as \
+                mock_fallocate,\
+                mock.patch.object(drv, '_create_regular_file') as \
+                mock_create_regular_file,\
                 mock.patch.object(drv, '_set_rw_permissions_for_all') as \
                 mock_set_rw_permissions_for_all:
-            hashed = drv._get_hash_str(volume['provider_location'])
-            path = '%s/%s/volume-%s' % (self.TEST_MNT_POINT_BASE,
-                                        hashed,
-                                        self.VOLUME_UUID)
-
+            mock_fallocate.side_effect = putils.ProcessExecutionError(
+                stderr='Fallocate: Operation not supported.')
             drv._do_create_volume(volume)
 
             volume_path = drv.local_path(volume)
             volume_size = volume['size']
-            mock_execute.assert_called_once_with('qemu-img', 'create',
-                                                 '-f', 'qcow2', '-o',
-                                                 'preallocation=metadata',
-                                                 path,
-                                                 str(volume_size * units.Gi),
-                                                 run_as_root=True)
+            mock_fallocate.assert_called_once_with(volume_path,
+                                                   volume_size)
+            mock_create_regular_file.assert_called_once_with(volume_path,
+                                                             volume_size)
             mock_set_rw_permissions_for_all.\
                 assert_called_once_with(volume_path)
-        self._configuration.glusterfs_qcow2_volumes = old_value
 
     def test_create_volume_should_ensure_glusterfs_mounted(self):
         """create_volume ensures shares provided in config are mounted."""
index 312e2fc361799804c3711504ab9e9903a3ecd905..114d792107a99806435439128de63f4d9680ff3d 100644 (file)
@@ -16,6 +16,7 @@
 import errno
 import os
 import stat
+import warnings
 
 from os_brick.remotefs import remotefs as remotefs_brick
 from oslo_concurrency import processutils
@@ -33,18 +34,11 @@ from cinder.volume.drivers import remotefs as remotefs_drv
 
 LOG = logging.getLogger(__name__)
 
+
 volume_opts = [
     cfg.StrOpt('glusterfs_shares_config',
                default='/etc/cinder/glusterfs_shares',
                help='File with the list of available gluster shares'),
-    cfg.BoolOpt('glusterfs_sparsed_volumes',
-                default=True,
-                help=('Create volumes as sparsed files which take no space.'
-                      'If set to False volume is created as regular file.'
-                      'In such case volume creation takes a lot of time.')),
-    cfg.BoolOpt('glusterfs_qcow2_volumes',
-                default=False,
-                help=('Create volumes as QCOW2 files rather than raw files.')),
     cfg.StrOpt('glusterfs_mount_point_base',
                default='$state_path/mnt',
                help='Base dir containing mount points for gluster shares.'),
@@ -170,8 +164,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver, driver.CloneableVD,
         global_capacity = data['total_capacity_gb']
         global_free = data['free_capacity_gb']
 
-        thin_enabled = (self.configuration.glusterfs_sparsed_volumes or
-                        self.configuration.glusterfs_qcow2_volumes)
+        thin_enabled = self.configuration.nas_volume_prov_type == 'thin'
         if thin_enabled:
             provisioned_capacity = self._get_provisioned_capacity()
         else:
@@ -228,7 +221,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver, driver.CloneableVD,
 
         LOG.debug("will copy from snapshot at %s", path_to_snap_img)
 
-        if self.configuration.glusterfs_qcow2_volumes:
+        if self.configuration.nas_volume_prov_type == 'thin':
             out_format = 'qcow2'
         else:
             out_format = 'raw'
@@ -348,13 +341,19 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver, driver.CloneableVD,
             LOG.error(msg)
             raise exception.InvalidVolume(reason=msg)
 
-        if self.configuration.glusterfs_qcow2_volumes:
+        if self.configuration.nas_volume_prov_type == 'thin':
             self._create_qcow2_file(volume_path, volume_size)
         else:
-            if self.configuration.glusterfs_sparsed_volumes:
-                self._create_sparsed_file(volume_path, volume_size)
-            else:
-                self._create_regular_file(volume_path, volume_size)
+            try:
+                self._fallocate(volume_path, volume_size)
+            except processutils.ProcessExecutionError as exc:
+                if 'Operation not supported' in exc.stderr:
+                    warnings.warn('Fallocate not supported by current version '
+                                  'of glusterfs. So falling back to dd.')
+                    self._create_regular_file(volume_path, volume_size)
+                else:
+                    fileutils.delete_if_exists(volume_path)
+                    raise
 
         self._set_rw_permissions_for_all(volume_path)
 
index f08b009c80a198c91cd7b724ed5068f3f67a9d5d..63349ab18f484694bbe95afdb5395de6626ad3bf 100644 (file)
@@ -81,7 +81,19 @@ nas_opts = [
     cfg.StrOpt('nas_mount_options',
                default=None,
                help=('Options used to mount the storage backend file system '
-                     'where Cinder volumes are stored.'))
+                     'where Cinder volumes are stored.')),
+]
+
+old_vol_type_opts = [cfg.DeprecatedOpt('glusterfs_sparsed_volumes'),
+                     cfg.DeprecatedOpt('glusterfs_qcow2_volumes')]
+
+volume_opts = [
+    cfg.StrOpt('nas_volume_prov_type',
+               default='thin',
+               choices=['thin', 'thick'],
+               deprecated_opts=old_vol_type_opts,
+               help=('Provisioning type that will be used when '
+                     'creating volumes.')),
 ]
 
 CONF = cfg.CONF
@@ -137,6 +149,7 @@ class RemoteFSDriver(driver.LocalVD, driver.TransferVD, driver.BaseVD):
 
         if self.configuration:
             self.configuration.append_config_values(nas_opts)
+            self.configuration.append_config_values(volume_opts)
 
     def check_for_setup_error(self):
         """Just to override parent behavior."""
@@ -322,6 +335,11 @@ class RemoteFSDriver(driver.LocalVD, driver.TransferVD, driver.BaseVD):
                       'count=%d' % block_count,
                       run_as_root=self._execute_as_root)
 
+    def _fallocate(self, path, size):
+        """Creates a raw file of given size in GiB using fallocate."""
+        self._execute('fallocate', '--length=%sG' % size,
+                      path, run_as_root=True)
+
     def _create_qcow2_file(self, path, size_gb):
         """Creates a QCOW2 file of a given size in GiB."""
 
index aa73e046ec6047a3e0c5ef831503a3d8d35c0329..552cabc97bedaae7d6d2e562079683d062bf7424 100644 (file)
@@ -103,6 +103,7 @@ netapp_nfs_find: RegExpFilter, find, root, find, ^[/]*([^/\0]+(/+)?)*$, -maxdept
 # cinder/volume/drivers/glusterfs.py
 chgrp: CommandFilter, chgrp, root
 umount: CommandFilter, umount, root
+fallocate: CommandFilter, fallocate, root
 
 # cinder/volumes/drivers/hds/hds.py:
 hus-cmd: CommandFilter, hus-cmd, root