diff --git a/pyghmi/ipmi/events.py b/pyghmi/ipmi/events.py index 47974237..7a9c4c2d 100644 --- a/pyghmi/ipmi/events.py +++ b/pyghmi/ipmi/events.py @@ -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: diff --git a/pyghmi/ipmi/oem/generic.py b/pyghmi/ipmi/oem/generic.py index be3195f3..55ee0f03 100644 --- a/pyghmi/ipmi/oem/generic.py +++ b/pyghmi/ipmi/oem/generic.py @@ -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, diff --git a/pyghmi/ipmi/oem/lenovo.py b/pyghmi/ipmi/oem/lenovo.py index cd2e197b..38754d8d 100644 --- a/pyghmi/ipmi/oem/lenovo.py +++ b/pyghmi/ipmi/oem/lenovo.py @@ -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):