From: Dinesh Subhraveti Date: Thu, 8 Aug 2013 21:24:40 +0000 (-0700) Subject: GPFS support for various volume attributes X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=932c3b6f0686bdca97faba4d71aa1c1246587623;p=openstack-build%2Fcinder-build.git GPFS support for various volume attributes Specify the following attributes when creating a new volume: * The storage pool on which the volume is placed * Number of block-level replicas * Whether the physical blocks should be allocated locally on the node issuing the IO or striped across the cluster * Whether writes to the volume should use direct IO * Number of file system blocks to be laid out sequentially on disk to behave like a single large block * Local storage attached to specific node(s) where the replicas of the volume should be allocated Change-Id: I77bb7de32bbde5a125f8c6f9a727b538ea99bc18 Implements: blueprint gpfs-volume-attributes --- diff --git a/cinder/tests/test_gpfs.py b/cinder/tests/test_gpfs.py index 171649e8b..bae51bff7 100644 --- a/cinder/tests/test_gpfs.py +++ b/cinder/tests/test_gpfs.py @@ -92,8 +92,8 @@ class GPFSDriverTestCase(test.TestCase): self._fake_delete_gpfs_file) self.stubs.Set(GPFSDriver, '_create_sparse_file', self._fake_create_sparse_file) - self.stubs.Set(GPFSDriver, '_create_regular_file', - self._fake_create_regular_file) + self.stubs.Set(GPFSDriver, '_allocate_file_blocks', + self._fake_allocate_file_blocks) self.stubs.Set(GPFSDriver, '_get_available_capacity', self._fake_get_available_capacity) self.stubs.Set(image_utils, 'qemu_img_info', @@ -153,6 +153,23 @@ class GPFSDriverTestCase(test.TestCase): self.volume.delete_volume(self.context, volume_id) self.assertFalse(os.path.exists(path)) + def test_create_volume_with_attributes(self): + self.stubs.Set(GPFSDriver, '_gpfs_change_attributes', + self._fake_gpfs_change_attributes) + attributes = {'dio': 'yes', 'data_pool_name': 'ssd_pool', + 'replicas': '2', 'write_affinity_depth': '1', + 'block_group_factor': '1', + 'write_affinity_failure-group': + '1,1,1:2;2,1,1:2;2,0,3:4'} + vol = self._create_volume(size=1, metadata=attributes) + volume_id = vol['id'] + self.assertTrue(os.path.exists(self.volumes_path)) + self.volume.create_volume(self.context, volume_id) + path = self.volumes_path + '/' + vol['name'] + self.assertTrue(os.path.exists(path)) + self.volume.delete_volume(self.context, volume_id) + self.assertFalse(os.path.exists(path)) + def _create_snapshot(self, volume_id, size='0'): """Create a snapshot object.""" snap = {} @@ -405,9 +422,12 @@ class GPFSDriverTestCase(test.TestCase): def _fake_create_sparse_file(self, path, size): self._fake_create_file(path) - def _fake_create_regular_file(self, path, size): + def _fake_allocate_file_blocks(self, path, size): self._fake_create_file(path) + def _fake_gpfs_change_attributes(self, options, path): + pass + def _fake_gpfs_redirect(self, src): return True diff --git a/cinder/volume/drivers/gpfs.py b/cinder/volume/drivers/gpfs.py index fe4577ce4..556f5cb1e 100644 --- a/cinder/volume/drivers/gpfs.py +++ b/cinder/volume/drivers/gpfs.py @@ -211,8 +211,8 @@ class GPFSDriver(driver.VolumeDriver): self._execute('truncate', '-s', sizestr, path, run_as_root=True) self._execute('chmod', '666', path, run_as_root=True) - def _create_regular_file(self, path, size): - """Creates regular file of given size.""" + def _allocate_file_blocks(self, path, size): + """Preallocate file blocks by writing zeros.""" block_size_mb = 1 block_count = size * units.GiB / (block_size_mb * units.MiB) @@ -221,19 +221,51 @@ class GPFSDriver(driver.VolumeDriver): 'bs=%dM' % block_size_mb, 'count=%d' % block_count, run_as_root=True) - self._execute('chmod', '666', path, run_as_root=True) + + def _gpfs_change_attributes(self, options, path): + cmd = ['mmchattr'] + cmd.extend(options) + cmd.append(path) + self._execute(*cmd, run_as_root=True) + + def _set_volume_attributes(self, path, metadata): + """Set various GPFS attributes for this volume.""" + + options = [] + for item in metadata: + if item['key'] == 'data_pool_name': + options.extend(['-P', item['value']]) + elif item['key'] == 'replicas': + options.extend(['-r', item['value'], '-m', item['value']]) + elif item['key'] == 'dio': + options.extend(['-D', item['value']]) + elif item['key'] == 'write_affinity_depth': + options.extend(['--write-affinity-depth', item['value']]) + elif item['key'] == 'block_group_factor': + options.extend(['--block-group-factor', item['value']]) + elif item['key'] == 'write_affinity_failure_group': + options.extend(['--write-affinity-failure-group', + item['value']]) + + if options: + self._gpfs_change_attributes(options, path) def create_volume(self, volume): """Creates a GPFS volume.""" volume_path = self.local_path(volume) volume_size = volume['size'] - if self.configuration.gpfs_sparse_volumes: - self._create_sparse_file(volume_path, volume_size) - else: - self._create_regular_file(volume_path, volume_size) + # Create a sparse file first; allocate blocks later if requested + self._create_sparse_file(volume_path, volume_size) + # Set the attributes prior to allocating any blocks so that + # they are allocated according to the policy v_metadata = volume.get('volume_metadata') + self._set_volume_attributes(volume_path, v_metadata) + + if not self.configuration.gpfs_sparse_volumes: + self._allocate_file_blocks(volume_path, volume_size) + fstype = None fslabel = None for item in v_metadata: diff --git a/etc/cinder/rootwrap.d/volume.filters b/etc/cinder/rootwrap.d/volume.filters index b095475b9..68109dd09 100644 --- a/etc/cinder/rootwrap.d/volume.filters +++ b/etc/cinder/rootwrap.d/volume.filters @@ -73,6 +73,7 @@ blockdev: CommandFilter, blockdev, root mmgetstate: CommandFilter, /usr/lpp/mmfs/bin/mmgetstate, root mmclone: CommandFilter, /usr/lpp/mmfs/bin/mmclone, root mmlsattr: CommandFilter, /usr/lpp/mmfs/bin/mmlsattr, root +mmchattr: CommandFilter, /usr/lpp/mmfs/bin/mmchattr, root mmlsconfig: CommandFilter, /usr/lpp/mmfs/bin/mmlsconfig, root mmlsfs: CommandFilter, /usr/lpp/mmfs/bin/mmlsfs, root find: CommandFilter, find, root