summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Drevet-Droguet <cdr@antemeta.fr>2021-02-04 18:18:46 +0100
committerManojkatari <mkatari@redhat.com>2022-12-13 07:18:38 +0000
commit7f283a9704f3b2bb56769ad2580cdf1426d22be2 (patch)
treedd5f0fa2ecf685f18870736a9d4c2079b7d6f895
parent985dd613c57e82c9c2831aaaa640c507d1b2e610 (diff)
downloadcinder-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.py121
-rw-r--r--cinder/volume/drivers/pure.py38
-rw-r--r--releasenotes/notes/pure-iscsi-cidrs-7195eda9f7214fce.yaml12
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.