mirror of
https://opendev.org/x/pyghmi
synced 2026-03-29 06:13:30 +00:00
Add system UUID to inventory
While not strictly in the FRU area, it is often desirable to have the system UUID available. The intent is for the UUID to match what dmidecode would return. If a manufacturer does it right, that UUID will be unique. For ThinkServers, override with the UUID from the OEM FRU fields rather than using the get system UUID result. Change-Id: Ie9a1b7e8fee2cb40ab679cbf2df04db61fd4e42f
This commit is contained in:
@@ -22,6 +22,7 @@ import pyghmi.exceptions as exc
|
||||
import pyghmi.ipmi.fru as fru
|
||||
from pyghmi.ipmi.oem.lookup import get_oem_handler
|
||||
from pyghmi.ipmi.private import session
|
||||
import pyghmi.ipmi.private.util as pygutil
|
||||
import pyghmi.ipmi.sdr as sdr
|
||||
import struct
|
||||
|
||||
@@ -384,7 +385,7 @@ class Command(object):
|
||||
"""
|
||||
self.oem_init()
|
||||
if component == 'System':
|
||||
return self._oem.process_fru(fru.FRU(ipmicmd=self).info)
|
||||
return self._get_zero_fru()
|
||||
if self._sdr is None:
|
||||
self._sdr = sdr.SDR(self)
|
||||
for fruid in self._sdr.fru:
|
||||
@@ -392,6 +393,23 @@ class Command(object):
|
||||
return self._oem.process_fru(fru.FRU(
|
||||
ipmicmd=self, fruid=fruid, sdr=self._sdr.fru[fruid]).info)
|
||||
|
||||
def _get_zero_fru(self):
|
||||
# It is expected that a manufacturer matches SMBIOS to IPMI
|
||||
# get system uuid return data. If a manufacturer does not
|
||||
# do so, they should handle either deletion or fixup in the
|
||||
# OEM processing pass. Code optimistically assumes that if
|
||||
# data is returned, than the vendor is properly using it.
|
||||
zerofru = fru.FRU(ipmicmd=self).info
|
||||
if zerofru is None:
|
||||
zerofru = {}
|
||||
guiddata = self.raw_command(netfn=6, command=0x37)
|
||||
if 'error' not in guiddata:
|
||||
zerofru['UUID'] = pygutil.decode_wireformat_uuid(
|
||||
guiddata['data'])
|
||||
if not zerofru:
|
||||
zerofru = None
|
||||
return self._oem.process_fru(zerofru)
|
||||
|
||||
def get_inventory(self):
|
||||
"""Retrieve inventory of system
|
||||
|
||||
@@ -403,10 +421,7 @@ class Command(object):
|
||||
or None for items not present.
|
||||
"""
|
||||
self.oem_init()
|
||||
zerofru = fru.FRU(ipmicmd=self).info
|
||||
if zerofru is not None:
|
||||
zerofru = self._oem.process_fru(zerofru)
|
||||
yield ("System", zerofru)
|
||||
yield ("System", self._get_zero_fru())
|
||||
if self._sdr is None:
|
||||
self._sdr = sdr.SDR(self)
|
||||
for fruid in self._sdr.fru:
|
||||
|
||||
@@ -221,7 +221,11 @@ class FRU(object):
|
||||
retinfo = retinfo.decode('utf-16le')
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
retinfo = retinfo.replace('\x00', '').strip()
|
||||
# Some things lie about being text. Do the best we can by
|
||||
# removing trailing spaces and nulls like makes sense for text
|
||||
# and rely on vendors to workaround deviations in their OEM
|
||||
# module
|
||||
retinfo = retinfo.rstrip('\x00 ')
|
||||
return retinfo, newoffset
|
||||
elif currtype == 1: # BCD 'plus'
|
||||
retdata = ''
|
||||
@@ -232,8 +236,7 @@ class FRU(object):
|
||||
retdata = retdata.strip()
|
||||
return retdata, newoffset
|
||||
elif currtype == 2: # 6-bit ascii
|
||||
retinfo = unpack6bitascii(retinfo)
|
||||
retinfo = retinfo.strip()
|
||||
retinfo = unpack6bitascii(retinfo).strip()
|
||||
return retinfo, newoffset
|
||||
|
||||
def _parse_chassis(self):
|
||||
|
||||
@@ -37,7 +37,7 @@ class OEMHandler(object):
|
||||
board/product/chassis_extra_data arrays if 'oem_parser' is None,
|
||||
and mask those fields if not None. It is expected that OEMs leave
|
||||
the fields intact so that if client code hard codes around the
|
||||
ordered lists that their expectations are not broken by an update
|
||||
ordered lists that their expectations are not broken by an update.
|
||||
"""
|
||||
# In the generic case, just pass through
|
||||
if fru is None:
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
# limitations under the License.
|
||||
|
||||
import pyghmi.ipmi.oem.generic as generic
|
||||
import pyghmi.ipmi.private.util as util
|
||||
|
||||
|
||||
class OEMHandler(generic.OEMHandler):
|
||||
# noinspection PyUnusedLocal
|
||||
def __init__(self, oemid, ipmicmd):
|
||||
# will need to retain data to differentiate
|
||||
# variations. For example System X versus Thinkserver
|
||||
@@ -40,8 +42,16 @@ class OEMHandler(generic.OEMHandler):
|
||||
fru['MAC Address 1'] = mac1
|
||||
if mac2 not in ('00:00:00:00:00:00', ''):
|
||||
fru['MAC Address 2'] = mac2
|
||||
# The product_extra is just UUID, we have that plenty of other ways
|
||||
# So for now, leave that portion of the data alone
|
||||
# The product_extra field is UUID as the system would present
|
||||
# in DMI. This is different than the two UUIDs that
|
||||
# it returns for get device and get system uuid...
|
||||
byteguid = fru['product_extra'][0]
|
||||
# It can present itself as claiming to be ASCII when it
|
||||
# is actually raw hex. As a result it triggers the mechanism
|
||||
# to strip \x00 from the end of text strings. Work around this
|
||||
# by padding with \x00 to the right if the string is not 16 long
|
||||
byteguid.extend('\x00' * (16 - len(byteguid)))
|
||||
fru['UUID'] = util.decode_wireformat_uuid(byteguid)
|
||||
return fru
|
||||
else:
|
||||
fru['oem_parser'] = None
|
||||
|
||||
31
pyghmi/ipmi/private/util.py
Normal file
31
pyghmi/ipmi/private/util.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
def decode_wireformat_uuid(rawguid):
|
||||
"""Decode a wire format UUID
|
||||
|
||||
It handles the rather particular scheme where half is little endian
|
||||
and half is big endian. It returns a string like dmidecode would output.
|
||||
"""
|
||||
if isinstance(rawguid, list):
|
||||
rawguid = bytearray(rawguid)
|
||||
lebytes = struct.unpack_from('<IHH', buffer(rawguid[:8]))
|
||||
bebytes = struct.unpack_from('>HHI', buffer(rawguid[8:]))
|
||||
return '{0:04X}-{1:02X}-{2:02X}-{3:02X}-{4:02X}{5:04X}'.format(
|
||||
lebytes[0], lebytes[1], lebytes[2], bebytes[0], bebytes[1], bebytes[2])
|
||||
Reference in New Issue
Block a user