From fadefc8206a612f035ce0530ce97c6703c4957b1 Mon Sep 17 00:00:00 2001 From: swapnil-nilangekar Date: Fri, 7 Apr 2017 08:17:20 -0600 Subject: [PATCH] HPE 3PAR: Adds CG capability in generic volume groups(GVG). This supports following functionality: * Create consistent group using GVG APIs * Delete consistent group using GVG APIs * Create consistent group snapshot using GVG APIs * Delete consistent group snapshot using GVG APIs * Update (add/remove) volume from a group using GVG APIs * Create group from a source using GVG APIs, where source can be a group or a snapshot of a group Change-Id: I00cc28edec612f401002da5773d11917700db2ab Implements: blueprint add-hpe3par-cg-capability-to-gvg --- .../unit/volume/drivers/hpe/test_hpe3par.py | 324 ++++++++++-------- cinder/volume/drivers/hpe/hpe_3par_common.py | 126 ++++--- cinder/volume/drivers/hpe/hpe_3par_fc.py | 41 ++- cinder/volume/drivers/hpe/hpe_3par_iscsi.py | 42 +-- ...Generic-Volume-Group-e048002e1c3469a3.yaml | 4 + 5 files changed, 319 insertions(+), 218 deletions(-) create mode 100644 releasenotes/notes/HPE-3par-Generic-Volume-Group-e048002e1c3469a3.yaml diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index b3078a64999..5f11e887d6c 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -110,7 +110,7 @@ class HPE3PARBaseDriver(object): VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw' SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ' RCG_3PAR_NAME = 'rcg-0DM4qZEVSKON-DXN-N' - CONSIS_GROUP_ID = '6044fedf-c889-4752-900f-2039d247a5df' + GROUP_ID = '6044fedf-c889-4752-900f-2039d247a5df' CONSIS_GROUP_NAME = 'vvs-YET.38iJR1KQDyA50kel3w' SRC_CONSIS_GROUP_ID = '7d7dfa02-ac6e-48cb-96af-8a0cd3008d47' SRC_CONSIS_GROUP_NAME = 'vvs-fX36AqxuSMuWr4oM0wCNRw' @@ -613,20 +613,31 @@ class HPE3PARBaseDriver(object): standard_logout = [ mock.call.logout()] - class fake_consistencygroup_object(object): - def __init__(self, cg_id='6044fedf-c889-4752-900f-2039d247a5df'): - self.id = cg_id - self.volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c' + class fake_volume_object(object): + def __init__(self, vol_id='d03338a9-9115-48a3-8dfc-35cdfcdc15a7'): + self.id = vol_id + self.name = 'volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7' + self.display_name = 'Foo Volume' + self.size = 2 + self.host = 'fakehost@foo#OpenStackCPG' + self.volume_type = None + self.volume_type_id = None + + class fake_group_object(object): + def __init__(self, grp_id='6044fedf-c889-4752-900f-2039d247a5df'): + self.id = grp_id + self.volume_type_ids = ['d03338a9-9115-48a3-8dfc-33333333333'] + self.volume_types = ['d03338a9-9115-48a3-8dfc-33333333333'] self.name = 'cg_name' - self.cgsnapshot_id = None + self.group_snapshot_id = None self.host = 'fakehost@foo#OpenStackCPG' self.description = 'consistency group' - class fake_cgsnapshot_object(object): + class fake_group_snapshot_object(object): def __init__(self, cgsnap_id='e91c5ed5-daee-4e84-8724-1c9e31e7a1f2'): self.id = cgsnap_id - self.consistencygroup_id = '6044fedf-c889-4752-900f-2039d247a5df' - self.description = 'cgsnapshot' + self.group_id = '6044fedf-c889-4752-900f-2039d247a5df' + self.description = 'group_snapshot' self.readOnly = False def setup_configuration(self): @@ -4112,22 +4123,26 @@ class HPE3PARBaseDriver(object): safe_host = common._safe_hostname(long_hostname) self.assertEqual(fixed_hostname, safe_host) - def test_create_consistency_group(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client') as mock_create_client: mock_create_client.return_value = mock_client mock_client.getCPG.return_value = {'domain': None} - # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + # create a group + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4143,23 +4158,38 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_create_consistency_group_from_src(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'get_volume_settings_from_type') + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_from_src(self, cg_ss_enable, vol_ss_enable, + typ_info): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume + volume = self.fake_volume_object() + type_info = {'cpg': 'OpenStackCPG', + 'tpvv': True, + 'tdvv': False, + 'snap_cpg': 'OpenStackCPG', + 'hpe3par_keys': {}} - cgsnap_comment = Comment({ - "consistency_group_id": "6044fedf-c889-4752-900f-2039d247a5df", - "description": "cgsnapshot", - "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2", + typ_info.return_value = type_info + + group_snap_comment = Comment({ + "group_id": "6044fedf-c889-4752-900f-2039d247a5df", + "description": "group_snapshot", + "group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2", }) - cgsnap_optional = ( - {'comment': cgsnap_comment, + group_snap_optional = ( + {'comment': group_snap_comment, 'readOnly': False}) - cg_comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + group_comment = Comment({ + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, @@ -4168,16 +4198,15 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), mock.call.createVolumeSet( self.CONSIS_GROUP_NAME, domain=None, - comment=cg_comment)] + comment=group_comment)] mock_client.assert_has_calls( self.get_id_login + @@ -4188,10 +4217,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # add a volume to the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[volume], - remove_volumes=[]) + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[volume], remove_volumes=[]) expected = [ mock.call.addVolumeToVolumeSet( @@ -4207,20 +4234,20 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # create a snapshot of the consistency group - cgsnapshot = self.fake_cgsnapshot_object() - self.driver.create_cgsnapshot(context.get_admin_context(), - cgsnapshot, []) + grp_snapshot = self.fake_group_snapshot_object() + self.driver.create_group_snapshot(context.get_admin_context(), + grp_snapshot, []) expected = [ mock.call.createSnapshotOfVolumeSet( self.CGSNAPSHOT_BASE_NAME + "-@count@", self.CONSIS_GROUP_NAME, - optional=cgsnap_optional)] + optional=group_snap_optional)] # create a consistency group from the cgsnapshot - self.driver.create_consistencygroup_from_src( + self.driver.create_group_from_src( context.get_admin_context(), group, - [volume], cgsnapshot=cgsnapshot, + [volume], group_snapshot=grp_snapshot, snapshots=[self.snapshot]) mock_client.assert_has_calls( @@ -4230,37 +4257,52 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_create_consistency_group_from_src_cg(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'get_volume_settings_from_type') + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_from_src_group(self, cg_ss_enable, vol_ss_enable, + typ_info): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume + volume = self.fake_volume_object() + type_info = {'cpg': 'OpenStackCPG', + 'tpvv': True, + 'tdvv': False, + 'snap_cpg': 'OpenStackCPG', + 'hpe3par_keys': {}} + + typ_info.return_value = type_info source_volume = self.volume_src_cg - cgsnap_optional = ( + group_snap_optional = ( {'expirationHours': 1}) - cg_comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + group_comment = Comment({ + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client') as mock_create_client: mock_create_client.return_value = mock_client mock_client.getCPG.return_value = {'domain': None} - group = self.fake_consistencygroup_object() - source_group = self.fake_consistencygroup_object( - cg_id=self.SRC_CONSIS_GROUP_ID) + group = self.fake_group_object() + source_grp = self.fake_group_object( + grp_id=self.SRC_CONSIS_GROUP_ID) expected = [ mock.call.getCPG(HPE3PAR_CPG), mock.call.createVolumeSet( self.CONSIS_GROUP_NAME, domain=None, - comment=cg_comment), + comment=group_comment), mock.call.createSnapshotOfVolumeSet( mock.ANY, self.SRC_CONSIS_GROUP_NAME, - optional=cgsnap_optional), + optional=group_snap_optional), mock.call.copyVolume( mock.ANY, self.VOLUME_NAME_3PAR, @@ -4272,9 +4314,9 @@ class HPE3PARBaseDriver(object): self.VOLUME_NAME_3PAR)] # Create a consistency group from a source consistency group. - self.driver.create_consistencygroup_from_src( + self.driver.create_group_from_src( context.get_admin_context(), group, - [volume], source_cg=source_group, + [volume], source_group=source_grp, source_vols=[source_volume]) mock_client.assert_has_calls( @@ -4284,12 +4326,17 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_delete_consistency_group(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, @@ -4298,9 +4345,8 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4318,9 +4364,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # remove the consistency group - group.status = fields.ConsistencyGroupStatus.DELETING - self.driver.delete_consistencygroup(context.get_admin_context(), - group, []) + group.status = fields.GroupStatus.DELETING + self.driver.delete_group(context.get_admin_context(), group, []) expected = [ mock.call.deleteVolumeSet( @@ -4333,7 +4378,12 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_delete_consistency_group_exceptions(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group_exceptions(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} @@ -4343,42 +4393,44 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() + group = self.fake_group_object() volume = fake_volume.fake_volume_obj(context.get_admin_context()) - self.driver.create_consistencygroup(context.get_admin_context(), - group) + self.driver.create_group(context.get_admin_context(), group) # remove the consistency group - group.status = fields.ConsistencyGroupStatus.DELETING + group.status = fields.GroupStatus.DELETING # mock HTTPConflict in delete volume set mock_client.deleteVolumeSet.side_effect = ( hpeexceptions.HTTPConflict()) # no exception should escape method - self.driver.delete_consistencygroup(context.get_admin_context(), - group, []) + self.driver.delete_group(context.get_admin_context(), group, []) # mock HTTPNotFound in delete volume set mock_client.deleteVolumeSet.side_effect = ( hpeexceptions.HTTPNotFound()) # no exception should escape method - self.driver.delete_consistencygroup(context.get_admin_context(), - group, []) + self.driver.delete_group(context.get_admin_context(), group, []) # mock HTTPConflict in delete volume mock_client.deleteVolume.side_effect = ( hpeexceptions.HTTPConflict()) # no exception should escape method - self.driver.delete_consistencygroup(context.get_admin_context(), - group, [volume]) + self.driver.delete_group(context.get_admin_context(), group, + [volume]) - def test_update_consistency_group_add_vol(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_update_group_add_vol(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume + volume = self.fake_volume_object() comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, @@ -4387,9 +4439,8 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4407,10 +4458,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # add a volume to the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[volume], - remove_volumes=[]) + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[volume], remove_volumes=[]) expected = [ mock.call.addVolumeToVolumeSet( @@ -4424,13 +4473,18 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_update_consistency_group_remove_vol(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_update_group_remove_vol(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume + volume = self.fake_volume_object() comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) with mock.patch.object(hpecommon.HPE3PARCommon, @@ -4439,9 +4493,8 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4459,10 +4512,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # add a volume to the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[volume], - remove_volumes=[]) + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[volume], remove_volumes=[]) expected = [ mock.call.addVolumeToVolumeSet( @@ -4478,10 +4529,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # remove the volume from the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[], - remove_volumes=[volume]) + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[], remove_volumes=[volume]) expected = [ mock.call.removeVolumeFromVolumeSet( @@ -4495,22 +4544,27 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_create_cgsnapshot(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_group_snapshot(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume + volume = self.fake_volume_object() cg_comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) - cgsnap_comment = Comment({ - "consistency_group_id": "6044fedf-c889-4752-900f-2039d247a5df", - "description": "cgsnapshot", - "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}) + group_snap_comment = Comment({ + "group_id": "6044fedf-c889-4752-900f-2039d247a5df", + "description": "group_snapshot", + "group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}) cgsnap_optional = ( - {'comment': cgsnap_comment, + {'comment': group_snap_comment, 'readOnly': False}) with mock.patch.object(hpecommon.HPE3PARCommon, @@ -4519,9 +4573,8 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4539,10 +4592,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # add a volume to the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[volume], - remove_volumes=[]) + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[volume], remove_volumes=[]) expected = [ mock.call.addVolumeToVolumeSet( @@ -4558,9 +4609,9 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # create a snapshot of the consistency group - cgsnapshot = self.fake_cgsnapshot_object() - self.driver.create_cgsnapshot(context.get_admin_context(), - cgsnapshot, []) + group_snapshot = self.fake_group_snapshot_object() + self.driver.create_group_snapshot(context.get_admin_context(), + group_snapshot, []) expected = [ mock.call.createSnapshotOfVolumeSet( @@ -4575,23 +4626,28 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) - def test_delete_cgsnapshot(self): + @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' + 'is_volume_group_snap_type') + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_delete_group_snapshot(self, cg_ss_enable, vol_ss_enable): + cg_ss_enable.return_value = True + vol_ss_enable.return_value = True mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - volume = self.volume - cgsnapshot = self.fake_cgsnapshot_object() + volume = self.fake_volume_object() + group_snapshot = self.fake_group_snapshot_object() cg_comment = Comment({ - 'consistency_group_id': self.CONSIS_GROUP_ID + 'group_id': self.GROUP_ID }) - cgsnap_comment = Comment({ - "consistency_group_id": "6044fedf-c889-4752-900f-2039d247a5df", - "description": "cgsnapshot", - "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}) + group_snap_comment = Comment({ + "group_id": "6044fedf-c889-4752-900f-2039d247a5df", + "description": "group_snapshot", + "group_snapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}) - cgsnap_optional = {'comment': cgsnap_comment, - 'readOnly': False} + group_snap_optional = {'comment': group_snap_comment, + 'readOnly': False} with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client') as mock_create_client: @@ -4599,9 +4655,8 @@ class HPE3PARBaseDriver(object): mock_client.getCPG.return_value = {'domain': None} # create a consistency group - group = self.fake_consistencygroup_object() - self.driver.create_consistencygroup(context.get_admin_context(), - group) + group = self.fake_group_object() + self.driver.create_group(context.get_admin_context(), group) expected = [ mock.call.getCPG(HPE3PAR_CPG), @@ -4619,11 +4674,8 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # add a volume to the consistency group - self.driver.update_consistencygroup(context.get_admin_context(), - group, - add_volumes=[volume], - remove_volumes=[]) - + self.driver.update_group(context.get_admin_context(), group, + add_volumes=[volume], remove_volumes=[]) expected = [ mock.call.addVolumeToVolumeSet( self.CONSIS_GROUP_NAME, @@ -4638,19 +4690,19 @@ class HPE3PARBaseDriver(object): mock_client.reset_mock() # create a snapshot of the consistency group - self.driver.create_cgsnapshot(context.get_admin_context(), - cgsnapshot, []) + self.driver.create_group_snapshot(context.get_admin_context(), + group_snapshot, []) expected = [ mock.call.createSnapshotOfVolumeSet( self.CGSNAPSHOT_BASE_NAME + "-@count@", self.CONSIS_GROUP_NAME, - optional=cgsnap_optional)] + optional=group_snap_optional)] # delete the snapshot of the consistency group - cgsnapshot.status = 'deleting' - self.driver.delete_cgsnapshot(context.get_admin_context(), - cgsnapshot, []) + group_snapshot.status = 'deleting' + self.driver.delete_group_snapshot(context.get_admin_context(), + group_snapshot, []) mock_client.assert_has_calls( self.get_id_login + diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 699d4d1753f..9c686e01040 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -254,10 +254,12 @@ class HPE3PARCommon(object): 3.0.29 - Fix convert snapshot volume to base volume type. bug #1656186 3.0.30 - Handle manage and unmanage hosts present. bug #1648067 3.0.31 - Enable HPE-3PAR Compression Feature. + 3.0.32 - Add consistency group capability to generic volume group + in HPE-3APR """ - VERSION = "3.0.31" + VERSION = "3.0.32" stats = {} @@ -525,34 +527,46 @@ class HPE3PARCommon(object): growth_size_mib = growth_size * units.Ki self._extend_volume(volume, volume_name, growth_size_mib) - def create_consistencygroup(self, context, group): - """Creates a consistencygroup.""" + def create_group(self, context, group): + """Creates a group.""" + if not volume_utils.is_group_a_cg_snapshot_type(group): + raise NotImplementedError() + if group.volume_type_ids is not None: + for volume_type in group.volume_types: + allow_type = self.is_volume_group_snap_type( + volume_type) + if not allow_type: + msg = _('For a volume type to be a part of consistent ' + 'group, volume type extra spec must have ' + 'consistent_group_snapshot_enabled=" True"') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) pool = volume_utils.extract_host(group.host, level='pool') domain = self.get_domain(pool) cg_name = self._get_3par_vvs_name(group.id) - extra = {'consistency_group_id': group.id} - if group.cgsnapshot_id: - extra['cgsnapshot_id'] = group.cgsnapshot_id + extra = {'group_id': group.id} + if group.group_snapshot_id is not None: + extra['group_snapshot_id'] = group.group_snapshot_id self.client.createVolumeSet(cg_name, domain=domain, comment=six.text_type(extra)) - model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} + model_update = {'status': fields.GroupStatus.AVAILABLE} return model_update - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): - self.create_consistencygroup(context, group) + self.create_group(context, group) vvs_name = self._get_3par_vvs_name(group.id) - if cgsnapshot and snapshots: - cgsnap_name = self._get_3par_snap_name(cgsnapshot.id) + if group_snapshot and snapshots: + cgsnap_name = self._get_3par_snap_name(group_snapshot.id) snap_base = cgsnap_name - elif source_cg and source_vols: - cg_id = source_cg.id + elif source_group and source_vols: + cg_id = source_group.id # Create a brand new uuid for the temp snap. snap_uuid = uuid.uuid4().hex @@ -569,16 +583,17 @@ class HPE3PARCommon(object): for i, volume in enumerate(volumes): snap_name = snap_base + "-" + six.text_type(i) - volume_name = self._get_3par_vol_name(volume['id']) + volume_name = self._get_3par_vol_name(volume.id) type_info = self.get_volume_settings_from_type(volume) cpg = type_info['cpg'] + snapcpg = type_info['snap_cpg'] tpvv = type_info.get('tpvv', False) tdvv = type_info.get('tdvv', False) compression = self.get_compression_policy( type_info['hpe3par_keys']) - optional = {'online': True, 'snapCPG': cpg, + optional = {'online': True, 'snapCPG': snapcpg, 'tpvv': tpvv, 'tdvv': tdvv} if compression is not None: @@ -589,10 +604,12 @@ class HPE3PARCommon(object): return None, None - def delete_consistencygroup(self, context, group, volumes): - """Deletes a consistency group.""" + def delete_group(self, context, group, volumes): + """Deletes a group.""" try: + if not volume_utils.is_group_a_cg_snapshot_type(group): + raise NotImplementedError() cg_name = self._get_3par_vvs_name(group.id) self.client.deleteVolumeSet(cg_name) except hpeexceptions.HTTPNotFound: @@ -617,20 +634,31 @@ class HPE3PARCommon(object): 'error': ex}) volume_update['status'] = 'error' volume_model_updates.append(volume_update) - model_update = {'status': group.status} - return model_update, volume_model_updates - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): - + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + grp_snap_enable = volume_utils.is_group_a_cg_snapshot_type(group) + if not grp_snap_enable: + raise NotImplementedError() volume_set_name = self._get_3par_vvs_name(group.id) - for volume in add_volumes: - volume_name = self._get_3par_vol_name(volume['id']) + volume_name = self._get_3par_vol_name(volume.id) + vol_snap_enable = self.is_volume_group_snap_type( + volume.volume_type) try: - self.client.addVolumeToVolumeSet(volume_set_name, volume_name) + if grp_snap_enable and vol_snap_enable: + self.client.addVolumeToVolumeSet(volume_set_name, + volume_name) + else: + msg = (_('Volume with volume id %s is not ' + 'supported as extra specs of this ' + 'volume does not have ' + 'consistent_group_snapshot_enabled=" True"' + ) % volume['id']) + LOG.error(msg) + raise exception.InvalidInput(reason=msg) except hpeexceptions.HTTPNotFound: msg = (_('Virtual Volume Set %s does not exist.') % volume_set_name) @@ -638,7 +666,7 @@ class HPE3PARCommon(object): raise exception.InvalidInput(reason=msg) for volume in remove_volumes: - volume_name = self._get_3par_vol_name(volume['id']) + volume_name = self._get_3par_vol_name(volume.id) try: self.client.removeVolumeFromVolumeSet( volume_set_name, volume_name) @@ -650,17 +678,19 @@ class HPE3PARCommon(object): return None, None, None - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - """Creates a cgsnapshot.""" + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group snapshot.""" + if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + raise NotImplementedError() - cg_id = cgsnapshot.consistencygroup_id - snap_shot_name = self._get_3par_snap_name(cgsnapshot.id) + ( + cg_id = group_snapshot.group_id + snap_shot_name = self._get_3par_snap_name(group_snapshot.id) + ( "-@count@") copy_of_name = self._get_3par_vvs_name(cg_id) - extra = {'cgsnapshot_id': cgsnapshot.id} - extra['consistency_group_id'] = cg_id - extra['description'] = cgsnapshot.description + extra = {'group_snapshot_id': group_snapshot.id} + extra['group_id'] = cg_id + extra['description'] = group_snapshot.description optional = {'comment': json.dumps(extra), 'readOnly': False} @@ -687,14 +717,15 @@ class HPE3PARCommon(object): 'status': fields.SnapshotStatus.AVAILABLE} snapshot_model_updates.append(snapshot_update) - model_update = {'status': 'available'} + model_update = {'status': fields.GroupSnapshotStatus.AVAILABLE} return model_update, snapshot_model_updates - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - """Deletes a cgsnapshot.""" - - cgsnap_name = self._get_3par_snap_name(cgsnapshot.id) + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group snapshot.""" + if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + raise NotImplementedError() + cgsnap_name = self._get_3par_snap_name(group_snapshot.id) snapshot_model_updates = [] for i, snapshot in enumerate(snapshots): @@ -718,7 +749,7 @@ class HPE3PARCommon(object): snapshot_update['status'] = fields.SnapshotStatus.ERROR snapshot_model_updates.append(snapshot_update) - model_update = {'status': cgsnapshot.status} + model_update = {'status': fields.GroupSnapshotStatus.DELETED} return model_update, snapshot_model_updates @@ -1378,7 +1409,7 @@ class HPE3PARCommon(object): 'filter_function': filter_function, 'goodness_function': goodness_function, 'multiattach': False, - 'consistencygroup_support': True, + 'consistent_group_snapshot_enabled': True, 'compression': compression_support, } @@ -1908,7 +1939,7 @@ class HPE3PARCommon(object): compression = self.get_compression_policy( type_info['hpe3par_keys']) - cg_id = volume.get('consistencygroup_id', None) + cg_id = volume.get('group_id', None) if cg_id: vvs_name = self._get_3par_vvs_name(cg_id) @@ -3273,6 +3304,15 @@ class HPE3PARCommon(object): rep_flag = False return rep_flag + def is_volume_group_snap_type(self, volume_type): + consis_group_snap_type = False + if volume_type: + extra_specs = volume_type.extra_specs + if 'consistent_group_snapshot_enabled' in extra_specs: + gsnap_val = extra_specs['consistent_group_snapshot_enabled'] + consis_group_snap_type = (gsnap_val == " True") + return consis_group_snap_type + def _volume_of_replicated_type(self, volume): replicated_type = False volume_type_id = volume.get('volume_type_id') diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index e9b83501f99..a4ffaa6c053 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -110,10 +110,11 @@ class HPE3PARFCDriver(driver.ManageableVD, by another host, while creating 3PAR FC Host. bug #1597454 3.0.10 - Added Entry point tracing 3.0.11 - Handle manage and unmanage hosts present. bug #1648067 + 3.0.12 - Adds consistency group capability in generic volume groups. """ - VERSION = "3.0.11" + VERSION = "3.0.12" # The name of the CI wiki page. CI_WIKI_NAME = "HPE_Storage_CI" @@ -561,56 +562,58 @@ class HPE3PARFCDriver(driver.ManageableVD, self._logout(common) @utils.trace - def create_consistencygroup(self, context, group): + def create_group(self, context, group): common = self._login() try: - return common.create_consistencygroup(context, group) + return common.create_group(context, group) finally: self._logout(common) @utils.trace - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): common = self._login() try: - return common.create_consistencygroup_from_src( - context, group, volumes, cgsnapshot, snapshots, source_cg, - source_vols) + return common.create_group_from_src( + context, group, volumes, group_snapshot, snapshots, + source_group, source_vols) finally: self._logout(common) @utils.trace - def delete_consistencygroup(self, context, group, volumes): + def delete_group(self, context, group, volumes): common = self._login() try: - return common.delete_consistencygroup(context, group, volumes) + return common.delete_group(context, group, volumes) finally: self._logout(common) @utils.trace - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): common = self._login() try: - return common.update_consistencygroup(context, group, add_volumes, - remove_volumes) + return common.update_group(context, group, add_volumes, + remove_volumes) finally: self._logout(common) @utils.trace - def create_cgsnapshot(self, context, cgsnapshot, snapshots): + def create_group_snapshot(self, context, group_snapshot, snapshots): common = self._login() try: - return common.create_cgsnapshot(context, cgsnapshot, snapshots) + return common.create_group_snapshot(context, group_snapshot, + snapshots) finally: self._logout(common) @utils.trace - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): + def delete_group_snapshot(self, context, group_snapshot, snapshots): common = self._login() try: - return common.delete_cgsnapshot(context, cgsnapshot, snapshots) + return common.delete_group_snapshot(context, group_snapshot, + snapshots) finally: self._logout(common) diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index b10d63b5703..872a4fcf8c4 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -122,10 +122,11 @@ class HPE3PARISCSIDriver(driver.ManageableVD, 3.0.13 - Handling HTTP conflict 409, host WWN/iSCSI name already used by another host, while creating 3PAR iSCSI Host. bug #1642945 3.0.14 - Handle manage and unmanage hosts present. bug #1648067 + 3.0.15 - Adds consistency group capability in generic volume groups. """ - VERSION = "3.0.14" + VERSION = "3.0.15" # The name of the CI wiki page. CI_WIKI_NAME = "HPE_Storage_CI" @@ -820,7 +821,6 @@ class HPE3PARISCSIDriver(driver.ManageableVD, if count < current_smallest_count: current_least_used_nsp = nsp current_smallest_count = count - return current_least_used_nsp @utils.trace @@ -832,56 +832,58 @@ class HPE3PARISCSIDriver(driver.ManageableVD, self._logout(common) @utils.trace - def create_consistencygroup(self, context, group): + def create_group(self, context, group): common = self._login() try: - common.create_consistencygroup(context, group) + common.create_group(context, group) finally: self._logout(common) @utils.trace - def create_consistencygroup_from_src(self, context, group, volumes, - cgsnapshot=None, snapshots=None, - source_cg=None, source_vols=None): + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): common = self._login() try: - return common.create_consistencygroup_from_src( - context, group, volumes, cgsnapshot, snapshots, source_cg, - source_vols) + return common.create_group_from_src( + context, group, volumes, group_snapshot, snapshots, + source_group, source_vols) finally: self._logout(common) @utils.trace - def delete_consistencygroup(self, context, group, volumes): + def delete_group(self, context, group, volumes): common = self._login() try: - return common.delete_consistencygroup(context, group, volumes) + return common.delete_group(context, group, volumes) finally: self._logout(common) @utils.trace - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): common = self._login() try: - return common.update_consistencygroup(context, group, add_volumes, - remove_volumes) + return common.update_group(context, group, add_volumes, + remove_volumes) finally: self._logout(common) @utils.trace - def create_cgsnapshot(self, context, cgsnapshot, snapshots): + def create_group_snapshot(self, context, group_snapshot, snapshots): common = self._login() try: - return common.create_cgsnapshot(context, cgsnapshot, snapshots) + return common.create_group_snapshot(context, group_snapshot, + snapshots) finally: self._logout(common) @utils.trace - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): + def delete_group_snapshot(self, context, group_snapshot, snapshots): common = self._login() try: - return common.delete_cgsnapshot(context, cgsnapshot, snapshots) + return common.delete_group_snapshot(context, group_snapshot, + snapshots) finally: self._logout(common) diff --git a/releasenotes/notes/HPE-3par-Generic-Volume-Group-e048002e1c3469a3.yaml b/releasenotes/notes/HPE-3par-Generic-Volume-Group-e048002e1c3469a3.yaml new file mode 100644 index 00000000000..c57b0f8ba1a --- /dev/null +++ b/releasenotes/notes/HPE-3par-Generic-Volume-Group-e048002e1c3469a3.yaml @@ -0,0 +1,4 @@ +--- +features: + - Added consistency group capability to generic volume groups in the + HPE 3PAR driver.