diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index 285e6f5d..7a8d732e 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -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( diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index c2c9add9..a0dc01e9 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -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 diff --git a/pyghmi/redfish/oem/lenovo/main.py b/pyghmi/redfish/oem/lenovo/main.py index 1d4c23f0..7e6db6a3 100644 --- a/pyghmi/redfish/oem/lenovo/main.py +++ b/pyghmi/redfish/oem/lenovo/main.py @@ -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', {}) diff --git a/pyghmi/redfish/oem/lenovo/xcc3.py b/pyghmi/redfish/oem/lenovo/xcc3.py new file mode 100644 index 00000000..e8457228 --- /dev/null +++ b/pyghmi/redfish/oem/lenovo/xcc3.py @@ -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 + +