diff --git a/confluent_server/aiohmi/ipmi/command.py b/confluent_server/aiohmi/ipmi/command.py index 8cdc0ca9..9bb12410 100644 --- a/confluent_server/aiohmi/ipmi/command.py +++ b/confluent_server/aiohmi/ipmi/command.py @@ -453,9 +453,11 @@ class Command(object): retry=True, timeout=None, rslun=0): """Send raw ipmi command to BMC, raising exception on error - This is identical to raw_command, except it raises exceptions + This is identical to oldraw_command, except it raises exceptions on IPMI errors and returns data as a buffer. This is the recommend - function to use. The response['data'] being a buffer allows + function to use, and a caller that has to act on a particular + completion code reads it from the exception's ipmicode. The + response['data'] being a buffer allows traditional indexed access as well as works nicely with struct.unpack_from when certain data is coming back. @@ -1089,13 +1091,14 @@ class Command(object): await self.init_sdr() for sensor in self._sdr.get_sensor_numbers(): currsensor = self._sdr.sensors[sensor] - rsp = await self.raw_command(command=0x2d, netfn=4, - rslun=currsensor.sensor_lun, - data=(currsensor.sensor_number,)) - if 'error' in rsp: - if rsp['code'] == 203: # Sensor does not exist, optional dev + try: + rsp = await self.raw_command(command=0x2d, netfn=4, + rslun=currsensor.sensor_lun, + data=(currsensor.sensor_number,)) + except exc.IpmiException as ie: + if ie.ipmicode == 203: # Sensor does not exist, optional dev continue - raise exc.IpmiException(rsp['error'], code=rsp['code']) + raise yield await self._sdr.sensors[sensor].\ decode_sensor_reading(self, rsp['data']) await self.oem_init() @@ -1304,9 +1307,18 @@ class Command(object): # handler of commands lanchan = await self.get_network_channel() if self._ipv6support is None: - rsp = await self.raw_command(netfn=0xc, command=0x2, data=(2, lanchan, - 0x32, 0, 0)) - self._ipv6support = rsp['code'] == 0 + try: + await self.raw_command(netfn=0xc, command=0x2, + data=(2, lanchan, 0x32, 0, 0)) + self._ipv6support = True + except exc.IpmiException as ie: + # A platform without the standard parameter says so in the + # completion code, which is the answer. Anything else, a + # timeout or a lost session, is not, and must not be remembered + # as the platform's answer for the rest of the session. + if not 0 < ie.ipmicode <= 0xff: + raise + self._ipv6support = False return self._ipv6support async def set_alert_destination(self, ip=None, acknowledge_required=None, diff --git a/confluent_server/aiohmi/ipmi/fru.py b/confluent_server/aiohmi/ipmi/fru.py index 50fa90c9..a83c320c 100644 --- a/confluent_server/aiohmi/ipmi/fru.py +++ b/confluent_server/aiohmi/ipmi/fru.py @@ -150,8 +150,6 @@ class FRU(object): async def fetch_fru(self, fruid): response = await self.ipmicmd.raw_command( netfn=0xa, command=0x10, data=[fruid]) - if 'error' in response: - raise iexc.IpmiException(response['error'], code=response['code']) frusize = response['data'][0] | (response['data'][1] << 8) # In our case, we don't need to think too hard about whether # the FRU is word or byte, we just process what we get back in the @@ -165,20 +163,21 @@ class FRU(object): offset = 0 self.rawfru = bytearray([]) while chunksize: - response = await self.ipmicmd.raw_command( - netfn=0xa, command=0x11, data=[fruid, offset & 0xff, - offset >> 8, chunksize]) - if response['code'] in (201, 202): + try: + response = await self.ipmicmd.raw_command( + netfn=0xa, command=0x11, data=[fruid, offset & 0xff, + offset >> 8, chunksize]) + except iexc.IpmiException as ie: # if it was too big, back off and try smaller # Try just over half to mitigate the chance of # one request becoming three rather than just two - if chunksize == 3: - raise iexc.IpmiException(response['error']) - chunksize //= 2 - chunksize += 2 + smaller = chunksize // 2 + 2 + if ie.ipmicode not in (201, 202) or smaller >= chunksize: + # Nothing left to give up, and asking again unchanged would + # never end + raise + chunksize = smaller continue - elif 'error' in response: - raise iexc.IpmiException(response['error'], response['code']) offset += response['data'][0] if response['data'][0] == 0: break diff --git a/confluent_server/aiohmi/ipmi/sdr.py b/confluent_server/aiohmi/ipmi/sdr.py index fcf05d3c..923bb810 100644 --- a/confluent_server/aiohmi/ipmi/sdr.py +++ b/confluent_server/aiohmi/ipmi/sdr.py @@ -769,24 +769,35 @@ class SDR(object): rqdata = [rsvid & 0xff, rsvid >> 8, recid & 0xff, recid >> 8, offset, size] - sdrrec = await self.ipmicmd.raw_command(netfn=0x0a, command=0x23, - data=rqdata) - if sdrrec['code'] == 0xca: - if size == 0xff: # get just 5 to get header to know length - size = 5 - elif size > 5: - size //= 2 + try: + sdrrec = await self.ipmicmd.raw_command( + netfn=0x0a, command=0x23, data=rqdata) + except exc.IpmiException as ie: + # The two codes this loop negotiates with, rather than + # failures: more asked for than the bmc will return at once, + # and a reservation that has gone stale + if ie.ipmicode == 0xca: + if size == 0xff: # get just 5 to get header to know length + size = 5 + continue # push things over such that it's less # likely to be just 1 short of a read # and incur a whole new request - size += 2 - chunksize = size - continue - if sdrrec['code'] == 0xc5: # need a new reservation id - rsvid = 0 - continue - if sdrrec['code'] != 0: - raise exc.IpmiException(sdrrec['error']) + smaller = size // 2 + 2 + if smaller >= size or (currlen == 0 and smaller < 5): + # Nothing left to give up, and asking again + # unchanged would never end. A header read has to + # be 5 bytes to carry the record length, so a + # smaller one could not be parsed either + raise + size = chunksize = smaller + continue + if ie.ipmicode == 0xc5: # need a new reservation id + # Take one here rather than leaving it to the top of + # the loop, which only reserves for a partial read + rsvid = await self.get_sdr_reservation() + continue + raise if newrecid == 0: newrecid = (sdrrec['data'][1] << 8) + sdrrec['data'][0] if currlen == 0: