From 6252bd8e5ad77e52e720132455ccc3410d45bf65 Mon Sep 17 00:00:00 2001 From: Gaurang Tapase Date: Thu, 1 Jun 2017 05:57:21 -0400 Subject: [PATCH] Add CG capability to generic groups in GPFS driver This patch adds the consistency group capability to generic group in GPFS driver. Consistency groups will be used as generic groups. Change-Id: I31b210a7186e9212b85ce4ec17693c1ae4bed908 Closes-bug: #1682249 --- cinder/tests/unit/volume/drivers/test_gpfs.py | 272 +++++++++++++++--- cinder/volume/drivers/ibm/gpfs.py | 186 +++++++++--- ...neric-groups-in-gpfs-00bb093945a02642.yaml | 3 + 3 files changed, 387 insertions(+), 74 deletions(-) create mode 100644 releasenotes/notes/generic-groups-in-gpfs-00bb093945a02642.yaml diff --git a/cinder/tests/unit/volume/drivers/test_gpfs.py b/cinder/tests/unit/volume/drivers/test_gpfs.py index e768c10df32..f33d856d820 100644 --- a/cinder/tests/unit/volume/drivers/test_gpfs.py +++ b/cinder/tests/unit/volume/drivers/test_gpfs.py @@ -1402,9 +1402,11 @@ class GPFSDriverTestCase(test.TestCase): self.driver.copy_volume_to_image('', volume, '', '') @mock.patch('cinder.utils.execute') + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_get_volume_path') @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' '_can_migrate_locally') - def test_migrate_volume_ok(self, mock_local, mock_exec): + def test_migrate_volume_ok(self, mock_local, volume_path, mock_exec): volume = self._fake_volume() host = {} host = {'host': 'foo', 'capabilities': {}} @@ -1439,9 +1441,11 @@ class GPFSDriverTestCase(test.TestCase): self.driver._migrate_volume(volume, host)) @mock.patch('cinder.utils.execute') + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_get_volume_path') @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' '_can_migrate_locally') - def test_migrate_volume_fail_mv(self, mock_local, mock_exec): + def test_migrate_volume_fail_mv(self, mock_local, mock_path, mock_exec): volume = self._fake_volume() host = {} host = {'host': 'foo', 'capabilities': {}} @@ -1459,8 +1463,10 @@ class GPFSDriverTestCase(test.TestCase): @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver._migrate_volume') @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' '_update_volume_storage_pool') + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.local_path') @mock.patch('cinder.volume.drivers.ibm.gpfs._different') - def test_retype_ok(self, mock_different, mock_strg_pool, mock_migrate_vol): + def test_retype_ok(self, mock_different, local_path, + mock_strg_pool, mock_migrate_vol): ctxt = self.context (volume, new_type, diff, host) = self._fake_retype_arguments() self.driver.db = mock.Mock() @@ -1537,16 +1543,18 @@ class GPFSDriverTestCase(test.TestCase): diff, host)) + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.local_path') @mock.patch('cinder.utils.execute') - def test_mkfs_ok(self, mock_exec): + def test_mkfs_ok(self, mock_exec, local_path): volume = self._fake_volume() self.driver._mkfs(volume, 'swap') self.driver._mkfs(volume, 'swap', 'test') self.driver._mkfs(volume, 'ext3', 'test') self.driver._mkfs(volume, 'vfat', 'test') + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.local_path') @mock.patch('cinder.utils.execute') - def test_mkfs_fail_mk(self, mock_exec): + def test_mkfs_fail_mk(self, mock_exec, local_path): volume = self._fake_volume() mock_exec.side_effect = ( processutils.ProcessExecutionError(stdout='test', stderr='test')) @@ -1591,7 +1599,7 @@ class GPFSDriverTestCase(test.TestCase): def test_create_consistencygroup(self, mock_exec): ctxt = self.context group = self._fake_group() - self.driver.create_consistencygroup(ctxt, group) + self.driver._create_consistencygroup(ctxt, group) fsdev = self.driver._gpfs_device cgname = "consisgroup-%s" % group['id'] cgpath = os.path.join(self.driver.configuration.gpfs_mount_point_base, @@ -1610,7 +1618,7 @@ class GPFSDriverTestCase(test.TestCase): mock_exec.side_effect = ( processutils.ProcessExecutionError(stdout='test', stderr='test')) self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_consistencygroup, ctxt, group) + self.driver._create_consistencygroup, ctxt, group) @mock.patch('cinder.utils.execute') def test_delete_consistencygroup(self, mock_exec): @@ -1625,7 +1633,7 @@ class GPFSDriverTestCase(test.TestCase): self.driver.db.volume_get_all_by_group = mock.Mock() self.driver.db.volume_get_all_by_group.return_value = volumes - self.driver.delete_consistencygroup(ctxt, group, []) + self.driver._delete_consistencygroup(ctxt, group, []) fsdev = self.driver._gpfs_device cgname = "consisgroup-%s" % group['id'] cmd = ['mmlsfileset', fsdev, cgname] @@ -1650,7 +1658,7 @@ class GPFSDriverTestCase(test.TestCase): mock_exec.side_effect = ( processutils.ProcessExecutionError(exit_code=2)) - self.driver.delete_consistencygroup(ctxt, group, []) + self.driver._delete_consistencygroup(ctxt, group, []) fsdev = self.driver._gpfs_device cgname = "consisgroup-%s" % group['id'] cmd = ['mmlsfileset', fsdev, cgname] @@ -1668,19 +1676,20 @@ class GPFSDriverTestCase(test.TestCase): mock_exec.side_effect = ( processutils.ProcessExecutionError(stdout='test', stderr='test')) self.assertRaises(exception.VolumeBackendAPIException, - self.driver.delete_consistencygroup, ctxt, group, []) + self.driver._delete_consistencygroup, + ctxt, group, []) def test_update_consistencygroup(self): ctxt = self.context group = self._fake_group() self.assertRaises(exception.GPFSDriverUnsupportedOperation, - self.driver.update_consistencygroup, ctxt, group) + self.driver._update_consistencygroup, ctxt, group) def test_create_consisgroup_from_src(self): ctxt = self.context group = self._fake_group() self.assertRaises(exception.GPFSDriverUnsupportedOperation, - self.driver.create_consistencygroup_from_src, + self.driver._create_consistencygroup_from_src, ctxt, group, []) @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.create_snapshot') @@ -1688,8 +1697,8 @@ class GPFSDriverTestCase(test.TestCase): ctxt = self.context cgsnap = self._fake_cgsnapshot() snapshot1 = self._fake_snapshot() - model_update, snapshots = self.driver.create_cgsnapshot(ctxt, cgsnap, - [snapshot1]) + model_update, snapshots = self.driver._create_cgsnapshot(ctxt, cgsnap, + [snapshot1]) self.driver.create_snapshot.assert_called_once_with(snapshot1) self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, model_update) @@ -1701,8 +1710,8 @@ class GPFSDriverTestCase(test.TestCase): def test_create_cgsnapshot_empty(self, mock_create_snap): ctxt = self.context cgsnap = self._fake_cgsnapshot() - model_update, snapshots = self.driver.create_cgsnapshot(ctxt, cgsnap, - []) + model_update, snapshots = self.driver._create_cgsnapshot(ctxt, cgsnap, + []) self.assertFalse(self.driver.create_snapshot.called) self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, model_update) @@ -1712,8 +1721,8 @@ class GPFSDriverTestCase(test.TestCase): ctxt = self.context cgsnap = self._fake_cgsnapshot() snapshot1 = self._fake_snapshot() - model_update, snapshots = self.driver.delete_cgsnapshot(ctxt, cgsnap, - [snapshot1]) + model_update, snapshots = self.driver._delete_cgsnapshot(ctxt, cgsnap, + [snapshot1]) self.driver.delete_snapshot.assert_called_once_with(snapshot1) self.assertEqual({'status': fields.ConsistencyGroupStatus.DELETED}, model_update) @@ -1725,8 +1734,8 @@ class GPFSDriverTestCase(test.TestCase): def test_delete_cgsnapshot_empty(self, mock_delete_snap): ctxt = self.context cgsnap = self._fake_cgsnapshot() - model_update, snapshots = self.driver.delete_cgsnapshot(ctxt, cgsnap, - []) + model_update, snapshots = self.driver._delete_cgsnapshot(ctxt, cgsnap, + []) self.assertFalse(self.driver.delete_snapshot.called) self.assertEqual({'status': fields.ConsistencyGroupStatus.DELETED}, model_update) @@ -1741,8 +1750,14 @@ class GPFSDriverTestCase(test.TestCase): ret = self.driver.local_path(volume) self.assertEqual(volume_path, ret) - def test_local_path_volume_in_cg(self): + @mock.patch('cinder.db.get_by_id') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_local_path_volume_in_cg(self, mock_group_cg_snapshot_type, + mock_group_obj): + mock_group_cg_snapshot_type.return_value = True volume = self._fake_volume() + group = self._fake_group() + mock_group_obj.return_value = group cgname = "consisgroup-%s" % volume['group_id'] volume_path = os.path.join( self.driver.configuration.gpfs_mount_point_base, @@ -1806,7 +1821,11 @@ class GPFSDriverTestCase(test.TestCase): group = {} group['name'] = 'test_group' group['id'] = fake.CONSISTENCY_GROUP_ID - return group + group['user_id'] = fake.USER_ID + group['group_type_id'] = fake.GROUP_TYPE_ID + group['project_id'] = fake.PROJECT_ID + + return objects.Group(self.context, **group) def _fake_cgsnapshot(self): snapshot = self._fake_snapshot() @@ -1851,6 +1870,150 @@ class GPFSDriverTestCase(test.TestCase): return (volume, new_type, diff, host) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group = self._fake_group() + self.assertRaises( + NotImplementedError, + self.driver.create_group, + ctxt, group + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_create_consistencygroup') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_cg(self, mock_cg_snapshot_type, + mock_consisgroup_create): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group = self._fake_group() + self.driver.create_group(ctxt, group) + mock_consisgroup_create.assert_called_once_with(ctxt, group) + + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group = self._fake_group() + volumes = [] + self.assertRaises( + NotImplementedError, + self.driver.delete_group, + ctxt, group, volumes + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_delete_consistencygroup') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group_cg(self, mock_cg_snapshot_type, + mock_consisgroup_delete): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group = self._fake_group() + volumes = [] + self.driver.delete_group(ctxt, group, volumes) + mock_consisgroup_delete.assert_called_once_with(ctxt, group, volumes) + + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_update_group(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group = self._fake_group() + self.assertRaises( + NotImplementedError, + self.driver.update_group, + ctxt, group + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_update_consistencygroup') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_update_group_cg(self, mock_cg_snapshot_type, + mock_consisgroup_update): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group = self._fake_group() + self.driver.update_group(ctxt, group) + mock_consisgroup_update.assert_called_once_with(ctxt, group, + None, None) + + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_snapshot(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group_snapshot = mock.MagicMock() + snapshots = [mock.Mock()] + self.assertRaises( + NotImplementedError, + self.driver.create_group_snapshot, + ctxt, group_snapshot, snapshots + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_create_cgsnapshot') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_snapshot_cg(self, mock_cg_snapshot_type, + mock_cgsnapshot_create): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group_snapshot = mock.MagicMock() + snapshots = [mock.Mock()] + self.driver.create_group_snapshot(ctxt, group_snapshot, snapshots) + mock_cgsnapshot_create.assert_called_once_with(ctxt, group_snapshot, + snapshots) + + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group_snapshot(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group_snapshot = mock.MagicMock() + snapshots = [mock.Mock()] + self.assertRaises( + NotImplementedError, + self.driver.delete_group_snapshot, + ctxt, group_snapshot, snapshots + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_delete_cgsnapshot') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group_snapshot_cg(self, mock_cg_snapshot_type, + mock_cgsnapshot_delete): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group_snapshot = mock.MagicMock() + snapshots = [mock.Mock()] + self.driver.delete_group_snapshot(ctxt, group_snapshot, snapshots) + mock_cgsnapshot_delete.assert_called_once_with(ctxt, group_snapshot, + snapshots) + + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_from_src(self, mock_cg_snapshot_type): + mock_cg_snapshot_type.return_value = False + ctxt = self.context + group = self._fake_group() + volumes = [] + self.assertRaises( + NotImplementedError, + self.driver.create_group_from_src, + ctxt, group, volumes + ) + + @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSDriver.' + '_create_consistencygroup_from_src') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_from_src_cg(self, mock_cg_snapshot_type, + mock_cg_clone_create): + mock_cg_snapshot_type.return_value = True + ctxt = self.context + group = self._fake_group() + volumes = [] + self.driver.create_group_from_src(ctxt, group, volumes) + mock_cg_clone_create.assert_called_once_with(ctxt, group, volumes, + None, None, None, None) + class GPFSRemoteDriverTestCase(test.TestCase): """Unit tests for GPFSRemoteDriver class""" @@ -2004,11 +2167,24 @@ class GPFSNFSDriverTestCase(test.TestCase): def _fake_volume(self): volume = {} - volume['id'] = '123456' - volume['name'] = 'test' + volume['id'] = fake.VOLUME_ID + volume['display_name'] = 'test' + volume['metadata'] = {'key1': 'val1'} + volume['_name_id'] = None volume['size'] = 1000 - volume['group_id'] = 'cg-1234' - return volume + volume['group_id'] = fake.CONSISTENCY_GROUP_ID + + return objects.Volume(self.context, **volume) + + def _fake_group(self): + group = {} + group['name'] = 'test_group' + group['id'] = fake.CONSISTENCY_GROUP_ID + group['user_id'] = fake.USER_ID + group['group_type_id'] = fake.GROUP_TYPE_ID + group['project_id'] = fake.PROJECT_ID + + return objects.Group(self.context, **group) def _fake_snapshot(self): snapshot = {} @@ -2056,26 +2232,50 @@ class GPFSNFSDriverTestCase(test.TestCase): self.assertEqual('GPFSNFS', stats['volume_backend_name']) self.assertEqual('file', stats['storage_protocol']) - def test_get_volume_path(self): + @mock.patch('cinder.db.get_by_id') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_get_volume_path(self, mock_group_cg_snapshot_type, mock_group): + mock_group_cg_snapshot_type.return_value = True self.driver.configuration.gpfs_mount_point_base = ( self.TEST_GPFS_MNT_POINT_BASE) volume = self._fake_volume() - self.assertEqual('/export/consisgroup-cg-1234/test', + group = self._fake_group() + mock_group.return_value = group + volume_path_in_cg = os.path.join(self.TEST_GPFS_MNT_POINT_BASE, + 'consisgroup-' + + fake.CONSISTENCY_GROUP_ID, + 'volume-' + fake.VOLUME_ID) + self.assertEqual(volume_path_in_cg, self.driver._get_volume_path(volume)) - volume['group_id'] = None - self.assertEqual('/export/test', + volume.group_id = None + volume_path = os.path.join(self.TEST_GPFS_MNT_POINT_BASE, + 'volume-' + fake.VOLUME_ID) + self.assertEqual(volume_path, self.driver._get_volume_path(volume)) + @mock.patch('cinder.db.get_by_id') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSNFSDriver.' '_get_mount_point_for_share') - def test_local_path(self, mock_mount_point): - mock_mount_point.return_value = self.TEST_MNT_POINT + def test_local_path(self, mock_mount_point, + mock_group_cg_snapshot_type, + mock_group): + mock_mount_point.return_value = self.TEST_MNT_POINT_BASE + mock_group_cg_snapshot_type.return_value = True volume = self._fake_volume() - volume['provider_location'] = self.TEST_GPFS_MNT_POINT_BASE - self.assertEqual('/mnt/nfs/consisgroup-cg-1234/test', + group = self._fake_group() + mock_group.return_value = group + volume['provider_location'] = self.TEST_MNT_POINT_BASE + local_volume_path_in_cg = os.path.join(self.TEST_MNT_POINT_BASE, + 'consisgroup-' + + fake.CONSISTENCY_GROUP_ID, + 'volume-' + fake.VOLUME_ID) + self.assertEqual(local_volume_path_in_cg, self.driver.local_path(volume)) - volume['group_id'] = None - self.assertEqual('/mnt/nfs/test', + volume.group_id = None + local_volume_path = os.path.join(self.TEST_MNT_POINT_BASE, + 'volume-' + fake.VOLUME_ID) + self.assertEqual(local_volume_path, self.driver.local_path(volume)) @mock.patch('cinder.volume.drivers.ibm.gpfs.GPFSNFSDriver.' diff --git a/cinder/volume/drivers/ibm/gpfs.py b/cinder/volume/drivers/ibm/gpfs.py index 7770d23f2a7..19a202ba76a 100644 --- a/cinder/volume/drivers/ibm/gpfs.py +++ b/cinder/volume/drivers/ibm/gpfs.py @@ -39,6 +39,7 @@ from cinder.volume import driver from cinder.volume.drivers import nfs from cinder.volume.drivers import remotefs from cinder.volume.drivers.san import san +from cinder.volume import utils as volume_utils GPFS_CLONE_MIN_RELEASE = 1200 GPFS_ENC_MIN_RELEASE = 1404 @@ -771,18 +772,20 @@ class GPFSDriver(driver.CloneableImageVD, """Return the local path for the specified volume.""" # Check if the volume is part of a consistency group and return # the local_path accordingly. - if volume['group_id'] is not None: - cgname = "consisgroup-%s" % volume['group_id'] - volume_path = os.path.join( - self.configuration.gpfs_mount_point_base, - cgname, - volume['name'] - ) - else: - volume_path = os.path.join( - self.configuration.gpfs_mount_point_base, - volume['name'] - ) + if volume.group_id is not None: + if volume_utils.is_group_a_cg_snapshot_type(volume.group): + cgname = "consisgroup-%s" % volume.group_id + volume_path = os.path.join( + self.configuration.gpfs_mount_point_base, + cgname, + volume.name + ) + return volume_path + + volume_path = os.path.join( + self.configuration.gpfs_mount_point_base, + volume.name + ) return volume_path def _get_gpfs_encryption_status(self): @@ -858,6 +861,7 @@ class GPFSDriver(driver.CloneableImageVD, 'root_path': gpfs_base}) data['consistencygroup_support'] = 'True' + data['consistent_group_snapshot_enabled'] = True if self._encryption_state.lower() == 'yes': data['gpfs_encryption_rest'] = 'True' @@ -1120,7 +1124,7 @@ class GPFSDriver(driver.CloneableImageVD, LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - def create_consistencygroup(self, context, group): + def _create_consistencygroup(self, context, group): """Create consistency group of GPFS volumes.""" cgname = "consisgroup-%s" % group['id'] fsdev = self._gpfs_device @@ -1156,10 +1160,10 @@ class GPFSDriver(driver.CloneableImageVD, LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} + model_update = {'status': fields.GroupStatus.AVAILABLE} return model_update - def delete_consistencygroup(self, context, group, volumes): + def _delete_consistencygroup(self, context, group, volumes): """Delete consistency group of GPFS volumes.""" cgname = "consisgroup-%s" % group['id'] fsdev = self._gpfs_device @@ -1208,9 +1212,9 @@ class GPFSDriver(driver.CloneableImageVD, return None, None - def create_cgsnapshot(self, context, cgsnapshot, snapshots): + def _create_cgsnapshot(self, context, cgsnapshot, snapshots): """Create snapshot of a consistency group of GPFS volumes.""" - model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} + model_update = {'status': fields.GroupStatus.AVAILABLE} snapshots_model_update = [] try: @@ -1218,7 +1222,7 @@ class GPFSDriver(driver.CloneableImageVD, self.create_snapshot(snapshot) except exception.VolumeBackendAPIException as err: model_update['status'] = ( - fields.ConsistencyGroupStatus.ERROR) + fields.GroupStatus.ERROR) LOG.error("Failed to create the snapshot %(snap)s of " "CGSnapshot. Exception: %(exception)s.", {'snap': snapshot.name, 'exception': err}) @@ -1230,9 +1234,9 @@ class GPFSDriver(driver.CloneableImageVD, return model_update, snapshots_model_update - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): + def _delete_cgsnapshot(self, context, cgsnapshot, snapshots): """Delete snapshot of a consistency group of GPFS volumes.""" - model_update = {'status': fields.ConsistencyGroupStatus.DELETED} + model_update = {'status': fields.GroupStatus.DELETED} snapshots_model_update = [] try: @@ -1240,7 +1244,7 @@ class GPFSDriver(driver.CloneableImageVD, self.delete_snapshot(snapshot) except exception.VolumeBackendAPIException as err: model_update['status'] = ( - fields.ConsistencyGroupStatus.ERROR_DELETING) + fields.GroupStatus.ERROR_DELETING) LOG.error("Failed to delete the snapshot %(snap)s of " "CGSnapshot. Exception: %(exception)s.", {'snap': snapshot.name, 'exception': err}) @@ -1252,20 +1256,127 @@ class GPFSDriver(driver.CloneableImageVD, return model_update, snapshots_model_update - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): + def _update_consistencygroup(self, context, group, + add_volumes=None, remove_volumes=None): msg = _('Updating a consistency group is not supported.') LOG.error(msg) raise exception.GPFSDriverUnsupportedOperation(msg=msg) - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): + def _create_consistencygroup_from_src(self, context, group, volumes, + cgsnapshot=None, snapshots=None, + source_cg=None, source_vols=None): msg = _('Creating a consistency group from any source consistency ' 'group or consistency group snapshot is not supported.') LOG.error(msg) raise exception.GPFSDriverUnsupportedOperation(msg=msg) + def create_group(self, ctxt, group): + """Creates a group. + + :param ctxt: the context of the caller. + :param group: the Group object of the group to be created. + :returns: model_update + """ + if volume_utils.is_group_a_cg_snapshot_type(group): + return self._create_consistencygroup(ctxt, group) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + + def delete_group(self, ctxt, group, volumes): + """Deletes a group. + + :param ctxt: the context of the caller. + :param group: the Group object of the group to be deleted. + :param volumes: a list of Volume objects in the group. + :returns: model_update, volumes_model_update + """ + if volume_utils.is_group_a_cg_snapshot_type(group): + return self._delete_consistencygroup(ctxt, group, volumes) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + + def update_group(self, ctxt, group, + add_volumes=None, remove_volumes=None): + """Updates a group. + + :param ctxt: the context of the caller. + :param group: the Group object of the group to be updated. + :param add_volumes: a list of Volume objects to be added. + :param remove_volumes: a list of Volume objects to be removed. + :returns: model_update, add_volumes_update, remove_volumes_update + """ + + if volume_utils.is_group_a_cg_snapshot_type(group): + return self._update_consistencygroup(ctxt, + group, + add_volumes, + remove_volumes) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + + def create_group_snapshot(self, ctxt, group_snapshot, snapshots): + """Creates a group_snapshot. + + :param ctxt: the context of the caller. + :param group_snapshot: the GroupSnapshot object to be created. + :param snapshots: a list of Snapshot objects in the group_snapshot. + :returns: model_update, snapshots_model_update + """ + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self._create_cgsnapshot(ctxt, group_snapshot, snapshots) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + + def delete_group_snapshot(self, ctxt, group_snapshot, snapshots): + """Deletes a group_snapshot. + + :param ctxt: the context of the caller. + :param group_snapshot: the GroupSnapshot object to be deleted. + :param snapshots: a list of snapshot objects in the group_snapshot. + :returns: model_update, snapshots_model_update + """ + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self._delete_cgsnapshot(ctxt, group_snapshot, snapshots) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + + def create_group_from_src(self, ctxt, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + """Creates a group from source. + + :param ctxt: the context of the caller. + :param group: the Group object to be created. + :param volumes: a list of Volume objects in the group. + :param group_snapshot: the GroupSnapshot object as source. + :param snapshots: a list of snapshot objects in group_snapshot. + :param source_group: the Group object as source. + :param source_vols: a list of volume objects in the source_group. + :returns: model_update, volumes_model_update + """ + if volume_utils.is_group_a_cg_snapshot_type(group): + return self._create_consistencygroup_from_src(ctxt, + group, + volumes, + group_snapshot, + snapshots, + source_group, + source_vols) + + # If it wasn't a consistency group request ignore it and we'll rely on + # the generic group implementation. + raise NotImplementedError() + @interface.volumedriver class GPFSRemoteDriver(GPFSDriver, san.SanDriver): @@ -1467,18 +1578,18 @@ class GPFSNFSDriver(GPFSDriver, nfs.NfsDriver, san.SanDriver): 'root_path': gpfs_base}) data['consistencygroup_support'] = 'True' + data['consistent_group_snapshot_enabled'] = True self._stats = data LOG.debug("Exit _update_volume_stats.") def _get_volume_path(self, volume): """Returns remote GPFS path for the given volume.""" export_path = self.configuration.gpfs_mount_point_base - if volume['group_id'] is not None: - cgname = "consisgroup-%s" % volume['group_id'] - volume_path = os.path.join(export_path, cgname, volume['name']) - else: - volume_path = os.path.join(export_path, volume['name']) - return volume_path + if volume.group_id is not None: + if volume_utils.is_group_a_cg_snapshot_type(volume.group): + cgname = "consisgroup-%s" % volume.group_id + return os.path.join(export_path, cgname, volume.name) + return os.path.join(export_path, volume.name) def local_path(self, volume): """Returns the local path for the specified volume.""" @@ -1487,12 +1598,11 @@ class GPFSNFSDriver(GPFSDriver, nfs.NfsDriver, san.SanDriver): # Check if the volume is part of a consistency group and return # the local_path accordingly. - if volume['group_id'] is not None: - cgname = "consisgroup-%s" % volume['group_id'] - volume_path = os.path.join(base_local_path, cgname, volume['name']) - else: - volume_path = os.path.join(base_local_path, volume['name']) - return volume_path + if volume.group_id is not None: + if volume_utils.is_group_a_cg_snapshot_type(volume.group): + cgname = "consisgroup-%s" % volume.group_id + return os.path.join(base_local_path, cgname, volume.name) + return os.path.join(base_local_path, volume.name) def _get_snapshot_path(self, snapshot): """Returns remote GPFS path for the given snapshot.""" diff --git a/releasenotes/notes/generic-groups-in-gpfs-00bb093945a02642.yaml b/releasenotes/notes/generic-groups-in-gpfs-00bb093945a02642.yaml new file mode 100644 index 00000000000..9e8ad113e2d --- /dev/null +++ b/releasenotes/notes/generic-groups-in-gpfs-00bb093945a02642.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added consistent group capability to generic volume groups in GPFS driver.