]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add white list support for target ports in VNX driver
authorpeter_wang <peter.wang13@emc.com>
Tue, 30 Jun 2015 02:56:40 +0000 (22:56 -0400)
committerpeter_wang <peter.wang13@emc.com>
Tue, 14 Jul 2015 02:02:25 +0000 (22:02 -0400)
Currently, VNX cinder driver registers host initiator to all
configured iSCSI/FC ports on array and chooses ports from
all available ports to return in initialize_connection.

By introducing a new driver option io_port_list:
1. driver will only register initiator to ports in io_port_list
2. driver will only return ports in io_port_list from initialize_connection

Change-Id: Ie64eceb73bc0d70076033a29295c3efc57f34db4
Closes-Bug: 1470003

cinder/tests/unit/test_emc_vnxdirect.py
cinder/volume/drivers/emc/emc_cli_fc.py
cinder/volume/drivers/emc/emc_cli_iscsi.py
cinder/volume/drivers/emc/emc_vnx_cli.py

index a951f584eef7deb1a3e38b88c4a1e8ae3e145af7..3aa1a13e4de1f6233cfa34d5d535bb7f5a4a80f2 100644 (file)
@@ -738,7 +738,50 @@ State:  Ready
                  "SP:  A\n" +
                  "Port ID:  5\n" +
                  "Port WWN:  iqn.1992-04.com.emc:cx.fnm00124000215.a5\n" +
-                 "iSCSI Alias:  0215.a5\n", 0)
+                 "iSCSI Alias:  0215.a5\n" +
+                 "SP:  A\n" +
+                 "Port ID:  0\n" +
+                 "Port WWN:  iqn.1992-04.com.emc:cx.fnm00124000215.a0\n" +
+                 "iSCSI Alias:  0215.a0\n\n" +
+                 "Virtual Port ID:  0\n" +
+                 "VLAN ID:  Disabled\n" +
+                 "IP Address:  10.244.214.119\n\n" +
+                 "SP:  B\n" +
+                 "Port ID:  2\n" +
+                 "Port WWN:  iqn.1992-04.com.emc:cx.fnm00124000215.b2\n" +
+                 "iSCSI Alias:  0215.b2\n\n" +
+                 "Virtual Port ID:  0\n" +
+                 "VLAN ID:  Disabled\n" +
+                 "IP Address:  10.244.214.120\n\n", 0)
+
+    WHITE_LIST_PORTS = ("""SP:  A
+Port ID:  0
+Port WWN:  iqn.1992-04.com.emc:cx.fnmxxx.a0
+iSCSI Alias:  0235.a7
+
+Virtual Port ID:  0
+VLAN ID:  Disabled
+IP Address:  192.168.3.52
+
+SP:  A
+Port ID:  9
+Port WWN:  iqn.1992-04.com.emc:cx.fnmxxx.a9
+iSCSI Alias:  0235.a9
+
+SP:  A
+Port ID:  4
+Port WWN:  iqn.1992-04.com.emc:cx.fnmxxx.a4
+iSCSI Alias:  0235.a4
+
+SP:  B
+Port ID:  2
+Port WWN:  iqn.1992-04.com.emc:cx.fnmxxx.b2
+iSCSI Alias:  0235.b6
+
+Virtual Port ID:  0
+VLAN ID:  Disabled
+IP Address:  192.168.4.53
+""", 0)
 
     iscsi_connection_info = \
         {'data': {'target_discovered': True,
@@ -793,7 +836,18 @@ State:  Ready
                 "50:06:01:62:08:60:01:95\n" +
                 "Link Status:         Down\n" +
                 "Port Status:         Online\n" +
-                "Switch Present:      NO\n", 0)
+                "Switch Present:      NO\n" +
+                "\n" +
+                "SP Name:             SP B\n" +
+                "SP Port ID:          2\n" +
+                "SP UID:              50:06:01:60:88:60:08:0F:"
+                "50:06:01:6A:08:60:08:0F\n" +
+                "Link Status:         Up\n" +
+                "Port Status:         Online\n" +
+                "Switch Present:      YES\n" +
+                "Switch UID:          10:00:50:EB:1A:03:3F:59:"
+                "20:11:50:EB:1A:03:3F:59\n" +
+                "SP Source ID:        69888\n", 0)
 
     FAKEHOST_PORTS = (
         "Information about each HBA:\n" +
@@ -822,6 +876,14 @@ State:  Ready
         "    Defined:               YES\n" +
         "    Initiator Type:           3\n" +
         "    StorageGroup Name:     fakehost\n\n" +
+        "    SP Name:               SP B\n" +
+        "    SP Port ID:            2\n" +
+        "    HBA Devicename:\n" +
+        "    Trusted:               NO\n" +
+        "    Logged In:             YES\n" +
+        "    Defined:               YES\n" +
+        "    Initiator Type:           3\n" +
+        "    StorageGroup Name:     fakehost\n\n"
         "Information about each SPPORT:\n" +
         "\n" +
         "SP Name:             SP A\n" +
@@ -849,7 +911,18 @@ State:  Ready
         "50:06:01:62:08:60:01:95\n" +
         "Link Status:         Down\n" +
         "Port Status:         Online\n" +
-        "Switch Present:      NO\n", 0)
+        "Switch Present:      NO\n" +
+        "\n" +
+        "SP Name:             SP B\n" +
+        "SP Port ID:          2\n" +
+        "SP UID:              50:06:01:60:88:60:01:95:" +
+        "50:06:01:6A:08:60:08:0F\n" +
+        "Link Status:         Up\n" +
+        "Port Status:         Online\n" +
+        "Switch Present:      YES\n" +
+        "Switch UID:          10:00:00:05:1E:72:EC:A6:" +
+        "20:46:00:05:1E:72:EC:A6\n" +
+        "SP Source ID:        272896\n", 0)
 
     def LUN_PROPERTY(self, name, is_thin=False, has_snap=False, size=1,
                      state='Ready', faulted='false', operation='None',
@@ -886,6 +959,26 @@ State:  Ready
             'operation': operation,
             'is_thin': 'Yes' if is_thin else 'No'}, 0)
 
+    def STORAGE_GROUP_ISCSI_FC_HBA(self, sgname):
+
+        return ("""\
+        Storage Group Name:    %s
+        Storage Group UID:     54:46:57:0F:15:A2:E3:11:9A:8D:FF:E5:3A:03:FD:6D
+        HBA/SP Pairs:
+
+          HBA UID                                          SP Name     SPPort
+          -------                                          -------     ------
+          iqn.1993-08.org.debian:01:222                     SP A         4
+          22:34:56:78:90:12:34:56:12:34:56:78:90:12:34:56   SP B         2
+          22:34:56:78:90:54:32:16:12:34:56:78:90:54:32:16   SP B         2
+
+        HLU/ALU Pairs:
+
+          HLU Number     ALU Number
+          ----------     ----------
+            1               1
+        Shareable:             YES""" % sgname, 0)
+
     def STORAGE_GROUP_NO_MAP(self, sgname):
         return ("""\
         Storage Group Name:    %s
@@ -910,6 +1003,26 @@ State:  Ready
             1               1
         Shareable:             YES""" % sgname, 0)
 
+    def STORAGE_GROUP_HAS_MAP_ISCSI(self, sgname):
+
+        return ("""\
+        Storage Group Name:    %s
+        Storage Group UID:     54:46:57:0F:15:A2:E3:11:9A:8D:FF:E5:3A:03:FD:6D
+        HBA/SP Pairs:
+
+          HBA UID                                          SP Name     SPPort
+          -------                                          -------     ------
+          iqn.1993-08.org.debian:01:222                     SP A         2
+          iqn.1993-08.org.debian:01:222                     SP A         0
+          iqn.1993-08.org.debian:01:222                     SP B         2
+
+        HLU/ALU Pairs:
+
+          HLU Number     ALU Number
+          ----------     ----------
+            1               1
+        Shareable:             YES""" % sgname, 0)
+
     def STORAGE_GROUP_HAS_MAP_MP(self, sgname):
 
         return ("""\
@@ -1100,12 +1213,15 @@ class DriverTestCaseBase(test.TestCase):
         return standard_default
 
     def fake_command_execute_for_driver_setup(self, *command, **kwargv):
-        if command == ('connection', '-getport', '-address', '-vlanid'):
+        if (command == ('connection', '-getport', '-address', '-vlanid') or
+                command == ('connection', '-getport', '-vlanid')):
             return self.testData.ALL_PORTS
         elif command == ('storagepool', '-list', '-state'):
             return self.testData.POOL_GET_STATE_RESULT([
                 {'pool_name': self.testData.test_pool_name, 'state': "Ready"},
                 {'pool_name': "unit_test_pool2", 'state': "Ready"}])
+        if command == self.testData.GETFCPORT_CMD():
+            return self.testData.FC_PORTS
         else:
             return SUCCEED
 
@@ -1754,6 +1870,14 @@ Time Remaining:  0 second(s)
                               '-hbauid', 'iqn.1993-08.org.debian:01:222',
                               '-sp', 'A', '-spport', 4, '-spvport', 0,
                               '-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
+                              '-hbauid', 'iqn.1993-08.org.debian:01:222',
+                              '-sp', 'A', '-spport', 0, '-spvport', 0,
+                              '-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
+                              '-hbauid', 'iqn.1993-08.org.debian:01:222',
+                              '-sp', 'B', '-spport', 2, '-spvport', 0,
+                              '-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
                     mock.call('storagegroup', '-list', '-gname', 'fakehost',
                               poll=True),
                     mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
@@ -1921,6 +2045,74 @@ Time Remaining:  0 second(s)
                                                           '10.0.0.2'))]
         fake_cli.assert_has_calls(expected)
 
+    @mock.patch('random.randint',
+                mock.Mock(return_value=0))
+    def test_initialize_connection_iscsi_white_list(self):
+        self.configuration.io_port_list = 'a-0-0,B-2-0'
+        test_volume = self.testData.test_volume.copy()
+        test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
+        # Test for auto registration
+        self.configuration.initiator_auto_registration = True
+        commands = [('storagegroup', '-list', '-gname', 'fakehost')]
+        results = [[("No group", 83),
+                    self.testData.STORAGE_GROUP_HAS_MAP_ISCSI('fakehost')]]
+        fake_cli = self.driverSetup(commands, results)
+        self.driver.cli.iscsi_targets = {'A': [{'SP': 'A', 'Port ID': 0,
+                                                'Virtual Port ID': 0,
+                                                'Port WWN': 'fake_iqn',
+                                                'IP Address': '192.168.1.1'}],
+                                         'B': [{'SP': 'B', 'Port ID': 2,
+                                                'Virtual Port ID': 0,
+                                                'Port WWN': 'fake_iqn1',
+                                                'IP Address': '192.168.1.2'}]}
+        self.driver.initialize_connection(
+            test_volume,
+            self.testData.connector)
+        expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=False),
+                    mock.call('storagegroup', '-create', '-gname', 'fakehost'),
+                    mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
+                              '-hbauid', 'iqn.1993-08.org.debian:01:222',
+                              '-sp', 'A', '-spport', 0, '-spvport', 0,
+                              '-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
+                              '-hbauid', 'iqn.1993-08.org.debian:01:222',
+                              '-sp', 'B', '-spport', 2, '-spvport', 0,
+                              '-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=True),
+                    mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
+                              '-gname', 'fakehost', '-o',
+                              poll=False),
+                    mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1'),
+                              poll=False)]
+        fake_cli.assert_has_calls(expected)
+
+    @mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
+                'EMCVnxCliBase._build_pool_stats',
+                mock.Mock(return_value=None))
+    @mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
+                'CommandLineHelper.get_pool',
+                mock.Mock(return_value={'total_capacity_gb': 0.0,
+                                        'free_capacity_gb': 0.0}))
+    def test_update_iscsi_io_ports(self):
+        self.configuration.io_port_list = 'a-0-0,B-2-0'
+        # Test for auto registration
+        self.configuration.initiator_auto_registration = True
+        commands = [self.testData.GETPORT_CMD()]
+        results = [self.testData.WHITE_LIST_PORTS]
+        fake_cli = self.driverSetup(commands, results)
+        self.driver.cli.update_volume_stats()
+        expected = [mock.call(*self.testData.GETPORT_CMD(), poll=False)]
+        fake_cli.assert_has_calls(expected)
+        io_ports = self.driver.cli.iscsi_targets
+        self.assertEqual((0, 'iqn.1992-04.com.emc:cx.fnmxxx.a0'),
+                         (io_ports['A'][0]['Port ID'],
+                          io_ports['A'][0]['Port WWN']))
+        self.assertEqual((2, 'iqn.1992-04.com.emc:cx.fnmxxx.b2'),
+                         (io_ports['B'][0]['Port ID'],
+                          io_ports['B'][0]['Port WWN']))
+
     @mock.patch(
         "oslo_concurrency.processutils.execute",
         mock.Mock(
@@ -3959,12 +4151,24 @@ class EMCVNXCLIDriverFCTestCase(DriverTestCaseBase):
                               '90:12:34:56',
                               '-sp', 'A', '-spport', '0', '-ip', '10.0.0.2',
                               '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:12:34:56:12:34:56:78:'
+                              '90:12:34:56',
+                              '-sp', 'B', '-spport', '2', '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
                     mock.call('storagegroup', '-gname', 'fakehost',
                               '-setpath', '-hbauid',
                               '22:34:56:78:90:54:32:16:12:34:56:78:'
                               '90:54:32:16',
                               '-sp', 'A', '-spport', '0', '-ip', '10.0.0.2',
                               '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:54:32:16:12:34:56:78:'
+                              '90:54:32:16',
+                              '-sp', 'B', '-spport', '2', '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
                     mock.call('storagegroup', '-list', '-gname', 'fakehost',
                               poll=True),
                     mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
@@ -4049,6 +4253,106 @@ class EMCVNXCLIDriverFCTestCase(DriverTestCaseBase):
                     mock.call('port', '-list', '-sp')]
         fake_cli.assert_has_calls(expected)
 
+    @mock.patch('random.randint',
+                mock.Mock(return_value=0))
+    def test_initialize_connection_fc_white_list(self):
+        self.configuration.io_port_list = 'a-0,B-2'
+        test_volume = self.testData.test_volume.copy()
+        test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
+        self.configuration.initiator_auto_registration = True
+        commands = [('storagegroup', '-list', '-gname', 'fakehost'),
+                    self.testData.GETFCPORT_CMD(),
+                    ('port', '-list', '-gname', 'fakehost')]
+        results = [[("No group", 83),
+                    self.testData.STORAGE_GROUP_HAS_MAP_ISCSI('fakehost')],
+                   self.testData.FC_PORTS,
+                   self.testData.FAKEHOST_PORTS]
+
+        fake_cli = self.driverSetup(commands, results)
+        data = self.driver.initialize_connection(
+            test_volume,
+            self.testData.connector)
+
+        expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=False),
+                    mock.call('storagegroup', '-create', '-gname', 'fakehost'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:12:34:56:'
+                              '12:34:56:78:90:12:34:56',
+                              '-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:12:34:56:'
+                              '12:34:56:78:90:12:34:56',
+                              '-sp', 'B', '-spport', 2, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:54:32:16:'
+                              '12:34:56:78:90:54:32:16',
+                              '-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:54:32:16:'
+                              '12:34:56:78:90:54:32:16',
+                              '-sp', 'B', '-spport', 2, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=True),
+                    mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
+                              '-gname', 'fakehost', '-o',
+                              poll=False),
+                    mock.call('port', '-list', '-gname', 'fakehost')]
+        fake_cli.assert_has_calls(expected)
+        self.assertEqual(['5006016A0860080F', '5006016008600195'],
+                         data['data']['target_wwn'])
+
+    @mock.patch('random.randint',
+                mock.Mock(return_value=0))
+    def test_initialize_connection_fc_port_registered_wl(self):
+        self.configuration.io_port_list = 'a-0,B-2'
+        test_volume = self.testData.test_volume.copy()
+        test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
+        self.configuration.initiator_auto_registration = True
+        commands = [('storagegroup', '-list', '-gname', 'fakehost'),
+                    self.testData.GETFCPORT_CMD(),
+                    ('port', '-list', '-gname', 'fakehost')]
+        results = [self.testData.STORAGE_GROUP_ISCSI_FC_HBA('fakehost'),
+                   self.testData.FC_PORTS,
+                   self.testData.FAKEHOST_PORTS]
+
+        fake_cli = self.driverSetup(commands, results)
+        data = self.driver.initialize_connection(
+            test_volume,
+            self.testData.connector)
+
+        expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=False),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:12:34:56:'
+                              '12:34:56:78:90:12:34:56',
+                              '-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-gname', 'fakehost',
+                              '-setpath', '-hbauid',
+                              '22:34:56:78:90:54:32:16:'
+                              '12:34:56:78:90:54:32:16',
+                              '-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
+                              '-host', 'fakehost', '-o'),
+                    mock.call('storagegroup', '-list', '-gname', 'fakehost',
+                              poll=True),
+                    mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
+                              '-gname', 'fakehost', '-o',
+                              poll=False),
+                    mock.call('port', '-list', '-gname', 'fakehost')]
+        fake_cli.assert_has_calls(expected)
+        self.assertEqual(['5006016A0860080F', '5006016008600195'],
+                         data['data']['target_wwn'])
+
     @mock.patch(
         "cinder.zonemanager.fc_san_lookup_service.FCSanLookupService." +
         "get_device_mapping_from_network",
index 3a4e3975266d3fa59f95cb2a3f5698527ee30960..08ffe3783eb261a406fe4775c0649109f6f0193c 100644 (file)
@@ -58,6 +58,7 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
                 Create consistency group from cgsnapshot support
                 Multiple pools support enhancement
                 Manage/unmanage volume revise
+                White list target ports support
     """
 
     def __init__(self, *args, **kwargs):
index 464c8db95926e7b4db7df61484c4084f13ba9bfb..8d5da132266299e882b343925965dc5071cf75a2 100644 (file)
@@ -56,6 +56,7 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
                 Create consistency group from cgsnapshot support
                 Multiple pools support enhancement
                 Manage/unmanage volume revise
+                White list target ports support
     """
 
     def __init__(self, *args, **kwargs):
index 69f68e344cbf1d6a847030b904b31c317b39d1fa..ab371f937eaed3aebfae21f297974f44ab611823 100644 (file)
@@ -99,6 +99,10 @@ loc_opts = [
                default='',
                help='Mapping between hostname and '
                'its iSCSI initiator IP addresses.'),
+    cfg.StrOpt('io_port_list',
+               default='*',
+               help='Comma separated iSCSI or FC ports '
+               'to be used in Nova or Cinder.'),
     cfg.BoolOpt('initiator_auto_registration',
                 default=False,
                 help='Automatically register initiators. '
@@ -1363,7 +1367,8 @@ class CommandLineHelper(object):
 
         return data
 
-    def get_status_up_ports(self, storage_group_name, poll=True):
+    def get_status_up_ports(self, storage_group_name, io_ports=None,
+                            poll=True):
         """Function to get ports whose status are up."""
         cmd_get_hba = ('storagegroup', '-list', '-gname', storage_group_name)
         out, rc = self.command_execute(*cmd_get_hba, poll=poll)
@@ -1379,6 +1384,9 @@ class CommandLineHelper(object):
             if 0 != rc:
                 self._raise_cli_error(cmd_get_port, rc, out)
             for i, sp in enumerate(sps):
+                if io_ports:  # Skip ports which are not in io_ports
+                    if (sp.split()[1], int(portid[i])) not in io_ports:
+                        continue
                 wwn = self.get_port_wwn(sp, portid[i], out)
                 if (wwn is not None) and (wwn not in wwns):
                     LOG.debug('Add wwn:%(wwn)s for sg:%(sg)s.',
@@ -1392,8 +1400,8 @@ class CommandLineHelper(object):
             self._raise_cli_error(cmd_get_hba, rc, out)
         return wwns
 
-    def get_login_ports(self, storage_group_name, connector_wwpns):
-
+    def get_login_ports(self, storage_group_name, connector_wwpns,
+                        io_ports=None):
         cmd_list_hba = ('port', '-list', '-gname', storage_group_name)
         out, rc = self.command_execute(*cmd_list_hba)
         ports = []
@@ -1414,9 +1422,14 @@ class CommandLineHelper(object):
                                   'HBA Devicename:.*\n\s*' +
                                   'Trusted:.*\n\s*' +
                                   'Logged In:\s*YES\n')
+
             for each in connector_hba_list:
                 ports.extend(re.findall(port_pat, each))
             ports = list(set(ports))
+            if io_ports:
+                ports = filter(lambda po:
+                               (po[0].split()[1], int(po[1])) in io_ports,
+                               ports)
             for each in ports:
                 wwn = self.get_port_wwn(each[0], each[1], allports)
                 if wwn:
@@ -1426,16 +1439,16 @@ class CommandLineHelper(object):
         return wwns
 
     def get_port_wwn(self, sp, port_id, allports=None):
+        """Returns wwn via sp and port_id
+
+        :param sp: should be in this format 'SP A'
+        :param port_id: '0' or 0
+        """
         wwn = None
         if allports is None:
-            cmd_get_port = ('port', '-list', '-sp')
-            out, rc = self.command_execute(*cmd_get_port)
-            if 0 != rc:
-                self._raise_cli_error(cmd_get_port, rc, out)
-            else:
-                allports = out
+            allports, rc = self.get_port_output()
         _re_port_wwn = re.compile('SP Name:\s*' + sp +
-                                  '\nSP Port ID:\s*' + port_id +
+                                  '\nSP Port ID:\s*' + str(port_id) +
                                   '\nSP UID:\s*((\w\w:){15}(\w\w))' +
                                   '\nLink Status:         Up' +
                                   '\nPort Status:         Online')
@@ -1445,11 +1458,7 @@ class CommandLineHelper(object):
         return wwn
 
     def get_fc_targets(self):
-        fc_getport = ('port', '-list', '-sp')
-        out, rc = self.command_execute(*fc_getport)
-        if rc != 0:
-            self._raise_cli_error(fc_getport, rc, out)
-
+        out, rc = self.get_port_output()
         fc_target_dict = {'A': [], 'B': []}
 
         _fcport_pat = (r'SP Name:             SP\s(\w)\s*'
@@ -1465,7 +1474,42 @@ class CommandLineHelper(object):
                                        'Port ID': sp_port_id})
         return fc_target_dict
 
-    def get_iscsi_targets(self, poll=True):
+    def get_port_output(self):
+        cmd_get_port = ('port', '-list', '-sp')
+        out, rc = self.command_execute(*cmd_get_port)
+        if 0 != rc:
+            self._raise_cli_error(cmd_get_port, rc, out)
+        return out, rc
+
+    def get_connection_getport_output(self):
+        connection_getport_cmd = ('connection', '-getport', '-vlanid')
+        out, rc = self.command_execute(*connection_getport_cmd)
+        if 0 != rc:
+            self._raise_cli_error(connection_getport_cmd, rc, out)
+        return out, rc
+
+    def _filter_iscsi_ports(self, all_ports, io_ports):
+        """Filter ports in white list from all iSCSI ports."""
+        new_iscsi_ports = {'A': [], 'B': []}
+        valid_ports = []
+        for sp in all_ports:
+            for port in all_ports[sp]:
+                port_tuple = (port['SP'],
+                              port['Port ID'],
+                              port['Virtual Port ID'])
+                if port_tuple in io_ports:
+                    new_iscsi_ports[sp].append(port)
+                    valid_ports.append(port_tuple)
+        if len(io_ports) != len(valid_ports):
+            invalid_port_set = set(io_ports) - set(valid_ports)
+            for invalid in invalid_port_set:
+                LOG.warning(_LW('Invalid iSCSI port %(sp)s-%(port)s-%(vlan)s '
+                                'found in io_port_list, will be ignored.'),
+                            {'sp': invalid[0], 'port': invalid[1],
+                             'vlan': invalid[2]})
+        return new_iscsi_ports
+
+    def get_iscsi_targets(self, poll=False, io_ports=None):
         cmd_getport = ('connection', '-getport', '-address', '-vlanid')
         out, rc = self.command_execute(*cmd_getport, poll=poll)
         if rc != 0:
@@ -1498,7 +1542,8 @@ class CommandLineHelper(object):
                                               'Port WWN': iqn,
                                               'Virtual Port ID': vport_id,
                                               'IP Address': ip_addr})
-
+        if io_ports:
+            return self._filter_iscsi_ports(iscsi_target_dict, io_ports)
         return iscsi_target_dict
 
     def get_registered_spport_set(self, initiator_iqn, sgname, sg_raw_out):
@@ -1755,8 +1800,11 @@ class EMCVnxCliBase(object):
         conf_pools = self.configuration.safe_get("storage_vnx_pool_names")
         self.storage_pools = self._get_managed_storage_pools(conf_pools)
         self.array_serial = None
+        self.io_ports = self._parse_ports(self.configuration.io_port_list,
+                                          self.protocol)
         if self.protocol == 'iSCSI':
-            self.iscsi_targets = self._client.get_iscsi_targets(poll=True)
+            self.iscsi_targets = self._client.get_iscsi_targets(
+                poll=True, io_ports=self.io_ports)
         self.hlu_cache = {}
         self.force_delete_lun_in_sg = (
             self.configuration.force_delete_lun_in_storagegroup)
@@ -1802,6 +1850,59 @@ class EMCVnxCliBase(object):
                       "manage all the pools on the VNX system.")
         return storage_pools
 
+    def _parse_ports(self, io_port_list, protocol):
+        """Validates IO port format, supported format is a-1, b-3, a-3-0."""
+        if not io_port_list or io_port_list == '*':
+            return None
+        ports = re.split('\s*,\s*', io_port_list)
+        valid_ports = []
+        invalid_ports = []
+        if 'iSCSI' == protocol:
+            out, rc = self._client.get_connection_getport_output()
+            for port in ports:
+                port_tuple = port.split('-')
+                if (re.match('[abAB]-\d+-\d+$', port) and
+                        self._validate_iscsi_port(
+                            port_tuple[0], port_tuple[1], port_tuple[2], out)):
+                    valid_ports.append(
+                        (port_tuple[0].upper(), int(port_tuple[1]),
+                         int(port_tuple[2])))
+                else:
+                    invalid_ports.append(port)
+        elif 'FC' == protocol:
+            out, rc = self._client.get_port_output()
+            for port in ports:
+                port_tuple = port.split('-')
+                if re.match('[abAB]-\d+$', port) and self._validate_fc_port(
+                        port_tuple[0], port_tuple[1], out):
+                    valid_ports.append(
+                        (port_tuple[0].upper(), int(port_tuple[1])))
+                else:
+                    invalid_ports.append(port)
+        if len(invalid_ports) > 0:
+            msg = _('Invalid %(protocol)s ports %(port)s specified '
+                    'for io_port_list.') % {'protocol': self.protocol,
+                                            'port': ','.join(invalid_ports)}
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+        return valid_ports
+
+    def _validate_iscsi_port(self, sp, port_id, vlan_id, cmd_output):
+        """Validates whether the iSCSI port is existed on VNX"""
+        iscsi_pattern = ('SP:\s+' + sp.upper() +
+                         '\nPort ID:\s+' + str(port_id) +
+                         '\nPort WWN:\s+.*' +
+                         '\niSCSI Alias:\s+.*\n'
+                         '\nVirtual Port ID:\s+' + str(vlan_id))
+        return re.search(iscsi_pattern, cmd_output)
+
+    def _validate_fc_port(self, sp, port_id, cmd_output):
+        """Validates whether the FC port is existed on VNX"""
+        fc_pattern = ('SP Name:\s*SP\s*' + sp.upper() +
+                      '\nSP Port ID:\s*' + str(port_id) +
+                      '\nSP UID:\s*((\w\w:){15}(\w\w))')
+        return re.search(fc_pattern, cmd_output)
+
     def get_array_serial(self):
         if not self.array_serial:
             self.array_serial = self._client.get_array_serial()
@@ -2275,7 +2376,6 @@ class EMCVnxCliBase(object):
 
         self.stats['consistencygroup_support'] = (
             'True' if '-VNXSnapshots' in self.enablers else 'False')
-
         return self.stats
 
     def create_snapshot(self, snapshot):
@@ -2673,8 +2773,60 @@ class EMCVnxCliBase(object):
                          'portid': port_id,
                          'msg': out})
 
-    def _register_iscsi_initiator(self, ip, host, initiator_uids):
-        iscsi_targets = self.iscsi_targets
+    def auto_register_with_io_port_filter(self, connector, sgdata,
+                                          io_port_filter):
+        """Automatically register specific IO ports to storage group."""
+        initiator = connector['initiator']
+        ip = connector['ip']
+        host = connector['host']
+        new_white = {'A': [], 'B': []}
+        if self.protocol == 'iSCSI':
+            if sgdata:
+                sp_ports = self._client.get_registered_spport_set(
+                    initiator, host, sgdata['raw_output'])
+                # Normalize io_ports
+                for sp in ('A', 'B'):
+                    new_ports = filter(
+                        lambda pt: (pt['SP'], pt['Port ID']) not in sp_ports,
+                        self.iscsi_targets[sp])
+                    new_white[sp] = map(lambda white:
+                                        {'SP': white['SP'],
+                                         'Port ID': white['Port ID'],
+                                         'Virtual Port ID':
+                                             white['Virtual Port ID']},
+                                        new_ports)
+            else:
+                new_white = self.iscsi_targets
+            self._register_iscsi_initiator(ip, host, [initiator], new_white)
+
+        elif self.protocol == 'FC':
+            wwns = self._extract_fc_uids(connector)
+            ports_list = []
+            if sgdata:
+                for wwn in wwns:
+                    for port in io_port_filter:
+                        if ((port not in ports_list) and
+                                (not re.search(wwn + '\s+SP\s+' +
+                                               port[0] + '\s+' + str(port[1]),
+                                               sgdata['raw_output'],
+                                               re.IGNORECASE))):
+                            # Record ports to be added
+                            ports_list.append(port)
+                            new_white[port[0]].append({
+                                'SP': port[0],
+                                'Port ID': port[1]})
+            else:
+                # Need to translate to dict format
+                for fc_port in io_port_filter:
+                    new_white[fc_port[0]].append({'SP': fc_port[0],
+                                                  'Port ID': fc_port[1]})
+            self._register_fc_initiator(ip, host, wwns, new_white)
+        return new_white['A'] or new_white['B']
+
+    def _register_iscsi_initiator(self, ip, host, initiator_uids,
+                                  port_to_register=None):
+        iscsi_targets = (port_to_register if port_to_register else
+                         self.iscsi_targets)
         for initiator_uid in initiator_uids:
             LOG.info(_LI('Get ISCSI targets %(tg)s to register '
                          'initiator %(in)s.'),
@@ -2698,8 +2850,10 @@ class EMCVnxCliBase(object):
                 self._exec_command_setpath(initiator_uid, sp, port_id,
                                            ip, host, vport_id)
 
-    def _register_fc_initiator(self, ip, host, initiator_uids):
-        fc_targets = self._client.get_fc_targets()
+    def _register_fc_initiator(self, ip, host, initiator_uids,
+                               ports_to_register=None):
+        fc_targets = (ports_to_register if ports_to_register else
+                      self._client.get_fc_targets())
         for initiator_uid in initiator_uids:
             LOG.info(_LI('Get FC targets %(tg)s to register '
                          'initiator %(in)s.'),
@@ -2754,7 +2908,7 @@ class EMCVnxCliBase(object):
                 unregistered_initiators.append(initiator_uid)
         return unregistered_initiators
 
-    def auto_register_initiator(self, connector, sgdata):
+    def auto_register_initiator_to_all(self, connector, sgdata):
         """Automatically registers available initiators.
 
         Returns True if has registered initiator otherwise returns False.
@@ -2799,6 +2953,17 @@ class EMCVnxCliBase(object):
             self._register_fc_initiator(ip, host, itors_toReg)
             return True
 
+    def auto_register_initiator(self, connector, sgdata, io_ports_filter=None):
+        """Automatically register available initiators.
+
+        :returns: True if has registered initiator otherwise return False
+        """
+        if io_ports_filter:
+            return self.auto_register_with_io_port_filter(connector, sgdata,
+                                                          io_ports_filter)
+        else:
+            return self.auto_register_initiator_to_all(connector, sgdata)
+
     def assure_host_access(self, volume, connector):
         hostname = connector['host']
         volumename = volume['name']
@@ -2812,7 +2977,7 @@ class EMCVnxCliBase(object):
             # Storage Group has not existed yet
             self.assure_storage_group(hostname)
             if self.itor_auto_reg:
-                self.auto_register_initiator(connector, None)
+                self.auto_register_initiator(connector, None, self.io_ports)
                 auto_registration_done = True
             else:
                 self._client.connect_host_to_storage_group(hostname, hostname)
@@ -2821,7 +2986,8 @@ class EMCVnxCliBase(object):
                                                     poll=True)
 
         if self.itor_auto_reg and not auto_registration_done:
-            new_registerred = self.auto_register_initiator(connector, sgdata)
+            new_registerred = self.auto_register_initiator(connector, sgdata,
+                                                           self.io_ports)
             if new_registerred:
                 sgdata = self._client.get_storage_group(hostname,
                                                         poll=True)
@@ -2909,13 +3075,6 @@ class EMCVnxCliBase(object):
                 properties['target_portal'] = \
                     "%s:3260" % targets[0]['IP Address']
                 properties['target_lun'] = hlu
-
-                auth = volume['provider_auth']
-                if auth:
-                    (auth_method, auth_username, auth_secret) = auth.split()
-                    properties['auth_method'] = auth_method
-                    properties['auth_username'] = auth_username
-                    properties['auth_password'] = auth_secret
         else:
             properties = {'target_discovered': False,
                           'target_iqns': None,
@@ -2940,11 +3099,12 @@ class EMCVnxCliBase(object):
                          'target_dicovered': True,
                          'target_wwn': None}
         if self.zonemanager_lookup_service is None:
-            fc_properties['target_wwn'] = self.get_login_ports(connector)
+            fc_properties['target_wwn'] = self.get_login_ports(connector,
+                                                               self.io_ports)
         else:
             target_wwns, itor_tgt_map = self.get_initiator_target_map(
                 connector['wwpns'],
-                self.get_status_up_ports(connector))
+                self.get_status_up_ports(connector, self.io_ports))
             fc_properties['target_wwn'] = target_wwns
             fc_properties['initiator_target_map'] = itor_tgt_map
         return fc_properties
@@ -3094,12 +3254,14 @@ class EMCVnxCliBase(object):
                         self._build_provider_location_for_lun(lun_id)}
         return model_update
 
-    def get_login_ports(self, connector):
+    def get_login_ports(self, connector, io_ports=None):
         return self._client.get_login_ports(connector['host'],
-                                            connector['wwpns'])
+                                            connector['wwpns'],
+                                            io_ports)
 
-    def get_status_up_ports(self, connector):
-        return self._client.get_status_up_ports(connector['host'])
+    def get_status_up_ports(self, connector, io_ports=None):
+        return self._client.get_status_up_ports(connector['host'],
+                                                io_ports=io_ports)
 
     def get_initiator_target_map(self, fc_initiators, fc_targets):
         target_wwns = []
@@ -3272,9 +3434,9 @@ class EMCVnxCliBase(object):
     def update_volume_stats(self):
         """Retrieves stats info."""
         self.update_enabler_in_volume_stats()
-
         if self.protocol == 'iSCSI':
-            self.iscsi_targets = self._client.get_iscsi_targets(poll=False)
+            self.iscsi_targets = self._client.get_iscsi_targets(
+                poll=False, io_ports=self.io_ports)
 
         properties = [self._client.POOL_FREE_CAPACITY,
                       self._client.POOL_TOTAL_CAPACITY,