Merge "Fix ssacli output parsing. New untitest for the fixed issue added."
This commit is contained in:
commit
7b06777a13
@ -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')
|
||||
|
@ -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
|
||||
'''
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user