mirror of
https://opendev.org/x/pyghmi
synced 2026-03-29 06:13:30 +00:00
Implement Thinkserver OEM SEL decode
The Thinkserver platform may have a number of OEM entries. Provide a reasonable decode of those events. Change-Id: Ie4be9848293c00e8b0d3490075101ed8be1cf72e
This commit is contained in:
@@ -303,11 +303,11 @@ def _fix_sel_time(records, ipmicmd):
|
||||
record = records[index]
|
||||
if 'timecode' not in record or record['timecode'] == 0xffffffff:
|
||||
continue
|
||||
if (record['event'] == 'Clock time change' and
|
||||
if ('event' in record and record['event'] == 'Clock time change' and
|
||||
record['event_data'] == 'After'):
|
||||
newtimestamp = record['timecode']
|
||||
trimindexes.append(index)
|
||||
elif (record['event'] == 'Clock time change' and
|
||||
elif ('event' in record and record['event'] == 'Clock time change' and
|
||||
record['event_data'] == 'Before'):
|
||||
if newtimestamp:
|
||||
if record['timecode'] < 0x20000000:
|
||||
@@ -453,7 +453,7 @@ class EventHandler(object):
|
||||
# In this class of OEM message, all bytes are OEM, interpretation
|
||||
# is wholly left up to the OEM layer, using the OEM ID of the BMC
|
||||
event['oemdata'] = selentry[3:]
|
||||
self._ipmicmd._oem.process_event(event)
|
||||
self._ipmicmd._oem.process_event(event, self._ipmicmd, selentry)
|
||||
if 'event_type_byte' in event:
|
||||
del event['event_type_byte']
|
||||
if 'event_data_bytes' in event:
|
||||
|
||||
@@ -27,7 +27,7 @@ class OEMHandler(object):
|
||||
def __init__(self, oemid, ipmicmd):
|
||||
pass
|
||||
|
||||
def process_event(self, event):
|
||||
def process_event(self, event, ipmicmd, seldata):
|
||||
"""Modify an event according with OEM understanding.
|
||||
|
||||
Given an event, allow an OEM module to augment it. For example,
|
||||
|
||||
@@ -14,9 +14,52 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pyghmi.constants as pygconst
|
||||
import pyghmi.ipmi.oem.generic as generic
|
||||
import pyghmi.ipmi.private.constants as ipmiconst
|
||||
import pyghmi.ipmi.private.util as util
|
||||
|
||||
firmware_types = {
|
||||
1: 'Management Controller',
|
||||
2: 'UEFI/BIOS',
|
||||
3: 'CPLD',
|
||||
4: 'Power Supply',
|
||||
5: 'Storage Adapter',
|
||||
6: 'Add-in Adapter',
|
||||
}
|
||||
|
||||
firmware_event = {
|
||||
0: ('Update failed', pygconst.Health.Failed),
|
||||
1: ('Update succeeded', pygconst.Health.Ok),
|
||||
2: ('Update aborted', pygconst.Health.Ok),
|
||||
3: ('Unknown', pygconst.Health.Warning),
|
||||
}
|
||||
|
||||
me_status = {
|
||||
0: ('Recovery GPIO forced', pygconst.Health.Warning),
|
||||
1: ('ME Image corrupt', pygconst.Health.Critical),
|
||||
2: ('Flash erase error', pygconst.Health.Critical),
|
||||
3: ('Unspecified flash state', pygconst.Health.Warning),
|
||||
4: ('ME watchdog timeout', pygconst.Health.Critical),
|
||||
5: ('ME platform reboot', pygconst.Health.Critical),
|
||||
6: ('ME update', pygconst.Health.Ok),
|
||||
7: ('Manufacturing error', pygconst.Health.Critical),
|
||||
8: ('ME Flash storage integrity error', pygconst.Health.Critical),
|
||||
9: ('ME firmware exception', pygconst.Health.Critical), # event data 3..
|
||||
0xa: ('ME firmware worn', pygconst.Health.Warning),
|
||||
0xc: ('Invalid SCMP state', pygconst.Health.Warning),
|
||||
0xd: ('PECI over DMI failure', pygconst.Health.Warning),
|
||||
0xe: ('MCTP interface failure', pygconst.Health.Warning),
|
||||
0xf: ('Auto configuration completed', pygconst.Health.Ok),
|
||||
}
|
||||
|
||||
me_flash_status = {
|
||||
0: ('ME flash corrupted', pygconst.Health.Critical),
|
||||
1: ('ME flash erase limit reached', pygconst.Health.Critical),
|
||||
2: ('ME flash write limit reached', pygconst.Health.Critical),
|
||||
3: ('ME flash write enabled', pygconst.Health.Ok),
|
||||
}
|
||||
|
||||
|
||||
class OEMHandler(generic.OEMHandler):
|
||||
# noinspection PyUnusedLocal
|
||||
@@ -25,11 +68,74 @@ class OEMHandler(generic.OEMHandler):
|
||||
# variations. For example System X versus Thinkserver
|
||||
self.oemid = oemid
|
||||
|
||||
def process_event(self, event):
|
||||
def process_event(self, event, ipmicmd, seldata):
|
||||
if 'oemdata' in event:
|
||||
event['event'] = 'OEM event: {0}'.format(repr(event['oemdata']))
|
||||
oemtype = seldata[2]
|
||||
oemdata = event['oemdata']
|
||||
if oemtype == 0xd0: # firmware update
|
||||
event['component'] = firmware_types.get(oemdata[0], None)
|
||||
event['component_type'] = ipmiconst.sensor_type_codes[0x2b]
|
||||
slotnumber = (oemdata[1] & 0b11111000) >> 3
|
||||
if slotnumber:
|
||||
event['component'] += ' {0}'.format(slotnumber)
|
||||
event['event'], event['severity'] = \
|
||||
firmware_event[oemdata[1] & 0b111]
|
||||
event['event_data'] = '{0}.{1}'.format(oemdata[2], oemdata[3])
|
||||
elif oemtype == 0xd1: # BIOS recovery
|
||||
event['severity'] = pygconst.Health.Warning
|
||||
event['component'] = 'BIOS/UEFI'
|
||||
event['component_type'] = ipmiconst.sensor_type_codes[0xf]
|
||||
status = oemdata[0]
|
||||
method = (status & 0b11110000) >> 4
|
||||
status = (status & 0b1111)
|
||||
if method == 1:
|
||||
event['event'] = 'Automatic recovery'
|
||||
elif method == 2:
|
||||
event['event'] = 'Manual recovery'
|
||||
if status == 0:
|
||||
event['event'] += '- Failed'
|
||||
event['severity'] = pygconst.Health.Failed
|
||||
if oemdata[1] == 0x1:
|
||||
event['event'] += '- BIOS recovery image not found'
|
||||
event['event_data'] = '{0}.{1}'.format(oemdata[2], oemdata[3])
|
||||
elif oemtype == 0xd2: # eMMC status
|
||||
if oemdata[0] == 1:
|
||||
event['component'] = 'eMMC'
|
||||
event['component_type'] = ipmiconst.sensor_type_codes[0xc]
|
||||
if oemdata[0] == 1:
|
||||
event['event'] = 'eMMC Format error'
|
||||
event['severity'] = pygconst.Health.Failed
|
||||
elif oemtype == 0xd3:
|
||||
if oemdata[0] == 1:
|
||||
event['event'] = 'User privilege modification'
|
||||
event['severity'] = pygconst.Health.Ok
|
||||
event['component'] = 'User Privilege'
|
||||
event['component_type'] = ipmiconst.sensor_type_codes[6]
|
||||
event['event_data'] = \
|
||||
'User {0} on channel {1} had privilege changed ' \
|
||||
'from {2} to {3}'.format(
|
||||
oemdata[2], oemdata[1], oemdata[3] & 0b1111,
|
||||
(oemdata[3] & 0b11110000) >> 4
|
||||
)
|
||||
else:
|
||||
event['event'] = 'OEM event: {0}'.format(
|
||||
' '.join(format(x, '02x') for x in event['oemdata']))
|
||||
del event['oemdata']
|
||||
return
|
||||
evdata = event['event_data_bytes']
|
||||
if event['event_type_byte'] == 0x75: # ME event
|
||||
event['component'] = 'ME Firmware'
|
||||
event['component_type'] = ipmiconst.sensor_type_codes[0xf]
|
||||
event['event'], event['severity'] = me_status.get(
|
||||
evdata[1], ('Unknown', pygconst.Health.Warning))
|
||||
if evdata[1] == 3:
|
||||
event['event'], event['severity'] = me_flash_status.get(
|
||||
evdata[2], ('Unknown state', pygconst.Health.Warning))
|
||||
elif evdata[1] == 9:
|
||||
event['event'] += ' (0x{0:2x})'.format(evdata[2])
|
||||
elif evdata[1] == 0xf and evdata[2] & 0b10000000:
|
||||
event['event'] = 'Auto configuration failed'
|
||||
event['severity'] = pygconst.Health.Critical
|
||||
# For HDD bay events, the event data 2 is the bay, modify
|
||||
# the description to be more specific
|
||||
if (event['event_type_byte'] == 0x6f and
|
||||
@@ -55,14 +161,14 @@ class OEMHandler(generic.OEMHandler):
|
||||
if mac2 not in ('00:00:00:00:00:00', ''):
|
||||
fru['MAC Address 2'] = mac2
|
||||
try:
|
||||
# The product_extra field is UUID as the system would present
|
||||
# in DMI. This is different than the two UUIDs that
|
||||
# it returns for get device and get system uuid...
|
||||
# The product_extra field is UUID as the system would present
|
||||
# in DMI. This is different than the two UUIDs that
|
||||
# it returns for get device and get system uuid...
|
||||
byteguid = fru['product_extra'][0]
|
||||
# It can present itself as claiming to be ASCII when it
|
||||
# is actually raw hex. As a result it triggers the mechanism
|
||||
# to strip \x00 from the end of text strings. Work around this
|
||||
# by padding with \x00 to the right if the string is not 16 long
|
||||
# It can present itself as claiming to be ASCII when it
|
||||
# is actually raw hex. As a result it triggers the mechanism
|
||||
# to strip \x00 from the end of text strings. Work around this
|
||||
# by padding with \x00 to the right if less than 16 long
|
||||
byteguid.extend('\x00' * (16 - len(byteguid)))
|
||||
fru['UUID'] = util.decode_wireformat_uuid(byteguid)
|
||||
except (AttributeError, KeyError):
|
||||
|
||||
Reference in New Issue
Block a user