From 1a6bfbdbf13aa4fd439a0c0a14d8d3cc7d6c947a Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 4 Feb 2025 09:51:52 -0500 Subject: [PATCH] Relocate event log to oem, override for SMMv3 SMMv3 does something a bit peculiar, and to accomodate it, provide a way for the OEM code to describe additional log services not attached to the BMC resource. Change-Id: I01a6ba50ac462840749eeb6b07400b66930beb1d --- pyghmi/redfish/command.py | 65 +-------------------------- pyghmi/redfish/oem/generic.py | 74 +++++++++++++++++++++++++++++++ pyghmi/redfish/oem/lenovo/smm3.py | 4 ++ 3 files changed, 79 insertions(+), 64 deletions(-) diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index 5ea2775c..635a7918 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -1141,70 +1141,7 @@ class Command(object): return self.oem.get_description(self) def get_event_log(self, clear=False): - bmcinfo = self._do_web_request(self._bmcurl) - lsurl = bmcinfo.get('LogServices', {}).get('@odata.id', None) - if not lsurl: - return - currtime = bmcinfo.get('DateTime', None) - correction = timedelta(0) - utz = tz.tzoffset('', 0) - ltz = tz.gettz() - if currtime: - currtime = parse_time(currtime) - if currtime: - now = datetime.now(utz) - try: - correction = now - currtime - except TypeError: - correction = now - currtime.replace(tzinfo=utz) - lurls = self._do_web_request(lsurl).get('Members', []) - for lurl in lurls: - lurl = lurl['@odata.id'] - loginfo = self._do_web_request(lurl, cache=(not clear)) - entriesurl = loginfo.get('Entries', {}).get('@odata.id', None) - if not entriesurl: - continue - logid = loginfo.get('Id', '') - entries = self._do_web_request(entriesurl, cache=False) - if clear: - # The clear is against the log service etag, not entries - # so we have to fetch service etag after we fetch entries - # until we can verify that the etag is consistent to prove - # that the clear is atomic - newloginfo = self._do_web_request(lurl, cache=False) - clearurl = newloginfo.get('Actions', {}).get( - '#LogService.ClearLog', {}).get('target', '') - while clearurl: - try: - self._do_web_request(clearurl, method='POST', - payload={}) - clearurl = False - except exc.PyghmiException as e: - if 'EtagPreconditionalFailed' not in str(e): - raise - # This doesn't guarantee atomicity, but it mitigates - # greatly. Unfortunately some implementations - # mutate the tag endlessly and we have no hope - entries = self._do_web_request(entriesurl, cache=False) - newloginfo = self._do_web_request(lurl, cache=False) - for log in entries.get('Members', []): - if ('Created' not in log and 'Message' not in log - and 'Severity' not in log): - # without any data, this log entry isn't actionable - continue - record = {} - record['log_id'] = logid - parsedtime = parse_time(log.get('Created', '')) - if parsedtime: - entime = parsedtime + correction - entime = entime.astimezone(ltz) - record['timestamp'] = entime.strftime('%Y-%m-%dT%H:%M:%S') - else: - record['timestamp'] = log.get('Created', '') - record['message'] = log.get('Message', None) - record['severity'] = _healthmap.get( - log.get('Severity', 'Warning'), const.Health.Ok) - yield record + return self.oem.get_event_log(clear, self) def _get_chassis_env(self, chassis): chassisurl = chassis['@odata.id'] diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index ab4a7bb9..4967f8fe 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -17,12 +17,17 @@ from fnmatch import fnmatch import json import os import re +from datetime import datetime +from datetime import timedelta +from dateutil import tz import time import pyghmi.constants as const import pyghmi.exceptions as exc import pyghmi.media as media import pyghmi.util.webclient as webclient +from pyghmi.util.parse import parse_time + class SensorReading(object): @@ -223,6 +228,75 @@ class OEMHandler(object): cputemps.append(temp) return cputemps + def get_event_log(self, clear=False, fishclient=None, extraurls=[]): + bmcinfo = self._do_web_request(fishclient._bmcurl) + lsurl = bmcinfo.get('LogServices', {}).get('@odata.id', None) + if not lsurl: + return + currtime = bmcinfo.get('DateTime', None) + correction = timedelta(0) + utz = tz.tzoffset('', 0) + ltz = tz.gettz() + if currtime: + currtime = parse_time(currtime) + if currtime: + now = datetime.now(utz) + try: + correction = now - currtime + except TypeError: + correction = now - currtime.replace(tzinfo=utz) + lurls = self._do_web_request(lsurl).get('Members', []) + lurls.extend(extraurls) + for lurl in lurls: + lurl = lurl['@odata.id'] + loginfo = self._do_web_request(lurl, cache=(not clear)) + entriesurl = loginfo.get('Entries', {}).get('@odata.id', None) + if not entriesurl: + continue + logid = loginfo.get('Id', '') + entries = self._do_web_request(entriesurl, cache=False) + if clear: + # The clear is against the log service etag, not entries + # so we have to fetch service etag after we fetch entries + # until we can verify that the etag is consistent to prove + # that the clear is atomic + newloginfo = self._do_web_request(lurl, cache=False) + clearurl = newloginfo.get('Actions', {}).get( + '#LogService.ClearLog', {}).get('target', '') + while clearurl: + try: + self._do_web_request(clearurl, method='POST', + payload={}) + clearurl = False + except exc.PyghmiException as e: + if 'EtagPreconditionalFailed' not in str(e): + raise + # This doesn't guarantee atomicity, but it mitigates + # greatly. Unfortunately some implementations + # mutate the tag endlessly and we have no hope + entries = self._do_web_request(entriesurl, cache=False) + newloginfo = self._do_web_request(lurl, cache=False) + for log in entries.get('Members', []): + if ('Created' not in log and 'Message' not in log + and 'Severity' not in log): + # without any data, this log entry isn't actionable + continue + record = {} + record['log_id'] = logid + parsedtime = parse_time(log.get('Created', '')) + if not parsedtime: + parsedtime = parse_time(log.get('EventTimestamp', '')) + if parsedtime: + entime = parsedtime + correction + entime = entime.astimezone(ltz) + record['timestamp'] = entime.strftime('%Y-%m-%dT%H:%M:%S') + else: + record['timestamp'] = log.get('Created', '') + record['message'] = log.get('Message', None) + record['severity'] = _healthmap.get( + log.get('Severity', 'Warning'), const.Health.Ok) + yield record + def get_average_processor_temperature(self, fishclient): cputemps = self._get_cpu_temps(fishclient) if not cputemps: diff --git a/pyghmi/redfish/oem/lenovo/smm3.py b/pyghmi/redfish/oem/lenovo/smm3.py index cfc24e96..8330c859 100644 --- a/pyghmi/redfish/oem/lenovo/smm3.py +++ b/pyghmi/redfish/oem/lenovo/smm3.py @@ -53,10 +53,14 @@ class OEMHandler(generic.OEMHandler): health = healthlookup.get(health, pygconst.Health.Critical) return {'health': health} + def reseat_bay(self, bay): bayid = _baytolabel(bay) url = '/redfish/v1/Chassis/chassis1/Oem/Lenovo/Nodes/{}/Actions/Node.Reseat'.format(bayid) rsp = self._do_web_request(url, method='POST') + def get_event_log(self, clear=False, fishclient=None): + return super().get_event_log(clear, fishclient, extraurls=[{'@odata.id':'/redfish/v1/Chassis/chassis1/LogServices/EventLog'}]) + def get_description(self, fishclient): return {'height': 13, 'slot': 0, 'slots': [8, 2]}