Merge "3PAR: Add config for NSP single path attach"
This commit is contained in:
commit
7bb05f327a
@ -726,6 +726,7 @@ class HPE3PARBaseDriver(test.TestCase):
|
||||
configuration.filter_function = FILTER_FUNCTION
|
||||
configuration.image_volume_cache_enabled = False
|
||||
configuration.replication_device = None
|
||||
configuration.hpe3par_target_nsp = None
|
||||
return configuration
|
||||
|
||||
@mock.patch(
|
||||
@ -6940,6 +6941,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getStorageSystemInfo.return_value = (
|
||||
{'id': self.CLIENT_ID})
|
||||
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
|
||||
mock_client.getCPG.return_value = {}
|
||||
mock_client.getHost.side_effect = [
|
||||
@ -7004,6 +7007,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
self.get_id_login +
|
||||
self.standard_logout +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
@ -7025,6 +7030,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
|
||||
return fake_map
|
||||
mock_lookup.return_value = fake_lookup_object()
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getStorageSystemInfo.return_value = (
|
||||
{'id': self.CLIENT_ID})
|
||||
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
|
||||
mock_client.getCPG.return_value = {}
|
||||
mock_client.getHost.side_effect = [
|
||||
@ -7098,6 +7105,89 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
self.get_id_login +
|
||||
self.standard_logout +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
self.assertDictEqual(expected_properties, result)
|
||||
|
||||
def test_initialize_connection_single_path_target_nsp(self):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
self.driver.configuration.hpe3par_target_nsp = '2:1:2'
|
||||
mock_client.getStorageSystemInfo.return_value = (
|
||||
{'id': self.CLIENT_ID})
|
||||
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
|
||||
mock_client.getCPG.return_value = {}
|
||||
mock_client.getHost.side_effect = [
|
||||
hpeexceptions.HTTPNotFound('fake'),
|
||||
{'name': self.FAKE_HOST,
|
||||
'FCPaths': [{'driverVersion': None,
|
||||
'firmwareVersion': None,
|
||||
'hostSpeed': 0,
|
||||
'model': None,
|
||||
'vendor': None,
|
||||
'wwn': self.wwn[0]}]}]
|
||||
mock_client.queryHost.return_value = {
|
||||
'members': [{
|
||||
'name': self.FAKE_HOST
|
||||
}]
|
||||
}
|
||||
|
||||
mock_client.getHostVLUNs.side_effect = [
|
||||
hpeexceptions.HTTPNotFound('fake'),
|
||||
[{'active': True,
|
||||
'volumeName': self.VOLUME_3PAR_NAME,
|
||||
'portPos': {'node': 7, 'slot': 1, 'cardPort': 1},
|
||||
'remoteName': self.wwn[0],
|
||||
'lun': 90, 'type': 0}]]
|
||||
|
||||
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
|
||||
{'volume_name': self.VOLUME_3PAR_NAME,
|
||||
'lun_id': 90,
|
||||
'host': self.FAKE_HOST,
|
||||
'nsp': 'something'})
|
||||
mock_client.createVLUN.return_value = location
|
||||
user_target_wwn = '0987654321234'
|
||||
expected_properties = {
|
||||
'driver_volume_type': 'fibre_channel',
|
||||
'data': {
|
||||
'encrypted': False,
|
||||
'target_lun': 90,
|
||||
'target_wwn': [user_target_wwn],
|
||||
'target_discovered': True,
|
||||
'initiator_target_map':
|
||||
{'123456789012345': [user_target_wwn]}}}
|
||||
|
||||
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
result = self.driver.initialize_connection(
|
||||
self.volume,
|
||||
self.connector.copy())
|
||||
|
||||
expected = [
|
||||
mock.call.getVolume(self.VOLUME_3PAR_NAME),
|
||||
mock.call.getCPG(HPE3PAR_CPG),
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.call.queryHost(wwns=['123456789012345']),
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.call.getPorts(),
|
||||
mock.call.getPorts(),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||
mock.call.createVLUN(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
auto=True,
|
||||
hostname=self.FAKE_HOST,
|
||||
lun=None),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
self.get_id_login +
|
||||
self.standard_logout +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
@ -118,6 +118,14 @@ hpe3par_opts = [
|
||||
cfg.BoolOpt('hpe3par_iscsi_chap_enabled',
|
||||
default=False,
|
||||
help="Enable CHAP authentication for iSCSI connections."),
|
||||
cfg.StrOpt('hpe3par_target_nsp',
|
||||
default="",
|
||||
help="The nsp of 3PAR backend to be used when: "
|
||||
"(1) multipath is not enabled in cinder.conf. "
|
||||
"(2) Fiber Channel Zone Manager is not used. "
|
||||
"(3) the 3PAR backend is prezoned with this "
|
||||
"specific nsp only. For example if nsp is 2 1 2, the "
|
||||
"format of the option's value is 2:1:2"),
|
||||
]
|
||||
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
"""
|
||||
Volume driver for HPE 3PAR Storage array.
|
||||
"""Volume driver for HPE 3PAR Storage array.
|
||||
|
||||
This driver requires 3.1.3 or later firmware on the 3PAR array, using
|
||||
the 4.x version of the hpe3parclient.
|
||||
|
||||
@ -112,6 +112,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
|
||||
4.0.4 - Handle force detach case. bug #1686745
|
||||
4.0.5 - Set proper backend on subsequent operation, after group
|
||||
failover. bug #1773069
|
||||
4.0.6 - Set NSP for single path attachments. Bug #1809249
|
||||
|
||||
"""
|
||||
|
||||
@ -171,12 +172,22 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
|
||||
try:
|
||||
# we have to make sure we have a host
|
||||
host = self._create_host(common, volume, connector)
|
||||
target_wwns, init_targ_map, numPaths = \
|
||||
self._build_initiator_target_map(common, connector)
|
||||
if not connector.get('multipath'):
|
||||
target_wwns = target_wwns[:1]
|
||||
target_wwns, init_targ_map, numPaths = (
|
||||
self._build_initiator_target_map(common, connector))
|
||||
|
||||
multipath = connector.get('multipath')
|
||||
LOG.debug("multipath: %s", multipath)
|
||||
user_target = None
|
||||
if not multipath:
|
||||
user_target = self._get_user_target(common)
|
||||
initiator = connector.get('wwpns')[0]
|
||||
init_targ_map[initiator] = init_targ_map[initiator][:1]
|
||||
if user_target is None:
|
||||
target_wwns = target_wwns[:1]
|
||||
init_targ_map[initiator] = init_targ_map[initiator][:1]
|
||||
else:
|
||||
target_wwns = [user_target]
|
||||
init_targ_map[initiator] = [user_target]
|
||||
|
||||
# check if a VLUN already exists for this host
|
||||
existing_vlun = common.find_existing_vlun(volume, host)
|
||||
|
||||
@ -411,3 +422,25 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
|
||||
self._modify_3par_fibrechan_host(common, host['name'], new_wwns)
|
||||
host = common._get_3par_host(host['name'])
|
||||
return host
|
||||
|
||||
def _get_user_target(self, common):
|
||||
target_nsp = common.config.hpe3par_target_nsp
|
||||
|
||||
if not target_nsp:
|
||||
return None
|
||||
|
||||
# Get target wwn from target nsp
|
||||
fc_ports = common.get_active_fc_target_ports()
|
||||
|
||||
target_wwn = None
|
||||
for port in fc_ports:
|
||||
nsp = port['nsp']
|
||||
if target_nsp == nsp:
|
||||
target_wwn = port['portWWN']
|
||||
break
|
||||
|
||||
if not target_wwn:
|
||||
LOG.warning("Did not get wwn for target nsp: "
|
||||
"%(nsp)s", {'nsp': target_nsp})
|
||||
|
||||
return target_wwn
|
||||
|
@ -413,3 +413,51 @@ the HPE 3PAR Fibre Channel and iSCSI drivers.
|
||||
:config-target: 3PAR
|
||||
|
||||
cinder.volume.drivers.hpe.hpe_3par_common
|
||||
|
||||
|
||||
Specify NSP for FC Bootable Volume
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Given a system connected to HPE 3PAR via FC and multipath setting is
|
||||
NOT used in cinder.conf. When the user tries to create a bootable
|
||||
volume, it fails intermittently with the following error:
|
||||
Fibre Channel volume device not found
|
||||
|
||||
This happens when a zone is created using second or later target from
|
||||
3PAR backend. In this case, HPE 3PAR client code picks up first target
|
||||
to form initiator target map. This can be illustrated with below
|
||||
example.
|
||||
|
||||
Sample output of showport command:
|
||||
|
||||
``$ showport -sortcol 6``
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
N:S:P Mode State ----Node_WWN---- -Port_WWN/HW_Addr- Type Protocol Partner FailoverState
|
||||
0:1:1 target ready 2FF70002AC002DB6 20110002AC002DB6 host FC - -
|
||||
0:1:2 target ready 2FF70002AC002DB6 20120002AC002DB6 host FC 1:1:2 none
|
||||
1:1:1 initiator ready 2FF70002AC002DB6 21110002AC002DB6 rcfc FC - -
|
||||
1:1:2 target ready 2FF70002AC002DB6 21120002AC002DB6 host FC 0:1:2 none
|
||||
2:1:1 initiator ready 2FF70002AC002DB6 22110002AC002DB6 rcfc FC - -
|
||||
2:1:2 target ready 2FF70002AC002DB6 22120002AC002DB6 host FC 3:1:2 none
|
||||
3:1:1 target ready 2FF70002AC002DB6 23110002AC002DB6 host FC - -
|
||||
3:1:2 target ready 2FF70002AC002DB6 23120002AC002DB6 host FC 2:1:2 none
|
||||
|
||||
Suppose zone is created using targets "2:1:2" and "3:1:2" from above
|
||||
output. Then initiator target map is created using target "0:1:1" only.
|
||||
In such a case, the path is not found, and bootable volume creation fails.
|
||||
|
||||
To avoid above mentioned failure, the user can specify the target in 3PAR
|
||||
backend section of cinder.conf as follows:
|
||||
|
||||
``hpe3par_target_nsp = 3:1:2``
|
||||
|
||||
Using above mentioned nsp, respective wwn information is fetched.
|
||||
Later initiator target map is created using wwn information and
|
||||
bootable volume is created successfully.
|
||||
|
||||
Note: If above mentioned option (nsp) is not specified in cinder.conf,
|
||||
then the original flow is executed i.e first target is picked and
|
||||
bootable volume creation may fail.
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
`Bug 1809249 <https://bugs.launchpad.net/cinder/+bug/1809249>`_ -
|
||||
3PAR driver adds the config option `hpe3par_target_nsp` that can be
|
||||
set to the 3PAR backend to use when multipath is not enabled and
|
||||
the Fibre Channel Zone Manager is not used.
|
Loading…
Reference in New Issue
Block a user