diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 7916792fa02..018d93694d3 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -675,6 +675,11 @@ class HPE3PARBaseDriver(test.TestCase): 'minor': 5, 'revision': 0} + wsapi_version_2023 = {'major': 1, + 'build': 100000050, + 'minor': 10, + 'revision': 0} + # Use this to point to latest version of wsapi wsapi_version_latest = wsapi_version_for_compression @@ -892,28 +897,41 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): mock_client.assert_has_calls(expected) self.assertEqual(self.STATUS_DONE, status) - def test_create_volume(self): + # (i) wsapi version is old/default + # (ii) wsapi version is 2023, then snapCPG isn't required + @ddt.data({'wsapi_version': None}, + {'wsapi_version': HPE3PARBaseDriver.wsapi_version_2023}) + @ddt.unpack + def test_create_volume(self, wsapi_version): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client - mock_client = self.setup_driver() + mock_client = self.setup_driver(wsapi_version=wsapi_version) + with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client') as mock_create_client: mock_create_client.return_value = mock_client - self.driver.create_volume(self.volume) + if not wsapi_version: + # (i) old/default + self.driver.create_volume(self.volume) + else: + # (ii) wsapi 2023 + common = self.driver._login() + common.create_volume(self.volume) comment = Comment({ "display_name": "Foo Volume", "type": "OpenStack", "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7", "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}) + optional = {'comment': comment, + 'tpvv': True, + 'tdvv': False} + if not wsapi_version: + optional['snapCPG'] = HPE3PAR_CPG_SNAP expected = [ mock.call.createVolume( self.VOLUME_3PAR_NAME, HPE3PAR_CPG, - 2048, { - 'comment': comment, - 'tpvv': True, - 'tdvv': False, - 'snapCPG': HPE3PAR_CPG_SNAP})] + 2048, optional)] mock_client.assert_has_calls(expected) @@ -1254,6 +1272,89 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): mirror_config=True)] mock_client.assert_has_calls(expected) + @mock.patch.object(volume_types, 'get_volume_type') + def test_create_volume_replicated_periodic_2023(self, _mock_volume_types): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + conf = self.setup_configuration() + self.replication_targets[0]['replication_mode'] = 'periodic' + conf.replication_device = self.replication_targets + mock_client = self.setup_driver(conf, None, self.wsapi_version_2023) + mock_client.getStorageSystemInfo.return_value = ( + {'id': self.CLIENT_ID}) + mock_client.getRemoteCopyGroup.side_effect = ( + hpeexceptions.HTTPNotFound) + mock_client.getCPG.return_value = {'domain': None} + mock_replicated_client = self.setup_driver(conf, None, + self.wsapi_version_2023) + mock_replicated_client.getStorageSystemInfo.return_value = ( + {'id': self.REPLICATION_CLIENT_ID}) + + _mock_volume_types.return_value = { + 'name': 'replicated', + 'extra_specs': { + 'replication_enabled': ' True', + 'replication:mode': 'periodic', + 'replication:sync_period': '900', + 'volume_type': self.volume_type_replicated}} + + with mock.patch.object( + hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client, \ + mock.patch.object( + hpecommon.HPE3PARCommon, + '_create_replication_client') as mock_replication_client: + mock_create_client.return_value = mock_client + mock_replication_client.return_value = mock_replicated_client + + common = self.driver._login() + return_model = common.create_volume(self.volume_replicated) + comment = Comment({ + "volume_type_name": "replicated", + "display_name": "Foo Volume", + "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7", + "volume_type_id": "be9181f1-4040-46f2-8298-e7532f2bf9db", + "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7", + "qos": {}, + "type": "OpenStack"}) + + backend_id = self.replication_targets[0]['backend_id'] + expected = [ + mock.call.createVolume( + self.VOLUME_3PAR_NAME, + HPE3PAR_CPG, + 2048, { + 'comment': comment, + 'tpvv': True, + 'tdvv': False}), + mock.call.getRemoteCopyGroup(self.RCG_3PAR_NAME), + mock.call.getCPG(HPE3PAR_CPG), + mock.call.createRemoteCopyGroup( + self.RCG_3PAR_NAME, + [{'userCPG': HPE3PAR_CPG_REMOTE, + 'targetName': backend_id, + 'mode': PERIODIC_MODE}], + {'localUserCPG': HPE3PAR_CPG}), + mock.call.addVolumeToRemoteCopyGroup( + self.RCG_3PAR_NAME, + self.VOLUME_3PAR_NAME, + [{'secVolumeName': self.VOLUME_3PAR_NAME, + 'targetName': backend_id}], + optional={'volumeAutoCreation': True}), + mock.call.modifyRemoteCopyGroup( + self.RCG_3PAR_NAME, + {'targets': [{'syncPeriod': SYNC_PERIOD, + 'targetName': backend_id}]}), + mock.call.startRemoteCopy(self.RCG_3PAR_NAME)] + mock_client.assert_has_calls( + self.get_id_login + + self.standard_logout + + self.standard_login + + expected) + self.assertEqual({'replication_status': 'enabled', + 'provider_location': self.CLIENT_ID}, + return_model) + @mock.patch.object(volume_types, 'get_volume_type') def test_create_volume_replicated_sync(self, _mock_volume_types): # setup_mock_client drive with default configuration @@ -4269,10 +4370,16 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): expected_retype_specs) self.assertEqual(expected_obj, obj) + # (i) wsapi version is old/default + # (ii) wsapi version is 2023, then snapCPG isn't required + @ddt.data({'wsapi_version': None}, + {'wsapi_version': HPE3PARBaseDriver.wsapi_version_2023}) + @ddt.unpack @mock.patch.object(volume_types, 'get_volume_type') - def test_manage_existing_with_no_snap_cpg(self, _mock_volume_types): + def test_manage_existing_with_no_snap_cpg(self, _mock_volume_types, + wsapi_version): _mock_volume_types.return_value = self.volume_type - mock_client = self.setup_driver() + mock_client = self.setup_driver(wsapi_version=wsapi_version) new_comment = Comment({ "display_name": "Foo Volume", @@ -4304,15 +4411,20 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): obj = self.driver.manage_existing(volume, existing_ref) + optional = {'newName': osv_matcher, + 'comment': new_comment} + + if not wsapi_version: + # (i) old/default + # manage_existing() should be setting + # blank snapCPG to the userCPG + optional['snapCPG'] = 'testUserCpg0' + expected_manage = [ mock.call.getVolume(existing_ref['source-name']), mock.call.modifyVolume( existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': new_comment, - # manage_existing() should be setting - # blank snapCPG to the userCPG - 'snapCPG': 'testUserCpg0'}) + optional) ] mock_client.assert_has_calls(self.standard_login + expected_manage) @@ -6076,16 +6188,21 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): mock_client.assert_has_calls(expected) + # (i) wsapi version is old/default + # (ii) wsapi version is 2023, then snapCPG isn't required + @ddt.data({'wsapi_version': None}, + {'wsapi_version': HPE3PARBaseDriver.wsapi_version_2023}) + @ddt.unpack @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.volume_utils.is_group_a_cg_snapshot_type') def test_create_group_from_src_group(self, cg_ss_enable, vol_ss_enable, - typ_info): + typ_info, wsapi_version): cg_ss_enable.return_value = True vol_ss_enable.return_value = True - mock_client = self.setup_driver() + mock_client = self.setup_driver(wsapi_version=wsapi_version) task_id = 1 mock_client.copyVolume.return_value = {'taskid': task_id} mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} @@ -6116,6 +6233,10 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): source_grp = self.fake_group_object( grp_id=self.SRC_CONSIS_GROUP_ID) + optional = {'online': True, + 'tpvv': mock.ANY, 'tdvv': mock.ANY} + if not wsapi_version: + optional['snapCPG'] = HPE3PAR_CPG expected = [ mock.call.getCPG(HPE3PAR_CPG), mock.call.createVolumeSet( @@ -6131,17 +6252,25 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): mock.ANY, self.VOLUME_NAME_3PAR, HPE3PAR_CPG, - {'snapCPG': HPE3PAR_CPG, 'online': True, - 'tpvv': mock.ANY, 'tdvv': mock.ANY}), + optional), mock.call.addVolumeToVolumeSet( self.CONSIS_GROUP_NAME, self.VOLUME_NAME_3PAR)] # Create a consistency group from a source consistency group. - self.driver.create_group_from_src( - context.get_admin_context(), group, - [volume], source_group=source_grp, - source_vols=[source_volume]) + if not wsapi_version: + # (i) old/default + self.driver.create_group_from_src( + context.get_admin_context(), group, + [volume], source_group=source_grp, + source_vols=[source_volume]) + else: + # (ii) wsapi 2023 + common = self.driver._login() + common.create_group_from_src( + context.get_admin_context(), group, + [volume], source_group=source_grp, + source_vols=[source_volume]) mock_client.assert_has_calls(expected) diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 94c346b3776..2d9534a16f6 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -81,6 +81,7 @@ FLASH_CACHE_API_VERSION = 30201200 COMPRESSION_API_VERSION = 30301215 SRSTATLD_API_VERSION = 30201200 REMOTE_COPY_API_VERSION = 30202290 +API_VERSION_2023 = 100000000 hpe3par_opts = [ cfg.StrOpt('hpe3par_api_url', @@ -302,11 +303,12 @@ class HPE3PARCommon(object): Bug #1819903 4.0.18 - During conversion of volume to base volume, error out if it has child snapshot(s). Bug #1994521 + 4.0.19 - Update code to work with new WSAPI (of 2023). Bug #2015746 """ - VERSION = "4.0.18" + VERSION = "4.0.19" stats = {} @@ -706,9 +708,12 @@ class HPE3PARCommon(object): compression = self.get_compression_policy( type_info['hpe3par_keys']) - optional = {'online': True, 'snapCPG': snapcpg, + optional = {'online': True, 'tpvv': tpvv, 'tdvv': tdvv} + if self.API_VERSION < API_VERSION_2023: + optional['snapCPG'] = snapcpg + if compression is not None: optional['compression'] = compression @@ -1006,7 +1011,7 @@ class HPE3PARCommon(object): 'comment': json.dumps(new_comment)} # Ensure that snapCPG is set - if 'snapCPG' not in vol: + if 'snapCPG' not in vol and self.API_VERSION < API_VERSION_2023: new_vals['snapCPG'] = vol['userCPG'] LOG.info("Virtual volume %(disp)s '%(new)s' snapCPG " "is empty so it will be set to: %(cpg)s", @@ -2395,9 +2400,14 @@ class HPE3PARCommon(object): comments['qos'] = qos extras = {'comment': json.dumps(comments), - 'snapCPG': snap_cpg, 'tpvv': tpvv} + LOG.debug("self.API_VERSION: %(version)s", + {'version': self.API_VERSION}) + + if self.API_VERSION < API_VERSION_2023: + extras['snapCPG'] = snap_cpg + # Only set the dedup option if the backend supports it. if self.API_VERSION >= DEDUP_API_VERSION: extras['tdvv'] = tdvv @@ -2468,7 +2478,7 @@ class HPE3PARCommon(object): {'src': src_name, 'dest': dest_name}) optional = {'tpvv': tpvv, 'online': True} - if snap_cpg is not None: + if snap_cpg is not None and self.API_VERSION < API_VERSION_2023: optional['snapCPG'] = snap_cpg if self.API_VERSION >= DEDUP_API_VERSION: @@ -4377,15 +4387,17 @@ class HPE3PARCommon(object): local_cpg) rcg_target = {'targetName': target['backend_id'], 'mode': replication_mode_num, - 'snapCPG': cpg, 'userCPG': cpg} + if self.API_VERSION < API_VERSION_2023: + rcg_target['snapCPG'] = cpg rcg_targets.append(rcg_target) sync_target = {'targetName': target['backend_id'], 'syncPeriod': replication_sync_period} sync_targets.append(sync_target) - optional = {'localSnapCPG': vol_settings['snap_cpg'], - 'localUserCPG': local_cpg} + optional = {'localUserCPG': local_cpg} + if self.API_VERSION < API_VERSION_2023: + optional['localSnapCPG'] = vol_settings['snap_cpg'] pool = volume_utils.extract_host(volume['host'], level='pool') domain = self.get_domain(pool) if domain: @@ -4400,6 +4412,8 @@ class HPE3PARCommon(object): LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) + LOG.debug("created rcg %(name)s", {'name': rcg_name}) + # Add volume to remote copy group. rcg_targets = [] for target in self._replication_targets: @@ -5319,7 +5333,11 @@ class ModifyVolumeTask(flow_utils.CinderTask): comment_dict = self._get_new_comment( old_comment, new_vvs, new_qos, new_type_name, new_type_id) - if new_snap_cpg != old_snap_cpg: + LOG.debug("API_VERSION: %(ver_1)s, API_VERSION_2023: %(ver_2)s", + {'ver_1': common.API_VERSION, + 'ver_2': API_VERSION_2023}) + if (new_snap_cpg != old_snap_cpg and + common.API_VERSION < API_VERSION_2023): # Modify the snap_cpg. This will fail with snapshots. LOG.info("Modifying %(volume_name)s snap_cpg from " "%(old_snap_cpg)s to %(new_snap_cpg)s.", diff --git a/releasenotes/notes/hpe-3par-code-changes-for-new-wsapi-25865a65a428ce46.yaml b/releasenotes/notes/hpe-3par-code-changes-for-new-wsapi-25865a65a428ce46.yaml new file mode 100644 index 00000000000..82cbc97d930 --- /dev/null +++ b/releasenotes/notes/hpe-3par-code-changes-for-new-wsapi-25865a65a428ce46.yaml @@ -0,0 +1,4 @@ +fixes: + - | + HPE 3PAR driver `Bug #2015746 `_: + Fixed: minor code changes to work with new wsapi.