diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index 2bd140d7..51ab1fbf 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -138,6 +138,7 @@ class Command(object): # operations without pushing the async complexities up the stack self.onlogon = onlogon self.bmc = bmc + self._sdrcachedir = None self._sdr = None self._oem = None self._oemknown = False @@ -165,6 +166,17 @@ class Command(object): kg=kg, privlevel=privlevel) + def set_sdr_cachedir(self, path): + """Register use of a directory for SDR cache. + + Takes the given directory and uses it to persist SDR cache run to run. + This can greatly improve performance across runs. + + :param path: + :return: + """ + self._sdrcachedir = path + def register_key_handler(self, callback, type='tls'): """Assign a verification handler for a public key @@ -541,7 +553,7 @@ class Command(object): # sdr timestamp, our data version revision, aux firmware revision, # and oem defined field if self._sdr is None: - self._sdr = sdr.SDR(self) + self._sdr = sdr.SDR(self, self._sdrcachedir) return self._sdr def get_event_log(self, clear=False): @@ -588,8 +600,7 @@ class Command(object): returns an iterable of string descriptions """ yield "System" - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for fruid in sorted(self._sdr.fru): yield self._sdr.fru[fruid].fru_name self.oem_init() @@ -605,8 +616,7 @@ class Command(object): self.oem_init() if component == 'System': return self._get_zero_fru() - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for fruid in self._sdr.fru: if self._sdr.fru[fruid].fru_name == component: return self._oem.process_fru(fru.FRU( @@ -649,8 +659,7 @@ class Command(object): """ self.oem_init() yield ("System", self._get_zero_fru()) - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for fruid in sorted(self._sdr.fru): fruinf = fru.FRU( ipmicmd=self, fruid=fruid, sdr=self._sdr.fru[fruid]).info @@ -712,8 +721,7 @@ class Command(object): :param sensorname: Name of the desired sensor :returns: sdr.SensorReading object """ - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for sensor in self._sdr.get_sensor_numbers(): if self._sdr.sensors[sensor].name == sensorname: rsp = self.raw_command(command=0x2d, netfn=4, data=(sensor,)) @@ -910,8 +918,7 @@ class Command(object): :returns: Iterator of sdr.SensorReading objects """ - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for sensor in self._sdr.get_sensor_numbers(): rsp = self.raw_command(command=0x2d, netfn=4, data=(sensor,)) if 'error' in rsp: @@ -930,8 +937,7 @@ class Command(object): :returns: Iterator of dicts describing each sensor """ - if self._sdr is None: - self._sdr = sdr.SDR(self) + self.init_sdr() for sensor in self._sdr.get_sensor_numbers(): yield {'name': self._sdr.sensors[sensor].name, 'type': self._sdr.sensors[sensor].sensor_type} diff --git a/pyghmi/ipmi/sdr.py b/pyghmi/ipmi/sdr.py index e605df3c..a054c196 100644 --- a/pyghmi/ipmi/sdr.py +++ b/pyghmi/ipmi/sdr.py @@ -30,11 +30,18 @@ # skipped for now import math +import os import pyghmi.constants as const import pyghmi.exceptions as exc import pyghmi.ipmi.private.constants as ipmiconst +import random +import string import struct import weakref +try: + import cPickle as pickle +except ImportError: + import pickle TYPE_UNKNOWN = 0 TYPE_SENSOR = 1 @@ -604,10 +611,11 @@ class SDR(object): :param ipmicmd: A Command class object """ - def __init__(self, ipmicmd): + def __init__(self, ipmicmd, cachedir=None): self.ipmicmd = weakref.proxy(ipmicmd) self.sensors = {} self.fru = {} + self.cachedir = cachedir self.read_info() def read_info(self): @@ -679,7 +687,31 @@ class SDR(object): 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 @@ -724,6 +756,8 @@ class SDR(object): 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 @@ -740,6 +774,12 @@ class SDR(object): '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) def get_sensor_numbers(self): for number in self.sensors: