Skip to content

Commit af815ee

Browse files
author
Jarrod Johnson
committedMay 8, 2014
Slow down to chunk-wise retrieval of SDR as needed
Some BMCs cannot fetch whole SDRs in one chunk. When faced with such a scenario, back off exponentially until things can work. Change-Id: Ifd9df93af56e6fedfeb4d46b662937bf8db80b01
1 parent cca6477 commit af815ee

File tree

1 file changed

+62
-16
lines changed

1 file changed

+62
-16
lines changed
 

‎pyghmi/ipmi/sdr.py

+62-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# vim: tabstop=4 shiftwidth=4 softtabstop=4
22
# coding=utf8
33

4-
# Copyright 2013 IBM Corporation
4+
# Copyright 2014 IBM Corporation
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -383,7 +383,7 @@ def _decode_state(self, state):
383383
desc = "Unknown state %d for reading type %d/sensor type %d" % (
384384
state, self.reading_type, self.sensor_type_number)
385385
health = const.Health.Warning
386-
return (desc, health)
386+
return desc, health
387387

388388
def decode_sensor_reading(self, reading):
389389
numeric = None
@@ -505,18 +505,18 @@ def decode_formula(self, entry):
505505

506506
def tlv_decode(self, tlv, data):
507507
# Per IPMI 'type/length byte format
508-
type = (tlv & 0b11000000) >> 6
508+
ipmitype = (tlv & 0b11000000) >> 6
509509
if not len(data):
510510
return ""
511-
if type == 0: # Unicode per 43.15 in ipmi 2.0 spec
511+
if ipmitype == 0: # Unicode per 43.15 in ipmi 2.0 spec
512512
# the spec is not specific about encoding, assuming utf8
513513
return unicode(struct.pack("%dB" % len(data), *data), "utf_8")
514-
elif type == 1: # BCD '+'
514+
elif ipmitype == 1: # BCD '+'
515515
tmpl = "%02X" * len(data)
516516
tstr = tmpl % tuple(data)
517517
tstr = tstr.replace("A", " ").replace("B", "-").replace("C", ".")
518518
return tstr.replace("D", ":").replace("E", ",").replace("F", "_")
519-
elif type == 2: # 6 bit ascii, start at 0x20 and stop when out of bits
519+
elif ipmitype == 2: # 6 bit ascii, start at 0x20
520520
# the ordering is very peculiar and is best understood from
521521
# IPMI SPEC "6-bit packed ascii example
522522
tstr = ""
@@ -528,7 +528,7 @@ def tlv_decode(self, tlv, data):
528528
(data[1] >> 4) + 0x20)
529529
tstr += chr((data[2] >> 2) + 0x20)
530530
return tstr
531-
elif type == 3: # ACSII+LATIN1
531+
elif ipmitype == 3: # ACSII+LATIN1
532532
return struct.pack("%dB" % len(data), *data)
533533

534534

@@ -569,9 +569,15 @@ def read_info(self):
569569
self.aux_fw = self.decode_aux(rsp['data'][11:15])
570570
self.get_sdr()
571571

572+
def get_sdr_reservation(self):
573+
rsp = self.ipmicmd.raw_command(netfn=0x0a, command=0x22)
574+
if rsp['code'] != 0:
575+
raise exc.IpmiException(rsp['error'])
576+
return rsp['data'][0] + (rsp['data'][1] << 8)
577+
572578
def get_sdr(self):
573-
rsp = self.ipmicmd.raw_command(netfn=0x0a, command=0x20)
574-
if (rsp['data'][0] != 0x51):
579+
repinfo = self.ipmicmd.raw_command(netfn=0x0a, command=0x20)
580+
if (repinfo['data'][0] != 0x51):
575581
# we only understand SDR version 51h, the only version defined
576582
# at time of this writing
577583
raise NotImplementedError
@@ -589,14 +595,54 @@ def get_sdr(self):
589595
rsvid = 0 # partial 'get sdr' will require this
590596
offset = 0
591597
size = 0xff
598+
chunksize = 128
592599
while recid != 0xffff: # per 33.12 Get SDR command, 0xffff marks end
593-
rqdata = [rsvid & 0xff, rsvid >> 8,
594-
recid & 0xff, recid >> 8,
595-
offset, size]
596-
rsp = self.ipmicmd.raw_command(netfn=0x0a, command=0x23,
597-
data=rqdata)
598-
newrecid = (rsp['data'][1] << 8) + rsp['data'][0]
599-
self.add_sdr(rsp['data'][2:])
600+
newrecid = 0
601+
currlen = 0
602+
sdrdata = bytearray()
603+
while True: # loop until SDR fetched wholly
604+
if size != 0xff and rsvid == 0:
605+
rsvid = self.get_sdr_reservation()
606+
rqdata = [rsvid & 0xff, rsvid >> 8,
607+
recid & 0xff, recid >> 8,
608+
offset, size]
609+
sdrrec = self.ipmicmd.raw_command(netfn=0x0a, command=0x23,
610+
data=rqdata)
611+
if sdrrec['code'] == 0xca:
612+
if size == 0xff: # get just 5 to get header to know length
613+
size = 5
614+
elif size > 5:
615+
size /= 2
616+
# push things over such that it's less
617+
# likely to be just 1 short of a read
618+
# and incur a whole new request
619+
size += 2
620+
chunksize = size
621+
continue
622+
if sdrrec['code'] == 0xc5: # need a new reservation id
623+
rsvid = 0
624+
continue
625+
if sdrrec['code'] != 0:
626+
raise exc.IpmiException(sdrrec['error'])
627+
if newrecid == 0:
628+
newrecid = (sdrrec['data'][1] << 8) + sdrrec['data'][0]
629+
if currlen == 0:
630+
currlen = sdrrec['data'][6] + 5 # compensate for header
631+
sdrdata.extend(sdrrec['data'][2:])
632+
# determine next offset to use based on current offset and the
633+
# size used last time.
634+
offset += size
635+
if offset >= currlen:
636+
break
637+
if size == 5 and offset == 5:
638+
# bump up size after header retrieval
639+
size = chunksize
640+
if (offset + size) > currlen:
641+
size = currlen - offset
642+
self.add_sdr(sdrdata)
643+
offset = 0
644+
if size != 0xff:
645+
size = 5
600646
if newrecid == recid:
601647
raise exc.BmcErrorException("Incorrect SDR record id from BMC")
602648
recid = newrecid

0 commit comments

Comments
 (0)
Please sign in to comment.