From bc23e0bf415defb1956c9864ac33390bb4cc2017 Mon Sep 17 00:00:00 2001 From: Bharat Kumar Kobagana Date: Wed, 1 Apr 2015 15:50:26 +0530 Subject: [PATCH] GlusterFS: Using 'fallocate' instead of 'dd' 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 Change-Id: If9691b7b9fcb90254d8c108f633aeea4ed4f956d --- cinder/tests/unit/test_glusterfs.py | 93 +++++++++------------------- cinder/volume/drivers/glusterfs.py | 31 +++++----- cinder/volume/drivers/remotefs.py | 20 +++++- etc/cinder/rootwrap.d/volume.filters | 1 + 4 files changed, 64 insertions(+), 81 deletions(-) diff --git a/cinder/tests/unit/test_glusterfs.py b/cinder/tests/unit/test_glusterfs.py index b0043f7a3..ce559441f 100644 --- a/cinder/tests/unit/test_glusterfs.py +++ b/cinder/tests/unit/test_glusterfs.py @@ -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.""" diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 312e2fc36..114d79210 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -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) diff --git a/cinder/volume/drivers/remotefs.py b/cinder/volume/drivers/remotefs.py index f08b009c8..63349ab18 100644 --- a/cinder/volume/drivers/remotefs.py +++ b/cinder/volume/drivers/remotefs.py @@ -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.""" diff --git a/etc/cinder/rootwrap.d/volume.filters b/etc/cinder/rootwrap.d/volume.filters index aa73e046e..552cabc97 100644 --- a/etc/cinder/rootwrap.d/volume.filters +++ b/etc/cinder/rootwrap.d/volume.filters @@ -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 -- 2.45.2