2
0
mirror of https://opendev.org/x/pyghmi synced 2026-04-01 07:43:39 +00:00

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
This commit is contained in:
Jarrod Johnson
2025-02-04 09:51:52 -05:00
parent ca0b100fbb
commit 1a6bfbdbf1
3 changed files with 79 additions and 64 deletions

View File

@@ -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']

View File

@@ -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:

View File

@@ -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]}