From 8bb216dc168769b399818a7bf8dff16afcfccea3 Mon Sep 17 00:00:00 2001 From: Simon Dodsley Date: Mon, 8 Nov 2021 16:14:41 -0500 Subject: [PATCH] [Pure Storage] Remove all REST version checks Enforce a minimum FlashArray Purity version that will make all REST version checking renudant as the enforced minimum version supports all the features that are currently version-gated. Remove any code that checks for different REST responses based on REST response due to old REST code. Enforced minimum Purity version matches the minimum supported Purity version for FlashArray as of December 2021. No change required to the purestorage pypi package used by this driver. Change-Id: I9fd03186b77ca442b62b1bfa28eed9026d91949b --- cinder/tests/unit/volume/drivers/test_pure.py | 170 ++------------ cinder/volume/drivers/pure.py | 216 +++++------------- ...hange-purity-support-b94057d3842a80a8.yaml | 16 ++ 3 files changed, 85 insertions(+), 317 deletions(-) create mode 100644 releasenotes/notes/pure-storage-change-purity-support-b94057d3842a80a8.yaml diff --git a/cinder/tests/unit/volume/drivers/test_pure.py b/cinder/tests/unit/volume/drivers/test_pure.py index f516c3de9e0..33b2485519e 100644 --- a/cinder/tests/unit/volume/drivers/test_pure.py +++ b/cinder/tests/unit/volume/drivers/test_pure.py @@ -582,7 +582,6 @@ class PureDriverTestCase(test.TestCase): self.async_array2.get.return_value = GET_ARRAY_SECONDARY self.async_array2.replication_type = 'async' self.purestorage_module = pure.purestorage - self.purestorage_module.VERSION = '1.4.0' self.purestorage_module.PureHTTPError = FakePureStorageHTTPError def fake_get_array(self, *args, **kwargs): @@ -627,15 +626,7 @@ class PureBaseSharedDriverTestCase(PureDriverTestCase): self.driver._array = self.array self.driver._replication_pod_name = 'cinder-pod' self.driver._replication_pg_name = 'cinder-group' - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.purestorage_module.FlashArray.side_effect = None - self.async_array2._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] def new_fake_vol(self, set_provider_id=True, fake_context=None, spec=None, type_extra_specs=None, type_qos_specs_id=None, @@ -808,10 +799,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): ] mock_target = mock.MagicMock() mock_target.get.return_value = GET_ARRAY_PRIMARY - mock_target._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.purestorage_module.FlashArray.return_value = mock_target self.driver.parse_replication_configs() @@ -848,10 +835,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): ] mock_target = mock.MagicMock() mock_target.get.return_value = GET_ARRAY_PRIMARY - mock_target._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.purestorage_module.FlashArray.return_value = mock_target self.driver.parse_replication_configs() @@ -912,15 +895,7 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): ] mock_sync_target = mock.MagicMock() mock_sync_target.get.return_value = GET_ARRAY_SECONDARY - mock_sync_target._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.array.get.return_value = GET_ARRAY_PRIMARY - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.purestorage_module.FlashArray.side_effect = [self.array, mock_sync_target] self.driver.do_setup(None) @@ -1287,10 +1262,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): vol, vol_name = self.new_fake_vol(type_extra_specs=type_spec) self.array.list_volume_private_connections.return_value = [] # Set the array to be in a sync-rep enabled version - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.driver.delete_volume(vol) @@ -1330,14 +1301,9 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): ] # Set the array to be in a sync-rep enabled version - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.driver.delete_volume(vol) expected = [ - mock.call._list_available_rest_versions(), mock.call.list_volume_private_connections(vol_name, remote=True), mock.call.disconnect_host(host_name_a, vol_name), mock.call.list_host_connections(host_name_a, private=True), @@ -2203,14 +2169,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): self.driver.manage_existing_snapshot, snap, snap_ref) - def test_manage_existing_snapshot_bad_api_version(self): - self.array._list_available_rest_versions.return_value = ['1.0', '1.1', - '1.2'] - snap, _ = self.new_fake_snap() - self.assertRaises(pure.PureDriverException, - self.driver.manage_existing_snapshot, - snap, {'name': PURE_SNAPSHOT['name']}) - def test_manage_existing_snapshot_get_size(self): ref_name = PURE_SNAPSHOT['name'] snap_ref = {'name': ref_name} @@ -2262,14 +2220,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): self.driver.manage_existing_snapshot_get_size, snap, {'name': 'non-existing-volume.snap1'}) - def test_manage_existing_snapshot_get_size_bad_api_version(self): - snap, _ = self.new_fake_snap() - self.array._list_available_rest_versions.return_value = ['1.0', '1.1', - '1.2'] - self.assertRaises(pure.PureDriverException, - self.driver.manage_existing_snapshot_get_size, - snap, {'name': PURE_SNAPSHOT['name']}) - @ddt.data( # 96 chars, will exceed allowable length 'volume-1e5177e7-95e5-4a0f-b170-e45f4b469f6a-cinder.' @@ -2312,14 +2262,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): self.array.rename_volume.assert_called_with(snap_name, unmanaged_snap_name) - def test_unmanage_snapshot_bad_api_version(self): - snap, _ = self.new_fake_snap() - self.array._list_available_rest_versions.return_value = ['1.0', '1.1', - '1.2'] - self.assertRaises(pure.PureDriverException, - self.driver.unmanage_snapshot, - snap) - def _test_get_manageable_things(self, pure_objs=MANAGEABLE_PURE_VOLS, expected_refs=MANAGEABLE_PURE_VOL_REFS, @@ -2922,12 +2864,11 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): self.async_array2.backend_id = secondary_device_id self.driver._replication_target_arrays = [self.async_array2] - array2_v1_3 = mock.Mock() - array2_v1_3.backend_id = secondary_device_id - array2_v1_3.array_name = GET_ARRAY_SECONDARY['array_name'] - array2_v1_3.array_id = GET_ARRAY_SECONDARY['id'] - array2_v1_3.version = '1.3' - mock_get_array.return_value = array2_v1_3 + array2 = mock.Mock() + array2.backend_id = secondary_device_id + array2.array_name = GET_ARRAY_SECONDARY['array_name'] + array2.array_id = GET_ARRAY_SECONDARY['id'] + mock_get_array.return_value = array2 target_array = self.async_array2 target_array.copy_volume = mock.Mock() @@ -2937,7 +2878,7 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): REPLICATED_PGSNAPS[1] ) - array2_v1_3.get_volume.return_value = REPLICATED_VOLUME_SNAPS + array2.get_volume.return_value = REPLICATED_VOLUME_SNAPS context = mock.MagicMock() new_active_id, volume_updates, __ = self.driver.failover_host( @@ -3070,17 +3011,16 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): REPLICATED_PGSNAPS[1] ) - array2_v1_3 = mock.Mock() - array2_v1_3.array_name = GET_ARRAY_SECONDARY['array_name'] - array2_v1_3.array_id = GET_ARRAY_SECONDARY['id'] - array2_v1_3.version = '1.3' - mock_get_array.return_value = array2_v1_3 + array2 = mock.Mock() + array2.array_name = GET_ARRAY_SECONDARY['array_name'] + array2.array_id = GET_ARRAY_SECONDARY['id'] + mock_get_array.return_value = array2 - array2_v1_3.get_volume.return_value = REPLICATED_VOLUME_SNAPS + array2.get_volume.return_value = REPLICATED_VOLUME_SNAPS self.assert_error_propagates( [mock_find_failover_target, mock_get_array, - array2_v1_3.get_volume, + array2.get_volume, self.async_array2.copy_volume], self.driver.failover_host, mock.Mock(), REPLICATED_VOLUME_OBJS, None @@ -3129,43 +3069,7 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): verify_https=True, ssl_cert=cert_path, user_agent=self.driver._user_agent, - ) - - def test_get_flasharray_with_request_kwargs_success(self): - san_ip = '1.2.3.4' - api_token = 'abcdef' - self.purestorage_module.FlashArray.return_value = mock.MagicMock() - self.purestorage_module.VERSION = "1.17.0" - - self.driver._get_flasharray(san_ip, - api_token, - request_kwargs={"some": "arg"}) - self.purestorage_module.FlashArray.assert_called_with( - san_ip, - api_token=api_token, - rest_version=None, - verify_https=None, - ssl_cert=None, - user_agent=self.driver._user_agent, - request_kwargs={"some": "arg"} - ) - - def test_get_flasharray_with_request_kwargs_version_too_old(self): - san_ip = '1.2.3.4' - api_token = 'abcdef' - self.purestorage_module.FlashArray.return_value = mock.MagicMock() - self.purestorage_module.VERSION = "1.10.0" - - self.driver._get_flasharray(san_ip, - api_token, - request_kwargs={"some": "arg"}) - self.purestorage_module.FlashArray.assert_called_with( - san_ip, - api_token=api_token, - rest_version=None, - verify_https=None, - ssl_cert=None, - user_agent=self.driver._user_agent + request_kwargs=None, ) def test_get_wwn(self): @@ -3244,10 +3148,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): type_qos_specs_id=qos.id) mock_get_volume_type.return_value = vol.volume_type - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] mock_get_qos_specs.return_value = qos mock_get_repl_type.return_value = None @@ -3277,10 +3177,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): type_qos_specs_id=qos.id) mock_get_volume_type.return_value = vol.volume_type - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] mock_get_qos_specs.return_value = qos mock_get_repl_type.return_value = None @@ -3310,10 +3206,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): mock_get_volume_type.return_value = vol.volume_type mock_get_qos_specs.return_value = qos self.array.list_volume_private_connections.return_value = [] - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] self.driver.manage_existing(vol, volume_ref) self.array.list_volume_private_connections.assert_called_with(ref_name) @@ -3330,10 +3222,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): new_type = fake_volume.fake_volume_type_obj(mock_context) new_type.qos_specs_id = qos.id - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] get_voltype = "cinder.objects.volume_type.VolumeType.get_by_name_or_id" with mock.patch(get_voltype) as mock_get_vol_type: mock_get_vol_type.return_value = new_type @@ -3357,10 +3245,6 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): vol, vol_name = self.new_fake_vol() new_type = fake_volume.fake_volume_type_obj(mock_context) - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] get_voltype = "cinder.objects.volume_type.VolumeType.get_by_name_or_id" with mock.patch(get_voltype) as mock_get_vol_type: mock_get_vol_type.return_value = new_type @@ -3739,20 +3623,6 @@ class PureISCSIDriverTestCase(PureBaseSharedDriverTestCase): self.mock_config.use_chap_auth = False self.mock_config.safe_get.return_value = 'oracle-vm-server' - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11'] - # Branch where we fail due to invalid version for setting personality - self.assertRaises(pure.PureDriverException, self.driver._connect, - self.array, vol_name, ISCSI_CONNECTOR, None, None) - self.assertFalse(self.array.create_host.called) - self.assertFalse(self.array.set_host.called) - - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] - # Branch where personality is set self.driver._connect(self.array, vol_name, ISCSI_CONNECTOR, None, None) @@ -4009,20 +3879,6 @@ class PureFCDriverTestCase(PureBaseSharedDriverTestCase): self.driver._connect, self.array, vol_name, FC_CONNECTOR) self.mock_config.safe_get.return_value = 'oracle-vm-server' - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11'] - - # Branch where we fail due to invalid version for setting personality - self.assertRaises(pure.PureDriverException, self.driver._connect, - self.array, vol_name, FC_CONNECTOR) - self.assertTrue(self.array.create_host.called) - self.assertFalse(self.array.set_host.called) - - self.array._list_available_rest_versions.return_value = [ - '1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', '1.8', - '1.9', '1.10', '1.11', '1.12', '1.13', '1.14', '1.15', '1.16', - '1.17', '1.18', '1.19'] # Branch where personality is set self.driver._connect(self.array, vol_name, FC_CONNECTOR) diff --git a/cinder/volume/drivers/pure.py b/cinder/volume/drivers/pure.py index 3b7ed323355..db292632289 100644 --- a/cinder/volume/drivers/pure.py +++ b/cinder/volume/drivers/pure.py @@ -137,11 +137,6 @@ EXTRA_SPECS_REPL_TYPE = "replication_type" MAX_VOL_LENGTH = 63 MAX_SNAP_LENGTH = 96 UNMANAGED_SUFFIX = '-unmanaged' -QOS_REQUIRED_API_VERSION = '1.17' -SYNC_REPLICATION_REQUIRED_API_VERSION = '1.13' -ASYNC_REPLICATION_REQUIRED_API_VERSION = '1.3' -MANAGE_SNAP_REQUIRED_API_VERSION = '1.4' -PERSONALITY_REQUIRED_API_VERSION = '1.14' REPL_SETTINGS_PROPAGATE_RETRY_INTERVAL = 5 # 5 seconds REPL_SETTINGS_PROPAGATE_MAX_RETRIES = 36 # 36 * 5 = 180 seconds @@ -279,24 +274,6 @@ class PureBaseVolumeDriver(san.SanDriver): ssl_cert_path=ssl_cert_path ) - api_versions = target_array._list_available_rest_versions() - - if repl_type == REPLICATION_TYPE_ASYNC: - req_api_version = ASYNC_REPLICATION_REQUIRED_API_VERSION - elif repl_type == REPLICATION_TYPE_SYNC: - req_api_version = SYNC_REPLICATION_REQUIRED_API_VERSION - else: - msg = _('Invalid replication type specified:') % repl_type - raise PureDriverException(reason=msg) - - if req_api_version not in api_versions: - msg = _('Unable to do replication with Purity REST ' - 'API version, requires minimum of ' - '%(required_version)s.') % { - 'required_version': req_api_version - } - raise PureDriverException(reason=msg) - target_array_info = target_array.get() target_array.array_name = target_array_info["array_name"] target_array.array_id = target_array_info["id"] @@ -378,6 +355,13 @@ class PureBaseVolumeDriver(san.SanDriver): ) array_info = self._array.get() + if version.LooseVersion(array_info["version"]) < version.LooseVersion( + '5.3.0' + ): + msg = _("FlashArray Purity version less than 5.3.0 unsupported." + " Please upgrade your backend to a supported version.") + raise PureDriverException(msg) + self._array.array_name = array_info["array_name"] self._array.array_id = array_info["id"] self._array.replication_type = None @@ -484,11 +468,9 @@ class PureBaseVolumeDriver(san.SanDriver): ctxt = context.get_admin_context() type_id = volume.get('volume_type_id') current_array = self._get_current_array() - rest_versions = current_array._list_available_rest_versions() if type_id is not None: volume_type = volume_types.get_volume_type(ctxt, type_id) - if QOS_REQUIRED_API_VERSION in rest_versions: - qos = self._get_qos_settings(volume_type) + qos = self._get_qos_settings(volume_type) if qos is not None: self.create_with_qos(current_array, vol_name, vol_size, qos) else: @@ -509,11 +491,9 @@ class PureBaseVolumeDriver(san.SanDriver): current_array = self._get_current_array() ctxt = context.get_admin_context() type_id = volume.get('volume_type_id') - rest_versions = current_array._list_available_rest_versions() if type_id is not None: volume_type = volume_types.get_volume_type(ctxt, type_id) - if QOS_REQUIRED_API_VERSION in rest_versions: - qos = self._get_qos_settings(volume_type) + qos = self._get_qos_settings(volume_type) current_array.copy_volume(snap_name, vol_name) self._extend_if_needed(current_array, @@ -606,16 +586,11 @@ class PureBaseVolumeDriver(san.SanDriver): """Disconnect all hosts and delete the volume""" vol_name = self._get_vol_name(volume) current_array = self._get_current_array() - rest_versions = current_array._list_available_rest_versions() try: # Do a pass over remaining connections on the current array, if # we can try and remove any remote connections too. - if SYNC_REPLICATION_REQUIRED_API_VERSION in rest_versions: - hosts = current_array.list_volume_private_connections( - vol_name, remote=True) - else: - hosts = current_array.list_volume_private_connections( - vol_name) + hosts = current_array.list_volume_private_connections( + vol_name, remote=True) for host_info in hosts: host_name = host_info["host"] self._disconnect_host(current_array, host_name, vol_name) @@ -710,18 +685,13 @@ class PureBaseVolumeDriver(san.SanDriver): Returns True if it was the hosts last connection. """ vol_name = self._get_vol_name(volume) - rest_versions = array._list_available_rest_versions() if connector is None: # If no connector was provided it is a force-detach, remove all # host connections for the volume LOG.warning("Removing ALL host connections for volume %s", vol_name) - if SYNC_REPLICATION_REQUIRED_API_VERSION in rest_versions: - # Remote connections are only allowed in newer API versions - connections = array.list_volume_private_connections( - vol_name, remote=True) - else: - connections = array.list_volume_private_connections(vol_name) + connections = array.list_volume_private_connections( + vol_name, remote=True) for connection in connections: self._disconnect_host(array, connection['host'], vol_name) @@ -1332,15 +1302,13 @@ class PureBaseVolumeDriver(san.SanDriver): # Check if the volume_type has QoS settings and if so # apply them to the newly managed volume qos = None - rest_versions = current_array._list_available_rest_versions() - if QOS_REQUIRED_API_VERSION in rest_versions: - qos = self._get_qos_settings(volume.volume_type) - if qos is not None: - self.set_qos(current_array, new_vol_name, qos) - else: - current_array.set_volume(new_vol_name, - iops_limit='', - bandwidth_limit='') + qos = self._get_qos_settings(volume.volume_type) + if qos is not None: + self.set_qos(current_array, new_vol_name, qos) + else: + current_array.set_volume(new_vol_name, + iops_limit='', + bandwidth_limit='') volume.provider_id = new_vol_name async_enabled = self._enable_async_replication_if_needed(current_array, volume) @@ -1400,24 +1368,12 @@ class PureBaseVolumeDriver(san.SanDriver): {"ref_name": vol_name, "new_name": unmanaged_vol_name}) self._rename_volume_object(vol_name, unmanaged_vol_name) - def _verify_manage_snap_api_requirements(self): - current_array = self._get_current_array() - rest_versions = current_array._list_available_rest_versions() - if MANAGE_SNAP_REQUIRED_API_VERSION not in rest_versions: - msg = _('Unable to do manage snapshot operations with Purity REST ' - 'API version, requires ' - '%(required_version)s.') % { - 'required_version': MANAGE_SNAP_REQUIRED_API_VERSION - } - raise PureDriverException(reason=msg) - def manage_existing_snapshot(self, snapshot, existing_ref): """Brings an existing backend storage object under Cinder management. We expect a snapshot name in the existing_ref that matches one in Purity. """ - self._verify_manage_snap_api_requirements() self._validate_manage_existing_ref(existing_ref, is_snap=True) ref_snap_name = existing_ref['name'] new_snap_name = self._get_snap_name(snapshot) @@ -1435,7 +1391,6 @@ class PureBaseVolumeDriver(san.SanDriver): We expect a snapshot name in the existing_ref that matches one in Purity. """ - self._verify_manage_snap_api_requirements() snap_info = self._validate_manage_existing_ref(existing_ref, is_snap=True) size = self._round_bytes_to_gib(snap_info['size']) @@ -1449,7 +1404,6 @@ class PureBaseVolumeDriver(san.SanDriver): We expect a snapshot name in the existing_ref that matches one in Purity. """ - self._verify_manage_snap_api_requirements() snap_name = self._get_snap_name(snapshot) if len(snap_name + UNMANAGED_SUFFIX) > MAX_SNAP_LENGTH: unmanaged_snap_name = snap_name[:-len(UNMANAGED_SUFFIX)] + \ @@ -1573,28 +1527,13 @@ class PureBaseVolumeDriver(san.SanDriver): verify_https=None, ssl_cert_path=None, request_kwargs=None): - if (version.LooseVersion(purestorage.VERSION) < - version.LooseVersion('1.17.0')): - if request_kwargs is not None: - LOG.warning("Unable to specify request_kwargs='%s' on " - "purestorage.FlashArray using 'purestorage' " - "python module <1.17.0. Current version: %s", - request_kwargs, - purestorage.VERSION) - array = purestorage.FlashArray(san_ip, - api_token=api_token, - rest_version=rest_version, - verify_https=verify_https, - ssl_cert=ssl_cert_path, - user_agent=self._user_agent) - else: - array = purestorage.FlashArray(san_ip, - api_token=api_token, - rest_version=rest_version, - verify_https=verify_https, - ssl_cert=ssl_cert_path, - user_agent=self._user_agent, - request_kwargs=request_kwargs) + array = purestorage.FlashArray(san_ip, + api_token=api_token, + rest_version=rest_version, + verify_https=verify_https, + ssl_cert=ssl_cert_path, + user_agent=self._user_agent, + request_kwargs=request_kwargs) array_info = array.get() array.array_name = array_info["array_name"] array.array_id = array_info["id"] @@ -1928,16 +1867,14 @@ class PureBaseVolumeDriver(san.SanDriver): # make sure the volume gets the correct new QoS settings. # This could mean removing existing QoS settings. current_array = self._get_current_array() - rest_versions = current_array._list_available_rest_versions() - if QOS_REQUIRED_API_VERSION in rest_versions: - qos = self._get_qos_settings(new_type) - vol_name = self._generate_purity_vol_name(volume) - if qos is not None: - self.set_qos(current_array, vol_name, qos) - else: - current_array.set_volume(vol_name, - iops_limit='', - bandwidth_limit='') + qos = self._get_qos_settings(new_type) + vol_name = self._generate_purity_vol_name(volume) + if qos is not None: + self.set_qos(current_array, vol_name, qos) + else: + current_array.set_volume(vol_name, + iops_limit='', + bandwidth_limit='') return True, model_update @@ -2078,23 +2015,6 @@ class PureBaseVolumeDriver(san.SanDriver): return secondary_array.backend_id, model_updates, [] - @pure_driver_debug_trace - def get_check_personality(self, array): - personality = self.configuration.safe_get('pure_host_personality') - rest_versions = array._list_available_rest_versions() - if personality: - if PERSONALITY_REQUIRED_API_VERSION not in rest_versions: - # Continuing here would mean creating a host not according - # to specificiations, possibly leading to unexpected - # behavior later on. - msg = _('Unable to set host personality with Purity REST ' - 'API version, requires ' - '%(required_version)s.') % { - 'required_version': PERSONALITY_REQUIRED_API_VERSION - } - raise PureDriverException(reason=msg) - return personality - @pure_driver_debug_trace def set_personality(self, array, host_name, personality): try: @@ -2446,28 +2366,12 @@ class PureBaseVolumeDriver(san.SanDriver): return secondary_array def _async_failover_host(self, volumes, secondary_array, pg_snap): - # NOTE(patrickeast): This currently requires a call with REST API 1.3. - # If we need to, create a temporary FlashArray for this operation. - api_version = secondary_array.get_rest_version() - LOG.debug("Current REST API for array id %(id)s is %(api_version)s", - {"id": secondary_array.array_id, "api_version": api_version}) - if api_version != '1.3': - # Try to copy the flasharray as close as we can.. - if hasattr(secondary_array, '_request_kwargs'): - target_array = self._get_flasharray( - secondary_array._target, - api_token=secondary_array._api_token, - rest_version='1.3', - request_kwargs=secondary_array._request_kwargs, - ) - else: - target_array = self._get_flasharray( - secondary_array._target, - api_token=secondary_array._api_token, - rest_version='1.3', - ) - else: - target_array = secondary_array + # Try to copy the flasharray as close as we can. + target_array = self._get_flasharray( + secondary_array._target, + api_token=secondary_array._api_token, + request_kwargs=secondary_array._request_kwargs, + ) volume_snaps = target_array.get_volume(pg_snap['name'], snap=True, @@ -2579,11 +2483,7 @@ class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): def _get_host(self, array, connector, remote=False): """Return dict describing existing Purity host object or None.""" - rest_versions = array._list_available_rest_versions() - if remote and SYNC_REPLICATION_REQUIRED_API_VERSION in rest_versions: - hosts = array.list_hosts(remote=True) - else: - hosts = array.list_hosts() + hosts = array.list_hosts(remote=remote) matching_hosts = [] for host in hosts: if connector["initiator"] in host["iqn"]: @@ -2746,7 +2646,7 @@ class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): reason=_("Unable to re-use host with unknown CHAP " "credentials configured.")) else: - personality = self.get_check_personality(array) + personality = self.configuration.safe_get('pure_host_personality') host_name = self._generate_purity_host_name(connector["host"]) LOG.info("Creating host object %(host_name)r with IQN:" " %(iqn)s.", {"host_name": host_name, "iqn": iqn}) @@ -2806,11 +2706,7 @@ class PureFCDriver(PureBaseVolumeDriver, driver.FibreChannelDriver): def _get_host(self, array, connector, remote=False): """Return dict describing existing Purity host object or None.""" - rest_versions = array._list_available_rest_versions() - if remote and SYNC_REPLICATION_REQUIRED_API_VERSION in rest_versions: - hosts = array.list_hosts(remote=True) - else: - hosts = array.list_hosts() + hosts = array.list_hosts(remote=remote) matching_hosts = [] for host in hosts: for wwn in connector["wwpns"]: @@ -2821,18 +2717,18 @@ class PureFCDriver(PureBaseVolumeDriver, driver.FibreChannelDriver): @staticmethod def _get_array_wwns(array): - """Return list of wwns from the array""" + """Return list of wwns from the array + + Ensure that only true scsi FC ports are selected + and not any that are enabled for NVMe-based FC with + an associated NQN. + """ ports = array.list_ports() - try: - valid_ports = [ - port["wwn"] - for port in ports - if port["wwn"] and not port["nqn"] - ] - except KeyError: # Older array code versions will not return nqn - valid_ports = [ - port["wwn"] for port in ports if port["wwn"] - ] + valid_ports = [ + port["wwn"] + for port in ports + if port["wwn"] and not port["nqn"] + ] return valid_ports @pure_driver_debug_trace @@ -2888,7 +2784,7 @@ class PureFCDriver(PureBaseVolumeDriver, driver.FibreChannelDriver): LOG.info("Re-using existing purity host %(host_name)r", {"host_name": host_name}) else: - personality = self.get_check_personality(array) + personality = self.configuration.safe_get('pure_host_personality') host_name = self._generate_purity_host_name(connector["host"]) LOG.info("Creating host object %(host_name)r with WWN:" " %(wwn)s.", {"host_name": host_name, "wwn": wwns}) diff --git a/releasenotes/notes/pure-storage-change-purity-support-b94057d3842a80a8.yaml b/releasenotes/notes/pure-storage-change-purity-support-b94057d3842a80a8.yaml new file mode 100644 index 00000000000..b86c136cf31 --- /dev/null +++ b/releasenotes/notes/pure-storage-change-purity-support-b94057d3842a80a8.yaml @@ -0,0 +1,16 @@ +--- +upgrade: + - | + Pure Storage: Minimum supported FlashArray Purity//FA is changed to 5.3.0. + All FlashArray backends must be at at least this minimum version or the + driver will not initialize. +fixes: + - | + Pure Storage: Remove all API version checks in driver as the new minimum + FlashArray Purity//FA version supports all previously version-gated + features and functionality support. +other: + - | + Pure Storage: FlashArray minimum Purity//FA version is increased to 5.3.0. + All FlashArray backends must be at at least this minimum version or the + driver will not initialize.