Critical fix for MSA 2060 and MSA 1060
Correct omission in HPE MSA driver doc and fix driver failures caused by use of deprecated API command syntax that's not accepted by the latest firmware. The changes are conditional on the firmware version so that arrays with older firmware will not be affected. Change-Id: I73b093bcee4ac83cb80480097818b28104f8e15f Closes-Bug: #1897926
This commit is contained in:
parent
c8ce118f0a
commit
d739b86714
@ -50,6 +50,9 @@ resp_fw_ti = '''<RESPONSE><PROPERTY name="sc-fw">T252R07</PROPERTY>
|
||||
resp_fw = '''<RESPONSE><PROPERTY name="sc-fw">GLS220R001</PROPERTY>
|
||||
<PROPERTY name="return-code">0</PROPERTY></RESPONSE>'''
|
||||
|
||||
resp_fw_nomatch = '''<RESPONSE><PROPERTY name="sc-fw">Z</PROPERTY>
|
||||
<PROPERTY name="return-code">0</PROPERTY></RESPONSE>'''
|
||||
|
||||
resp_system = '''<RESPONSE>
|
||||
<PROPERTY name="midplane-serial-number">00C0FFEEEEEE</PROPERTY>
|
||||
<PROPERTY name="return-code">0</PROPERTY>
|
||||
@ -186,6 +189,12 @@ class TestSeagateClient(test.TestCase):
|
||||
self.assertRaises(stx_exception.AuthenticationError,
|
||||
self.client.login)
|
||||
|
||||
m.text.encode.side_effect = [resp_login, resp_fw_nomatch, resp_system]
|
||||
self.client.login()
|
||||
self.assertEqual('Z', self.client._fw_type)
|
||||
self.assertEqual(0, self.client._fw_rev)
|
||||
self.assertEqual(False, self.client.is_g5_fw())
|
||||
|
||||
m.text.encode.side_effect = [resp_login, resp_fw, resp_system]
|
||||
self.client.login()
|
||||
self.assertEqual(session_key, self.client._session_key)
|
||||
@ -313,11 +322,15 @@ class TestSeagateClient(test.TestCase):
|
||||
@mock.patch.object(STXClient, '_request')
|
||||
def test_list_luns_for_host(self, mock_request):
|
||||
mock_request.side_effect = [etree.XML(response_no_lun),
|
||||
etree.XML(response_lun),
|
||||
etree.XML(response_lun)]
|
||||
self.client._fw = 'T100'
|
||||
self.client._fw_type = 'T'
|
||||
self.client.list_luns_for_host('dummy')
|
||||
mock_request.assert_called_with('/show/host-maps', 'dummy')
|
||||
self.client._fw = 'G221'
|
||||
self.client._fw_type = 'G'
|
||||
self.client.list_luns_for_host('dummy')
|
||||
mock_request.assert_called_with('/show/maps/initiator', 'dummy')
|
||||
self.client._fw_type = 'I'
|
||||
self.client.list_luns_for_host('dummy')
|
||||
mock_request.assert_called_with('/show/maps/initiator', 'dummy')
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import hashlib
|
||||
import math
|
||||
import re
|
||||
import time
|
||||
|
||||
from lxml import etree
|
||||
@ -44,7 +45,8 @@ class STXClient(object):
|
||||
self._session_key = None
|
||||
self.ssl_verify = ssl_verify
|
||||
self._set_host(self._mgmt_ip_addrs[0])
|
||||
self._fw = ''
|
||||
self._fw_type = ''
|
||||
self._fw_rev = 0
|
||||
self._driver_name = self.__class__.__name__.split('.')[0]
|
||||
self._array_name = 'unknown'
|
||||
self._luns_in_use_by_host = {}
|
||||
@ -245,8 +247,19 @@ class STXClient(object):
|
||||
return False
|
||||
|
||||
def is_titanium(self):
|
||||
"""True if array is an older generation."""
|
||||
return True if len(self._fw) > 0 and self._fw[0] == 'T' else False
|
||||
"""True for older array firmware."""
|
||||
return self._fw_type == 'T'
|
||||
|
||||
def is_g5_fw(self):
|
||||
"""Identify firmware updated in/after 2020.
|
||||
|
||||
Long-deprecated commands have or will be removed.
|
||||
"""
|
||||
if self._fw_type in ['I', 'V']:
|
||||
return True
|
||||
if self._fw_type == 'G' and self._fw_rev >= 280:
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_volume(self, name, size, backend_name, backend_type):
|
||||
# NOTE: size is in this format: [0-9]+GiB
|
||||
@ -390,7 +403,8 @@ class STXClient(object):
|
||||
if not isinstance(ids, list):
|
||||
ids = [ids]
|
||||
try:
|
||||
xml = self._request('/show/volume-maps', volume_name)
|
||||
cmd = "/show/volume-maps" if self.is_titanium() else "/show/maps"
|
||||
xml = self._request(cmd, volume_name)
|
||||
|
||||
for obj in xml.xpath("//OBJECT[@basetype='volume-view-mappings']"):
|
||||
lun = obj.findtext("PROPERTY[@name='lun']")
|
||||
@ -420,6 +434,10 @@ class STXClient(object):
|
||||
if host_status != 0:
|
||||
hostname = self._safe_hostname(connector['host'])
|
||||
try:
|
||||
if self.is_g5_fw():
|
||||
self._request("/set/initiator", nickname=hostname,
|
||||
id=host)
|
||||
else:
|
||||
self._request("/create/host", hostname, id=host)
|
||||
except stx_exception.RequestError as e:
|
||||
# -10058: The host identifier or nickname is already in use
|
||||
@ -434,6 +452,13 @@ class STXClient(object):
|
||||
|
||||
while lun < 255:
|
||||
try:
|
||||
if self.is_g5_fw():
|
||||
self._request("/map/volume",
|
||||
volume_name,
|
||||
lun=str(lun),
|
||||
initiator=host,
|
||||
access="rw")
|
||||
else:
|
||||
self._request("/map/volume",
|
||||
volume_name,
|
||||
lun=str(lun),
|
||||
@ -468,6 +493,9 @@ class STXClient(object):
|
||||
else:
|
||||
host = connector['initiator']
|
||||
try:
|
||||
if self.is_g5_fw():
|
||||
self._request("/unmap/volume", volume_name, initiator=host)
|
||||
else:
|
||||
self._request("/unmap/volume", volume_name, host=host)
|
||||
except stx_exception.RequestError as e:
|
||||
# -10050 => The volume was not found on this system.
|
||||
@ -574,12 +602,20 @@ class STXClient(object):
|
||||
return True
|
||||
|
||||
def _check_host(self, host):
|
||||
host_status = -1
|
||||
"""Return 0 if initiator id found in the array's host table."""
|
||||
if self.is_g5_fw():
|
||||
tree = self._request("/show/initiators")
|
||||
for prop in tree.xpath("//PROPERTY[@name='id' and text()='%s']"
|
||||
% host):
|
||||
return 0
|
||||
return -1
|
||||
|
||||
# Use older syntax for older firmware
|
||||
tree = self._request("/show/hosts")
|
||||
for prop in tree.xpath("//PROPERTY[@name='host-id' and text()='%s']"
|
||||
% host):
|
||||
host_status = 0
|
||||
return host_status
|
||||
return 0
|
||||
return -1
|
||||
|
||||
def _safe_hostname(self, hostname):
|
||||
"""Modify an initiator name to match firmware requirements.
|
||||
@ -650,7 +686,16 @@ class STXClient(object):
|
||||
return self._get_size(size)
|
||||
|
||||
def get_firmware_version(self):
|
||||
"""Get the array firmware version"""
|
||||
tree = self._request("/show/controllers")
|
||||
self._fw = tree.xpath("//PROPERTY[@name='sc-fw']")[0].text
|
||||
LOG.debug("Array firmware is %s\n", self._fw)
|
||||
return self._fw
|
||||
s = tree.xpath("//PROPERTY[@name='sc-fw']")[0].text
|
||||
if len(s):
|
||||
self._fw_type = s[0]
|
||||
fw_rev_match = re.match('^[^0-9]*([0-9]+).*', s)
|
||||
if not fw_rev_match:
|
||||
LOG.error('firmware revision not found in "%s"', s)
|
||||
return s
|
||||
self._fw_rev = int(fw_rev_match.groups()[0])
|
||||
LOG.debug("Array firmware is %s (%s%d)\n",
|
||||
s, self._fw_type, self._fw_rev)
|
||||
return s
|
||||
|
@ -2,9 +2,9 @@
|
||||
HPE MSA Fibre Channel and iSCSI drivers
|
||||
=======================================
|
||||
|
||||
The ``HPMSAFCDriver`` and ``HPMSAISCSIDriver`` Cinder drivers allow the
|
||||
HPE MSA 2050, 1050, 2040, and 1040 arrays to be used for Block Storage in
|
||||
OpenStack deployments.
|
||||
The ``HPMSAFCDriver`` and ``HPMSAISCSIDriver`` Cinder drivers allow
|
||||
the HPE MSA 2060, 1060, 2050, 1050, 2040, and 1040 arrays to be used
|
||||
for Block Storage in OpenStack deployments.
|
||||
|
||||
System requirements
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
7
releasenotes/notes/msa2060-99150398a9c416f6.yaml
Normal file
7
releasenotes/notes/msa2060-99150398a9c416f6.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
HPMSA driver: The HPE MSA driver was updated to avoid using
|
||||
deprecated command syntax that has been removed in the latest
|
||||
version of the MSA API. This is required to support the newest
|
||||
firmware in the MSA 2060/1060.
|
Loading…
Reference in New Issue
Block a user