def add_sdr(self, sdrbytes): newent = SDREntry(sdrbytes) if newent.sdrtype == TYPE_SENSOR: id = newent.sensor_number if id in self.sensors: raise exc.BmcErrorException("Duplicate sensor number " + id) self.sensors[id] = newent elif newent.sdrtype == TYPE_FRU: id = newent.fru_number if id in self.fru: raise exc.BmcErrorException("Duplicate FRU identifier " + id) self.fru[id] = newent
def get_sdr(self): rsp = self.ipmicmd.raw_command(netfn=0x0a, command=0x20) if (rsp['data'][0] != 0x51): # we only understand SDR version 51h, the only version defined # at time of this writing raise NotImplementedError #NOTE(jbjohnso): we actually don't need to care about 'numrecords' # since FFFF marks the end explicitly #numrecords = (rsp['data'][2] << 8) + rsp['data'][1] #NOTE(jbjohnso): don't care about 'free space' at the moment #NOTE(jbjohnso): most recent timstamp data for add and erase could be # handy to detect cache staleness, but for now will assume invariant # over life of session #NOTE(jbjohnso): not looking to support the various options in op # support, ignore those for now, reservation if some BMCs can't read # full SDR in one slurp recid = 0 rsvid = 0 # partial 'get sdr' will require this offset = 0 size = 0xff while recid != 0xffff: # per 33.12 Get SDR command, 0xffff marks end rqdata = [rsvid & 0xff, rsvid >> 8, recid & 0xff, recid >> 8, offset, size] rsp = self.ipmicmd.raw_command(netfn=0x0a, command=0x23, data=rqdata) newrecid = (rsp['data'][1] << 8) + rsp['data'][0] self.add_sdr(rsp['data'][2:]) if newrecid == recid: raise exc.BmcErrorException("Incorrect SDR record id from BMC") recid = newrecid
def _parse_chassis(self): offset = 8 * self.databytes[2] if offset == 0: return if self.databytes[offset] & 0b1111 != 1: raise iexc.BmcErrorException("Invallid/Unsupported chassis area") inf = self.info # ignore length field, just process the data inf['Chassis type'] = enclosure_types[self.databytes[offset + 2]] inf['Chassis part number'], offset = self._decode_tlv(offset + 3) inf['Chassis serial number'], offset = self._decode_tlv(offset) inf['chassis_extra'] = [] self.extract_extra(inf['chassis_extra'], offset)
def _parse_board(self): offset = 8 * self.databytes[3] if offset == 0: return if self.databytes[offset] & 0b1111 != 1: raise iexc.BmcErrorException("Invalid/Unsupported board info area") inf = self.info language = self.databytes[offset + 2] inf['Board manufacture date'] = decode_fru_date( self.databytes[offset + 3:offset + 6]) inf['Board manufacturer'], offset = self._decode_tlv(offset + 6) inf['Board product name'], offset = self._decode_tlv(offset, language) inf['Board serial number'], offset = self._decode_tlv(offset, language) inf['Board model'], offset = self._decode_tlv(offset, language) _, offset = self._decode_tlv(offset, language) # decode but discard inf['board_extra'] = [] self.extract_extra(inf['board_extra'], offset, language)
def get_sdr(self): repinfo = self.ipmicmd.xraw_command(netfn=0x0a, command=0x20) repinfo['data'] = bytearray(repinfo['data']) if (repinfo['data'][0] != 0x51): # we only understand SDR version 51h, the only version defined # at time of this writing raise NotImplementedError #NOTE(jbjohnso): we actually don't need to care about 'numrecords' # since FFFF marks the end explicitly #numrecords = (rsp['data'][2] << 8) + rsp['data'][1] #NOTE(jbjohnso): don't care about 'free space' at the moment #NOTE(jbjohnso): most recent timstamp data for add and erase could be # handy to detect cache staleness, but for now will assume invariant # over life of session #NOTE(jbjohnso): not looking to support the various options in op # support, ignore those for now, reservation if some BMCs can't read # full SDR in one slurp recid = 0 rsvid = 0 # partial 'get sdr' will require this offset = 0 size = 0xff chunksize = 128 self.broken_sensor_ids = {} while recid != 0xffff: # per 33.12 Get SDR command, 0xffff marks end newrecid = 0 currlen = 0 sdrdata = bytearray() while True: # loop until SDR fetched wholly if size != 0xff and rsvid == 0: rsvid = self.get_sdr_reservation() rqdata = [ rsvid & 0xff, rsvid >> 8, recid & 0xff, recid >> 8, offset, size ] sdrrec = 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 # 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:]) # determine next offset to use based on current offset and the # size used last time. 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 self.add_sdr(sdrdata) offset = 0 if size != 0xff: size = 5 if newrecid == recid: raise exc.BmcErrorException("Incorrect SDR record id from BMC") recid = newrecid for sid in self.broken_sensor_ids: del self.sensors[sid]
def get_sdr(self): repinfo = self.ipmicmd.xraw_command(netfn=0x0a, command=0x20) repinfo['data'] = bytearray(repinfo['data']) if (repinfo['data'][0] != 0x51): # we only understand SDR version 51h, the only version defined # at time of this writing raise NotImplementedError # NOTE(jbjohnso): we actually don't need to care about 'numrecords' # since FFFF marks the end explicitly # numrecords = (rsp['data'][2] << 8) + rsp['data'][1] # NOTE(jbjohnso): don't care about 'free space' at the moment # NOTE(jbjohnso): most recent timstamp data for add and erase could be # handy to detect cache staleness, but for now will assume invariant # over life of session # NOTE(jbjohnso): not looking to support the various options in op # support, ignore those for now, reservation if some BMCs can't read # full SDR in one slurp modtime = struct.unpack('!Q', repinfo['data'][5:13])[0] recid = 0 rsvid = 0 # partial 'get sdr' will require this offset = 0 size = 0xff chunksize = 128 try: csdrs = shared_sdrs[ (self.fw_major, self.fw_minor, self.mfg_id, self.prod_id, self.device_id, modtime)] self.sensors = csdrs['sensors'] self.fru = csdrs['fru'] return except KeyError: pass cachefilename = None self.broken_sensor_ids = {} if self.cachedir: cachefilename = 'sdrcache.{0}.{1}.{2}.{3}.{4}.{5}'.format( self.mfg_id, self.prod_id, self.device_id, self.fw_major, self.fw_minor, modtime) cachefilename = os.path.join(self.cachedir, cachefilename) if cachefilename and os.path.isfile(cachefilename): with open(cachefilename, 'r') as cfile: csdrs = pickle.load(cfile) for sdrdata in csdrs: self.add_sdr(sdrdata) for sid in self.broken_sensor_ids: try: del self.sensors[sid] except KeyError: pass shared_sdrs[ (self.fw_major, self.fw_minor, self.mfg_id, self.prod_id, self.device_id, modtime)] = { 'sensors': self.sensors, 'fru': self.fru, } return sdrraw = [] if cachefilename else None while recid != 0xffff: # per 33.12 Get SDR command, 0xffff marks end newrecid = 0 currlen = 0 sdrdata = bytearray() while True: # loop until SDR fetched wholly if size != 0xff and rsvid == 0: rsvid = self.get_sdr_reservation() rqdata = [rsvid & 0xff, rsvid >> 8, recid & 0xff, recid >> 8, offset, size] sdrrec = 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 # 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:]) # determine next offset to use based on current offset and the # size used last time. 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 self.add_sdr(sdrdata) if sdrraw is not None: sdrraw.append(sdrdata) offset = 0 if size != 0xff: size = 5 if newrecid == recid: raise exc.BmcErrorException("Incorrect SDR record id from BMC") recid = newrecid for sid in self.broken_sensor_ids: try: del self.sensors[sid] except KeyError: pass shared_sdrs[(self.fw_major, self.fw_minor, self.mfg_id, self.prod_id, self.device_id, modtime)] = { 'sensors': self.sensors, 'fru': self.fru, } if cachefilename: suffix = ''.join( random.choice(string.ascii_lowercase) for _ in range(12)) with open(cachefilename + '.' + suffix, 'w') as cfile: pickle.dump(sdrraw, cfile) os.rename(cachefilename + '.' + suffix, cachefilename)