From 6359dcecd54b54b18edbffbd55e91809383cea6a Mon Sep 17 00:00:00 2001 From: Tina Tang Date: Mon, 14 Nov 2016 16:40:26 +0800 Subject: [PATCH] Add CG capability to generic groups in VNX driver This patch adds the consistency group capability to generic group in VNX driver. Consistency groups will be used as generic groups. DocImpact: Implements: blueprint vnx-generic-group Change-Id: I92ad0032e5b4e0f72157febe64205a4d72a561e5 --- .../drivers/dell_emc/vnx/mocked_cinder.yaml | 45 ++-- .../drivers/dell_emc/vnx/mocked_vnx.yaml | 31 ++- .../drivers/dell_emc/vnx/test_adapter.py | 223 ++++++++++++++++-- .../drivers/dell_emc/vnx/test_driver.py | 8 + .../volume/drivers/dell_emc/vnx/test_utils.py | 23 ++ cinder/volume/drivers/dell_emc/vnx/adapter.py | 105 +++++++-- cinder/volume/drivers/dell_emc/vnx/driver.py | 47 ++++ cinder/volume/drivers/dell_emc/vnx/utils.py | 13 +- ...eneric-groups-in-vnx-cbbe1346e889b5c2.yaml | 3 + 9 files changed, 436 insertions(+), 62 deletions(-) create mode 100644 releasenotes/notes/generic-groups-in-vnx-cbbe1346e889b5c2.yaml 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.