2
0
mirror of https://opendev.org/x/pyghmi synced 2026-01-11 10:42:32 +00:00

Refactor redfish settings and leverage in XCC3

XCC3 has some OEM settings managed in a redfish
settings registry sort of way.  Refactor
to pull out generic registry handling and use it
with XCC3 settings as well as UEFI.

Manifest 'XCC' settings with 'XCC.' prefix, imitating the 'IMM.' of
the past.  Since OData forbids '.', this means XCC. cannot
conflict with hypothetical BIOS names that may conflict.

Change-Id: Idb9e5ecffdc0d27f20ae070acdc3cd78658a0955
This commit is contained in:
Jarrod Johnson
2024-08-28 19:30:27 -04:00
parent a8a03632a0
commit 4e166bd246
4 changed files with 165 additions and 49 deletions

View File

@@ -81,20 +81,6 @@ def _mask_to_cidr(mask):
return cidr
def _to_boolean(attrval):
attrval = attrval.lower()
if not attrval:
return False
if ('true'.startswith(attrval) or 'yes'.startswith(attrval)
or 'enabled'.startswith(attrval) or attrval == '1'):
return True
if ('false'.startswith(attrval) or 'no'.startswith(attrval)
or 'disabled'.startswith(attrval) or attrval == '0'):
return False
raise Exception(
'Unrecognized candidate for boolean: {0}'.format(attrval))
def _cidr_to_mask(cidr):
return socket.inet_ntop(
socket.AF_INET, struct.pack(

View File

@@ -44,11 +44,29 @@ class SensorReading(object):
self.units = units
self.unavailable = unavailable
def _to_boolean(attrval):
attrval = attrval.lower()
if not attrval:
return False
if ('true'.startswith(attrval) or 'yes'.startswith(attrval)
or 'enabled'.startswith(attrval) or attrval == '1'):
return True
if ('false'.startswith(attrval) or 'no'.startswith(attrval)
or 'disabled'.startswith(attrval) or attrval == '0'):
return False
raise Exception(
'Unrecognized candidate for boolean: {0}'.format(attrval))
def _normalize_mac(mac):
if ':' not in mac:
mac = ':'.join((mac[:2], mac[2:4], mac[4:6], mac[6:8], mac[8:10], mac[10:12]))
mac = ':'.join((
mac[:2], mac[2:4], mac[4:6],
mac[6:8], mac[8:10], mac[10:12]))
return mac.lower()
_healthmap = {
'Critical': const.Health.Critical,
'Unknown': const.Health.Warning,
@@ -345,6 +363,38 @@ class OEMHandler(object):
def get_system_configuration(self, hideadvanced=True, fishclient=None):
return self._getsyscfg(fishclient)[0]
def _get_attrib_registry(self, fishclient, attribreg):
overview = fishclient._do_web_request('/redfish/v1/')
reglist = overview['Registries']['@odata.id']
reglist = fishclient._do_web_request(reglist)
regurl = None
for cand in reglist.get('Members', []):
cand = cand.get('@odata.id', '')
candname = cand.split('/')[-1]
if candname == '': # implementation uses trailing slash
candname = cand.split('/')[-2]
if candname == attribreg:
regurl = cand
break
if not regurl:
# Workaround a vendor bug where they link to a
# non-existant name
for cand in reglist.get('Members', []):
cand = cand.get('@odata.id', '')
candname = cand.split('/')[-1]
candname = candname.split('.')[0]
if candname == attribreg.split('.')[0]:
regurl = cand
break
if regurl:
reginfo = fishclient._do_web_request(regurl)
for reg in reginfo.get('Location', []):
if reg.get('Language', 'en').startswith('en'):
reguri = reg['Uri']
reginfo = self._get_biosreg(reguri, fishclient)
return reginfo
extrainfo, valtodisplay, _, self.attrdeps = reginfo
def _getsyscfg(self, fishclient):
biosinfo = self._do_web_request(fishclient._biosurl, cache=False)
reginfo = ({}, {}, {}, {})
@@ -352,36 +402,9 @@ class OEMHandler(object):
valtodisplay = {}
self.attrdeps = {'Dependencies': [], 'Attributes': []}
if 'AttributeRegistry' in biosinfo:
overview = fishclient._do_web_request('/redfish/v1/')
reglist = overview['Registries']['@odata.id']
reglist = fishclient._do_web_request(reglist)
regurl = None
for cand in reglist.get('Members', []):
cand = cand.get('@odata.id', '')
candname = cand.split('/')[-1]
if candname == '': # implementation uses trailing slash
candname = cand.split('/')[-2]
if candname == biosinfo['AttributeRegistry']:
regurl = cand
break
if not regurl:
# Workaround a vendor bug where they link to a
# non-existant name
for cand in reglist.get('Members', []):
cand = cand.get('@odata.id', '')
candname = cand.split('/')[-1]
candname = candname.split('.')[0]
if candname == biosinfo[
'AttributeRegistry'].split('.')[0]:
regurl = cand
break
if regurl:
reginfo = fishclient._do_web_request(regurl)
for reg in reginfo.get('Location', []):
if reg.get('Language', 'en').startswith('en'):
reguri = reg['Uri']
reginfo = self._get_biosreg(reguri, fishclient)
extrainfo, valtodisplay, _, self.attrdeps = reginfo
reginfo = self._get_attrib_registry(fishclient, biosinfo['AttributeRegistry'])
if reginfo:
extrainfo, valtodisplay, _, self.attrdeps = reginfo
currsettings = {}
try:
pendingsettings = fishclient._do_web_request(
@@ -418,10 +441,19 @@ class OEMHandler(object):
rawsettings = fishclient._do_web_request(fishclient._biosurl,
cache=False)
rawsettings = rawsettings.get('Attributes', {})
pendingsettings = fishclient._do_web_request(fishclient._setbiosurl)
pendingsettings = fishclient._do_web_request(
fishclient._setbiosurl)
return self._set_redfish_settings(
changeset, fishclient, currsettings, rawsettings,
pendingsettings, self.attrdeps, reginfo,
fishclient._setbiosurl)
def _set_redfish_settings(self, changeset, fishclient, currsettings,
rawsettings, pendingsettings, attrdeps, reginfo,
seturl):
etag = pendingsettings.get('@odata.etag', None)
pendingsettings = pendingsettings.get('Attributes', {})
dephandler = AttrDependencyHandler(self.attrdeps, rawsettings,
dephandler = AttrDependencyHandler(attrdeps, rawsettings,
pendingsettings)
for change in list(changeset):
if change not in currsettings:
@@ -440,7 +472,7 @@ class OEMHandler(object):
changeval = changeset[change]
overrides, blameattrs = dephandler.get_overrides(change)
meta = {}
for attr in self.attrdeps['Attributes']:
for attr in attrdeps['Attributes']:
if attr['AttributeName'] == change:
meta = dict(attr)
break
@@ -479,7 +511,7 @@ class OEMHandler(object):
changeset[change] = _to_boolean(changeset[change])
redfishsettings = {'Attributes': changeset}
fishclient._do_web_request(
fishclient._setbiosurl, redfishsettings, 'PATCH', etag=etag)
seturl, redfishsettings, 'PATCH', etag=etag)
def attach_remote_media(self, url, username, password, vmurls):
return None

View File

@@ -15,6 +15,7 @@
import pyghmi.redfish.oem.generic as generic
from pyghmi.redfish.oem.lenovo import tsma
from pyghmi.redfish.oem.lenovo import xcc
from pyghmi.redfish.oem.lenovo import xcc3
def get_handler(sysinfo, sysurl, webclient, cache, cmd):
@@ -26,6 +27,9 @@ def get_handler(sysinfo, sysurl, webclient, cache, cmd):
if 'FrontPanelUSB' in leninf or 'USBManagementPortAssignment' in leninf or sysinfo.get('SKU', '').startswith('7X58'):
return xcc.OEMHandler(sysinfo, sysurl, webclient, cache,
gpool=cmd._gpool)
elif 'NextOneTimeBootDevice' in leninf:
return xcc3.OEMHandler(sysinfo, sysurl, webclient, cache,
gpool=cmd._gpool)
else:
leninv = sysinfo.get('Links', {}).get('OEM', {}).get(
'Lenovo', {}).get('Inventory', {})

View File

@@ -0,0 +1,94 @@
import pyghmi.redfish.oem.generic as generic
class OEMHandler(generic.OEMHandler):
def get_system_configuration(self, hideadvanced=True, fishclient=None):
stgs = self._getsyscfg(fishclient)[0]
outstgs = {}
for stg in stgs:
outstgs[f'UEFI.{stg}'] = stgs[stg]
return outstgs
def set_system_configuration(self, changeset, fishclient):
bmchangeset = {}
vpdchangeset = {}
for stg in list(changeset):
if stg.startswith('BMC.'):
bmchangeset[stg.replace('BMC.', '')] = changeset[stg]
del changeset[stg]
if stg.startswith('UEFI.'):
changeset[stg.replace('UEFI.' '')] = changeset[stg]
del changeset[stg]
if stg.startswith('VPD.'):
vpdchangeset[stg.replace('VPD.', '')] = changeset[stg]
del changeset[stg]
if changeset:
super().set_system_configuration(changeset, fishclient)
if bmchangeset:
self._set_xcc3_settings(bmchangeset, fishclient)
if vpdchangeset:
self._set_xcc3_vpd(vpdchangeset, fishclient)
def _set_xcc3_vpd(self, changeset, fishclient):
newvpd = {'Attributes': changeset}
fishclient._do_web_request(
'/redfish/v1/Chassis/1/Oem/Lenovo/SysvpdSettings/Actions/LenovoSysVpdSettings.SetVpdSettings',
newvpd)
def _set_xcc3_settings(self, changeset, fishclient):
currsettings, reginfo = self._get_lnv_bmcstgs(fishclient)
rawsettings = fishclient._do_web_request('/redfish/v1/Managers/1/Oem/Lenovo/BMCSettings',
cache=False)
rawsettings = rawsettings.get('Attributes', {})
pendingsettings = {}
ret = self._set_redfish_settings(
changeset, fishclient, currsettings, rawsettings,
pendingsettings, self.lenovobmcattrdeps, reginfo,
'/redfish/v1/Managers/1/Oem/Lenovo/BMCSettings')
fishclient._do_web_request('/redfish/v1/Managers/1/Oem/Lenovo/BMCSettings', cache=False)
return ret
def get_extended_bmc_configuration(self, fishclient, hideadvanced=True):
cfgin = self._get_lnv_bmcstgs(fishclient)[0]
cfgout = {}
for stgname in cfgin:
cfgout[f'BMC.{stgname}'] = cfgin[stgname]
vpdin = self._get_lnv_vpd(fishclient)[0]
for stgname in vpdin:
cfgout[f'VPD.{stgname}'] = vpdin[stgname]
return cfgout
def _get_lnv_vpd(self, fishclient):
currsettings, reginfo = self._get_lnv_stgs(
fishclient, '/redfish/v1/Chassis/1/Oem/Lenovo/SysvpdSettings')
self.lenovobmcattrdeps = reginfo[3]
return currsettings, reginfo
def _get_lnv_bmcstgs(self, fishclient):
currsettings, reginfo = self._get_lnv_stgs(
fishclient, '/redfish/v1/Managers/1/Oem/Lenovo/BMCSettings')
self.lenovobmcattrdeps = reginfo[3]
return currsettings, reginfo
def _get_lnv_stgs(self, fishclient, url):
bmcstgs = fishclient._do_web_request(url)
bmcreg = bmcstgs.get('AttributeRegistry', None)
extrainfo = {}
valtodisplay = {}
currsettings = {}
reginfo = {}, {}, {}, {}
if bmcreg:
reginfo = self._get_attrib_registry(fishclient, bmcreg)
if reginfo:
extrainfo, valtodisplay, _, _ = reginfo
for setting in bmcstgs.get('Attributes', {}):
val = bmcstgs['Attributes'][setting]
currval = val
val = valtodisplay.get(setting, {}).get(val, val)
val = {'value': val}
val.update(**extrainfo.get(setting, {}))
currsettings[setting] = val
return currsettings, reginfo