Merge "Critical fix for MSA 2060 and MSA 1060"

This commit is contained in:
Zuul 2020-10-07 07:02:17 +00:00 committed by Gerrit Code Review
commit 6ad1ab0c72
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> resp_fw = '''<RESPONSE><PROPERTY name="sc-fw">GLS220R001</PROPERTY>
<PROPERTY name="return-code">0</PROPERTY></RESPONSE>''' <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> resp_system = '''<RESPONSE>
<PROPERTY name="midplane-serial-number">00C0FFEEEEEE</PROPERTY> <PROPERTY name="midplane-serial-number">00C0FFEEEEEE</PROPERTY>
<PROPERTY name="return-code">0</PROPERTY> <PROPERTY name="return-code">0</PROPERTY>
@ -186,6 +189,12 @@ class TestSeagateClient(test.TestCase):
self.assertRaises(stx_exception.AuthenticationError, self.assertRaises(stx_exception.AuthenticationError,
self.client.login) 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] m.text.encode.side_effect = [resp_login, resp_fw, resp_system]
self.client.login() self.client.login()
self.assertEqual(session_key, self.client._session_key) self.assertEqual(session_key, self.client._session_key)
@ -313,11 +322,15 @@ class TestSeagateClient(test.TestCase):
@mock.patch.object(STXClient, '_request') @mock.patch.object(STXClient, '_request')
def test_list_luns_for_host(self, mock_request): def test_list_luns_for_host(self, mock_request):
mock_request.side_effect = [etree.XML(response_no_lun), mock_request.side_effect = [etree.XML(response_no_lun),
etree.XML(response_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') self.client.list_luns_for_host('dummy')
mock_request.assert_called_with('/show/host-maps', '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') self.client.list_luns_for_host('dummy')
mock_request.assert_called_with('/show/maps/initiator', 'dummy') mock_request.assert_called_with('/show/maps/initiator', 'dummy')

View File

@ -17,6 +17,7 @@
import hashlib import hashlib
import math import math
import re
import time import time
from lxml import etree from lxml import etree
@ -44,7 +45,8 @@ class STXClient(object):
self._session_key = None self._session_key = None
self.ssl_verify = ssl_verify self.ssl_verify = ssl_verify
self._set_host(self._mgmt_ip_addrs[0]) 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._driver_name = self.__class__.__name__.split('.')[0]
self._array_name = 'unknown' self._array_name = 'unknown'
self._luns_in_use_by_host = {} self._luns_in_use_by_host = {}
@ -245,8 +247,19 @@ class STXClient(object):
return False return False
def is_titanium(self): def is_titanium(self):
"""True if array is an older generation.""" """True for older array firmware."""
return True if len(self._fw) > 0 and self._fw[0] == 'T' else False 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): def create_volume(self, name, size, backend_name, backend_type):
# NOTE: size is in this format: [0-9]+GiB # NOTE: size is in this format: [0-9]+GiB
@ -390,7 +403,8 @@ class STXClient(object):
if not isinstance(ids, list): if not isinstance(ids, list):
ids = [ids] ids = [ids]
try: 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']"): for obj in xml.xpath("//OBJECT[@basetype='volume-view-mappings']"):
lun = obj.findtext("PROPERTY[@name='lun']") lun = obj.findtext("PROPERTY[@name='lun']")
@ -420,7 +434,11 @@ class STXClient(object):
if host_status != 0: if host_status != 0:
hostname = self._safe_hostname(connector['host']) hostname = self._safe_hostname(connector['host'])
try: 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: except stx_exception.RequestError as e:
# -10058: The host identifier or nickname is already in use # -10058: The host identifier or nickname is already in use
if '(-10058)' in e.msg: if '(-10058)' in e.msg:
@ -434,11 +452,18 @@ class STXClient(object):
while lun < 255: while lun < 255:
try: try:
self._request("/map/volume", if self.is_g5_fw():
volume_name, self._request("/map/volume",
lun=str(lun), volume_name,
host=host, lun=str(lun),
access="rw") initiator=host,
access="rw")
else:
self._request("/map/volume",
volume_name,
lun=str(lun),
host=host,
access="rw")
return lun return lun
except stx_exception.RequestError as e: except stx_exception.RequestError as e:
# -3177 => "The specified LUN overlaps a previously defined LUN # -3177 => "The specified LUN overlaps a previously defined LUN
@ -468,7 +493,10 @@ class STXClient(object):
else: else:
host = connector['initiator'] host = connector['initiator']
try: 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: except stx_exception.RequestError as e:
# -10050 => The volume was not found on this system. # -10050 => The volume was not found on this system.
# This can occur during controller failover. # This can occur during controller failover.
@ -574,12 +602,20 @@ class STXClient(object):
return True return True
def _check_host(self, host): 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") tree = self._request("/show/hosts")
for prop in tree.xpath("//PROPERTY[@name='host-id' and text()='%s']" for prop in tree.xpath("//PROPERTY[@name='host-id' and text()='%s']"
% host): % host):
host_status = 0 return 0
return host_status return -1
def _safe_hostname(self, hostname): def _safe_hostname(self, hostname):
"""Modify an initiator name to match firmware requirements. """Modify an initiator name to match firmware requirements.
@ -650,7 +686,16 @@ class STXClient(object):
return self._get_size(size) return self._get_size(size)
def get_firmware_version(self): def get_firmware_version(self):
"""Get the array firmware version"""
tree = self._request("/show/controllers") tree = self._request("/show/controllers")
self._fw = tree.xpath("//PROPERTY[@name='sc-fw']")[0].text s = tree.xpath("//PROPERTY[@name='sc-fw']")[0].text
LOG.debug("Array firmware is %s\n", self._fw) if len(s):
return self._fw 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 HPE MSA Fibre Channel and iSCSI drivers
======================================= =======================================
The ``HPMSAFCDriver`` and ``HPMSAISCSIDriver`` Cinder drivers allow the The ``HPMSAFCDriver`` and ``HPMSAISCSIDriver`` Cinder drivers allow
HPE MSA 2050, 1050, 2040, and 1040 arrays to be used for Block Storage in the HPE MSA 2060, 1060, 2050, 1050, 2040, and 1040 arrays to be used
OpenStack deployments. for Block Storage in OpenStack deployments.
System requirements 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.