diff --git a/cinder/tests/unit/volume/drivers/dell_emc/test_xtremio.py b/cinder/tests/unit/volume/drivers/dell_emc/test_xtremio.py index 55c1a850c91..937a210e024 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/test_xtremio.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/test_xtremio.py @@ -62,6 +62,14 @@ xms_init = {'xms': {1: {'version': '4.2.0', "name": "10.205.68.5/16", "index": 1, }, + '10.205.68.6/16': + {"port-address": + "iqn.2008-05.com.xtremio:002e67939c34", + "ip-port": 3260, + "ip-addr": "10.205.68.6/16", + "name": "10.205.68.6/16", + "index": 1, + }, }, 'targets': {'X1-SC2-target1': {'index': 1, "name": "X1-SC2-fc1", "port-address": @@ -347,7 +355,8 @@ class BaseXtremIODriverTestCase(test.TestCase): driver_ssl_cert_path='/test/path/root_ca.crt', xtremio_array_busy_retry_count=5, xtremio_array_busy_retry_interval=5, - xtremio_clean_unused_ig=False) + xtremio_clean_unused_ig=False, + xtremio_ports=[]) def safe_get(key): return getattr(self.config, key) @@ -650,10 +659,26 @@ class XtremIODriverISCSITestCase(BaseXtremIODriverTestCase): portals = xms_data['iscsi-portals'].copy() xms_data['iscsi-portals'].clear() lunmap = {'lun': 4} - self.assertRaises(exception.VolumeDriverException, + self.assertRaises(exception.VolumeBackendAPIException, self.driver._get_iscsi_properties, lunmap) xms_data['iscsi-portals'] = portals + def test_no_allowed_portals(self, req): + req.side_effect = xms_request + lunmap = {'lun': 4} + self.driver.allowed_ports = ['1.2.3.4'] + self.assertRaises(exception.VolumeBackendAPIException, + self.driver._get_iscsi_properties, lunmap) + + def test_filtered_portals(self, req): + req.side_effect = xms_request + lunmap = {'lun': 4} + self.driver.allowed_ports = ['10.205.68.6'] + connection_properties = self.driver._get_iscsi_properties(lunmap) + self.assertEqual(1, len(connection_properties['target_portals'])) + self.assertIn('10.205.68.6:3260', + connection_properties['target_portals']) + def test_initialize_connection(self, req): req.side_effect = xms_request self.driver.create_volume(self.data.test_volume) @@ -1365,6 +1390,27 @@ class XtremIODriverFCTestCase(BaseXtremIODriverTestCase): configuration=self.config) # ##### Connection FC##### + def test_no_targets_configured(self, req): + req.side_effect = xms_request + targets = xms_data['targets'].copy() + xms_data['targets'].clear() + self.assertRaises(exception.VolumeBackendAPIException, + self.driver.get_targets) + xms_data['targets'] = targets + + def test_no_allowed_targets(self, req): + req.side_effect = xms_request + self.driver.allowed_ports = ['58:cc:f0:98:49:22:07:02'] + self.assertRaises(exception.VolumeBackendAPIException, + self.driver.get_targets) + + def test_filtered_targets(self, req): + req.side_effect = xms_request + self.driver.allowed_ports = ['21:00:00:24:ff:57:b2:36'] + targets = self.driver.get_targets() + self.assertEqual(1, len(targets)) + self.assertIn('21000024ff57b236', targets) + def test_initialize_connection(self, req): req.side_effect = xms_request diff --git a/cinder/volume/drivers/dell_emc/xtremio.py b/cinder/volume/drivers/dell_emc/xtremio.py index 18723c53a2b..6cff3b8967e 100644 --- a/cinder/volume/drivers/dell_emc/xtremio.py +++ b/cinder/volume/drivers/dell_emc/xtremio.py @@ -31,6 +31,7 @@ Supports XtremIO version 2.4 and up. 1.0.9 - performance improvements, support force detach, support for X2 1.0.10 - option to clean unused IGs 1.0.11 - add support for multiattach + 1.0.12 - add support for ports filtering """ import json @@ -82,7 +83,13 @@ XTREMIO_OPTS = [ 'the IG be, we default to False (not deleting IGs ' 'without connected volumes); setting this parameter ' 'to True will remove any IG after terminating its ' - 'connection to the last volume.')] + 'connection to the last volume.'), + cfg.ListOpt('xtremio_ports', + default=[], + help='Allowed ports. Comma separated list of XtremIO ' + 'iSCSI IPs or FC WWNs (ex. 58:cc:f0:98:49:22:07:02) ' + 'to be used. If option is not set all ports are allowed.') +] CONF.register_opts(XTREMIO_OPTS, group=configuration.SHARED_CONF_GROUP) @@ -420,7 +427,7 @@ class XtremIOClient42(XtremIOClient4): class XtremIOVolumeDriver(san.SanDriver): """Executes commands relating to Volumes.""" - VERSION = '1.0.11' + VERSION = '1.0.12' # ThirdPartySystems wiki CI_WIKI_NAME = "EMC_XIO_CI" @@ -444,6 +451,10 @@ class XtremIOVolumeDriver(san.SanDriver): self.clean_ig = (self.configuration.safe_get('xtremio_clean_unused_ig') or False) self._stats = {} + self.allowed_ports = [ + port.strip().lower() for port in + self.configuration.safe_get('xtremio_ports') + ] self.client = XtremIOClient3(self.configuration, self.cluster_id) @classmethod @@ -1054,6 +1065,19 @@ class XtremIOVolumeDriver(san.SanDriver): raise (exception.VolumeBackendAPIException (data=_("Failed to create IG, %s") % name)) + def _port_is_allowed(self, port): + """Check if port is in allowed ports list. + + If allowed ports are empty then all ports are allowed. + + :param port: iSCSI IP/FC WWN to check + :return: is port allowed + """ + + if not self.allowed_ports: + return True + return port.lower() in self.allowed_ports + @interface.volumedriver class XtremIOISCSIDriver(XtremIOVolumeDriver, driver.ISCSIDriver): @@ -1171,26 +1195,33 @@ class XtremIOISCSIDriver(XtremIOVolumeDriver, driver.ISCSIDriver): multiple values. The main portal information is also returned in :target_iqn, :target_portal, :target_lun for backward compatibility. """ - portals = self.client.get_iscsi_portals() - if not portals: - msg = _("XtremIO not configured correctly, no iscsi portals found") - LOG.error(msg) - raise exception.VolumeDriverException(message=msg) - portal = RANDOM.choice(portals) + iscsi_portals = self.client.get_iscsi_portals() + allowed_portals = [] + for iscsi_portal in iscsi_portals: + iscsi_portal['ip-addr'] = iscsi_portal['ip-addr'].split('/')[0] + if self._port_is_allowed(iscsi_portal['ip-addr']): + allowed_portals.append(iscsi_portal) + if not allowed_portals: + msg = _("There are no accessible iSCSI targets on the " + "system.") + raise exception.VolumeBackendAPIException(data=msg) + portal = RANDOM.choice(allowed_portals) portal_addr = ('%(ip)s:%(port)d' % - {'ip': portal['ip-addr'].split('/')[0], + {'ip': portal['ip-addr'], 'port': portal['ip-port']}) - tg_portals = ['%(ip)s:%(port)d' % {'ip': p['ip-addr'].split('/')[0], + tg_portals = ['%(ip)s:%(port)d' % {'ip': p['ip-addr'], 'port': p['ip-port']} - for p in portals] + for p in allowed_portals] properties = {'target_discovered': False, 'target_iqn': portal['port-address'], 'target_lun': lunmap['lun'], 'target_portal': portal_addr, - 'target_iqns': [p['port-address'] for p in portals], + 'target_iqns': [ + p['port-address'] for p in allowed_portals + ], 'target_portals': tg_portals, - 'target_luns': [lunmap['lun']] * len(portals)} + 'target_luns': [lunmap['lun']] * len(allowed_portals)} return properties def _get_initiator_names(self, connector): @@ -1213,8 +1244,17 @@ class XtremIOFCDriver(XtremIOVolumeDriver, if not self._targets: try: targets = self.client.get_fc_up_ports() - self._targets = [target['port-address'].replace(':', '') - for target in targets] + allowed_targets = [] + for target in targets: + if self._port_is_allowed(target['port-address']): + allowed_targets.append( + target['port-address'].replace(':', '') + ) + if not allowed_targets: + msg = _("There are no accessible Fibre Channel targets " + "on the system.") + raise exception.VolumeBackendAPIException(data=msg) + self._targets = allowed_targets except exception.NotFound: raise (exception.VolumeBackendAPIException (data=_("Failed to get targets"))) diff --git a/doc/source/configuration/block-storage/drivers/dell-emc-xtremio-driver.rst b/doc/source/configuration/block-storage/drivers/dell-emc-xtremio-driver.rst index ab24b906fd3..7c5ad1e8268 100644 --- a/doc/source/configuration/block-storage/drivers/dell-emc-xtremio-driver.rst +++ b/doc/source/configuration/block-storage/drivers/dell-emc-xtremio-driver.rst @@ -233,6 +233,16 @@ The CHAP initiator authentication and discovery credentials (username and password) are generated automatically by the Block Storage driver. Therefore, there is no need to configure the initial CHAP credentials manually in XMS. +Configuring ports filtering +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The XtremIO Block Storage driver supports ports filtering to define a list +of iSCSI IP-addresses or FC WWNs which will be used to attach volumes. +If option is not set all ports are allowed. + +.. code-block:: ini + + xtremio_ports = iSCSI IPs or FC WWNs + .. _emc_extremio_configuration_example: Configuration example @@ -250,6 +260,7 @@ follows: volume_driver = cinder.volume.drivers.dell_emc.xtremio.XtremIOFibreChannelDriver san_ip = XMS_IP xtremio_cluster_name = Cluster01 + xtremio_ports = 21:00:00:24:ff:57:b2:36,21:00:00:24:ff:57:b2:55 san_login = XMS_USER san_password = XMS_PASSWD volume_backend_name = XtremIOAFA diff --git a/releasenotes/notes/bug-1900979-xtremio-ports-filtering-e68f90d47f17a7d9.yaml b/releasenotes/notes/bug-1900979-xtremio-ports-filtering-e68f90d47f17a7d9.yaml new file mode 100644 index 00000000000..1b65880843f --- /dev/null +++ b/releasenotes/notes/bug-1900979-xtremio-ports-filtering-e68f90d47f17a7d9.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + `Bug #1915800 `_: + Add support for ports filtering in XtremIO driver.