diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_cinder.yaml b/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_cinder.yaml index ae781f94445..1b350851d7d 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_cinder.yaml +++ b/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_cinder.yaml @@ -175,12 +175,12 @@ test_create_snapshot_adapter: test_delete_snapshot_adapter: snapshot: *snapshot_base -test_create_cgsnapshot: &cg_snap_and_snaps +test_do_create_cgsnap: &cg_snap_and_snaps cg_snap: *cg_snapshot_base snap1: *snapshot_base snap2: *snapshot_base -test_delete_cgsnapshot: *cg_snap_and_snaps +test_do_delete_cgsnap: *cg_snap_and_snaps test_manage_existing_lun_no_exist: volume: *volume_base @@ -244,7 +244,7 @@ test_create_volume_from_snapshot_snapcopy: test_get_base_lun_name: volume: *volume_base -test_create_cg_from_cgsnapshot: +test_do_create_cg_from_cgsnap: vol1: _type: 'volume' _properties: @@ -257,8 +257,6 @@ test_create_cg_from_cgsnapshot: <<: *volume_base_properties id: _uuid: volume2_id - cg: *cg_base - cg_snap: *cg_snapshot_base snap1: _type: 'snapshot' _properties: @@ -272,21 +270,13 @@ test_create_cg_from_cgsnapshot: id: _uuid: snapshot2_id -test_create_cloned_cg: +test_do_clone_cg: vol1: _type: 'volume' _properties: <<: *volume_base_properties id: _uuid: consistency_group_id - cg: *cg_base - src_cg: - _type: 'cg' - _properties: - <<: *cg_base_properties - id: - _uuid: consistency_group2_id - name: 'src_cg_name' src_vol1: _type: 'volume' @@ -322,7 +312,7 @@ test_remove_host_access_sg_absent: test_remove_host_access_volume_not_in_sg: volume: *volume_base -test_update_consistencygroup: +test_do_update_cg: cg: *cg_base volume_add: <<: *volume_base @@ -421,13 +411,36 @@ test_update_migrated_volume_smp: <<: *provider_location_dict type: smp +test_create_group_snap: + +test_create_cgsnapshot: + +test_create_cloned_cg: + +test_create_cloned_group: + +test_create_cg_from_cgsnapshot: + +test_create_group_from_group_snapshot: + +test_create_cgsnapshot: + +test_create_group_snapshot: + +test_delete_group_snapshot: + +test_delete_cgsnapshot: ########################################################### # TestUtils ########################################################### test_validate_cg_type: - cg: *cg_base_with_type + cg: + _properties: + id: + _uuid: GROUP_ID + volume_type_ids: ['type1'] ########################################################### diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_vnx.yaml b/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_vnx.yaml index 2bac48eaba7..52b55c76a4e 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_vnx.yaml +++ b/cinder/tests/unit/volume/drivers/dell_emc/vnx/mocked_vnx.yaml @@ -150,6 +150,7 @@ mirror_base: &mirror_base _type: VNXMirrorImageState value: 'SYNCHRONIZED' + ########################################################### # TestClient ########################################################### @@ -1365,7 +1366,9 @@ test_delete_snapshot_adapter: *test_delete_snapshot test_create_cgsnapshot: *test_create_cg_snapshot -test_delete_cgsnapshot: +test_do_create_cgsnap: *test_create_cg_snapshot + +test_do_delete_cgsnap: cg_snap: &cg_snap_delete _methods: delete: @@ -1373,7 +1376,7 @@ test_delete_cgsnapshot: _methods: get_snap: *cg_snap_delete -test_create_cg_from_cgsnapshot: +test_do_create_cg_from_cgsnap: snap: &copied_cg_snap _methods: copy: @@ -1400,7 +1403,7 @@ test_create_cg_from_cgsnapshot: get_migration_session: *session_verify create_cg: *cg_for_create -test_create_cloned_cg: +test_do_clone_cg: vnx: _properties: _methods: @@ -1773,6 +1776,8 @@ test_terminate_connection_cleanup_sg_is_not_empty: test_update_consistencygroup: +test_do_update_cg: + test_update_migrated_volume: test_update_migrated_volume_smp: @@ -1785,6 +1790,26 @@ test_normalize_config_iscsi_initiators_empty_str: test_normalize_config_iscsi_initiators_not_dict: +test_create_group_snap: + +test_create_cgsnapshot: + +test_create_cloned_cg: + +test_create_cloned_group: + +test_create_cg_from_cgsnapshot: + +test_create_group_from_group_snapshot: + +test_create_cgsnapshot: + +test_create_group_snapshot: + +test_delete_group_snapshot: + +test_delete_cgsnapshot: + ########################################################### # TestISCSIAdapter diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_adapter.py b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_adapter.py index 54f6da52d7d..b27b7e75ced 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_adapter.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_adapter.py @@ -15,9 +15,12 @@ import mock import re +from cinder import context from cinder import exception from cinder.objects import fields from cinder import test +from cinder.tests.unit import fake_constants +from cinder.tests.unit import utils as test_utils from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_exception \ as storops_ex from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_storops \ @@ -39,6 +42,7 @@ class TestCommonAdapter(test.TestCase): vnx_utils.init_ops(self.configuration) self.configuration.san_ip = '192.168.1.1' self.configuration.storage_vnx_authentication_type = 'global' + self.ctxt = context.get_admin_context() def tearDown(self): super(TestCommonAdapter, self).tearDown() @@ -136,32 +140,124 @@ class TestCommonAdapter(test.TestCase): update = vnx_common.create_volume_from_snapshot(volume, snapshot) self.assertEqual('True', update['metadata']['snapcopy']) + @res_mock.patch_common_adapter + def test_create_cg_from_cgsnapshot(self, common, _): + common.do_create_cg_from_cgsnap = mock.Mock( + return_value='fake_return') + new_cg = test_utils.create_consistencygroup( + self.ctxt, + id=fake_constants.CONSISTENCY_GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + cg_snapshot = test_utils.create_cgsnapshot( + self.ctxt, + fake_constants.CONSISTENCY_GROUP2_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + vol_new = test_utils.create_volume(self.ctxt) + ret = common.create_cg_from_cgsnapshot( + None, new_cg, [vol_new], cg_snapshot, snaps) + self.assertEqual('fake_return', ret) + common.do_create_cg_from_cgsnap.assert_called_once_with( + new_cg.id, new_cg.host, [vol_new], cg_snapshot.id, snaps) + + @res_mock.patch_common_adapter + def test_create_group_from_group_snapshot(self, common, _): + common.do_create_cg_from_cgsnap = mock.Mock( + return_value='fake_return') + group = test_utils.create_group( + self.ctxt, + id=fake_constants.CONSISTENCY_GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + group_snapshot = test_utils.create_group_snapshot( + self.ctxt, + fake_constants.CGSNAPSHOT_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + vol_new = test_utils.create_volume(self.ctxt) + ret = common.create_group_from_group_snapshot( + None, group, [vol_new], group_snapshot, snaps) + self.assertEqual('fake_return', ret) + common.do_create_cg_from_cgsnap.assert_called_once_with( + group.id, group.host, [vol_new], group_snapshot.id, snaps) + @res_mock.mock_driver_input @res_mock.patch_common_adapter - def test_create_cg_from_cgsnapshot(self, vnx_common, mocked, - cinder_input): - group = cinder_input['cg'] + def test_do_create_cg_from_cgsnap( + self, vnx_common, mocked, cinder_input): + cg_id = fake_constants.CONSISTENCY_GROUP_ID + cg_host = 'host@backend#unit_test_pool' volumes = [cinder_input['vol1']] - cg_snap = cinder_input['cg_snap'] + cgsnap_id = fake_constants.CGSNAPSHOT_ID snaps = [cinder_input['snap1']] - model_update, volume_updates = vnx_common.create_cg_from_cgsnapshot( - None, group, volumes, cg_snap, snaps) + model_update, volume_updates = ( + vnx_common.do_create_cg_from_cgsnap( + cg_id, cg_host, volumes, cgsnap_id, snaps)) self.assertIsNone(model_update) self.assertIsNotNone( re.findall('id^12', volume_updates[0]['provider_location'])) + @res_mock.patch_common_adapter + def test_create_cloned_cg(self, common, _): + common.do_clone_cg = mock.Mock( + return_value='fake_return') + group = test_utils.create_consistencygroup( + self.ctxt, + id=fake_constants.CONSISTENCY_GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + src_group = test_utils.create_consistencygroup( + self.ctxt, + id=fake_constants.CONSISTENCY_GROUP2_ID, + host='host@backend#unit_test_pool2', + group_type_id=fake_constants.VOLUME_TYPE_ID) + vol = test_utils.create_volume(self.ctxt) + src_vol = test_utils.create_volume(self.ctxt) + ret = common.create_cloned_group( + None, group, [vol], src_group, [src_vol]) + self.assertEqual('fake_return', ret) + common.do_clone_cg.assert_called_once_with( + group.id, group.host, [vol], src_group.id, [src_vol]) + + @res_mock.patch_common_adapter + def test_create_cloned_group(self, common, _): + common.do_clone_cg = mock.Mock( + return_value='fake_return') + group = test_utils.create_group( + self.ctxt, + id=fake_constants.GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + src_group = test_utils.create_group( + self.ctxt, + id=fake_constants.GROUP2_ID, + host='host@backend#unit_test_pool2', + group_type_id=fake_constants.VOLUME_TYPE_ID) + vol = test_utils.create_volume(self.ctxt) + src_vol = test_utils.create_volume(self.ctxt) + ret = common.create_cloned_group( + None, group, [vol], src_group, [src_vol]) + self.assertEqual('fake_return', ret) + common.do_clone_cg.assert_called_once_with( + group.id, group.host, [vol], src_group.id, [src_vol]) + @res_mock.mock_driver_input @res_mock.patch_common_adapter - def test_create_cloned_cg(self, vnx_common, mocked, - cinder_input): - group = cinder_input['cg'] - src_group = cinder_input['src_cg'] + def test_do_clone_cg(self, vnx_common, _, cinder_input): + cg_id = fake_constants.CONSISTENCY_GROUP_ID + cg_host = 'host@backend#unit_test_pool' volumes = [cinder_input['vol1']] + src_cg_id = fake_constants.CONSISTENCY_GROUP2_ID src_volumes = [cinder_input['src_vol1']] - model_update, volume_updates = vnx_common.create_cloned_cg( - None, group, volumes, src_group, src_volumes) + model_update, volume_updates = vnx_common.do_clone_cg( + cg_id, cg_host, volumes, src_cg_id, src_volumes) self.assertIsNone(model_update) self.assertIsNotNone( re.findall('id^12', @@ -474,24 +570,105 @@ class TestCommonAdapter(test.TestCase): mocked_input): common_adapter.delete_snapshot(mocked_input['snapshot']) + @res_mock.patch_common_adapter + def test_create_cgsnapshot(self, common_adapter, _): + common_adapter.do_create_cgsnap = mock.Mock( + return_value='fake_return') + cg_snapshot = test_utils.create_cgsnapshot( + self.ctxt, + fake_constants.CONSISTENCY_GROUP_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + ret = common_adapter.create_cgsnapshot( + None, cg_snapshot, snaps) + self.assertEqual('fake_return', ret) + common_adapter.do_create_cgsnap.assert_called_once_with( + cg_snapshot.consistencygroup_id, + cg_snapshot.id, + snaps) + + @res_mock.patch_common_adapter + def test_create_group_snap(self, common_adapter, _): + common_adapter.do_create_cgsnap = mock.Mock( + return_value='fake_return') + group_snapshot = test_utils.create_group_snapshot( + self.ctxt, + fake_constants.GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + ret = common_adapter.create_group_snapshot( + None, group_snapshot, snaps) + self.assertEqual('fake_return', ret) + common_adapter.do_create_cgsnap.assert_called_once_with( + group_snapshot.group_id, + group_snapshot.id, + snaps) + @res_mock.mock_driver_input @res_mock.patch_common_adapter - def test_create_cgsnapshot(self, common_adapter, mocked, mocked_input): - cg_snap = mocked_input['cg_snap'] + def test_do_create_cgsnap(self, common_adapter, _, mocked_input): + group_name = fake_constants.CONSISTENCY_GROUP_ID + snap_name = fake_constants.CGSNAPSHOT_ID snap1 = mocked_input['snap1'] snap2 = mocked_input['snap2'] model_update, snapshots_model_update = ( - common_adapter.create_cgsnapshot(None, cg_snap, [snap1, snap2])) + common_adapter.do_create_cgsnap(group_name, snap_name, + [snap1, snap2])) self.assertEqual('available', model_update['status']) for update in snapshots_model_update: self.assertEqual(fields.SnapshotStatus.AVAILABLE, update['status']) + @res_mock.patch_common_adapter + def test_delete_group_snapshot(self, common_adapter, _): + common_adapter.do_delete_cgsnap = mock.Mock( + return_value='fake_return') + group_snapshot = test_utils.create_group_snapshot( + self.ctxt, + fake_constants.GROUP_ID, + host='host@backend#unit_test_pool', + group_type_id=fake_constants.VOLUME_TYPE_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + ret = common_adapter.delete_group_snapshot( + None, group_snapshot, snaps) + self.assertEqual('fake_return', ret) + common_adapter.do_delete_cgsnap.assert_called_once_with( + group_snapshot.group_id, + group_snapshot.id, + group_snapshot.status, + snaps) + + @res_mock.patch_common_adapter + def test_delete_cgsnapshot(self, common_adapter, _): + common_adapter.do_delete_cgsnap = mock.Mock( + return_value='fake_return') + cg_snapshot = test_utils.create_cgsnapshot( + self.ctxt, + fake_constants.CONSISTENCY_GROUP_ID) + vol = test_utils.create_volume(self.ctxt) + snaps = [ + test_utils.create_snapshot(self.ctxt, vol.id)] + ret = common_adapter.delete_cgsnapshot(None, cg_snapshot, snaps) + self.assertEqual('fake_return', ret) + common_adapter.do_delete_cgsnap.assert_called_once_with( + cg_snapshot.consistencygroup_id, + cg_snapshot.id, + cg_snapshot.status, + snaps) + @res_mock.mock_driver_input @res_mock.patch_common_adapter - def test_delete_cgsnapshot(self, common_adapter, mocked, mocked_input): + def test_do_delete_cgsnap(self, common_adapter, _, mocked_input): + group_name = fake_constants.CGSNAPSHOT_ID + snap_name = fake_constants.CGSNAPSHOT_ID model_update, snapshot_updates = ( - common_adapter.delete_cgsnapshot( - None, mocked_input['cg_snap'], + common_adapter.do_delete_cgsnap( + group_name, snap_name, 'available', [mocked_input['snap1'], mocked_input['snap2']])) self.assertEqual('deleted', model_update['status']) for snap in snapshot_updates: @@ -792,15 +969,13 @@ class TestCommonAdapter(test.TestCase): @res_mock.mock_driver_input @res_mock.patch_common_adapter - def test_update_consistencygroup(self, common_adapter, mocked_res, - mocked_input): + def test_do_update_cg(self, common_adapter, _, mocked_input): common_adapter.client.update_consistencygroup = mock.Mock() cg = mocked_input['cg'] common_adapter.client.get_cg = mock.Mock(return_value=cg) - - common_adapter.update_consistencygroup(None, cg, - [mocked_input['volume_add']], - [mocked_input['volume_remove']]) + common_adapter.do_update_cg(cg.id, + [mocked_input['volume_add']], + [mocked_input['volume_remove']]) common_adapter.client.update_consistencygroup.assert_called_once_with( cg, [1], [2]) diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_driver.py b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_driver.py index 39a898a658e..f27fbd2d913 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_driver.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_driver.py @@ -71,3 +71,11 @@ class TestVNXDriver(test.TestCase): _driver.terminate_connection('fake_volume', {'host': 'fake_host'}) _driver.adapter.terminate_connection.assert_called_once_with( 'fake_volume', {'host': 'fake_host'}) + + def test_is_consistent_group_snapshot_enabled(self): + _driver = self._get_driver('iscsi') + _driver._stats = {'consistent_group_snapshot_enabled': True} + self.assertTrue(_driver.is_consistent_group_snapshot_enabled()) + _driver._stats = {'consistent_group_snapshot_enabled': False} + self.assertFalse(_driver.is_consistent_group_snapshot_enabled()) + self.assertFalse(_driver.is_consistent_group_snapshot_enabled()) diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_utils.py b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_utils.py index 01ba6de4067..c16a5f9a920 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_utils.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/vnx/test_utils.py @@ -27,6 +27,18 @@ from cinder.volume.drivers.dell_emc.vnx import common from cinder.volume.drivers.dell_emc.vnx import utils +class FakeDriver(object): + def __init__(self, support): + self.support = support + + def is_consistent_group_snapshot_enabled(self): + return self.support + + @utils.require_consistent_group_snapshot_enabled + def fake_method(self): + return 'called' + + class TestUtils(test.TestCase): def setUp(self): super(TestUtils, self).setUp() @@ -173,3 +185,14 @@ class TestUtils(test.TestCase): 'wwn2_1': ['wwnt_1', 'wwnt_3'], 'wwn2_2': ['wwnt_1', 'wwnt_3']}, itor_tgt_map) + + def test_cg_snapshot_is_not_enabled(self): + def do(): + driver = FakeDriver(False) + driver.fake_method() + self.assertRaises(NotImplementedError, do) + + def test_cg_snapshot_is_enabled(self): + driver = FakeDriver(True) + ret = driver.fake_method() + self.assertEqual('called', ret) diff --git a/cinder/volume/drivers/dell_emc/vnx/adapter.py b/cinder/volume/drivers/dell_emc/vnx/adapter.py index bb81ef5f3f2..375872c053f 100644 --- a/cinder/volume/drivers/dell_emc/vnx/adapter.py +++ b/cinder/volume/drivers/dell_emc/vnx/adapter.py @@ -222,9 +222,10 @@ class CommonAdapter(object): 'provision': provision, 'tier': tier}) + cg_id = volume.group_id or volume.consistencygroup_id lun = self.client.create_lun( pool, volume_name, volume_size, - provision, tier, volume.consistencygroup_id, + provision, tier, cg_id, ignore_thresholds=self.config.ignore_pool_full_threshold) location = self._build_provider_location( lun_type='lun', @@ -464,15 +465,21 @@ class CommonAdapter(object): return model_update, volumes_model_update def create_cgsnapshot(self, context, cgsnapshot, snapshots): + """Creates a CG snapshot(snap group).""" + return self.do_create_cgsnap(cgsnapshot.consistencygroup_id, + cgsnapshot.id, + snapshots) + + def do_create_cgsnap(self, group_name, snap_name, snapshots): model_update = {} snapshots_model_update = [] - LOG.info(_LI('Creating CG snapshot for consistency group' + LOG.info(_LI('Creating consistency snapshot for group' ': %(group_name)s'), - {'group_name': cgsnapshot.consistencygroup_id}) + {'group_name': group_name}) - self.client.create_cg_snapshot(cgsnapshot.id, - cgsnapshot.consistencygroup_id) + self.client.create_cg_snapshot(snap_name, + group_name) for snapshot in snapshots: snapshots_model_update.append( {'id': snapshot.id, 'status': 'available'}) @@ -482,15 +489,22 @@ class CommonAdapter(object): def delete_cgsnapshot(self, context, cgsnapshot, snapshots): """Deletes a CG snapshot(snap group).""" + return self.do_delete_cgsnap(cgsnapshot.consistencygroup_id, + cgsnapshot.id, + cgsnapshot.status, + snapshots) + + def do_delete_cgsnap(self, group_name, snap_name, + snap_status, snapshots): model_update = {} snapshots_model_update = [] - model_update['status'] = cgsnapshot.status - LOG.info(_LI('Deleting CG snapshot %(snap_name)s for consistency ' + model_update['status'] = snap_status + LOG.info(_LI('Deleting consistency snapshot %(snap_name)s for ' 'group: %(group_name)s'), - {'snap_name': cgsnapshot.id, - 'group_name': cgsnapshot.consistencygroup_id}) + {'snap_name': snap_name, + 'group_name': group_name}) - self.client.delete_cg_snapshot(cgsnapshot.id) + self.client.delete_cg_snapshot(snap_name) for snapshot in snapshots: snapshots_model_update.append( {'id': snapshot.id, 'status': 'deleted'}) @@ -500,6 +514,11 @@ class CommonAdapter(object): def create_cg_from_cgsnapshot(self, context, group, volumes, cgsnapshot, snapshots): + return self.do_create_cg_from_cgsnap( + group.id, group.host, volumes, cgsnapshot.id, snapshots) + + def do_create_cg_from_cgsnap(self, cg_id, cg_host, volumes, + cgsnap_id, snapshots): # 1. Copy a temp CG snapshot from CG snapshot # and allow RW for it # 2. Create SMPs from source volumes @@ -509,9 +528,9 @@ class CommonAdapter(object): # 6. Wait completion of migration # 7. Create a new CG, add all LUNs to it # 8. Delete the temp CG snapshot - cg_name = group.id - src_cg_snap_name = cgsnapshot.id - pool_name = utils.get_pool_from_host(group.host) + cg_name = cg_id + src_cg_snap_name = cgsnap_id + pool_name = utils.get_pool_from_host(cg_host) lun_sizes = [] lun_names = [] src_lun_names = [] @@ -549,9 +568,14 @@ class CommonAdapter(object): def create_cloned_cg(self, context, group, volumes, source_cg, source_vols): + self.do_clone_cg(group.id, group.host, volumes, + source_cg.id, source_vols) + + def do_clone_cg(self, cg_id, cg_host, volumes, + source_cg_id, source_vols): # 1. Create temp CG snapshot from source_cg # Same with steps 2-8 of create_cg_from_cgsnapshot - pool_name = utils.get_pool_from_host(group.host) + pool_name = utils.get_pool_from_host(cg_host) lun_sizes = [] lun_names = [] src_lun_names = [] @@ -564,8 +588,8 @@ class CommonAdapter(object): lun_id_list = emc_taskflow.create_cloned_cg( client=self.client, - cg_name=group.id, - src_cg_name=source_cg.id, + cg_name=cg_id, + src_cg_name=source_cg_id, pool_name=pool_name, lun_sizes=lun_sizes, lun_names=lun_names, @@ -623,6 +647,8 @@ class CommonAdapter(object): stats['thin_provisioning_support'] = self.client.is_thin_enabled() stats['consistencygroup_support'] = self.client.is_snap_enabled() stats['replication_enabled'] = True if self.mirror_view else False + stats['consistent_group_snapshot_enabled'] = ( + self.client.is_snap_enabled()) return stats def get_pool_stats(self, enabler_stats=None): @@ -1017,7 +1043,12 @@ class CommonAdapter(object): def update_consistencygroup(self, context, group, add_volumes, remove_volumes): - cg = self.client.get_cg(name=group.id) + return self.do_update_cg(group.id, add_volumes, + remove_volumes) + + def do_update_cg(self, cg_name, add_volumes, + remove_volumes): + cg = self.client.get_cg(name=cg_name) lun_ids_to_add = [self.client.get_lun_id(volume) for volume in add_volumes] lun_ids_to_remove = [self.client.get_lun_id(volume) @@ -1204,6 +1235,46 @@ class CommonAdapter(object): return {'provider_location': new_volume.provider_location, 'metadata': metadata} + def create_group(self, context, group): + return self.create_consistencygroup(context, group) + + def delete_group(self, context, group, volumes): + return self.delete_consistencygroup(context, group, volumes) + + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group_snapshot.""" + return self.do_create_cgsnap(group_snapshot.group_id, + group_snapshot.id, + snapshots) + + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group snapshot.""" + return self.do_delete_cgsnap( + group_snapshot.group_id, + group_snapshot.id, + group_snapshot.status, + snapshots) + + def create_group_from_group_snapshot(self, + context, group, volumes, + group_snapshot, snapshots): + """Creates a group from a group snapshot.""" + return self.do_create_cg_from_cgsnap(group.id, group.host, volumes, + group_snapshot.id, snapshots) + + def update_group(self, context, group, + add_volumes=None, remove_volumes=None): + """Updates a group.""" + return self.do_update_cg(group.id, + add_volumes, + remove_volumes) + + def create_cloned_group(self, context, group, volumes, + source_group, source_vols): + """Clones a group""" + return self.do_clone_cg(group.id, group.host, volumes, + source_group.id, source_vols) + class ISCSIAdapter(CommonAdapter): def __init__(self, configuration, active_backend_id): diff --git a/cinder/volume/drivers/dell_emc/vnx/driver.py b/cinder/volume/drivers/dell_emc/vnx/driver.py index 55c3a4af7cc..f59075d0065 100644 --- a/cinder/volume/drivers/dell_emc/vnx/driver.py +++ b/cinder/volume/drivers/dell_emc/vnx/driver.py @@ -86,6 +86,7 @@ class VNXDriver(driver.TransferVD, self.protocol = self.configuration.storage_protocol.lower() self.active_backend_id = kwargs.get('active_backend_id', None) self.adapter = None + self._stats = {} def do_setup(self, context): if self.protocol == common.PROTOCOL_FC: @@ -331,3 +332,49 @@ class VNXDriver(driver.TransferVD, def failover_host(self, context, volumes, secondary_id=None): """Fail-overs volumes from primary device to secondary.""" return self.adapter.failover_host(context, volumes, secondary_id) + + @utils.require_consistent_group_snapshot_enabled + def create_group(self, context, group): + """Creates a group.""" + return self.adapter.create_group(context, group) + + @utils.require_consistent_group_snapshot_enabled + def delete_group(self, context, group, volumes): + """Deletes a group.""" + return self.adapter.delete_group( + context, group, volumes) + + @utils.require_consistent_group_snapshot_enabled + def update_group(self, context, group, + add_volumes=None, remove_volumes=None): + """Updates a group.""" + return self.adapter.update_group(context, group, + add_volumes, + remove_volumes) + + @utils.require_consistent_group_snapshot_enabled + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + """Creates a group from source.""" + if group_snapshot: + return self.adapter.create_group_from_group_snapshot( + context, group, volumes, group_snapshot, snapshots) + elif source_group: + return self.adapter.create_cloned_group( + context, group, volumes, source_group, source_vols) + + @utils.require_consistent_group_snapshot_enabled + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group_snapshot.""" + return self.adapter.create_group_snapshot( + context, group_snapshot, snapshots) + + @utils.require_consistent_group_snapshot_enabled + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group_snapshot.""" + return self.adapter.delete_group_snapshot( + context, group_snapshot, snapshots) + + def is_consistent_group_snapshot_enabled(self): + return self._stats.get('consistent_group_snapshot_enabled') diff --git a/cinder/volume/drivers/dell_emc/vnx/utils.py b/cinder/volume/drivers/dell_emc/vnx/utils.py index 19af1a3a166..949e2648a41 100644 --- a/cinder/volume/drivers/dell_emc/vnx/utils.py +++ b/cinder/volume/drivers/dell_emc/vnx/utils.py @@ -246,9 +246,9 @@ def get_migration_rate(volume): def validate_cg_type(group): - if group.get('volume_type_id') is None: + if not group.get('volume_type_ids'): return - for type_id in group['volume_type_id'].split(","): + for type_id in group.get('volume_type_ids'): if type_id: specs = volume_types.get_volume_type_extra_specs(type_id) extra_specs = common.ExtraSpecs(specs) @@ -337,3 +337,12 @@ def truncate_fc_port_wwn(wwn): def is_volume_smp(volume): return 'smp' == extract_provider_location(volume.provider_location, 'type') + + +def require_consistent_group_snapshot_enabled(func): + @six.wraps(func) + def inner(self, *args, **kwargs): + if not self.is_consistent_group_snapshot_enabled(): + raise NotImplementedError + return func(self, *args, **kwargs) + return inner diff --git a/releasenotes/notes/generic-groups-in-vnx-cbbe1346e889b5c2.yaml b/releasenotes/notes/generic-groups-in-vnx-cbbe1346e889b5c2.yaml new file mode 100644 index 00000000000..9af2d412301 --- /dev/null +++ b/releasenotes/notes/generic-groups-in-vnx-cbbe1346e889b5c2.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add consistent group capability to generic volume groups in VNX driver.