diff --git a/cinder/tests/unit/volume/drivers/test_pure.py b/cinder/tests/unit/volume/drivers/test_pure.py index ff329a47809..db487f340a7 100644 --- a/cinder/tests/unit/volume/drivers/test_pure.py +++ b/cinder/tests/unit/volume/drivers/test_pure.py @@ -4934,14 +4934,54 @@ class PureNVMEDriverTestCase(PureBaseSharedDriverTestCase): self.driver.initialize_connection(vol, multipath_connector) def test_get_target_nvme_ports(self): - self.array.list_ports.return_value = NVME_PORTS + ports = [{'name': 'CT0.ETH4', + 'wwn': None, + 'iqn': None, + 'nqn': TARGET_NQN}, + {'name': 'CT0.ETH5', + 'wwn': None, + 'iqn': TARGET_IQN, + 'nqn': None}, + {'name': 'CT0.ETH20', + 'wwn': None, + 'iqn': None, + 'nqn': TARGET_NQN}, + {'name': 'CT0.FC4', + 'wwn': TARGET_WWN, + 'iqn': None, + 'nqn': TARGET_NQN}] + interfaces = [ + {'name': 'ct0.eth4', 'services': ['nvme-tcp']}, + {'name': 'ct0.eth5', 'services': ['iscsi']}, + {'name': 'ct0.eth20', 'services': ['nvme-roce']}, + {'name': 'ct0.fc4', 'services': ['nvme-fc']} + ] + # Test for the nvme-tcp port + self.driver.configuration.pure_nvme_transport = "tcp" + self.array.get_network_interface.return_value = interfaces[0] + self.array.list_ports.return_value = [ports[0]] ret = self.driver._get_target_nvme_ports(self.array) - self.assertEqual(NVME_PORTS, ret) - - def test_get_target_nvme_ports_with_nvme_and_fc(self): - self.array.list_ports.return_value = NVME_PORTS_WITH + self.assertEqual([ports[0]], ret) + # Test for failure if no NVMe ports + self.array.get_network_interface.return_value = interfaces[1] + self.array.list_ports.return_value = [ports[1]] + self.assertRaises( + pure.PureDriverException, + self.driver._get_target_nvme_ports, + self.array, + ) + # Test for the nvme-roce port + self.driver.configuration.pure_nvme_transport = "roce" + self.array.get_network_interface.return_value = interfaces[2] + self.array.list_ports.return_value = [ports[2]] ret = self.driver._get_target_nvme_ports(self.array) - self.assertEqual(NVME_PORTS, ret) + self.assertEqual([ports[2]], ret) + # Test for empty dict if only nvme-fc port + self.driver.configuration.pure_nvme_transport = "roce" + self.array.get_network_interface.return_value = interfaces[3] + self.array.list_ports.return_value = [ports[3]] + ret = self.driver._get_target_nvme_ports(self.array) + self.assertEqual([], ret) def test_get_target_nvme_ports_with_no_ports(self): # Should raise an exception if there are no ports diff --git a/cinder/volume/drivers/pure.py b/cinder/volume/drivers/pure.py index bf38d88f9ea..30004cae068 100644 --- a/cinder/volume/drivers/pure.py +++ b/cinder/volume/drivers/pure.py @@ -111,9 +111,8 @@ PURE_OPTS = [ "IPv4 and IPv6 subnets. This parameter supersedes " "pure_nvme_cidr."), cfg.StrOpt("pure_nvme_transport", default="roce", - choices=['roce'], - help="The NVMe transport layer to be used by the NVMe driver. " - "This only supports RoCE at this time."), + choices=['roce', 'tcp'], + help="The NVMe transport layer to be used by the NVMe driver."), cfg.BoolOpt("pure_eradicate_on_delete", default=False, help="When enabled, all Pure volumes, snapshots, and " @@ -406,6 +405,13 @@ class PureBaseVolumeDriver(san.SanDriver): "unsupported. Please upgrade your backend to " "a supported version.") raise PureDriverException(msg) + if version.parse(array_info["version"]) < version.parse( + '6.4.2' + ) and self._storage_protocol == constants.NVMEOF_TCP: + msg = _("FlashArray Purity version less than 6.4.2 " + "unsupported for NVMe-TCP. 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"] @@ -3217,6 +3223,9 @@ class PureNVMEDriver(PureBaseVolumeDriver, driver.BaseVD): if self.configuration.pure_nvme_transport == "roce": self.transport_type = "rdma" self._storage_protocol = constants.NVMEOF_ROCE + else: + self.transport_type = "tcp" + self._storage_protocol = constants.NVMEOF_TCP def _get_nguid(self, pure_vol_name): """Return the NGUID based on the volume's serial number @@ -3331,14 +3340,24 @@ class PureNVMEDriver(PureBaseVolumeDriver, driver.BaseVD): return props def _get_target_nvme_ports(self, array): - """Return list of nvme-enabled port descriptions.""" + """Return list of correct nvme-enabled port descriptions.""" ports = array.list_ports() + valid_nvme_ports = [] nvme_ports = [port for port in ports if port["nqn"]] + for port in range(0, len(nvme_ports)): + if "ETH" in nvme_ports[port]["name"]: + port_detail = array.get_network_interface( + interface=nvme_ports[port]["name"] + ) + if port_detail["services"][0] == "nvme-" + \ + self.configuration.pure_nvme_transport: + valid_nvme_ports.append(nvme_ports[port]) if not nvme_ports: raise PureDriverException( - reason=_("No nvme-enabled ports on target array.") + reason=_("No %(type)s enabled ports on target array.") % + {"type": self._storage_protocol} ) - return nvme_ports + return valid_nvme_ports @utils.retry(PureRetryableException, retries=HOST_CREATE_MAX_RETRIES) def _connect(self, array, vol_name, connector): diff --git a/doc/source/configuration/block-storage/drivers/pure-storage-driver.rst b/doc/source/configuration/block-storage/drivers/pure-storage-driver.rst index 1a4a46a5d5b..459747a6952 100644 --- a/doc/source/configuration/block-storage/drivers/pure-storage-driver.rst +++ b/doc/source/configuration/block-storage/drivers/pure-storage-driver.rst @@ -8,12 +8,12 @@ operations. Support for iSCSI storage protocol is available with the PureISCSIDriver Volume Driver class, Fibre Channel with the PureFCDriver and -NVMe-ROCE with the PureNVMEDriver. +NVMe-ROCE or NVMe-TCP with the PureNVMEDriver. -iSCSI and Fibre Channel drivers are compatible with Purity FlashArrays -that support the REST API version 1.6 and higher (Purity 4.7.0 and newer). -The NVMe driver is compatible with Purity FlashArrays +iSCSI, Fibre Channel and NVMe-RoCE drivers are compatible with FlashArrays that support the REST API version 1.16 and higher (Purity 5.2.0 and newer). +The NVMe-TCP driver is compatible with FlashArrays +that are running Purity 6.4.2 and higher. Some features may require newer versions of Purity. Limitations and known issues @@ -161,7 +161,7 @@ Pure Storage FlashArray as back-end storage. NVME connectivity. If using the NVME driver, specify the ``pure_nvme_transport`` value. - Currently only ``roce`` is supported. + Supported values are ``roce`` or ``tcp``. IP_PURE_MGMT The IP address of the Pure Storage array's management interface or a diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index 7b4b3c38def..dc4da1bcbff 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -175,7 +175,7 @@ title=Open-E JovianDSS Storage Driver (iSCSI) title=ProphetStor Flexvisor Driver (iSCSI, NFS) [driver.pure] -title=Pure Storage Driver (iSCSI, FC, NVMe-RoCE) +title=Pure Storage Driver (iSCSI, FC, NVMe-RoCE, NVMe-TCP) [driver.qnap] title=QNAP Storage Driver (iSCSI) diff --git a/releasenotes/notes/pure_nvme_tcp-a00efa8966a74f77.yaml b/releasenotes/notes/pure_nvme_tcp-a00efa8966a74f77.yaml new file mode 100644 index 00000000000..f4685aebee2 --- /dev/null +++ b/releasenotes/notes/pure_nvme_tcp-a00efa8966a74f77.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Pure Storage FlashArray driver: Added support NVMe-TCP transport layer.