Merge "Fix ssacli output parsing. New untitest for the fixed issue added."

This commit is contained in:
Zuul 2018-10-25 10:45:01 +00:00 committed by Gerrit Code Review
commit 7b06777a13
3 changed files with 134 additions and 29 deletions

View File

@ -37,7 +37,7 @@ def _get_key_value(string):
key = ''
value = ''
try:
key, value = string.split(':')
key, value = string.split(': ')
except ValueError:
# This handles the case when the property of a logical drive
# returned is as follows. Here we cannot split by ':' because
@ -47,16 +47,18 @@ def _get_key_value(string):
string = string.lstrip(' ')
if string.startswith('physicaldrive'):
fields = string.split(' ')
key = fields[0]
# Include fields[1] to key to avoid duplicate pairs
# with the same 'physicaldrive' key
key = fields[0] + " " + fields[1]
value = fields[1]
else:
# TODO(rameshg87): Check if this ever occurs.
return None, None
return string.strip(' '), None
return key.lstrip(' ').rstrip(' '), value.lstrip(' ').rstrip(' ')
return key.strip(' '), value.strip(' ')
def _get_dict(lines, start_index, indentation):
def _get_dict(lines, start_index, indentation, deep):
"""Recursive function for parsing hpssacli/ssacli output."""
info = {}
@ -68,37 +70,38 @@ def _get_dict(lines, start_index, indentation):
current_line = lines[i]
current_line_indentation = _get_indentation(current_line)
# Check for multi-level returns
if current_line_indentation < indentation:
return info, i-1
if current_line_indentation == indentation:
current_item = current_line.lstrip(' ')
info[current_item] = {}
i = i + 1
continue
if i >= len(lines) - 1:
if i < len(lines) - 1:
next_line_indentation = _get_indentation(lines[i+1])
else:
next_line_indentation = current_line_indentation
if next_line_indentation > current_line_indentation:
ret_dict, i = _get_dict(lines, i, current_line_indentation, deep+1)
for key in ret_dict.keys():
if key in info[current_item]:
info[current_item][key].update(ret_dict[key])
else:
info[current_item][key] = ret_dict[key]
else:
key, value = _get_key_value(current_line)
# If this is some unparsable information, then
# just skip it.
if key:
info[current_item][key] = value
# Do not return if it's the top level of recursion
if next_line_indentation < current_line_indentation and deep > 0:
return info, i
next_line = lines[i+1]
next_line_indentation = _get_indentation(next_line)
if current_line_indentation == next_line_indentation:
key, value = _get_key_value(current_line)
if key:
info[current_item][key] = value
i = i + 1
elif next_line_indentation > current_line_indentation:
ret_dict, j = _get_dict(lines, i, current_line_indentation)
info[current_item].update(ret_dict)
i = j + 1
elif next_line_indentation < current_line_indentation:
key, value = _get_key_value(current_line)
if key:
info[current_item][key] = value
return info, i
i = i + 1
return info, i
@ -113,7 +116,7 @@ def _convert_to_dict(stdout):
lines = stdout.split("\n")
lines = list(filter(None, lines))
info_dict, j = _get_dict(lines, 0, 0)
info_dict, j = _get_dict(lines, 0, 0, 0)
return info_dict
@ -556,14 +559,22 @@ class LogicalDrive(object):
# 'string_to_bytes' takes care of converting any returned
# (like 500MB, 25GB) unit of storage space to bytes (Integer value).
# It requires space to be stripped.
size = self.properties['Size'].replace(' ', '')
try:
size = self.properties['Size'].replace(' ', '')
# TODO(rameshg87): Reduce the disk size by 1 to make sure Ironic
# has enough space to write a config drive. Remove this when
# Ironic doesn't need it.
self.size_gb = int(strutils.string_to_bytes(size,
return_int=True) /
(1024*1024*1024)) - 1
except KeyError:
msg = ("Can't get 'Size' parameter from ssacli output for logical "
"disk '%(logical_disk)s' of RAID array '%(array)s' in "
"controller '%(controller)s'." %
{'logical_disk': self.id,
'array': self.parent.id,
'controller': self.parent.parent.id})
raise exception.HPSSAOperationError(reason=msg)
except ValueError:
msg = ("ssacli returned unknown size '%(size)s' for logical "
"disk '%(logical_disk)s' of RAID array '%(array)s' in "
@ -617,14 +628,21 @@ class PhysicalDrive(object):
# Strip off physicaldrive before storing it in id
self.id = id[14:]
size = self.properties['Size'].replace(' ', '')
# 'string_to_bytes' takes care of converting any returned
# (like 500MB, 25GB) unit of storage space to bytes (Integer value).
# It requires space to be stripped.
try:
size = self.properties['Size'].replace(' ', '')
self.size_gb = int(strutils.string_to_bytes(size,
return_int=True) /
(1024*1024*1024))
except KeyError:
msg = ("Can't get 'Size' parameter from ssacli output for "
"physical disk '%(physical_disk)s' of controller "
"'%(controller)s'." %
{'physical_disk': self.id,
'controller': self.parent.parent.id})
raise exception.HPSSAOperationError(reason=msg)
except ValueError:
msg = ("ssacli returned unknown size '%(size)s' for physical "
"disk '%(physical_disk)s' of controller "
@ -633,7 +651,16 @@ class PhysicalDrive(object):
'controller': self.parent.id})
raise exception.HPSSAOperationError(reason=msg)
ssa_interface = self.properties['Interface Type']
try:
ssa_interface = self.properties['Interface Type']
except KeyError:
msg = ("Can't get 'Interface Type' parameter from ssacli output "
"for physical disk '%(physical_disk)s' of controller "
"'%(controller)s'." %
{'physical_disk': self.id,
'controller': self.parent.parent.id})
raise exception.HPSSAOperationError(reason=msg)
self.interface_type = constants.get_interface_type(ssa_interface)
self.disk_type = constants.get_disk_type(ssa_interface)
self.model = self.properties.get('Model')

View File

@ -2507,3 +2507,62 @@ Smart Array P440 in Slot 2
Sanitize Estimated Max Erase Time: 0 hour(s)36 minute(s)
Unrestricted Sanitize Supported: False
'''
SSACLI_PARSING_TESTS = '''
Smart HBA H240 in Slot 1 (RAID Mode)
Slot: 1
Controller Mode: RAID Mode
Internal Drive Cage at Port 1I, Box 1, OK
Drive Bays: 4
Port: 1I
Box: 1
Physical Drives
physicaldrive 1I:1:4 (port 1I:box 1:bay 4, SAS HDD, 900 GB, OK)
physicaldrive 1I:1:3 (port 1I:box 1:bay 3, SAS HDD, 900 GB, OK)
Internal Drive Cage at Port 2I, Box 1, OK
Drive Bays: 4
Port: 2I
Box: 1
Physical Drives
physicaldrive 2I:1:5 (port 2I:box 1:bay 5, SAS HDD, 900 GB, OK)
physicaldrive 2I:1:6 (port 2I:box 1:bay 6, SAS HDD, 900 GB, OK)
Unassigned
physicaldrive 1I:1:4
Port: 1I
Box: 1
Bay: 4
Size: 900 GB
Interface Type: SAS
Smart HBA H240 in Slot 2 (RAID Mode)
Slot: 2
Controller Mode: RAID Mode
PCI Address (Domain:Bus:Device.Function): 0000:0B:00.0
Array: H
Interface Type: SAS
Logical Drive: 8
Size: 838.3 GB
Status: OK
physicaldrive 2I:2:8
Port: 2I
Box: 2
Bay: 8
Size: 900 GB
Interface Type: SAS
Smart HBA H240 in Slot 3 (RAID Mode)
Slot: 3
Controller Mode: RAID Mode
Smart HBA H240ar in Slot 0 (Embedded) (RAID Mode)
Bus Interface: PCI
Slot: 0
'''

View File

@ -606,6 +606,25 @@ class PhysicalDriveTest(testtools.TestCase):
self.assertEqual('ready', ret['status'])
self.assertEqual('OK', ret['erase_status'])
def test_ssacli_output_parsing(self, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.SSACLI_PARSING_TESTS
server = objects.Server()
self.assertEqual(4, len(server.controllers))
id = 'Smart HBA H240ar in Slot 0 (Embedded) (RAID Mode)'
self.assertIsNotNone(server.get_controller_by_id(id))
id = 'Smart HBA H240 in Slot 2 (RAID Mode)'
controller = server.get_controller_by_id(id)
self.assertIsInstance(controller.properties, dict)
self.assertIn("PCI Address (Domain:Bus:Device.Function)",
controller.properties)
id = 'Smart HBA H240 in Slot 1 (RAID Mode)'
controller = server.get_controller_by_id(id)
self.assertIsInstance(controller.properties, dict)
self.assertEqual(4, len(controller.properties['Physical Drives']))
class PrivateMethodsTestCase(testtools.TestCase):