From 80fe5f1fe4eb2f9d63c36fbfed3dcc388e4a931b Mon Sep 17 00:00:00 2001 From: Ben Swartzlander Date: Thu, 3 Aug 2017 16:32:07 -0400 Subject: [PATCH] Re-enable broken CG code in NetApp driver Override the new create_share_group_snapshot() method in the driver class and call the old CG snapshot code if the share group specifies CG support, otherwise fall back on the new (existing) code. This patch also removes dead code from the old CG feature from Newton and earlier releases. Change-Id: Ief71b9900c2c84e0df1d12d303517fa20ff7908b Closes-bug: #1659023 --- .../dataontap/cluster_mode/drv_multi_svm.py | 47 +++-- .../dataontap/cluster_mode/drv_single_svm.py | 47 +++-- .../netapp/dataontap/cluster_mode/lib_base.py | 84 ++++---- .../dataontap/cluster_mode/test_lib_base.py | 179 ++++++++++-------- .../share/drivers/netapp/dataontap/fakes.py | 13 +- ...659023-netapp-cg-fix-56bb77b7bc61c3f5.yaml | 5 + 6 files changed, 224 insertions(+), 151 deletions(-) create mode 100644 releasenotes/notes/bug-1659023-netapp-cg-fix-56bb77b7bc61c3f5.yaml diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py index 9f7701cec7..293273b503 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py @@ -71,25 +71,6 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver): def shrink_share(self, share, new_size, **kwargs): self.library.shrink_share(share, new_size, **kwargs) - def create_consistency_group(self, context, cg_dict, **kwargs): - return self.library.create_consistency_group(context, cg_dict, - **kwargs) - - def create_consistency_group_from_cgsnapshot(self, context, cg_dict, - cgsnapshot_dict, **kwargs): - return self.library.create_consistency_group_from_cgsnapshot( - context, cg_dict, cgsnapshot_dict, **kwargs) - - def delete_consistency_group(self, context, cg_dict, **kwargs): - return self.library.delete_consistency_group(context, cg_dict, - **kwargs) - - def create_cgsnapshot(self, context, snap_dict, **kwargs): - return self.library.create_cgsnapshot(context, snap_dict, **kwargs) - - def delete_cgsnapshot(self, context, snap_dict, **kwargs): - return self.library.delete_cgsnapshot(context, snap_dict, **kwargs) - def ensure_share(self, context, share, **kwargs): pass @@ -225,3 +206,31 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver): context, source_share, destination_share, source_snapshots, snapshot_mappings, share_server=share_server, destination_share_server=destination_share_server) + + def create_share_group_snapshot(self, context, snap_dict, + share_server=None): + fallback_create = super(NetAppCmodeMultiSvmShareDriver, + self).create_share_group_snapshot + return self.library.create_group_snapshot(context, snap_dict, + fallback_create, + share_server) + + def delete_share_group_snapshot(self, context, snap_dict, + share_server=None): + fallback_delete = super(NetAppCmodeMultiSvmShareDriver, + self).delete_share_group_snapshot + return self.library.delete_group_snapshot(context, snap_dict, + fallback_delete, + share_server) + + def create_share_group_from_share_group_snapshot( + self, context, share_group_dict, snapshot_dict, + share_server=None): + fallback_create = super( + NetAppCmodeMultiSvmShareDriver, + self).create_share_group_from_share_group_snapshot + return self.library.create_group_from_snapshot(context, + share_group_dict, + snapshot_dict, + fallback_create, + share_server) diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py index 2433afdba6..356962193f 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py @@ -71,25 +71,6 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver): def shrink_share(self, share, new_size, **kwargs): self.library.shrink_share(share, new_size, **kwargs) - def create_consistency_group(self, context, cg_dict, **kwargs): - return self.library.create_consistency_group(context, cg_dict, - **kwargs) - - def create_consistency_group_from_cgsnapshot(self, context, cg_dict, - cgsnapshot_dict, **kwargs): - return self.library.create_consistency_group_from_cgsnapshot( - context, cg_dict, cgsnapshot_dict, **kwargs) - - def delete_consistency_group(self, context, cg_dict, **kwargs): - return self.library.delete_consistency_group(context, cg_dict, - **kwargs) - - def create_cgsnapshot(self, context, snap_dict, **kwargs): - return self.library.create_cgsnapshot(context, snap_dict, **kwargs) - - def delete_cgsnapshot(self, context, snap_dict, **kwargs): - return self.library.delete_cgsnapshot(context, snap_dict, **kwargs) - def ensure_share(self, context, share, **kwargs): pass @@ -241,3 +222,31 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver): context, source_share, destination_share, source_snapshots, snapshot_mappings, share_server=share_server, destination_share_server=destination_share_server) + + def create_share_group_snapshot(self, context, snap_dict, + share_server=None): + fallback_create = super(NetAppCmodeSingleSvmShareDriver, + self).create_share_group_snapshot + return self.library.create_group_snapshot(context, snap_dict, + fallback_create, + share_server) + + def delete_share_group_snapshot(self, context, snap_dict, + share_server=None): + fallback_delete = super(NetAppCmodeSingleSvmShareDriver, + self).delete_share_group_snapshot + return self.library.delete_group_snapshot(context, snap_dict, + fallback_delete, + share_server) + + def create_share_group_from_share_group_snapshot( + self, context, share_group_dict, snapshot_dict, + share_server=None): + fallback_create = super( + NetAppCmodeSingleSvmShareDriver, + self).create_share_group_from_share_group_snapshot + return self.library.create_group_from_snapshot(context, + share_group_dict, + snapshot_dict, + fallback_create, + share_server) diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py index ebb1d07336..e07c2346fa 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py @@ -258,6 +258,9 @@ class NetAppCmodeFileStorageLibrary(object): 'storage_protocol': 'NFS_CIFS', 'pools': self._get_pools(filter_function=filter_function, goodness_function=goodness_function), + 'share_group_stats': { + 'consistent_snapshot_support': 'host', + }, } if (self.configuration.replication_domain and @@ -1096,15 +1099,6 @@ class NetAppCmodeFileStorageLibrary(object): def unmanage_snapshot(self, snapshot): """Removes the specified snapshot from Manila management.""" - @na_utils.trace - def create_consistency_group(self, context, cg_dict, share_server=None): - """Creates a consistency group. - - cDOT has no persistent CG object, so apart from validating the - share_server info is passed correctly, this method has nothing to do. - """ - vserver, vserver_client = self._get_vserver(share_server=share_server) - @na_utils.trace def create_consistency_group_from_cgsnapshot( self, context, cg_dict, cgsnapshot_dict, share_server=None): @@ -1112,7 +1106,7 @@ class NetAppCmodeFileStorageLibrary(object): vserver, vserver_client = self._get_vserver(share_server=share_server) # Ensure there is something to do - if not cgsnapshot_dict['cgsnapshot_members']: + if not cgsnapshot_dict['share_group_snapshot_members']: return None, None clone_list = self._collate_cg_snapshot_info(cg_dict, cgsnapshot_dict) @@ -1153,12 +1147,13 @@ class NetAppCmodeFileStorageLibrary(object): clone_info = {'share': share} - for cgsnapshot_member in cgsnapshot_dict['cgsnapshot_members']: - if (share['source_cgsnapshot_member_id'] == + for cgsnapshot_member in ( + cgsnapshot_dict['share_group_snapshot_members']): + if (share['source_share_group_snapshot_member_id'] == cgsnapshot_member['id']): clone_info['snapshot'] = { 'share_id': cgsnapshot_member['share_id'], - 'id': cgsnapshot_member['cgsnapshot_id'] + 'id': cgsnapshot_dict['id'] } break @@ -1171,31 +1166,14 @@ class NetAppCmodeFileStorageLibrary(object): return clone_list - @na_utils.trace - def delete_consistency_group(self, context, cg_dict, share_server=None): - """Deletes a consistency group. - - cDOT has no persistent CG object, so apart from validating the - share_server info is passed correctly, this method has nothing to do. - """ - try: - vserver, vserver_client = self._get_vserver( - share_server=share_server) - except (exception.InvalidInput, - exception.VserverNotSpecified, - exception.VserverNotFound) as error: - LOG.warning("Could not determine share server for consistency " - "group being deleted: %(cg)s. Deletion of CG " - "record will proceed anyway. Error: %(error)s", - {'cg': cg_dict['id'], 'error': error}) - @na_utils.trace def create_cgsnapshot(self, context, snap_dict, share_server=None): """Creates a consistency group snapshot.""" vserver, vserver_client = self._get_vserver(share_server=share_server) share_names = [self._get_backend_share_name(member['share_id']) - for member in snap_dict.get('cgsnapshot_members', [])] + for member in + snap_dict.get('share_group_snapshot_members', [])] snapshot_name = self._get_backend_cg_snapshot_name(snap_dict['id']) if share_names: @@ -1220,7 +1198,8 @@ class NetAppCmodeFileStorageLibrary(object): return None, None share_names = [self._get_backend_share_name(member['share_id']) - for member in snap_dict.get('cgsnapshot_members', [])] + for member in ( + snap_dict.get('share_group_snapshot_members', []))] snapshot_name = self._get_backend_cg_snapshot_name(snap_dict['id']) for share_name in share_names: @@ -1236,6 +1215,45 @@ class NetAppCmodeFileStorageLibrary(object): return None, None + @staticmethod + def _is_group_cg(context, share_group): + return 'host' == share_group.consistent_snapshot_support + + @na_utils.trace + def create_group_snapshot(self, context, snap_dict, fallback_create, + share_server=None): + share_group = snap_dict['share_group'] + if self._is_group_cg(context, share_group): + return self.create_cgsnapshot(context, snap_dict, + share_server=share_server) + else: + return fallback_create(context, snap_dict, + share_server=share_server) + + @na_utils.trace + def delete_group_snapshot(self, context, snap_dict, fallback_delete, + share_server=None): + share_group = snap_dict['share_group'] + if self._is_group_cg(context, share_group): + return self.delete_cgsnapshot(context, snap_dict, + share_server=share_server) + else: + return fallback_delete(context, snap_dict, + share_server=share_server) + + @na_utils.trace + def create_group_from_snapshot(self, context, share_group, + snapshot_dict, fallback_create, + share_server=None): + share_group2 = snapshot_dict['share_group'] + if self._is_group_cg(context, share_group2): + return self.create_consistency_group_from_cgsnapshot( + context, share_group, snapshot_dict, + share_server=share_server) + else: + return fallback_create(context, share_group, snapshot_dict, + share_server=share_server) + @na_utils.trace def _adjust_qos_policy_with_volume_resize(self, share, new_size, vserver_client): diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py index 701319665d..d56e870fca 100644 --- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py +++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py @@ -367,6 +367,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'netapp_storage_family': 'ontap_cluster', 'storage_protocol': 'NFS_CIFS', 'pools': fake.POOLS, + 'share_group_stats': {'consistent_snapshot_support': 'host'}, } self.assertDictEqual(expected, result) mock_get_pools.assert_called_once_with(filter_function='filter', @@ -392,6 +393,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'replication_type': 'dr', 'replication_domain': 'fake_domain', 'pools': fake.POOLS, + 'share_group_stats': {'consistent_snapshot_support': 'host'}, } self.assertDictEqual(expected, result) mock_get_pools.assert_called_once_with(filter_function='filter', @@ -1839,40 +1841,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): fake.FLEXVOL_TO_MANAGE, vserver_client) - def test_create_consistency_group(self): - - vserver_client = mock.Mock() - mock_get_vserver = self.mock_object( - self.library, '_get_vserver', - mock.Mock(return_value=(fake.VSERVER1, vserver_client))) - - result = self.library.create_consistency_group( - self.context, fake.EMPTY_CONSISTENCY_GROUP, - share_server=fake.SHARE_SERVER) - - self.assertIsNone(result) - mock_get_vserver.assert_called_once_with( - share_server=fake.SHARE_SERVER) - - @ddt.data(exception.InvalidInput(reason='fake_reason'), - exception.VserverNotSpecified(), - exception.VserverNotFound(vserver='fake_vserver')) - def test_create_consistency_group_no_share_server(self, - get_vserver_exception): - - mock_get_vserver = self.mock_object( - self.library, '_get_vserver', - mock.Mock(side_effect=get_vserver_exception)) - - self.assertRaises(type(get_vserver_exception), - self.library.create_consistency_group, - self.context, - fake.EMPTY_CONSISTENCY_GROUP, - share_server=fake.SHARE_SERVER) - - mock_get_vserver.assert_called_once_with( - share_server=fake.SHARE_SERVER) - def test_create_consistency_group_from_cgsnapshot(self): vserver_client = mock.Mock() @@ -1936,7 +1904,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): mock.Mock(side_effect=[['loc3'], ['loc4']])) fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) - fake_cg_snapshot['cgsnapshot_members'] = [] + fake_cg_snapshot['share_group_snapshot_members'] = [] result = self.library.create_consistency_group_from_cgsnapshot( self.context, @@ -1961,48 +1929,12 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): def test_collate_cg_snapshot_info_invalid(self): fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) - fake_cg_snapshot['cgsnapshot_members'] = [] + fake_cg_snapshot['share_group_snapshot_members'] = [] self.assertRaises(exception.InvalidShareGroup, self.library._collate_cg_snapshot_info, fake.CONSISTENCY_GROUP_DEST, fake_cg_snapshot) - def test_delete_consistency_group(self): - - vserver_client = mock.Mock() - mock_get_vserver = self.mock_object( - self.library, '_get_vserver', - mock.Mock(return_value=(fake.VSERVER1, vserver_client))) - - result = self.library.delete_consistency_group( - self.context, - fake.EMPTY_CONSISTENCY_GROUP, - share_server=fake.SHARE_SERVER) - - self.assertIsNone(result) - mock_get_vserver.assert_called_once_with( - share_server=fake.SHARE_SERVER) - - @ddt.data(exception.InvalidInput(reason='fake_reason'), - exception.VserverNotSpecified(), - exception.VserverNotFound(vserver='fake_vserver')) - def test_delete_consistency_group_no_share_server(self, - get_vserver_exception): - - mock_get_vserver = self.mock_object( - self.library, '_get_vserver', - mock.Mock(side_effect=get_vserver_exception)) - - result = self.library.delete_consistency_group( - self.context, - fake.EMPTY_CONSISTENCY_GROUP, - share_server=fake.SHARE_SERVER) - - self.assertIsNone(result) - self.assertEqual(1, lib_base.LOG.warning.call_count) - mock_get_vserver.assert_called_once_with( - share_server=fake.SHARE_SERVER) - def test_create_cgsnapshot(self): vserver_client = mock.Mock() @@ -2037,7 +1969,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): mock.Mock(return_value=(fake.VSERVER1, vserver_client))) fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) - fake_cg_snapshot['cgsnapshot_members'] = [] + fake_cg_snapshot['share_group_snapshot_members'] = [] result = self.library.create_cgsnapshot( self.context, @@ -2090,7 +2022,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): '_delete_snapshot') fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) - fake_cg_snapshot['cgsnapshot_members'] = [] + fake_cg_snapshot['share_group_snapshot_members'] = [] result = self.library.delete_cgsnapshot( self.context, @@ -4779,3 +4711,102 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): share_obj, fake.VSERVER1, {'maxiops': '3000'}) self.library._client.qos_policy_group_modify.assert_not_called() self.library._client.qos_policy_group_rename.assert_not_called() + + @ddt.data(('host', True), ('pool', False), (None, False), ('fake', False)) + @ddt.unpack + def test__is_group_cg(self, css, is_cg): + share_group = mock.Mock() + share_group.consistent_snapshot_support = css + self.assertEqual(is_cg, + self.library._is_group_cg(self.context, share_group)) + + def test_create_group_snapshot_cg(self): + share_group = mock.Mock() + share_group.consistent_snapshot_support = 'host' + snap_dict = {'share_group': share_group} + fallback_create = mock.Mock() + mock_create_cgsnapshot = self.mock_object(self.library, + 'create_cgsnapshot') + self.library.create_group_snapshot(self.context, snap_dict, + fallback_create, + share_server=fake.SHARE_SERVER) + mock_create_cgsnapshot.assert_called_once_with( + self.context, snap_dict, share_server=fake.SHARE_SERVER) + fallback_create.assert_not_called() + + @ddt.data('pool', None, 'fake') + def test_create_group_snapshot_fallback(self, css): + share_group = mock.Mock() + share_group.consistent_snapshot_support = css + snap_dict = {'share_group': share_group} + fallback_create = mock.Mock() + mock_create_cgsnapshot = self.mock_object(self.library, + 'create_cgsnapshot') + self.library.create_group_snapshot(self.context, snap_dict, + fallback_create, + share_server=fake.SHARE_SERVER) + mock_create_cgsnapshot.assert_not_called() + fallback_create.assert_called_once_with(self.context, + snap_dict, + share_server=fake.SHARE_SERVER) + + def test_delete_group_snapshot_cg(self): + share_group = mock.Mock() + share_group.consistent_snapshot_support = 'host' + snap_dict = {'share_group': share_group} + fallback_delete = mock.Mock() + mock_delete_cgsnapshot = self.mock_object(self.library, + 'delete_cgsnapshot') + self.library.delete_group_snapshot(self.context, snap_dict, + fallback_delete, + share_server=fake.SHARE_SERVER) + mock_delete_cgsnapshot.assert_called_once_with( + self.context, snap_dict, share_server=fake.SHARE_SERVER) + fallback_delete.assert_not_called() + + @ddt.data('pool', None, 'fake') + def test_delete_group_snapshot_fallback(self, css): + share_group = mock.Mock() + share_group.consistent_snapshot_support = css + snap_dict = {'share_group': share_group} + fallback_delete = mock.Mock() + mock_delete_cgsnapshot = self.mock_object(self.library, + 'delete_cgsnapshot') + self.library.delete_group_snapshot(self.context, snap_dict, + fallback_delete, + share_server=fake.SHARE_SERVER) + mock_delete_cgsnapshot.assert_not_called() + fallback_delete.assert_called_once_with(self.context, + snap_dict, + share_server=fake.SHARE_SERVER) + + def test_create_group_from_snapshot_cg(self): + share_group = mock.Mock() + share_group.consistent_snapshot_support = 'host' + snap_dict = {'share_group': share_group} + fallback_create = mock.Mock() + mock_create_cg_from_snapshot = self.mock_object( + self.library, 'create_consistency_group_from_cgsnapshot') + self.library.create_group_from_snapshot(self.context, share_group, + snap_dict, fallback_create, + share_server=fake.SHARE_SERVER) + mock_create_cg_from_snapshot.assert_called_once_with( + self.context, share_group, snap_dict, + share_server=fake.SHARE_SERVER) + fallback_create.assert_not_called() + + @ddt.data('pool', None, 'fake') + def test_create_group_from_snapshot_fallback(self, css): + share_group = mock.Mock() + share_group.consistent_snapshot_support = css + snap_dict = {'share_group': share_group} + fallback_create = mock.Mock() + mock_create_cg_from_snapshot = self.mock_object( + self.library, 'create_consistency_group_from_cgsnapshot') + self.library.create_group_from_snapshot(self.context, share_group, + snap_dict, fallback_create, + share_server=fake.SHARE_SERVER) + mock_create_cg_from_snapshot.assert_not_called() + fallback_create.assert_called_once_with(self.context, share_group, + snap_dict, + share_server=fake.SHARE_SERVER) diff --git a/manila/tests/share/drivers/netapp/dataontap/fakes.py b/manila/tests/share/drivers/netapp/dataontap/fakes.py index 3df254ab43..6890be9460 100644 --- a/manila/tests/share/drivers/netapp/dataontap/fakes.py +++ b/manila/tests/share/drivers/netapp/dataontap/fakes.py @@ -385,7 +385,7 @@ SHARE_FOR_CG1 = { 'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME}, 'name': 'share_1', 'share_proto': 'NFS', - 'source_cgsnapshot_member_id': None, + 'source_share_group_snapshot_member_id': None, } SHARE_FOR_CG2 = { @@ -394,7 +394,7 @@ SHARE_FOR_CG2 = { 'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME}, 'name': 'share_2', 'share_proto': 'NFS', - 'source_cgsnapshot_member_id': None, + 'source_share_group_snapshot_member_id': None, } # Clone dest of SHARE_FOR_CG1 @@ -404,7 +404,7 @@ SHARE_FOR_CG3 = { 'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME}, 'name': 'share3', 'share_proto': 'NFS', - 'source_cgsnapshot_member_id': CG_SNAPSHOT_MEMBER_ID1, + 'source_share_group_snapshot_member_id': CG_SNAPSHOT_MEMBER_ID1, } # Clone dest of SHARE_FOR_CG2 @@ -414,7 +414,7 @@ SHARE_FOR_CG4 = { 'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME}, 'name': 'share4', 'share_proto': 'NFS', - 'source_cgsnapshot_member_id': CG_SNAPSHOT_MEMBER_ID2, + 'source_share_group_snapshot_member_id': CG_SNAPSHOT_MEMBER_ID2, } EMPTY_CONSISTENCY_GROUP = { @@ -462,9 +462,10 @@ CG_SNAPSHOT_MEMBER_2 = { } CG_SNAPSHOT = { - 'cgsnapshot_members': [CG_SNAPSHOT_MEMBER_1, CG_SNAPSHOT_MEMBER_2], + 'share_group_snapshot_members': [CG_SNAPSHOT_MEMBER_1, + CG_SNAPSHOT_MEMBER_2], 'share_group': CONSISTENCY_GROUP, - 'consistency_group_id': CONSISTENCY_GROUP_ID, + 'share_group_id': CONSISTENCY_GROUP_ID, 'id': CG_SNAPSHOT_ID, 'project_id': TENANT_ID, } diff --git a/releasenotes/notes/bug-1659023-netapp-cg-fix-56bb77b7bc61c3f5.yaml b/releasenotes/notes/bug-1659023-netapp-cg-fix-56bb77b7bc61c3f5.yaml new file mode 100644 index 0000000000..6ec1a029ca --- /dev/null +++ b/releasenotes/notes/bug-1659023-netapp-cg-fix-56bb77b7bc61c3f5.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - Re-enabled the consistent snapshot code in the NetApp driver, now + compatible with the Manila Share Groups API instead of the deprecated + and removed Manila Consistency Groups API.