diff --git a/confluent_server/aiohmi/ipmi/sdr.py b/confluent_server/aiohmi/ipmi/sdr.py index 2fc573f7..eaadb65b 100644 --- a/confluent_server/aiohmi/ipmi/sdr.py +++ b/confluent_server/aiohmi/ipmi/sdr.py @@ -620,12 +620,12 @@ class SDREntry(object): return "" if ipmitype == 0: # Unicode per 43.15 in ipmi 2.0 spec # the spec is not specific about encoding, assuming utf8 - return struct.pack("%dB" % len(data), *data).decode("utf-8") + ret = struct.pack("%dB" % len(data), *data).decode("utf-8") elif ipmitype == 1: # BCD '+' tmpl = "%02X" * len(data) tstr = tmpl % tuple(data) tstr = tstr.replace("A", " ").replace("B", "-").replace("C", ".") - return tstr.replace("D", ":").replace("E", ",").replace("F", "_") + ret = tstr.replace("D", ":").replace("E", ",").replace("F", "_") elif ipmitype == 2: # 6 bit ascii, start at 0x20 # the ordering is very peculiar and is best understood from # IPMI SPEC "6-bit packed ascii example @@ -637,12 +637,14 @@ class SDREntry(object): tstr += chr((data[2] >> 2) + 0x20) if not isinstance(tstr, str): tstr = tstr.decode('utf-8') - return tstr - elif ipmitype == 3: # ACSII+LATIN1 + ret = tstr + else: # ipmitype == 3, ASCII+LATIN1 ret = struct.pack("%dB" % len(data), *data) if not isinstance(ret, str): ret = ret.decode('utf-8') - return ret + # A fixed width field is padded out with nulls. They are not part of + # the name, and would stop a caller ever matching on it. + return ret.rstrip('\x00') class SDR(object): @@ -684,13 +686,9 @@ class SDR(object): # device, so we are meant to use an alternative mechanism to get # SDR data if rsp['data'][5] & 1: - # The device has sensor device support, so in theory we should - # be able to proceed - # However at the moment, we haven't done so - raise exc.UnsupportedFunctionality( - 'This bmc keeps its sensor data records on the sensor ' - 'device rather than in a repository, which is not ' - 'supported') + # The device has sensor device support, so the records are + # read from the sensor device a lun at a time instead + return await self.get_device_sdr() # We have Device SDR, without SDR Repository device, but # also without sensor device support, no idea how to # continue @@ -704,6 +702,151 @@ class SDR(object): raise exc.IpmiException(rsp['error']) return rsp['data'][0] + (rsp['data'][1] << 8) + async def get_device_sdr_reservation(self): + rsp = await self.ipmicmd.raw_command(netfn=4, command=0x22) + if rsp['code'] != 0: + raise exc.IpmiException(rsp['error']) + return rsp['data'][0] + (rsp['data'][1] << 8) + + async def get_device_sdr_info(self): + """Ask the sensor device about its records + + Answers the luns that have sensors and a change indicator to cache on, + which is only present when the device says its sensors are populated + dynamically. + """ + rsp = await self.ipmicmd.raw_command(netfn=4, command=0x20, data=(1,)) + if rsp['code'] != 0: + raise exc.IpmiException(rsp['error']) + data = bytearray(rsp['data']) + luns = [lun for lun in range(4) if data[1] & (1 << lun)] + if not luns: + # A device that names no lun still has to answer for lun 0, and + # some do not set the bit for it + luns = [0] + modtime = 0 + if data[1] & 0b10000000 and len(data) >= 6: + modtime = struct.unpack('> 8, + recid & 0xff, recid >> 8, + offset, size] + sdrrec = await self.ipmicmd.raw_command( + netfn=4, command=0x21, data=rqdata, rslun=lun) + if sdrrec['code'] == 0xca: + if size == 0xff: # get just 5 to get header to know length + size = 5 + elif size > 5: + size //= 2 + # 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']) + if newrecid == 0: + newrecid = (sdrrec['data'][1] << 8) + sdrrec['data'][0] + if currlen == 0: + currlen = sdrrec['data'][6] + 5 # compensate for header + sdrdata.extend(sdrrec['data'][2:]) + offset += size + if offset >= currlen: + break + if size == 5 and offset == 5: + # bump up size after header retrieval + size = chunksize + if (offset + size) > currlen: + size = currlen - offset + await self.add_sdr(sdrdata) + if sdrraw is not None: + sdrraw.append(bytes(sdrdata)) + offset = 0 + if size != 0xff: + size = 5 + if newrecid == recid: + raise exc.BmcErrorException("Incorrect SDR record id from BMC") + recid = newrecid + async def get_sdr(self): repinfo = await self.ipmicmd.raw_command(netfn=0x0a, command=0x20) repinfo['data'] = bytearray(repinfo['data'])