Browse Source

Merge "3PAR: Add config for NSP single path attach" into stable/stein

changes/37/681737/1
Zuul 4 days ago
parent
commit
9e4dec7c73

+ 90
- 0
cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py View File

@@ -711,6 +711,7 @@ class HPE3PARBaseDriver(test.TestCase):
711 711
         configuration.filter_function = FILTER_FUNCTION
712 712
         configuration.image_volume_cache_enabled = False
713 713
         configuration.replication_device = None
714
+        configuration.hpe3par_target_nsp = None
714 715
         return configuration
715 716
 
716 717
     @mock.patch(
@@ -6929,6 +6930,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
6929 6930
         # setup_mock_client drive with default configuration
6930 6931
         # and return the mock HTTP 3PAR client
6931 6932
         mock_client = self.setup_driver()
6933
+        mock_client.getStorageSystemInfo.return_value = (
6934
+            {'id': self.CLIENT_ID})
6932 6935
         mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
6933 6936
         mock_client.getCPG.return_value = {}
6934 6937
         mock_client.getHost.side_effect = [
@@ -6993,6 +6996,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
6993 6996
                 mock.call.getHostVLUNs(self.FAKE_HOST)]
6994 6997
 
6995 6998
             mock_client.assert_has_calls(
6999
+                self.get_id_login +
7000
+                self.standard_logout +
6996 7001
                 self.standard_login +
6997 7002
                 expected +
6998 7003
                 self.standard_logout)
@@ -7014,6 +7019,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
7014 7019
                 return fake_map
7015 7020
         mock_lookup.return_value = fake_lookup_object()
7016 7021
         mock_client = self.setup_driver()
7022
+        mock_client.getStorageSystemInfo.return_value = (
7023
+            {'id': self.CLIENT_ID})
7017 7024
         mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
7018 7025
         mock_client.getCPG.return_value = {}
7019 7026
         mock_client.getHost.side_effect = [
@@ -7087,6 +7094,89 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
7087 7094
                 mock.call.getHostVLUNs(self.FAKE_HOST)]
7088 7095
 
7089 7096
             mock_client.assert_has_calls(
7097
+                self.get_id_login +
7098
+                self.standard_logout +
7099
+                self.standard_login +
7100
+                expected +
7101
+                self.standard_logout)
7102
+
7103
+            self.assertDictEqual(expected_properties, result)
7104
+
7105
+    def test_initialize_connection_single_path_target_nsp(self):
7106
+        # setup_mock_client drive with default configuration
7107
+        # and return the mock HTTP 3PAR client
7108
+        mock_client = self.setup_driver()
7109
+        self.driver.configuration.hpe3par_target_nsp = '2:1:2'
7110
+        mock_client.getStorageSystemInfo.return_value = (
7111
+            {'id': self.CLIENT_ID})
7112
+        mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
7113
+        mock_client.getCPG.return_value = {}
7114
+        mock_client.getHost.side_effect = [
7115
+            hpeexceptions.HTTPNotFound('fake'),
7116
+            {'name': self.FAKE_HOST,
7117
+                'FCPaths': [{'driverVersion': None,
7118
+                             'firmwareVersion': None,
7119
+                             'hostSpeed': 0,
7120
+                             'model': None,
7121
+                             'vendor': None,
7122
+                             'wwn': self.wwn[0]}]}]
7123
+        mock_client.queryHost.return_value = {
7124
+            'members': [{
7125
+                'name': self.FAKE_HOST
7126
+            }]
7127
+        }
7128
+
7129
+        mock_client.getHostVLUNs.side_effect = [
7130
+            hpeexceptions.HTTPNotFound('fake'),
7131
+            [{'active': True,
7132
+              'volumeName': self.VOLUME_3PAR_NAME,
7133
+              'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
7134
+              'remoteName': self.wwn[0],
7135
+              'lun': 90, 'type': 0}]]
7136
+
7137
+        location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
7138
+                    {'volume_name': self.VOLUME_3PAR_NAME,
7139
+                     'lun_id': 90,
7140
+                     'host': self.FAKE_HOST,
7141
+                     'nsp': 'something'})
7142
+        mock_client.createVLUN.return_value = location
7143
+        user_target_wwn = '0987654321234'
7144
+        expected_properties = {
7145
+            'driver_volume_type': 'fibre_channel',
7146
+            'data': {
7147
+                'encrypted': False,
7148
+                'target_lun': 90,
7149
+                'target_wwn': [user_target_wwn],
7150
+                'target_discovered': True,
7151
+                'initiator_target_map':
7152
+                    {'123456789012345': [user_target_wwn]}}}
7153
+
7154
+        with mock.patch.object(hpecommon.HPE3PARCommon,
7155
+                               '_create_client') as mock_create_client:
7156
+            mock_create_client.return_value = mock_client
7157
+            result = self.driver.initialize_connection(
7158
+                self.volume,
7159
+                self.connector.copy())
7160
+
7161
+            expected = [
7162
+                mock.call.getVolume(self.VOLUME_3PAR_NAME),
7163
+                mock.call.getCPG(HPE3PAR_CPG),
7164
+                mock.call.getHost(self.FAKE_HOST),
7165
+                mock.call.queryHost(wwns=['123456789012345']),
7166
+                mock.call.getHost(self.FAKE_HOST),
7167
+                mock.call.getPorts(),
7168
+                mock.call.getPorts(),
7169
+                mock.call.getHostVLUNs(self.FAKE_HOST),
7170
+                mock.call.createVLUN(
7171
+                    self.VOLUME_3PAR_NAME,
7172
+                    auto=True,
7173
+                    hostname=self.FAKE_HOST,
7174
+                    lun=None),
7175
+                mock.call.getHostVLUNs(self.FAKE_HOST)]
7176
+
7177
+            mock_client.assert_has_calls(
7178
+                self.get_id_login +
7179
+                self.standard_logout +
7090 7180
                 self.standard_login +
7091 7181
                 expected +
7092 7182
                 self.standard_logout)

+ 8
- 0
cinder/volume/drivers/hpe/hpe_3par_common.py View File

@@ -118,6 +118,14 @@ hpe3par_opts = [
118 118
     cfg.BoolOpt('hpe3par_iscsi_chap_enabled',
119 119
                 default=False,
120 120
                 help="Enable CHAP authentication for iSCSI connections."),
121
+    cfg.StrOpt('hpe3par_target_nsp',
122
+               default="",
123
+               help="The nsp of 3PAR backend to be used when: "
124
+                    "(1) multipath is not enabled in cinder.conf. "
125
+                    "(2) Fiber Channel Zone Manager is not used. "
126
+                    "(3) the 3PAR backend is prezoned with this "
127
+                    "specific nsp only. For example if nsp is 2 1 2, the "
128
+                    "format of the option's value is 2:1:2"),
121 129
 ]
122 130
 
123 131
 

+ 40
- 7
cinder/volume/drivers/hpe/hpe_3par_fc.py View File

@@ -15,8 +15,8 @@
15 15
 #    License for the specific language governing permissions and limitations
16 16
 #    under the License.
17 17
 #
18
-"""
19
-Volume driver for HPE 3PAR Storage array.
18
+"""Volume driver for HPE 3PAR Storage array.
19
+
20 20
 This driver requires 3.1.3 or later firmware on the 3PAR array, using
21 21
 the 4.x version of the hpe3parclient.
22 22
 
@@ -111,6 +111,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
111 111
         4.0.4 - Handle force detach case. bug #1686745
112 112
         4.0.5 - Set proper backend on subsequent operation, after group
113 113
                 failover. bug #1773069
114
+        4.0.6 - Set NSP for single path attachments. Bug #1809249
114 115
 
115 116
     """
116 117
 
@@ -169,12 +170,22 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
169 170
         try:
170 171
             # we have to make sure we have a host
171 172
             host = self._create_host(common, volume, connector)
172
-            target_wwns, init_targ_map, numPaths = \
173
-                self._build_initiator_target_map(common, connector)
174
-            if not connector.get('multipath'):
175
-                target_wwns = target_wwns[:1]
173
+            target_wwns, init_targ_map, numPaths = (
174
+                self._build_initiator_target_map(common, connector))
175
+
176
+            multipath = connector.get('multipath')
177
+            LOG.debug("multipath: %s", multipath)
178
+            user_target = None
179
+            if not multipath:
180
+                user_target = self._get_user_target(common)
176 181
                 initiator = connector.get('wwpns')[0]
177
-                init_targ_map[initiator] = init_targ_map[initiator][:1]
182
+                if user_target is None:
183
+                    target_wwns = target_wwns[:1]
184
+                    init_targ_map[initiator] = init_targ_map[initiator][:1]
185
+                else:
186
+                    target_wwns = [user_target]
187
+                    init_targ_map[initiator] = [user_target]
188
+
178 189
             # check if a VLUN already exists for this host
179 190
             existing_vlun = common.find_existing_vlun(volume, host)
180 191
 
@@ -408,3 +419,25 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
408 419
             self._modify_3par_fibrechan_host(common, host['name'], new_wwns)
409 420
             host = common._get_3par_host(host['name'])
410 421
         return host
422
+
423
+    def _get_user_target(self, common):
424
+        target_nsp = common.config.hpe3par_target_nsp
425
+
426
+        if not target_nsp:
427
+            return None
428
+
429
+        # Get target wwn from target nsp
430
+        fc_ports = common.get_active_fc_target_ports()
431
+
432
+        target_wwn = None
433
+        for port in fc_ports:
434
+            nsp = port['nsp']
435
+            if target_nsp == nsp:
436
+                target_wwn = port['portWWN']
437
+                break
438
+
439
+        if not target_wwn:
440
+            LOG.warning("Did not get wwn for target nsp: "
441
+                        "%(nsp)s", {'nsp': target_nsp})
442
+
443
+        return target_wwn

+ 48
- 0
doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst View File

@@ -413,3 +413,51 @@ the HPE 3PAR Fibre Channel and iSCSI drivers.
413 413
    :config-target: 3PAR
414 414
 
415 415
    cinder.volume.drivers.hpe.hpe_3par_common
416
+
417
+
418
+Specify NSP for FC Bootable Volume
419
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
420
+
421
+Given a system connected to HPE 3PAR via FC and multipath setting is
422
+NOT used in cinder.conf. When the user tries to create a bootable
423
+volume, it fails intermittently with the following error:
424
+Fibre Channel volume device not found
425
+
426
+This happens when a zone is created using second or later target from
427
+3PAR backend. In this case, HPE 3PAR client code picks up first target
428
+to form initiator target map. This can be illustrated with below
429
+example.
430
+
431
+Sample output of showport command:
432
+
433
+``$ showport -sortcol 6``
434
+
435
+.. code-block:: console
436
+
437
+   N:S:P      Mode State ----Node_WWN---- -Port_WWN/HW_Addr-  Type Protocol Partner FailoverState
438
+   0:1:1    target ready 2FF70002AC002DB6   20110002AC002DB6  host       FC       -             -
439
+   0:1:2    target ready 2FF70002AC002DB6   20120002AC002DB6  host       FC   1:1:2          none
440
+   1:1:1 initiator ready 2FF70002AC002DB6   21110002AC002DB6  rcfc       FC       -             -
441
+   1:1:2    target ready 2FF70002AC002DB6   21120002AC002DB6  host       FC   0:1:2          none
442
+   2:1:1 initiator ready 2FF70002AC002DB6   22110002AC002DB6  rcfc       FC       -             -
443
+   2:1:2    target ready 2FF70002AC002DB6   22120002AC002DB6  host       FC   3:1:2          none
444
+   3:1:1    target ready 2FF70002AC002DB6   23110002AC002DB6  host       FC       -             -
445
+   3:1:2    target ready 2FF70002AC002DB6   23120002AC002DB6  host       FC   2:1:2          none
446
+
447
+Suppose zone is created using targets "2:1:2" and "3:1:2" from above
448
+output. Then initiator target map is created using target "0:1:1" only.
449
+In such a case, the path is not found, and bootable volume creation fails.
450
+
451
+To avoid above mentioned failure, the user can specify the target in 3PAR
452
+backend section of cinder.conf as follows:
453
+
454
+``hpe3par_target_nsp = 3:1:2``
455
+
456
+Using above mentioned nsp, respective wwn information is fetched.
457
+Later initiator target map is created using wwn information and
458
+bootable volume is created successfully.
459
+
460
+Note: If above mentioned option (nsp) is not specified in cinder.conf,
461
+then the original flow is executed i.e first target is picked and
462
+bootable volume creation may fail.
463
+

+ 7
- 0
releasenotes/notes/hpe-3par-specify-nsp-for-fc-bootable-volume-f372879e1b625b4d.yaml View File

@@ -0,0 +1,7 @@
1
+---
2
+fixes:
3
+  - |
4
+    `Bug 1809249 <https://bugs.launchpad.net/cinder/+bug/1809249>`_ -
5
+    3PAR driver adds the config option `hpe3par_target_nsp` that can be
6
+    set to the 3PAR backend to use when multipath is not enabled and
7
+    the Fibre Channel Zone Manager is not used.

Loading…
Cancel
Save