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 <deepakcs@redhat.com> Change-Id: If9691b7b9fcb90254d8c108f633aeea4ed4f956d
This commit is contained in:
parent
70b6ee3ab3
commit
bc23e0bf41
cinder
etc/cinder/rootwrap.d
@ -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_fallocate.assert_called_once_with(volume_path,
|
||||
volume_size)
|
||||
mock_set_rw_permissions_for_all.\
|
||||
assert_called_once_with(volume_path)
|
||||
|
||||
def test_create_thick_dd_volume(self):
|
||||
drv = self._driver
|
||||
volume = self._simple_volume()
|
||||
|
||||
self._configuration.nas_volume_prov_type = 'thick'
|
||||
|
||||
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:
|
||||
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_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_sparsed_volumes = old_value
|
||||
|
||||
def test_create_qcow2_volume(self):
|
||||
drv = self._driver
|
||||
volume = self._simple_volume()
|
||||
|
||||
old_value = self._configuration.glusterfs_qcow2_volumes
|
||||
self._configuration.glusterfs_qcow2_volumes = True
|
||||
|
||||
with mock.patch.object(drv, '_execute') as mock_execute,\
|
||||
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)
|
||||
|
||||
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_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."""
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user