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:
Chris M 2020-09-30 02:04:01 +00:00
parent c8ce118f0a
commit d739b86714
4 changed files with 87 additions and 22 deletions

View File

@ -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')

View File

@ -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,7 +434,11 @@ class STXClient(object):
if host_status != 0:
hostname = self._safe_hostname(connector['host'])
try:
self._request("/create/host", hostname, id=host)
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
if '(-10058)' in e.msg:
@ -434,11 +452,18 @@ class STXClient(object):
while lun < 255:
try:
self._request("/map/volume",
volume_name,
lun=str(lun),
host=host,
access="rw")
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),
host=host,
access="rw")
return lun
except stx_exception.RequestError as e:
# -3177 => "The specified LUN overlaps a previously defined LUN
@ -468,7 +493,10 @@ class STXClient(object):
else:
host = connector['initiator']
try:
self._request("/unmap/volume", volume_name, host=host)
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.
# This can occur during controller failover.
@ -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

View File

@ -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
~~~~~~~~~~~~~~~~~~~

View 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.