mirror of
https://opendev.org/x/pyghmi
synced 2026-03-31 15:23:31 +00:00
Implement OEM Inventory components
Provide a framework for OEM to add inventory devices without requiring FRU locator records. This enables vendors to provide additional data in a more flexible scheme. Change-Id: I9ef9685de9150940321aecbf2cc275cfbbe8ce6a
This commit is contained in:
@@ -439,6 +439,9 @@ class Command(object):
|
||||
self._sdr = sdr.SDR(self)
|
||||
for fruid in self._sdr.fru:
|
||||
yield self._sdr.fru[fruid].fru_name
|
||||
self.oem_init()
|
||||
for compname in self._oem.get_oem_inventory_descriptions():
|
||||
yield compname
|
||||
|
||||
def get_inventory_of_component(self, component):
|
||||
"""Retrieve inventory of a component
|
||||
@@ -455,6 +458,7 @@ class Command(object):
|
||||
if self._sdr.fru[fruid].fru_name == component:
|
||||
return self._oem.process_fru(fru.FRU(
|
||||
ipmicmd=self, fruid=fruid, sdr=self._sdr.fru[fruid]).info)
|
||||
return self._oem.get_inventory_of_component(component)
|
||||
|
||||
def _get_zero_fru(self):
|
||||
# It is expected that a manufacturer matches SMBIOS to IPMI
|
||||
@@ -493,6 +497,8 @@ class Command(object):
|
||||
if fruinf is not None:
|
||||
fruinf = self._oem.process_fru(fruinf)
|
||||
yield (self._sdr.fru[fruid].fru_name, fruinf)
|
||||
for componentpair in self._oem.get_oem_inventory():
|
||||
yield componentpair
|
||||
|
||||
def get_health(self):
|
||||
"""Summarize health of managed system
|
||||
|
||||
@@ -41,6 +41,35 @@ class OEMHandler(object):
|
||||
if evdata[0] & 0b110000 == 0b100000:
|
||||
event['oem_byte3'] = evdata[2]
|
||||
|
||||
def get_oem_inventory_descriptions(self):
|
||||
"""Get descriptions of available additional inventory items
|
||||
|
||||
OEM implementation may provide additional records not indicated
|
||||
by FRU locator SDR records. An implementation is expected to
|
||||
implement this function to list component names that would map to
|
||||
OEM behavior beyond the specification. It should return an iterable
|
||||
of names
|
||||
"""
|
||||
return ()
|
||||
|
||||
def get_oem_inventory(self):
|
||||
"""Get tuples of component names and inventory data.
|
||||
|
||||
This returns an iterable of tuples. The first member of each tuple
|
||||
is a string description of the inventory item. The second member
|
||||
is a dict of inventory information about the component.
|
||||
"""
|
||||
for desc in self.get_oem_inventory_descriptions():
|
||||
yield (desc, self.get_inventory_of_component(desc))
|
||||
|
||||
def get_inventory_of_component(self, component):
|
||||
"""Get inventory detail of an OEM defined component
|
||||
|
||||
Given a string that may be an OEM component, return the detail of that
|
||||
component. If the component does not exist, returns None
|
||||
"""
|
||||
return None
|
||||
|
||||
def process_fru(self, fru):
|
||||
"""Modify a fru entry with OEM understanding.
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
import pyghmi.constants as pygconst
|
||||
import pyghmi.ipmi.oem.generic as generic
|
||||
import pyghmi.ipmi.private.constants as ipmiconst
|
||||
import pyghmi.ipmi.private.spd as spd
|
||||
import pyghmi.ipmi.private.util as util
|
||||
import struct
|
||||
|
||||
firmware_types = {
|
||||
1: 'Management Controller',
|
||||
@@ -67,6 +69,8 @@ class OEMHandler(generic.OEMHandler):
|
||||
# will need to retain data to differentiate
|
||||
# variations. For example System X versus Thinkserver
|
||||
self.oemid = oemid
|
||||
self.ipmicmd = ipmicmd
|
||||
self.oem_inventory_info = None
|
||||
|
||||
def process_event(self, event, ipmicmd, seldata):
|
||||
if 'oemdata' in event:
|
||||
@@ -143,11 +147,99 @@ class OEMHandler(generic.OEMHandler):
|
||||
event['component_type_id'] == 13):
|
||||
event['component'] += ' {0}'.format(evdata[1] & 0b11111)
|
||||
|
||||
@property
|
||||
def has_tsm(self):
|
||||
"""True if this particular server have a TSM based service processor
|
||||
"""
|
||||
return (self.oemid['manufacturer_id'] == 19046 and
|
||||
self.oemid['device_id'] == 32)
|
||||
|
||||
def get_oem_inventory_descriptions(self):
|
||||
if self.has_tsm:
|
||||
# Thinkserver with TSM
|
||||
if not self.oem_inventory_info:
|
||||
self._collect_tsm_inventory()
|
||||
return iter(self.oem_inventory_info)
|
||||
return ()
|
||||
|
||||
def get_oem_inventory(self):
|
||||
if self.has_tsm:
|
||||
self._collect_tsm_inventory()
|
||||
for compname in self.oem_inventory_info:
|
||||
yield (compname, self.oem_inventory_info[compname])
|
||||
|
||||
def get_inventory_of_component(self, component):
|
||||
if self.has_tsm:
|
||||
self._collect_tsm_inventory()
|
||||
return self.oem_inventory_info.get(component, None)
|
||||
|
||||
def _decode_tsm_cpu(self, offset, cpudata):
|
||||
keytext = 'CPU {0}'.format(ord(cpudata[offset]))
|
||||
self.oem_inventory_info[keytext] = {}
|
||||
if cpudata[offset + 1] == '\x00':
|
||||
self.oem_inventory_info[keytext] = None
|
||||
return
|
||||
self.oem_inventory_info[keytext]['cores'] = ord(
|
||||
cpudata[offset + 1])
|
||||
self.oem_inventory_info[keytext]['threads'] = ord(
|
||||
cpudata[offset + 2])
|
||||
self.oem_inventory_info[keytext]['manufacturer'] = \
|
||||
cpudata[offset + 3:offset + 16].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['family'] = \
|
||||
cpudata[offset + 16: offset + 46].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['model'] = \
|
||||
cpudata[offset + 46: offset + 76].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['stepping'] = \
|
||||
cpudata[offset + 76: offset + 79].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['frequency'] = \
|
||||
'{0:.1f} GHz'.format(
|
||||
struct.unpack('<I',
|
||||
cpudata[offset + 79: offset + 83])[0] / 1000.0)
|
||||
|
||||
def _decode_tsm_dimm(self, offset, memdata):
|
||||
keytext = 'DIMM {0}'.format(ord(memdata[offset]))
|
||||
if memdata[offset + 1] == '\x00':
|
||||
self.oem_inventory_info[keytext] = None
|
||||
return
|
||||
self.oem_inventory_info[keytext] = {}
|
||||
self.oem_inventory_info[keytext]['module_type'] = \
|
||||
memdata[offset + 3: offset + 13].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['voltage'] = \
|
||||
memdata[offset + 13: offset + 23].rstrip('\x00')
|
||||
clock = struct.unpack(
|
||||
'<H', memdata[offset + 23:offset + 25])[0]
|
||||
self.oem_inventory_info[keytext]['speed'] = spd.speed_by_clock.get(
|
||||
clock, 'Unknown')
|
||||
self.oem_inventory_info[keytext]['capacity_mb'] = struct.unpack(
|
||||
'<H', memdata[offset + 25:offset + 27])[0] * 1024
|
||||
self.oem_inventory_info[keytext]['manufacturer'] = \
|
||||
memdata[offset + 27:offset + 57].rstrip('\x00')
|
||||
self.oem_inventory_info[keytext]['serial'] = \
|
||||
struct.unpack('>I', memdata[offset + 57:offset + 61])[0]
|
||||
self.oem_inventory_info[keytext]['model'] = \
|
||||
memdata[offset + 61:offset + 82].rstrip('\x00')
|
||||
|
||||
def _collect_tsm_inventory(self):
|
||||
# Collect CPU inventory
|
||||
self.oem_inventory_info = {}
|
||||
rsp = self.ipmicmd.xraw_command(netfn=6, command=0x59,
|
||||
data=(0, 0xc1, 1, 0))
|
||||
compcount = ord(rsp['data'][1])
|
||||
for cpu in xrange(0, compcount):
|
||||
offset = 2 + (85 * cpu)
|
||||
self._decode_tsm_cpu(offset, rsp['data'])
|
||||
# Collect memory inventory
|
||||
rsp = self.ipmicmd.xraw_command(netfn=6, command=0x59,
|
||||
data=(0, 0xc1, 2, 0))
|
||||
compcount = ord(rsp['data'][1])
|
||||
for dimm in xrange(0, compcount):
|
||||
offset = 2 + (dimm * 84)
|
||||
self._decode_tsm_dimm(offset, rsp['data'])
|
||||
|
||||
def process_fru(self, fru):
|
||||
if fru is None:
|
||||
return fru
|
||||
if (self.oemid['manufacturer_id'] == 19046 and
|
||||
self.oemid['device_id'] == 32):
|
||||
if self.has_tsm:
|
||||
fru['oem_parser'] = 'lenovo'
|
||||
# Thinkserver lays out specific interpretation of the
|
||||
# board extra fields
|
||||
|
||||
Reference in New Issue
Block a user