From d77e71967a221e8f2fcf94d79231fb33386e628e Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Fri, 14 Aug 2026 01:51:44 +0200 Subject: [PATCH] Say plainly when a platform has no alert destinations Reading the alert destinations of a bmc that has none reported "Unknown code 0x80 encountered", which is the fallback text for a completion code the library has no name for. 0x80 on this parameter is not a failure, it is the platform saying it does not have alert destinations, and the redfish side of the same resource has said so in words for a while. The lan parameter fetch already knew how to tell those apart, so build the alert reads on it rather than on a raw command that raises on any non-zero code, and raise UnsupportedFunctionality with something to read. Both the count and an individual destination are covered, so a platform that offers one and not the other says the same thing instead of failing differently. Splitting the completion code handling out of the parameter fetch is what makes that reuse possible; the interpretation of the payload, and every answer it gives, is unchanged. The oem hook for the destination count was passing its byte through ord(), which raises TypeError on the bytearray it is given. No handler in tree implements the hook, so it had never been called; hand it the integer. --- confluent_server/aiohmi/ipmi/command.py | 67 +++++++++++++++---------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/confluent_server/aiohmi/ipmi/command.py b/confluent_server/aiohmi/ipmi/command.py index d0151422..51d68f81 100644 --- a/confluent_server/aiohmi/ipmi/command.py +++ b/confluent_server/aiohmi/ipmi/command.py @@ -817,6 +817,25 @@ class Command(object): await self.oem_init() return await self._oem.get_sensor_reading(sensorname) + async def _fetch_lancfg_data(self, channel, param, selector=0): + """Internal helper for fetching a lan cfg parameter's raw data + + Answers None if the bmc does not have the parameter. Such a bmc says + so in the completion code and sends no data at all, so the code has to + be read before the payload is, and oldraw_command reports the code + rather than raising on it, which is why this cannot be done by + catching something. + """ + fetchcmd = bytearray((channel, param, selector, 0)) + fetched = await self.oldraw_command(0xc, 2, data=fetchcmd) + if fetched['code'] in (0x80, 0xc9): + # parameter not supported, and parameter out of range + return None + if fetched['code']: + raise exc.IpmiException(util.get_ipmi_error(fetched), + fetched['code']) + return bytearray(fetched['data']) + async def _fetch_lancfg_param(self, channel, param, prefixlen=False): """Internal helper for fetching lan cfg parameters @@ -824,20 +843,8 @@ class Command(object): string with ipv4. If 6 bytes, colon delimited hex (mac address). If one byte, return the int value """ - fetchcmd = bytearray((channel, param, 0, 0)) - fetched = await self.oldraw_command(0xc, 2, data=fetchcmd) - # A bmc without the parameter says so in the completion code and sends - # no data at all, so the code has to be read before the payload is. - # oldraw_command reports the code rather than raising on it, which is - # why this cannot be done by catching something. - if fetched['code'] in (0x80, 0xc9): - # parameter not supported, and parameter out of range - return None - if fetched['code']: - raise exc.IpmiException(util.get_ipmi_error(fetched), - fetched['code']) - fetchdata = fetched['data'] - if not fetchdata or bytearray(fetchdata)[0] != 17: + fetchdata = await self._fetch_lancfg_data(channel, param) + if not fetchdata or fetchdata[0] != 17: return None if param == 0x14: vlaninfo = struct.unpack('