diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py index 7732f4673bb..c5f8862053d 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/fakes.py @@ -529,7 +529,6 @@ VG_VOLUME_SNAPSHOT = { 'id': VG_VOLUME_SNAPSHOT_ID, 'status': 'fake_status', 'volume_id': VG_VOLUME_ID, - } diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py index c1a1f93114a..8ab96af69ae 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_7mode.py @@ -25,8 +25,10 @@ from lxml import etree import mock from oslo_utils import timeutils +from oslo_utils import units from cinder import exception +from cinder.objects import fields from cinder import test import cinder.tests.unit.volume.drivers.netapp.dataontap.client.fakes \ as client_fakes @@ -39,6 +41,7 @@ from cinder.volume.drivers.netapp.dataontap.client import client_base from cinder.volume.drivers.netapp.dataontap.performance import perf_7mode from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils from cinder.volume.drivers.netapp import utils as na_utils +from cinder.volume import utils as volume_utils @ddt.ddt @@ -777,3 +780,188 @@ class NetAppBlockStorage7modeLibraryTestCase(test.TestCase): result = self.library._get_backing_flexvol_names() self.assertEqual('vol2', result[2]) + + def test_create_cgsnapshot(self): + snapshot = fake.CG_SNAPSHOT + snapshot['volume'] = fake.CG_VOLUME + + mock_extract_host = self.mock_object( + volume_utils, 'extract_host', return_value=fake.POOL_NAME) + + mock_clone_lun = self.mock_object(self.library, '_clone_lun') + mock_busy = self.mock_object( + self.zapi_client, 'wait_for_busy_snapshot') + mock_delete_snapshot = self.mock_object( + self.zapi_client, 'delete_snapshot') + + self.library.create_cgsnapshot(fake.CG_SNAPSHOT, [snapshot]) + + mock_extract_host.assert_called_once_with(fake.CG_VOLUME['host'], + level='pool') + self.zapi_client.create_cg_snapshot.assert_called_once_with( + set([fake.POOL_NAME]), fake.CG_SNAPSHOT_ID) + mock_clone_lun.assert_called_once_with( + fake.CG_VOLUME_NAME, fake.CG_SNAPSHOT_NAME, + source_snapshot=fake.CG_SNAPSHOT_ID) + mock_busy.assert_called_once_with(fake.POOL_NAME, fake.CG_SNAPSHOT_ID) + mock_delete_snapshot.assert_called_once_with( + fake.POOL_NAME, fake.CG_SNAPSHOT_ID) + + def test_create_cgsnapshot_busy_snapshot(self): + snapshot = fake.CG_SNAPSHOT + snapshot['volume'] = fake.CG_VOLUME + + mock_extract_host = self.mock_object( + volume_utils, 'extract_host', + return_value=fake.POOL_NAME) + mock_clone_lun = self.mock_object(self.library, '_clone_lun') + mock_busy = self.mock_object( + self.zapi_client, 'wait_for_busy_snapshot') + mock_busy.side_effect = exception.SnapshotIsBusy(snapshot['name']) + mock_delete_snapshot = self.mock_object( + self.zapi_client, 'delete_snapshot') + mock_mark_snapshot_for_deletion = self.mock_object( + self.zapi_client, 'mark_snapshot_for_deletion') + + self.library.create_cgsnapshot(fake.CG_SNAPSHOT, [snapshot]) + + mock_extract_host.assert_called_once_with( + fake.CG_VOLUME['host'], level='pool') + self.zapi_client.create_cg_snapshot.assert_called_once_with( + set([fake.POOL_NAME]), fake.CG_SNAPSHOT_ID) + mock_clone_lun.assert_called_once_with( + fake.CG_VOLUME_NAME, fake.CG_SNAPSHOT_NAME, + source_snapshot=fake.CG_SNAPSHOT_ID) + mock_delete_snapshot.assert_not_called() + mock_mark_snapshot_for_deletion.assert_called_once_with( + fake.POOL_NAME, fake.CG_SNAPSHOT_ID) + + def test_delete_cgsnapshot(self): + + mock_delete_snapshot = self.mock_object( + self.library, '_delete_lun') + + self.library.delete_cgsnapshot(fake.CG_SNAPSHOT, [fake.CG_SNAPSHOT]) + + mock_delete_snapshot.assert_called_once_with(fake.CG_SNAPSHOT['name']) + + def test_delete_cgsnapshot_not_found(self): + self.mock_object(block_base, 'LOG') + self.mock_object(self.library, '_get_lun_attr', return_value=None) + + self.library.delete_cgsnapshot(fake.CG_SNAPSHOT, [fake.CG_SNAPSHOT]) + + self.assertEqual(0, block_base.LOG.error.call_count) + self.assertEqual(1, block_base.LOG.warning.call_count) + self.assertEqual(0, block_base.LOG.info.call_count) + + def test_create_volume_with_cg(self): + volume_size_in_bytes = int(fake.CG_VOLUME_SIZE) * units.Gi + self._create_volume_test_helper() + + self.library.create_volume(fake.CG_VOLUME) + + self.library._create_lun.assert_called_once_with( + fake.POOL_NAME, fake.CG_VOLUME_NAME, volume_size_in_bytes, + fake.CG_LUN_METADATA, None) + self.library._get_volume_model_update.assert_called_once_with( + fake.CG_VOLUME) + self.assertEqual(0, self.library. + _mark_qos_policy_group_for_deletion.call_count) + self.assertEqual(0, block_base.LOG.error.call_count) + + def _create_volume_test_helper(self): + self.mock_object(na_utils, 'get_volume_extra_specs') + self.mock_object(na_utils, 'log_extra_spec_warnings') + self.mock_object(block_base, 'LOG') + self.mock_object(volume_utils, 'extract_host', + return_value=fake.POOL_NAME) + self.mock_object(self.library, '_setup_qos_for_volume', + return_value=None) + self.mock_object(self.library, '_create_lun') + self.mock_object(self.library, '_create_lun_handle') + self.mock_object(self.library, '_add_lun_to_table') + self.mock_object(self.library, '_mark_qos_policy_group_for_deletion') + self.mock_object(self.library, '_get_volume_model_update') + + def test_create_consistency_group(self): + model_update = self.library.create_consistencygroup( + fake.CONSISTENCY_GROUP) + self.assertEqual('available', model_update['status']) + + def test_delete_consistencygroup_volume_delete_failure(self): + self.mock_object(block_7mode, 'LOG') + self.mock_object(self.library, '_delete_lun', side_effect=Exception) + + model_update, volumes = self.library.delete_consistencygroup( + fake.CONSISTENCY_GROUP, [fake.CG_VOLUME]) + + self.assertEqual('deleted', model_update['status']) + self.assertEqual('error_deleting', volumes[0]['status']) + self.assertEqual(1, block_7mode.LOG.exception.call_count) + + def test_delete_consistencygroup_not_found(self): + self.mock_object(block_7mode, 'LOG') + self.mock_object(self.library, '_get_lun_attr', return_value=None) + + model_update, volumes = self.library.delete_consistencygroup( + fake.CONSISTENCY_GROUP, [fake.CG_VOLUME]) + + self.assertEqual(0, block_7mode.LOG.error.call_count) + self.assertEqual(0, block_7mode.LOG.info.call_count) + + self.assertEqual('deleted', model_update['status']) + self.assertEqual('deleted', volumes[0]['status']) + + @ddt.data(None, + {'replication_status': fields.ReplicationStatus.ENABLED}) + def test_create_consistencygroup_from_src_cg_snapshot(self, + volume_model_update): + mock_clone_source_to_destination = self.mock_object( + self.library, '_clone_source_to_destination', + return_value=volume_model_update) + + actual_return_value = self.library.create_consistencygroup_from_src( + fake.CONSISTENCY_GROUP, [fake.VOLUME], cgsnapshot=fake.CG_SNAPSHOT, + snapshots=[fake.CG_VOLUME_SNAPSHOT]) + + clone_source_to_destination_args = { + 'name': fake.CG_SNAPSHOT['name'], + 'size': fake.CG_SNAPSHOT['volume_size'], + } + mock_clone_source_to_destination.assert_called_once_with( + clone_source_to_destination_args, fake.VOLUME) + if volume_model_update: + volume_model_update['id'] = fake.VOLUME['id'] + expected_return_value = ((None, [volume_model_update]) + if volume_model_update else (None, [])) + self.assertEqual(expected_return_value, actual_return_value) + + @ddt.data(None, + {'replication_status': fields.ReplicationStatus.ENABLED}) + def test_create_consistencygroup_from_src_cg(self, volume_model_update): + lun_name = fake.SOURCE_CG_VOLUME['name'] + mock_lun = block_base.NetAppLun( + lun_name, lun_name, '3', {'UUID': 'fake_uuid'}) + self.mock_object(self.library, '_get_lun_from_table', + return_value=mock_lun) + mock_clone_source_to_destination = self.mock_object( + self.library, '_clone_source_to_destination', + return_value=volume_model_update) + + actual_return_value = self.library.create_consistencygroup_from_src( + fake.CONSISTENCY_GROUP, [fake.VOLUME], + source_cg=fake.SOURCE_CONSISTENCY_GROUP, + source_vols=[fake.SOURCE_CG_VOLUME]) + + clone_source_to_destination_args = { + 'name': fake.SOURCE_CG_VOLUME['name'], + 'size': fake.SOURCE_CG_VOLUME['size'], + } + if volume_model_update: + volume_model_update['id'] = fake.VOLUME['id'] + expected_return_value = ((None, [volume_model_update]) + if volume_model_update else (None, [])) + mock_clone_source_to_destination.assert_called_once_with( + clone_source_to_destination_args, fake.VOLUME) + self.assertEqual(expected_return_value, actual_return_value) diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py index 7e9d0d4e969..598d47ed8e8 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_base.py @@ -1399,192 +1399,6 @@ class NetAppBlockStorageLibraryTestCase(test.TestCase): self.assertEqual('user1', data['discovery_auth_username']) self.assertEqual('pass1', data['discovery_auth_password']) - def test_create_cgsnapshot(self): - snapshot = fake.CG_SNAPSHOT - snapshot['volume'] = fake.CG_VOLUME - - mock_extract_host = self.mock_object( - volume_utils, 'extract_host', return_value=fake.POOL_NAME) - - mock_clone_lun = self.mock_object(self.library, '_clone_lun') - mock_busy = self.mock_object( - self.zapi_client, 'wait_for_busy_snapshot') - mock_delete_snapshot = self.mock_object( - self.zapi_client, 'delete_snapshot') - - self.library.create_cgsnapshot(fake.CG_SNAPSHOT, [snapshot]) - - mock_extract_host.assert_called_once_with(fake.CG_VOLUME['host'], - level='pool') - self.zapi_client.create_cg_snapshot.assert_called_once_with( - set([fake.POOL_NAME]), fake.CG_SNAPSHOT_ID) - mock_clone_lun.assert_called_once_with( - fake.CG_VOLUME_NAME, fake.CG_SNAPSHOT_NAME, - source_snapshot=fake.CG_SNAPSHOT_ID) - mock_busy.assert_called_once_with(fake.POOL_NAME, fake.CG_SNAPSHOT_ID) - mock_delete_snapshot.assert_called_once_with( - fake.POOL_NAME, fake.CG_SNAPSHOT_ID) - - def test_create_cgsnapshot_busy_snapshot(self): - snapshot = fake.CG_SNAPSHOT - snapshot['volume'] = fake.CG_VOLUME - - mock_extract_host = self.mock_object( - volume_utils, 'extract_host', - return_value=fake.POOL_NAME) - mock_clone_lun = self.mock_object(self.library, '_clone_lun') - mock_busy = self.mock_object( - self.zapi_client, 'wait_for_busy_snapshot') - mock_busy.side_effect = exception.SnapshotIsBusy(snapshot['name']) - mock_delete_snapshot = self.mock_object( - self.zapi_client, 'delete_snapshot') - mock_mark_snapshot_for_deletion = self.mock_object( - self.zapi_client, 'mark_snapshot_for_deletion') - - self.library.create_cgsnapshot(fake.CG_SNAPSHOT, [snapshot]) - - mock_extract_host.assert_called_once_with( - fake.CG_VOLUME['host'], level='pool') - self.zapi_client.create_cg_snapshot.assert_called_once_with( - set([fake.POOL_NAME]), fake.CG_SNAPSHOT_ID) - mock_clone_lun.assert_called_once_with( - fake.CG_VOLUME_NAME, fake.CG_SNAPSHOT_NAME, - source_snapshot=fake.CG_SNAPSHOT_ID) - mock_delete_snapshot.assert_not_called() - mock_mark_snapshot_for_deletion.assert_called_once_with( - fake.POOL_NAME, fake.CG_SNAPSHOT_ID) - - def test_delete_cgsnapshot(self): - - mock_delete_snapshot = self.mock_object( - self.library, '_delete_lun') - - self.library.delete_cgsnapshot(fake.CG_SNAPSHOT, [fake.CG_SNAPSHOT]) - - mock_delete_snapshot.assert_called_once_with(fake.CG_SNAPSHOT['name']) - - def test_delete_cgsnapshot_not_found(self): - self.mock_object(block_base, 'LOG') - self.mock_object(self.library, '_get_lun_attr', return_value=None) - - self.library.delete_cgsnapshot(fake.CG_SNAPSHOT, [fake.CG_SNAPSHOT]) - - self.assertEqual(0, block_base.LOG.error.call_count) - self.assertEqual(1, block_base.LOG.warning.call_count) - self.assertEqual(0, block_base.LOG.info.call_count) - - def test_create_volume_with_cg(self): - volume_size_in_bytes = int(fake.CG_VOLUME_SIZE) * units.Gi - self._create_volume_test_helper() - - self.library.create_volume(fake.CG_VOLUME) - - self.library._create_lun.assert_called_once_with( - fake.POOL_NAME, fake.CG_VOLUME_NAME, volume_size_in_bytes, - fake.CG_LUN_METADATA, None) - self.library._get_volume_model_update.assert_called_once_with( - fake.CG_VOLUME) - self.assertEqual(0, self.library. - _mark_qos_policy_group_for_deletion.call_count) - self.assertEqual(0, block_base.LOG.error.call_count) - - def _create_volume_test_helper(self): - self.mock_object(na_utils, 'get_volume_extra_specs') - self.mock_object(na_utils, 'log_extra_spec_warnings') - self.mock_object(block_base, 'LOG') - self.mock_object(volume_utils, 'extract_host', - return_value=fake.POOL_NAME) - self.mock_object(self.library, '_setup_qos_for_volume', - return_value=None) - self.mock_object(self.library, '_create_lun') - self.mock_object(self.library, '_create_lun_handle') - self.mock_object(self.library, '_add_lun_to_table') - self.mock_object(self.library, '_mark_qos_policy_group_for_deletion') - self.mock_object(self.library, '_get_volume_model_update') - - def test_create_consistency_group(self): - model_update = self.library.create_consistencygroup( - fake.CONSISTENCY_GROUP) - self.assertEqual('available', model_update['status']) - - def test_delete_consistencygroup_volume_delete_failure(self): - self.mock_object(block_base, 'LOG') - self.mock_object(self.library, '_delete_lun', side_effect=Exception) - - model_update, volumes = self.library.delete_consistencygroup( - fake.CONSISTENCY_GROUP, [fake.CG_VOLUME]) - - self.assertEqual('deleted', model_update['status']) - self.assertEqual('error_deleting', volumes[0]['status']) - self.assertEqual(1, block_base.LOG.exception.call_count) - - def test_delete_consistencygroup_not_found(self): - self.mock_object(block_base, 'LOG') - self.mock_object(self.library, '_get_lun_attr', return_value=None) - - model_update, volumes = self.library.delete_consistencygroup( - fake.CONSISTENCY_GROUP, [fake.CG_VOLUME]) - - self.assertEqual(0, block_base.LOG.error.call_count) - self.assertEqual(1, block_base.LOG.warning.call_count) - self.assertEqual(0, block_base.LOG.info.call_count) - - self.assertEqual('deleted', model_update['status']) - self.assertEqual('deleted', volumes[0]['status']) - - @ddt.data(None, - {'replication_status': fields.ReplicationStatus.ENABLED}) - def test_create_consistencygroup_from_src_cg_snapshot(self, - volume_model_update): - mock_clone_source_to_destination = self.mock_object( - self.library, '_clone_source_to_destination', - return_value=volume_model_update) - - actual_return_value = self.library.create_consistencygroup_from_src( - fake.CONSISTENCY_GROUP, [fake.VOLUME], cgsnapshot=fake.CG_SNAPSHOT, - snapshots=[fake.CG_VOLUME_SNAPSHOT]) - - clone_source_to_destination_args = { - 'name': fake.CG_SNAPSHOT['name'], - 'size': fake.CG_SNAPSHOT['volume_size'], - } - mock_clone_source_to_destination.assert_called_once_with( - clone_source_to_destination_args, fake.VOLUME) - if volume_model_update: - volume_model_update['id'] = fake.VOLUME['id'] - expected_return_value = ((None, [volume_model_update]) - if volume_model_update else (None, [])) - self.assertEqual(expected_return_value, actual_return_value) - - @ddt.data(None, - {'replication_status': fields.ReplicationStatus.ENABLED}) - def test_create_consistencygroup_from_src_cg(self, volume_model_update): - lun_name = fake.SOURCE_CG_VOLUME['name'] - mock_lun = block_base.NetAppLun( - lun_name, lun_name, '3', {'UUID': 'fake_uuid'}) - self.mock_object(self.library, '_get_lun_from_table', - return_value=mock_lun) - mock_clone_source_to_destination = self.mock_object( - self.library, '_clone_source_to_destination', - return_value=volume_model_update) - - actual_return_value = self.library.create_consistencygroup_from_src( - fake.CONSISTENCY_GROUP, [fake.VOLUME], - source_cg=fake.SOURCE_CONSISTENCY_GROUP, - source_vols=[fake.SOURCE_CG_VOLUME]) - - clone_source_to_destination_args = { - 'name': fake.SOURCE_CG_VOLUME['name'], - 'size': fake.SOURCE_CG_VOLUME['size'], - } - if volume_model_update: - volume_model_update['id'] = fake.VOLUME['id'] - expected_return_value = ((None, [volume_model_update]) - if volume_model_update else (None, [])) - mock_clone_source_to_destination.assert_called_once_with( - clone_source_to_destination_args, fake.VOLUME) - self.assertEqual(expected_return_value, actual_return_value) - def test_add_looping_tasks(self): mock_add_task = self.mock_object(self.library.loopingcalls, 'add_task') mock_call_snap_cleanup = self.mock_object( diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py index 7db17c3f609..14e53861260 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_cmode.py @@ -22,6 +22,7 @@ import ddt import mock from cinder import exception +from cinder.objects import fields from cinder import test import cinder.tests.unit.volume.drivers.netapp.dataontap.fakes as fake from cinder.tests.unit.volume.drivers.netapp.dataontap.utils import fakes as\ @@ -37,6 +38,7 @@ from cinder.volume.drivers.netapp.dataontap.utils import data_motion from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils from cinder.volume.drivers.netapp import utils as na_utils +from cinder.volume import utils as volume_utils @ddt.ddt @@ -404,6 +406,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): 'pool_name': 'vola', 'QoS_support': True, 'consistencygroup_support': True, + 'consistent_group_snapshot_enabled': True, 'reserved_percentage': 5, 'max_over_subscription_ratio': 10.0, 'multiattach': False, @@ -749,3 +752,178 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase): self.library._get_backing_flexvol_names() mock_ssc_library.assert_called_once_with() + + def test_create_group(self): + + model_update = self.library.create_group( + fake.VOLUME_GROUP) + + self.assertEqual('available', model_update['status']) + + def test_delete_group_volume_delete_failure(self): + self.mock_object(block_cmode, 'LOG') + self.mock_object(self.library, '_delete_lun', side_effect=Exception) + + model_update, volumes = self.library.delete_group( + fake.VOLUME_GROUP, [fake.VG_VOLUME]) + + self.assertEqual('deleted', model_update['status']) + self.assertEqual('error_deleting', volumes[0]['status']) + self.assertEqual(1, block_cmode.LOG.exception.call_count) + + def test_update_group(self): + + model_update, add_volumes_update, remove_volumes_update = ( + self.library.update_group(fake.VOLUME_GROUP)) + + self.assertIsNone(model_update) + self.assertIsNone(add_volumes_update) + self.assertIsNone(remove_volumes_update) + + def test_delete_group_not_found(self): + self.mock_object(block_cmode, 'LOG') + self.mock_object(self.library, '_get_lun_attr', return_value=None) + + model_update, volumes = self.library.delete_group( + fake.VOLUME_GROUP, [fake.VG_VOLUME]) + + self.assertEqual(0, block_cmode.LOG.error.call_count) + self.assertEqual(0, block_cmode.LOG.info.call_count) + + self.assertEqual('deleted', model_update['status']) + self.assertEqual('deleted', volumes[0]['status']) + + def test_create_group_snapshot_raise_exception(self): + self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type', + return_value=True) + + mock_extract_host = self.mock_object( + volume_utils, 'extract_host', return_value=fake.POOL_NAME) + + self.mock_object(self.zapi_client, 'create_cg_snapshot', + side_effect=netapp_api.NaApiError) + + self.assertRaises(exception.NetAppDriverException, + self.library.create_group_snapshot, + fake.VOLUME_GROUP, + [fake.VG_SNAPSHOT]) + + mock_extract_host.assert_called_once_with( + fake.VG_SNAPSHOT['volume']['host'], level='pool') + + def test_create_group_snapshot(self): + self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type', + return_value=False) + + fake_lun = block_base.NetAppLun(fake.LUN_HANDLE, fake.LUN_ID, + fake.LUN_SIZE, fake.LUN_METADATA) + self.mock_object(self.library, '_get_lun_from_table', + return_value=fake_lun) + mock__clone_lun = self.mock_object(self.library, '_clone_lun') + + model_update, snapshots_model_update = ( + self.library.create_group_snapshot(fake.VOLUME_GROUP, + [fake.SNAPSHOT])) + + self.assertIsNone(model_update) + self.assertIsNone(snapshots_model_update) + mock__clone_lun.assert_called_once_with(fake_lun.name, + fake.SNAPSHOT['name'], + space_reserved='false', + is_snapshot=True) + + def test_create_consistent_group_snapshot(self): + self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type', + return_value=True) + + self.mock_object(volume_utils, 'extract_host', + return_value=fake.POOL_NAME) + mock_create_cg_snapshot = self.mock_object( + self.zapi_client, 'create_cg_snapshot') + mock__clone_lun = self.mock_object(self.library, '_clone_lun') + mock_wait_for_busy_snapshot = self.mock_object( + self.zapi_client, 'wait_for_busy_snapshot') + mock_delete_snapshot = self.mock_object( + self.zapi_client, 'delete_snapshot') + + model_update, snapshots_model_update = ( + self.library.create_group_snapshot(fake.VOLUME_GROUP, + [fake.VG_SNAPSHOT])) + + self.assertIsNone(model_update) + self.assertIsNone(snapshots_model_update) + + mock_create_cg_snapshot.assert_called_once_with( + set([fake.POOL_NAME]), fake.VOLUME_GROUP['id']) + mock__clone_lun.assert_called_once_with( + fake.VG_SNAPSHOT['volume']['name'], + fake.VG_SNAPSHOT['name'], + source_snapshot=fake.VOLUME_GROUP['id']) + mock_wait_for_busy_snapshot.assert_called_once_with( + fake.POOL_NAME, fake.VOLUME_GROUP['id']) + mock_delete_snapshot.assert_called_once_with( + fake.POOL_NAME, fake.VOLUME_GROUP['id']) + + @ddt.data(None, + {'replication_status': fields.ReplicationStatus.ENABLED}) + def test_create_group_from_src_snapshot(self, volume_model_update): + mock_clone_source_to_destination = self.mock_object( + self.library, '_clone_source_to_destination', + return_value=volume_model_update) + + actual_return_value = self.library.create_group_from_src( + fake.VOLUME_GROUP, [fake.VOLUME], group_snapshot=fake.VG_SNAPSHOT, + snapshots=[fake.VG_VOLUME_SNAPSHOT]) + + clone_source_to_destination_args = { + 'name': fake.VG_SNAPSHOT['name'], + 'size': fake.VG_SNAPSHOT['volume_size'], + } + mock_clone_source_to_destination.assert_called_once_with( + clone_source_to_destination_args, fake.VOLUME) + if volume_model_update: + volume_model_update['id'] = fake.VOLUME['id'] + expected_return_value = ((None, [volume_model_update]) + if volume_model_update else (None, [])) + self.assertEqual(expected_return_value, actual_return_value) + + @ddt.data(None, + {'replication_status': fields.ReplicationStatus.ENABLED}) + def test_create_group_from_src_group(self, volume_model_update): + lun_name = fake.SOURCE_VG_VOLUME['name'] + mock_lun = block_base.NetAppLun( + lun_name, lun_name, '3', {'UUID': 'fake_uuid'}) + self.mock_object(self.library, '_get_lun_from_table', + return_value=mock_lun) + mock_clone_source_to_destination = self.mock_object( + self.library, '_clone_source_to_destination', + return_value=volume_model_update) + + actual_return_value = self.library.create_group_from_src( + fake.VOLUME_GROUP, [fake.VOLUME], + source_group=fake.SOURCE_VOLUME_GROUP, + source_vols=[fake.SOURCE_VG_VOLUME]) + + clone_source_to_destination_args = { + 'name': fake.SOURCE_VG_VOLUME['name'], + 'size': fake.SOURCE_VG_VOLUME['size'], + } + if volume_model_update: + volume_model_update['id'] = fake.VOLUME['id'] + expected_return_value = ((None, [volume_model_update]) + if volume_model_update else (None, [])) + mock_clone_source_to_destination.assert_called_once_with( + clone_source_to_destination_args, fake.VOLUME) + self.assertEqual(expected_return_value, actual_return_value) + + def test_delete_group_snapshot(self): + mock__delete_lun = self.mock_object(self.library, '_delete_lun') + + model_update, snapshots_model_update = ( + self.library.delete_group_snapshot(fake.VOLUME_GROUP, + [fake.VG_SNAPSHOT])) + + self.assertIsNone(model_update) + self.assertIsNone(snapshots_model_update) + + mock__delete_lun.assert_called_once_with(fake.VG_SNAPSHOT['name']) diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_driver_interfaces.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_driver_interfaces.py index 6a42fde1ced..dccd23abaf4 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_driver_interfaces.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_block_driver_interfaces.py @@ -54,15 +54,11 @@ class NetAppBlockStorageDriverInterfaceTestCase(test.TestCase): """ # Get local functions of each driver interface - iscsi_7mode = self._get_local_functions(self.iscsi_7mode_driver) iscsi_cmode = self._get_local_functions(self.iscsi_cmode_driver) - fc_7mode = self._get_local_functions(self.fc_7mode_driver) fc_cmode = self._get_local_functions(self.fc_cmode_driver) # Ensure NetApp block storage driver shims are identical - self.assertSetEqual(iscsi_7mode, iscsi_cmode) - self.assertSetEqual(iscsi_7mode, fc_7mode) - self.assertSetEqual(iscsi_7mode, fc_cmode) + self.assertSetEqual(iscsi_cmode, fc_cmode) def _get_local_functions(self, obj): """Get function names of an object without superclass functions.""" diff --git a/cinder/volume/drivers/netapp/dataontap/block_7mode.py b/cinder/volume/drivers/netapp/dataontap/block_7mode.py index 006a7327fbf..7e97ee0221d 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_7mode.py @@ -32,6 +32,7 @@ import six from cinder import exception from cinder.i18n import _ +from cinder.objects import fields from cinder import utils from cinder.volume import configuration from cinder.volume.drivers.netapp.dataontap import block_base @@ -40,6 +41,7 @@ from cinder.volume.drivers.netapp.dataontap.performance import perf_7mode from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils from cinder.volume.drivers.netapp import options as na_opts from cinder.volume.drivers.netapp import utils as na_utils +from cinder.volume import utils as volume_utils LOG = logging.getLogger(__name__) @@ -471,3 +473,139 @@ class NetAppBlockStorage7modeLibrary(block_base.NetAppBlockStorageLibrary): def _get_backing_flexvol_names(self): """Returns a list of backing flexvol names.""" return self.volume_list or [] + + def create_consistencygroup(self, group): + """Driver entry point for creating a consistency group. + + ONTAP does not maintain an actual CG construct. As a result, no + communication to the backend is necessary for consistency group + creation. + + :returns: Hard-coded model update for consistency group model. + """ + model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} + return model_update + + def delete_consistencygroup(self, group, volumes): + """Driver entry point for deleting a consistency group. + + :returns: Updated consistency group model and list of volume models + for the volumes that were deleted. + """ + model_update = {'status': fields.ConsistencyGroupStatus.DELETED} + volumes_model_update = [] + for volume in volumes: + try: + self._delete_lun(volume['name']) + volumes_model_update.append( + {'id': volume['id'], 'status': 'deleted'}) + except Exception: + volumes_model_update.append( + {'id': volume['id'], + 'status': 'error_deleting'}) + LOG.exception("Volume %(vol)s in the consistency group " + "could not be deleted.", {'vol': volume}) + return model_update, volumes_model_update + + def update_consistencygroup(self, group, add_volumes=None, + remove_volumes=None): + """Driver entry point for updating a consistency group. + + Since no actual CG construct is ever created in ONTAP, it is not + necessary to update any metadata on the backend. Since this is a NO-OP, + there is guaranteed to be no change in any of the volumes' statuses. + """ + return None, None, None + + def create_cgsnapshot(self, cgsnapshot, snapshots): + """Creates a Cinder cgsnapshot object. + + The Cinder cgsnapshot object is created by making use of an + ephemeral ONTAP CG in order to provide write-order consistency for a + set of flexvol snapshots. First, a list of the flexvols backing the + given Cinder CG must be gathered. An ONTAP cg-snapshot of these + flexvols will create a snapshot copy of all the Cinder volumes in the + CG group. For each Cinder volume in the CG, it is then necessary to + clone its backing LUN from the ONTAP cg-snapshot. The naming convention + used for the clones is what indicates the clone's role as a Cinder + snapshot and its inclusion in a Cinder CG. The ONTAP CG-snapshot of + the flexvols is no longer required after having cloned the LUNs + backing the Cinder volumes in the Cinder CG. + + :returns: An implicit update for cgsnapshot and snapshots models that + is interpreted by the manager to set their models to + available. + """ + flexvols = set() + for snapshot in snapshots: + flexvols.add(volume_utils.extract_host(snapshot['volume']['host'], + level='pool')) + + self.zapi_client.create_cg_snapshot(flexvols, cgsnapshot['id']) + + for snapshot in snapshots: + self._clone_lun(snapshot['volume']['name'], snapshot['name'], + source_snapshot=cgsnapshot['id']) + + for flexvol in flexvols: + try: + self.zapi_client.wait_for_busy_snapshot( + flexvol, cgsnapshot['id']) + self.zapi_client.delete_snapshot( + flexvol, cgsnapshot['id']) + except exception.SnapshotIsBusy: + self.zapi_client.mark_snapshot_for_deletion( + flexvol, cgsnapshot['id']) + + return None, None + + def delete_cgsnapshot(self, cgsnapshot, snapshots): + """Delete LUNs backing each snapshot in the cgsnapshot. + + :returns: An implicit update for snapshots models that is interpreted + by the manager to set their models to deleted. + """ + for snapshot in snapshots: + self._delete_lun(snapshot['name']) + LOG.debug("Snapshot %s deletion successful", snapshot['name']) + + return None, None + + def create_consistencygroup_from_src(self, group, volumes, + cgsnapshot=None, snapshots=None, + source_cg=None, source_vols=None): + """Creates a CG from a either a cgsnapshot or group of cinder vols. + + :returns: An implicit update for the volumes model that is + interpreted by the manager as a successful operation. + """ + LOG.debug("VOLUMES %s ", ', '.join([vol['id'] for vol in volumes])) + volume_model_updates = [] + + if cgsnapshot: + vols = zip(volumes, snapshots) + + for volume, snapshot in vols: + source = { + 'name': snapshot['name'], + 'size': snapshot['volume_size'], + } + volume_model_update = self._clone_source_to_destination( + source, volume) + if volume_model_update is not None: + volume_model_update['id'] = volume['id'] + volume_model_updates.append(volume_model_update) + + else: + vols = zip(volumes, source_vols) + + for volume, old_src_vref in vols: + src_lun = self._get_lun_from_table(old_src_vref['name']) + source = {'name': src_lun.name, 'size': old_src_vref['size']} + volume_model_update = self._clone_source_to_destination( + source, volume) + if volume_model_update is not None: + volume_model_update['id'] = volume['id'] + volume_model_updates.append(volume_model_update) + + return None, volume_model_updates diff --git a/cinder/volume/drivers/netapp/dataontap/block_base.py b/cinder/volume/drivers/netapp/dataontap/block_base.py index 9a035ca6fba..fb117b28c13 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_base.py +++ b/cinder/volume/drivers/netapp/dataontap/block_base.py @@ -312,7 +312,9 @@ class NetAppBlockStorageLibrary(object): This driver implements snapshots by using efficient single-file (LUN) cloning. """ + self._create_snapshot(snapshot) + def _create_snapshot(self, snapshot): vol_name = snapshot['volume_name'] snapshot_name = snapshot['name'] lun = self._get_lun_from_table(vol_name) @@ -1035,141 +1037,6 @@ class NetAppBlockStorageLibrary(object): return target_wwpns, init_targ_map, num_paths - def create_consistencygroup(self, group): - """Driver entry point for creating a consistency group. - - ONTAP does not maintain an actual CG construct. As a result, no - communication to the backend is necessary for consistency group - creation. - - :return: Hard-coded model update for consistency group model. - """ - model_update = {'status': 'available'} - return model_update - - def delete_consistencygroup(self, group, volumes): - """Driver entry point for deleting a consistency group. - - :return: Updated consistency group model and list of volume models - for the volumes that were deleted. - """ - model_update = {'status': 'deleted'} - volumes_model_update = [] - for volume in volumes: - try: - self._delete_lun(volume['name']) - volumes_model_update.append( - {'id': volume['id'], 'status': 'deleted'}) - except Exception: - volumes_model_update.append( - {'id': volume['id'], 'status': 'error_deleting'}) - LOG.exception("Volume %(vol)s in the consistency group " - "could not be deleted.", {'vol': volume}) - return model_update, volumes_model_update - - def update_consistencygroup(self, group, add_volumes=None, - remove_volumes=None): - """Driver entry point for updating a consistency group. - - Since no actual CG construct is ever created in ONTAP, it is not - necessary to update any metadata on the backend. Since this is a NO-OP, - there is guaranteed to be no change in any of the volumes' statuses. - """ - return None, None, None - - def create_cgsnapshot(self, cgsnapshot, snapshots): - """Creates a Cinder cgsnapshot object. - - The Cinder cgsnapshot object is created by making use of an - ephemeral ONTAP CG in order to provide write-order consistency for a - set of flexvol snapshots. First, a list of the flexvols backing the - given Cinder CG must be gathered. An ONTAP cg-snapshot of these - flexvols will create a snapshot copy of all the Cinder volumes in the - CG group. For each Cinder volume in the CG, it is then necessary to - clone its backing LUN from the ONTAP cg-snapshot. The naming convention - used for the clones is what indicates the clone's role as a Cinder - snapshot and its inclusion in a Cinder CG. The ONTAP CG-snapshot of - the flexvols is no longer required after having cloned the LUNs - backing the Cinder volumes in the Cinder CG. - - :return: An implicit update for cgsnapshot and snapshots models that - is interpreted by the manager to set their models to - available. - """ - flexvols = set() - for snapshot in snapshots: - flexvols.add(volume_utils.extract_host(snapshot['volume']['host'], - level='pool')) - - self.zapi_client.create_cg_snapshot(flexvols, cgsnapshot['id']) - - for snapshot in snapshots: - self._clone_lun(snapshot['volume']['name'], snapshot['name'], - source_snapshot=cgsnapshot['id']) - - for flexvol in flexvols: - try: - self.zapi_client.wait_for_busy_snapshot( - flexvol, cgsnapshot['id']) - self.zapi_client.delete_snapshot( - flexvol, cgsnapshot['id']) - except exception.SnapshotIsBusy: - self.zapi_client.mark_snapshot_for_deletion( - flexvol, cgsnapshot['id']) - - return None, None - - def delete_cgsnapshot(self, cgsnapshot, snapshots): - """Delete LUNs backing each snapshot in the cgsnapshot. - - :return: An implicit update for snapshots models that is interpreted - by the manager to set their models to deleted. - """ - for snapshot in snapshots: - self._delete_lun(snapshot['name']) - LOG.debug("Snapshot %s deletion successful", snapshot['name']) - - return None, None - - def create_consistencygroup_from_src(self, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): - """Creates a CG from a either a cgsnapshot or group of cinder vols. - - :return: An implicit update for the volumes model that is - interpreted by the manager as a successful operation. - """ - LOG.debug("VOLUMES %s ", [dict(vol) for vol in volumes]) - volume_model_updates = [] - - if cgsnapshot: - vols = zip(volumes, snapshots) - - for volume, snapshot in vols: - source = { - 'name': snapshot['name'], - 'size': snapshot['volume_size'], - } - volume_model_update = self._clone_source_to_destination( - source, volume) - if volume_model_update is not None: - volume_model_update['id'] = volume['id'] - volume_model_updates.append(volume_model_update) - - else: - vols = zip(volumes, source_vols) - - for volume, old_src_vref in vols: - src_lun = self._get_lun_from_table(old_src_vref['name']) - source = {'name': src_lun.name, 'size': old_src_vref['size']} - volume_model_update = self._clone_source_to_destination( - source, volume) - if volume_model_update is not None: - volume_model_update['id'] = volume['id'] - volume_model_updates.append(volume_model_update) - - return None, volume_model_updates - def _get_backing_flexvol_names(self): """Returns a list of backing flexvol names.""" raise NotImplementedError() diff --git a/cinder/volume/drivers/netapp/dataontap/block_cmode.py b/cinder/volume/drivers/netapp/dataontap/block_cmode.py index 5b3fb18c6b5..bdbc5a3ad72 100644 --- a/cinder/volume/drivers/netapp/dataontap/block_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/block_cmode.py @@ -40,6 +40,7 @@ from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls from cinder.volume.drivers.netapp.dataontap.utils import utils as dot_utils from cinder.volume.drivers.netapp import options as na_opts from cinder.volume.drivers.netapp import utils as na_utils +from cinder.volume import utils as volume_utils LOG = logging.getLogger(__name__) @@ -301,6 +302,7 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary, pool['QoS_support'] = True pool['multiattach'] = False pool['consistencygroup_support'] = True + pool['consistent_group_snapshot_enabled'] = True pool['reserved_percentage'] = self.reserved_percentage pool['max_over_subscription_ratio'] = ( self.max_over_subscription_ratio) @@ -461,3 +463,152 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary, def _get_backing_flexvol_names(self): """Returns a list of backing flexvol names.""" return self.ssc_library.get_ssc().keys() + + def create_group(self, group): + """Driver entry point for creating a generic volume group. + + ONTAP does not maintain an actual Group construct. As a result, no + communication to the backend is necessary for generic volume group + creation. + + :returns: Hard-coded model update for generic volume group model. + """ + model_update = {'status': fields.GroupStatus.AVAILABLE} + return model_update + + def delete_group(self, group, volumes): + """Driver entry point for deleting a group. + + :returns: Updated group model and list of volume models + for the volumes that were deleted. + """ + model_update = {'status': fields.GroupStatus.DELETED} + volumes_model_update = [] + for volume in volumes: + try: + self._delete_lun(volume['name']) + volumes_model_update.append( + {'id': volume['id'], 'status': 'deleted'}) + except Exception: + volumes_model_update.append( + {'id': volume['id'], + 'status': 'error_deleting'}) + LOG.exception("Volume %(vol)s in the group could not be " + "deleted.", {'vol': volume}) + return model_update, volumes_model_update + + def update_group(self, group, add_volumes=None, remove_volumes=None): + """Driver entry point for updating a generic volume group. + + Since no actual group construct is ever created in ONTAP, it is not + necessary to update any metadata on the backend. Since this is a NO-OP, + there is guaranteed to be no change in any of the volumes' statuses. + """ + return None, None, None + + def create_group_snapshot(self, group_snapshot, snapshots): + """Creates a Cinder group snapshot object. + + The Cinder group snapshot object is created by making use of an + ephemeral ONTAP consistency group snapshot in order to provide + write-order consistency for a set of flexvol snapshots. First, a list + of the flexvols backing the given Cinder group must be gathered. An + ONTAP group-snapshot of these flexvols will create a snapshot copy of + all the Cinder volumes in the generic volume group. For each Cinder + volume in the group, it is then necessary to clone its backing LUN from + the ONTAP cg-snapshot. The naming convention used for the clones is + what indicates the clone's role as a Cinder snapshot and its inclusion + in a Cinder group. The ONTAP cg-snapshot of the flexvols is no longer + required after having cloned the LUNs backing the Cinder volumes in + the Cinder group. + + :returns: An implicit update for group snapshot and snapshots models + that is interpreted by the manager to set their models to + available. + """ + try: + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + self._create_consistent_group_snapshot(group_snapshot, + snapshots) + else: + for snapshot in snapshots: + self._create_snapshot(snapshot) + except Exception as ex: + err_msg = (_("Create group snapshot failed (%s).") % ex) + LOG.exception(err_msg, resource=group_snapshot) + raise exception.NetAppDriverException(err_msg) + + return None, None + + def _create_consistent_group_snapshot(self, group_snapshot, snapshots): + flexvols = set() + for snapshot in snapshots: + flexvols.add(volume_utils.extract_host( + snapshot['volume']['host'], level='pool')) + + self.zapi_client.create_cg_snapshot(flexvols, group_snapshot['id']) + + for snapshot in snapshots: + self._clone_lun(snapshot['volume']['name'], snapshot['name'], + source_snapshot=group_snapshot['id']) + + for flexvol in flexvols: + try: + self.zapi_client.wait_for_busy_snapshot( + flexvol, group_snapshot['id']) + self.zapi_client.delete_snapshot( + flexvol, group_snapshot['id']) + except exception.SnapshotIsBusy: + self.zapi_client.mark_snapshot_for_deletion( + flexvol, group_snapshot['id']) + + def delete_group_snapshot(self, group_snapshot, snapshots): + """Delete LUNs backing each snapshot in the group snapshot. + + :returns: An implicit update for snapshots models that is interpreted + by the manager to set their models to deleted. + """ + for snapshot in snapshots: + self._delete_lun(snapshot['name']) + LOG.debug("Snapshot %s deletion successful", snapshot['name']) + + return None, None + + def create_group_from_src(self, group, volumes, group_snapshot=None, + snapshots=None, source_group=None, + source_vols=None): + """Creates a group from a group snapshot or a group of cinder vols. + + :returns: An implicit update for the volumes model that is + interpreted by the manager as a successful operation. + """ + LOG.debug("VOLUMES %s ", ', '.join([vol['id'] for vol in volumes])) + volume_model_updates = [] + + if group_snapshot: + vols = zip(volumes, snapshots) + + for volume, snapshot in vols: + source = { + 'name': snapshot['name'], + 'size': snapshot['volume_size'], + } + volume_model_update = self._clone_source_to_destination( + source, volume) + if volume_model_update is not None: + volume_model_update['id'] = volume['id'] + volume_model_updates.append(volume_model_update) + + else: + vols = zip(volumes, source_vols) + + for volume, old_src_vref in vols: + src_lun = self._get_lun_from_table(old_src_vref['name']) + source = {'name': src_lun.name, 'size': old_src_vref['size']} + volume_model_update = self._clone_source_to_destination( + source, volume) + if volume_model_update is not None: + volume_model_update['id'] = volume['id'] + volume_model_updates.append(volume_model_update) + + return None, volume_model_updates diff --git a/cinder/volume/drivers/netapp/dataontap/fc_cmode.py b/cinder/volume/drivers/netapp/dataontap/fc_cmode.py index a69de769195..665a326c954 100644 --- a/cinder/volume/drivers/netapp/dataontap/fc_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/fc_cmode.py @@ -106,29 +106,29 @@ class NetAppCmodeFibreChannelDriver(driver.BaseVD, def get_pool(self, volume): return self.library.get_pool(volume) - def create_consistencygroup(self, context, group): - return self.library.create_consistencygroup(group) + def create_group(self, context, group): + return self.library.create_group(group) - def delete_consistencygroup(self, context, group, volumes): - return self.library.delete_consistencygroup(group, volumes) + def delete_group(self, context, group, volumes): + return self.library.delete_group(group, volumes) - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): - return self.library.update_consistencygroup(group, add_volumes=None, - remove_volumes=None) + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + return self.library.update_group(group, add_volumes=None, + remove_volumes=None) - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - return self.library.create_cgsnapshot(cgsnapshot, snapshots) + def create_group_snapshot(self, context, group_snapshot, snapshots): + return self.library.create_group_snapshot(group_snapshot, snapshots) - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - return self.library.delete_cgsnapshot(cgsnapshot, snapshots) + def delete_group_snapshot(self, context, group_snapshot, snapshots): + return self.library.delete_group_snapshot(group_snapshot, snapshots) - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): - return self.library.create_consistencygroup_from_src( - group, volumes, cgsnapshot=cgsnapshot, snapshots=snapshots, - source_cg=source_cg, source_vols=source_vols) + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + return self.library.create_group_from_src( + group, volumes, group_snapshot=group_snapshot, snapshots=snapshots, + source_group=source_group, source_vols=source_vols) def failover_host(self, context, volumes, secondary_id=None, groups=None): return self.library.failover_host( diff --git a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py index 13f6fb3abbf..8487f23fdb8 100644 --- a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py @@ -103,29 +103,29 @@ class NetAppCmodeISCSIDriver(driver.BaseVD, def get_pool(self, volume): return self.library.get_pool(volume) - def create_consistencygroup(self, context, group): - return self.library.create_consistencygroup(group) + def create_group(self, context, group): + return self.library.create_group(group) - def delete_consistencygroup(self, context, group, volumes): - return self.library.delete_consistencygroup(group, volumes) + def delete_group(self, context, group, volumes): + return self.library.delete_group(group, volumes) - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): - return self.library.update_consistencygroup(group, add_volumes=None, - remove_volumes=None) + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + return self.library.update_group(group, add_volumes=None, + remove_volumes=None) - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - return self.library.create_cgsnapshot(cgsnapshot, snapshots) + def create_group_snapshot(self, context, group_snapshot, snapshots): + return self.library.create_group_snapshot(group_snapshot, snapshots) - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - return self.library.delete_cgsnapshot(cgsnapshot, snapshots) + def delete_group_snapshot(self, context, group_snapshot, snapshots): + return self.library.delete_group_snapshot(group_snapshot, snapshots) - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): - return self.library.create_consistencygroup_from_src( - group, volumes, cgsnapshot=cgsnapshot, snapshots=snapshots, - source_cg=source_cg, source_vols=source_vols) + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + return self.library.create_group_from_src( + group, volumes, group_snapshot=group_snapshot, snapshots=snapshots, + source_group=source_group, source_vols=source_vols) def failover_host(self, context, volumes, secondary_id=None, groups=None): return self.library.failover_host(