From 7b6c703c9f09e9e043f49d3ecd9b8fa74829dff1 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 8 Oct 2025 09:03:36 -0400 Subject: [PATCH] Enhancements for multi-system, multi-manager Allow OEMs to describe the 'core' system and manager. In systems that start manifesting components as managers and systems in their own right, allow OEM to indicate which is the 'real' one. Usually, extra Managers and Systems are something that has been modeled as such, but from a usual user perspective are not what they would consider BMCs and systems. Change-Id: Ib04e146a7fb0e988dac94c148005e07e2b487b12 --- pyghmi/redfish/command.py | 40 +++--------------------- pyghmi/redfish/oem/dell/main.py | 2 +- pyghmi/redfish/oem/generic.py | 51 ++++++++++++++++++++++++++++++- pyghmi/redfish/oem/lenovo/main.py | 16 +++++++--- pyghmi/redfish/oem/lookup.py | 13 +++++--- 5 files changed, 75 insertions(+), 47 deletions(-) diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index fa3995a6..91399a45 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -188,43 +188,11 @@ class Command(object): self._varsensormap = {} self.powerurl = None self.sysurl = None - if 'Managers' in overview: - bmcoll = systems = overview['Managers']['@odata.id'] - res = self.wc.grab_json_response_with_status(bmcoll) - if res[1] == 401: - raise exc.PyghmiException('Access Denied') - elif res[1] < 200 or res[1] >= 300: - raise exc.PyghmiException(repr(res[0])) - bmcs = res[0]['Members'] - if len(bmcs) == 1: - self._varbmcurl = bmcs[0]['@odata.id'] + tmpoem = oem.get_oem_handler({}, sysurl, self.wc, self._urlcache, self, + rootinfo=overview) + self._varbmcurl = tmpoem.get_default_mgrurl() if 'Systems' in overview: - systems = overview['Systems']['@odata.id'] - res = self.wc.grab_json_response_with_status(systems) - if res[1] == 401: - raise exc.PyghmiException('Access Denied') - elif res[1] < 200 or res[1] >= 300: - raise exc.PyghmiException(repr(res[0])) - members = res[0] - systems = members['Members'] - if sysurl: - for system in systems: - if system['@odata.id'] == sysurl or system['@odata.id'].split('/')[-1] == sysurl: - self.sysurl = system['@odata.id'] - break - else: - raise exc.PyghmiException( - 'Specified sysurl not found: {0}'.format(sysurl)) - else: - if len(systems) > 1: - systems = [x for x in systems if 'DPU' not in x['@odata.id']] - if len(systems) > 1: - raise exc.PyghmiException( - 'Multi system manager, sysurl is required parameter') - if len(systems): - self.sysurl = systems[0]['@odata.id'] - else: - self.sysurl = None + self.sysurl = tmpoem.get_default_sysurl() self.powerurl = self.sysinfo.get('Actions', {}).get( '#ComputerSystem.Reset', {}).get('target', None) diff --git a/pyghmi/redfish/oem/dell/main.py b/pyghmi/redfish/oem/dell/main.py index 2e47baab..9422726f 100644 --- a/pyghmi/redfish/oem/dell/main.py +++ b/pyghmi/redfish/oem/dell/main.py @@ -15,6 +15,6 @@ from pyghmi.redfish.oem.dell import idrac -def get_handler(sysinfo, sysurl, webclient, cache, cmd): +def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}): return idrac.OEMHandler(sysinfo, sysurl, webclient, cache, gpool=cmd._gpool) diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index 17bbfd1c..190e3157 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -188,17 +188,66 @@ class OEMHandler(object): hostnic = None usegenericsensors = True - def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None): + def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None, rootinfo={}): self._gpool = gpool self._varsysinfo = sysinfo self._varsysurl = sysurl self._urlcache = cache self.webclient = webclient self._hwnamemap = {} + self._rootinfo = rootinfo + if not self._rootinfo: + self._rootinfo = self.webclient.grab_json_response( + '/redfish/v1/') + self._varbmcurl = None + self._varsysurl = sysurl def get_screenshot(self, outfile): raise exc.UnsupportedFunctionality( 'Retrieving screenshot is not implemented for this platform') + + def get_default_mgrurl(self): + if not self._varbmcurl and 'Managers' in self._rootinfo: + bmcoll = self._rootinfo['Managers']['@odata.id'] + res = self.webclient.grab_json_response_with_status(bmcoll) + if res[1] == 401: + raise exc.PyghmiException('Access Denied') + elif res[1] < 200 or res[1] >= 300: + raise exc.PyghmiException(repr(res[0])) + bmcs = res[0]['Members'] + if len(bmcs) == 1: + self._varbmcurl = bmcs[0]['@odata.id'] + return self._varbmcurl + + def get_default_sysurl(self): + if not self._varsysurl and 'Systems' in self._rootinfo: + systems = self._rootinfo['Systems']['@odata.id'] + res = self.webclient.grab_json_response_with_status(systems) + if res[1] == 401: + raise exc.PyghmiException('Access Denied') + elif res[1] < 200 or res[1] >= 300: + raise exc.PyghmiException(repr(res[0])) + members = res[0] + systems = members['Members'] + if self._varsysurl: + for system in systems: + if system['@odata.id'] == self._varsysurl or system['@odata.id'].split('/')[-1] == self._varsysurl: + self._varsysurl = system['@odata.id'] + break + else: + raise exc.PyghmiException( + 'Specified sysurl not found: {0}'.format(self._varsysurl)) + else: + if len(systems) > 1: + systems = [x for x in systems if 'DPU' not in x['@odata.id']] + if len(systems) > 1: + raise exc.PyghmiException( + 'Multi system manager, sysurl is required parameter') + if len(systems): + self._varsysurl = systems[0]['@odata.id'] + else: + self._varsysurl = None + return self._varsysurl def supports_expand(self, url): # Unfortunately, the state of expand in redfish is pretty dicey, diff --git a/pyghmi/redfish/oem/lenovo/main.py b/pyghmi/redfish/oem/lenovo/main.py index 3b9d6337..7ab1281e 100644 --- a/pyghmi/redfish/oem/lenovo/main.py +++ b/pyghmi/redfish/oem/lenovo/main.py @@ -18,19 +18,27 @@ from pyghmi.redfish.oem.lenovo import xcc from pyghmi.redfish.oem.lenovo import xcc3 from pyghmi.redfish.oem.lenovo import smm3 -def get_handler(sysinfo, sysurl, webclient, cache, cmd): +def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}): + if not sysinfo: # we are before establishing there is one system, and one manager... + systems, status = webclient.grab_json_response_with_status('/redfish/v1/Systems') + if status == 200: + for system in systems.get('Members', []): + if system.get('@odata.id', '').endswith('/1'): + sysurl = system['@odata.id'] + sysinfo, status = webclient.grab_json_response_with_status(sysurl) + break leninf = sysinfo.get('Oem', {}).get('Lenovo', {}) mgrinfo = {} if leninf: - mgrinf, status = webclient.grab_json_response_with_status('/redfish/v1/Managers/1') + mgrinfo, status = webclient.grab_json_response_with_status('/redfish/v1/Managers/1') if status != 200: mgrinfo = {} if not leninf: bmcinfo = cmd.bmcinfo if 'Ami' in bmcinfo.get('Oem', {}): return tsma.TsmHandler(sysinfo, sysurl, webclient, cache) - elif 'xclarity controller' in mgrinf.get('Model', '').lower(): - if mgrinf['Model'].endswith('3'): + elif 'xclarity controller' in mgrinfo.get('Model', '').lower(): + if mgrinfo['Model'].endswith('3'): return xcc3.OEMHandler(sysinfo, sysurl, webclient, cache, gpool=cmd._gpool) else: diff --git a/pyghmi/redfish/oem/lookup.py b/pyghmi/redfish/oem/lookup.py index 8c78f6b8..7f9d3578 100644 --- a/pyghmi/redfish/oem/lookup.py +++ b/pyghmi/redfish/oem/lookup.py @@ -22,18 +22,21 @@ OEMMAP = { } -def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd): +def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}): + if rootinfo.get('Vendor', None) in OEMMAP: + return OEMMAP[rootinfo['Vendor']].get_handler(sysinfo, sysurl, + webclient, cache, cmd, rootinfo) for oem in sysinfo.get('Oem', {}): if oem in OEMMAP: return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache, - cmd) + cmd, rootinfo) for oem in sysinfo.get('Links', {}).get('OEM', []): if oem in OEMMAP: return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache, - cmd) + cmd, rootinfo) bmcinfo = cmd.bmcinfo for oem in bmcinfo.get('Oem', {}): if oem in OEMMAP: return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache, - cmd) - return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool) + cmd, rootinfo) + return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)