diff options
author | Christophe Drevet-Droguet <cdr@antemeta.fr> | 2021-02-04 18:18:46 +0100 |
---|---|---|
committer | Manojkatari <mkatari@redhat.com> | 2022-12-13 07:18:38 +0000 |
commit | 7f283a9704f3b2bb56769ad2580cdf1426d22be2 (patch) | |
tree | dd5f0fa2ecf685f18870736a9d4c2079b7d6f895 | |
parent | 985dd613c57e82c9c2831aaaa640c507d1b2e610 (diff) | |
download | cinder-7f283a9704f3b2bb56769ad2580cdf1426d22be2.tar.gz |
[PURE] support IPv6 / add parameter pure_iscsi_cidr_list
Pure storage: Add support for IPv6 in pure_iscsi_cidr parameter.
Pure storage: Add parameter pure_iscsi_cidr_list to support a list of networks.
Depends-On: Ib001e2b4d1347754c2b46730bc10d86e8cdab7ad
Change-Id: Ib04e49dac9fdabd81809ca389553ce3638bb9bbf
Closes-Bug: 1910143
(cherry picked from commit 7aa5e95d48c1bd0110001fb967c80e101f5b94dd)
(cherry picked from commit 84dc4fb0599743ad19eac43ca2e10dedb5d5386b)
(cherry picked from commit d061455d4f7974a6f4c86ba7e54b4415be4a3385)
-rw-r--r-- | cinder/tests/unit/volume/drivers/test_pure.py | 121 | ||||
-rw-r--r-- | cinder/volume/drivers/pure.py | 38 | ||||
-rw-r--r-- | releasenotes/notes/pure-iscsi-cidrs-7195eda9f7214fce.yaml | 12 |
3 files changed, 153 insertions, 18 deletions
diff --git a/cinder/tests/unit/volume/drivers/test_pure.py b/cinder/tests/unit/volume/drivers/test_pure.py index 5f71a74db..7cd4b5c0f 100644 --- a/cinder/tests/unit/volume/drivers/test_pure.py +++ b/cinder/tests/unit/volume/drivers/test_pure.py @@ -76,13 +76,20 @@ VOLUME_BACKEND_NAME = "Pure_iSCSI" ISCSI_PORT_NAMES = ["ct0.eth2", "ct0.eth3", "ct1.eth2", "ct1.eth3"] FC_PORT_NAMES = ["ct0.fc2", "ct0.fc3", "ct1.fc2", "ct1.fc3"] # These two IP blocks should use the same prefix (see ISCSI_CIDR_FILTERED to -# make sure changes make sense) +# make sure changes make sense). Our arrays now have 4 IPv4 + 4 IPv6 ports. ISCSI_IPS = ["10.0.0." + str(i + 1) for i in range(len(ISCSI_PORT_NAMES))] +ISCSI_IPS += ["[2001:db8::" + str(i + 1) + "]" + for i in range(len(ISCSI_PORT_NAMES))] AC_ISCSI_IPS = ["10.0.0." + str(i + 1 + len(ISCSI_PORT_NAMES)) for i in range(len(ISCSI_PORT_NAMES))] +AC_ISCSI_IPS += ["[2001:db8::1:" + str(i + 1) + "]" + for i in range(len(ISCSI_PORT_NAMES))] ISCSI_CIDR = "0.0.0.0/0" +ISCSI_CIDR_V6 = "::/0" # Designed to filter out only one of the AC ISCSI IPs, leaving the rest in ISCSI_CIDR_FILTERED = '10.0.0.0/29' +# Include several IP / networks: 10.0.0.2, 10.0.0.3, 10.0.0.6, 10.0.0.7 +ISCSI_CIDRS_FILTERED = ['10.0.0.2', '10.0.0.3', '2001:db8::1:2/127'] FC_WWNS = ["21000024ff59fe9" + str(i + 1) for i in range(len(FC_PORT_NAMES))] AC_FC_WWNS = [ "21000024ff59fab" + str(i + 1) for i in range(len(FC_PORT_NAMES))] @@ -126,16 +133,17 @@ AC_DEVICE_MAPPING = { }, } +# We now have IPv6 in addition to IPv4 on each interface ISCSI_PORTS = [{"name": name, "iqn": TARGET_IQN, "portal": ip + ":" + TARGET_PORT, "wwn": None, - } for name, ip in zip(ISCSI_PORT_NAMES, ISCSI_IPS)] + } for name, ip in zip(ISCSI_PORT_NAMES * 2, ISCSI_IPS)] AC_ISCSI_PORTS = [{"name": name, "iqn": AC_TARGET_IQN, "portal": ip + ":" + TARGET_PORT, "wwn": None, - } for name, ip in zip(ISCSI_PORT_NAMES, AC_ISCSI_IPS)] + } for name, ip in zip(ISCSI_PORT_NAMES * 2, AC_ISCSI_IPS)] FC_PORTS = [{"name": name, "iqn": None, "portal": None, @@ -193,6 +201,20 @@ ISCSI_CONNECTION_INFO = { "wwn": "3624a93709714b5cb91634c470002b2c8", }, } +ISCSI_CONNECTION_INFO_V6 = { + "driver_volume_type": "iscsi", + "data": { + "target_discovered": False, + "discard": True, + "target_luns": [1, 1, 1, 1], + "target_iqns": [TARGET_IQN, TARGET_IQN, TARGET_IQN, TARGET_IQN], + "target_portals": [ISCSI_IPS[4] + ":" + TARGET_PORT, + ISCSI_IPS[5] + ":" + TARGET_PORT, + ISCSI_IPS[6] + ":" + TARGET_PORT, + ISCSI_IPS[7] + ":" + TARGET_PORT], + "wwn": "3624a93709714b5cb91634c470002b2c8", + }, +} ISCSI_CONNECTION_INFO_AC = { "driver_volume_type": "iscsi", "data": { @@ -236,6 +258,23 @@ ISCSI_CONNECTION_INFO_AC_FILTERED = { "wwn": "3624a93709714b5cb91634c470002b2c8", }, } +ISCSI_CONNECTION_INFO_AC_FILTERED_LIST = { + "driver_volume_type": "iscsi", + "data": { + "target_discovered": False, + "discard": True, + "target_luns": [1, 1, 5, 5], + # Final entry filtered by ISCSI_CIDR_FILTERED + "target_iqns": [TARGET_IQN, TARGET_IQN, + AC_TARGET_IQN, AC_TARGET_IQN], + # Final entry filtered by ISCSI_CIDR_FILTERED + "target_portals": [ISCSI_IPS[1] + ":" + TARGET_PORT, + ISCSI_IPS[2] + ":" + TARGET_PORT, + AC_ISCSI_IPS[5] + ":" + TARGET_PORT, # IPv6 + AC_ISCSI_IPS[6] + ":" + TARGET_PORT], # IPv6 + "wwn": "3624a93709714b5cb91634c470002b2c8", + }, +} FC_CONNECTION_INFO = { "driver_volume_type": "fibre_channel", @@ -519,6 +558,7 @@ class PureDriverTestCase(test.TestCase): self.mock_config.driver_ssl_cert_verify = False self.mock_config.driver_ssl_cert_path = None self.mock_config.pure_iscsi_cidr = ISCSI_CIDR + self.mock_config.pure_iscsi_cidr_list = None self.array = mock.Mock() self.array.get.return_value = GET_ARRAY_PRIMARY self.array.array_name = GET_ARRAY_PRIMARY["array_name"] @@ -3075,6 +3115,34 @@ class PureISCSIDriverTestCase(PureBaseSharedDriverTestCase): @mock.patch(ISCSI_DRIVER_OBJ + "._get_wwn") @mock.patch(ISCSI_DRIVER_OBJ + "._connect") @mock.patch(ISCSI_DRIVER_OBJ + "._get_target_iscsi_ports") + def test_initialize_connection_ipv6(self, mock_get_iscsi_ports, + mock_connection, mock_get_wwn): + vol, vol_name = self.new_fake_vol() + mock_get_iscsi_ports.return_value = ISCSI_PORTS + mock_get_wwn.return_value = '3624a93709714b5cb91634c470002b2c8' + lun = 1 + connection = { + "vol": vol_name, + "lun": lun, + } + mock_connection.return_value = connection + + self.mock_config.pure_iscsi_cidr = ISCSI_CIDR_V6 + result = deepcopy(ISCSI_CONNECTION_INFO_V6) + + real_result = self.driver.initialize_connection(vol, + ISCSI_CONNECTOR) + self.assertDictEqual(result, real_result) + mock_get_iscsi_ports.assert_called_with(self.array) + mock_connection.assert_called_with(self.array, vol_name, + ISCSI_CONNECTOR, None, None) + self.assert_error_propagates([mock_get_iscsi_ports, mock_connection], + self.driver.initialize_connection, + vol, ISCSI_CONNECTOR) + + @mock.patch(ISCSI_DRIVER_OBJ + "._get_wwn") + @mock.patch(ISCSI_DRIVER_OBJ + "._connect") + @mock.patch(ISCSI_DRIVER_OBJ + "._get_target_iscsi_ports") def test_initialize_connection_uniform_ac(self, mock_get_iscsi_ports, mock_connection, mock_get_wwn): repl_extra_specs = { @@ -3159,6 +3227,53 @@ class PureISCSIDriverTestCase(PureBaseSharedDriverTestCase): ]) @mock.patch(ISCSI_DRIVER_OBJ + "._get_wwn") + @mock.patch(ISCSI_DRIVER_OBJ + "._connect") + @mock.patch(ISCSI_DRIVER_OBJ + "._get_target_iscsi_ports") + def test_initialize_connection_uniform_ac_cidrs(self, + mock_get_iscsi_ports, + mock_connection, + mock_get_wwn): + repl_extra_specs = { + 'replication_type': '<in> sync', + 'replication_enabled': '<is> true', + } + vol, vol_name = self.new_fake_vol(type_extra_specs=repl_extra_specs) + mock_get_iscsi_ports.side_effect = [ISCSI_PORTS, AC_ISCSI_PORTS] + mock_get_wwn.return_value = '3624a93709714b5cb91634c470002b2c8' + mock_connection.side_effect = [ + { + "vol": vol_name, + "lun": 1, + }, + { + "vol": vol_name, + "lun": 5, + } + ] + result = deepcopy(ISCSI_CONNECTION_INFO_AC_FILTERED_LIST) + + self.driver._is_active_cluster_enabled = True + # Set up some CIDRs to block: this will allow only 2 addresses from + # each host of the ActiveCluster, so we should check that we only + # get two+two results back + self.driver.configuration.pure_iscsi = ISCSI_CIDR + self.driver.configuration.pure_iscsi_cidr_list = ISCSI_CIDRS_FILTERED + mock_secondary = mock.MagicMock() + self.driver._uniform_active_cluster_target_arrays = [mock_secondary] + + real_result = self.driver.initialize_connection(vol, + ISCSI_CONNECTOR) + self.assertDictEqual(result, real_result) + mock_get_iscsi_ports.assert_has_calls([ + mock.call(self.array), + mock.call(mock_secondary), + ]) + mock_connection.assert_has_calls([ + mock.call(self.array, vol_name, ISCSI_CONNECTOR, None, None), + mock.call(mock_secondary, vol_name, ISCSI_CONNECTOR, None, None), + ]) + + @mock.patch(ISCSI_DRIVER_OBJ + "._get_wwn") @mock.patch(ISCSI_DRIVER_OBJ + "._get_chap_credentials") @mock.patch(ISCSI_DRIVER_OBJ + "._connect") @mock.patch(ISCSI_DRIVER_OBJ + "._get_target_iscsi_ports") diff --git a/cinder/volume/drivers/pure.py b/cinder/volume/drivers/pure.py index 7f6d424d5..35b5fc235 100644 --- a/cinder/volume/drivers/pure.py +++ b/cinder/volume/drivers/pure.py @@ -85,7 +85,13 @@ PURE_OPTS = [ cfg.StrOpt("pure_iscsi_cidr", default="0.0.0.0/0", help="CIDR of FlashArray iSCSI targets hosts are allowed to " "connect to. Default will allow connection to any " - "IP address."), + "IPv4 address. This parameter now supports IPv6 subnets. " + "Ignored when pure_iscsi_cidr_list is set."), + cfg.ListOpt("pure_iscsi_cidr_list", default=None, + help="Comma-separated list of CIDR of FlashArray iSCSI " + "targets hosts are allowed to connect to. It supports " + "IPv4 and IPv6 subnets. This parameter supersedes " + "pure_iscsi_cidr."), cfg.BoolOpt("pure_eradicate_on_delete", default=False, help="When enabled, all Pure volumes, snapshots, and " @@ -2411,12 +2417,15 @@ class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): }, } - # Convert CIDR to the expected type - if not isinstance(self.configuration.pure_iscsi_cidr, str): - cidr = self.configuration.pure_iscsi_cidr.decode('utf8') + if self.configuration.pure_iscsi_cidr_list: + cidrs = self.configuration.pure_iscsi_cidr_list + if self.configuration.pure_iscsi_cidr != "0.0.0.0/0": + LOG.warning("pure_iscsi_cidr was ignored as " + "pure_iscsi_cidr_list is set") else: - cidr = self.configuration.pure_iscsi_cidr - check_cidr = ipaddress.IPv4Network(cidr) + cidrs = [self.configuration.pure_iscsi_cidr] + + check_cidrs = [ipaddress.ip_network(item) for item in cidrs] target_luns = [] target_iqns = [] @@ -2431,15 +2440,14 @@ class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): # Check to ensure that the portal IP is in the iSCSI target # CIDR before adding it target_portal = port["portal"] - if not isinstance(target_portal.split(":")[0], str): - portal = (target_portal.split(":")[0]).decode('utf8') - else: - portal = target_portal.split(":")[0] - check_ip = ipaddress.IPv4Address(portal) - if check_ip in check_cidr: - target_luns.append(target["connection"]["lun"]) - target_iqns.append(port["iqn"]) - target_portals.append(target_portal) + portal, p_sep, p_port = target_portal.rpartition(':') + portal = portal.strip('[]') + check_ip = ipaddress.ip_address(portal) + for check_cidr in check_cidrs: + if check_ip in check_cidr: + target_luns.append(target["connection"]["lun"]) + target_iqns.append(port["iqn"]) + target_portals.append(target_portal) LOG.info("iSCSI target portals that match CIDR range: '%s'", target_portals) diff --git a/releasenotes/notes/pure-iscsi-cidrs-7195eda9f7214fce.yaml b/releasenotes/notes/pure-iscsi-cidrs-7195eda9f7214fce.yaml new file mode 100644 index 000000000..0fcf8eb1d --- /dev/null +++ b/releasenotes/notes/pure-iscsi-cidrs-7195eda9f7214fce.yaml @@ -0,0 +1,12 @@ +--- +fixes: + - | + Pure Storage FlashArray driver `bug 1910143 + <https://bugs.launchpad.net/cinder/+bug/1910143>`_: Parameter + ``pure_iscsi_cidr`` is now IPv4/v6 agnostic. +features: + - | + Pure Storage FlashArray driver: added configuration option + ``pure_iscsi_cidr_list`` for setting several network CIDRs for iSCSI target + connection. Both IPv4 and IPv6 is supported. The default still allows all + IPv4 targets. |