[DellEMC]: EEPROM decoder for S6000, S6000-ON (#4718)

**- Why I did it**

For decoding system EEPROM of S6000 based on Dell offset format and S6000-ON’s system EEPROM in ONIE TLV format.

**- How I did it**

- Differentiate between S6000 and S6000-ON using the product name available in ‘dmi’  ( “/sys/class/dmi/id/product_name” )
- For decoding S6000 system EEPROM in Dell offset format and updating the redis DB with the EEPROM contents, added a new class ‘EepromS6000’ in eeprom.py, 
- Renamed certain methods in both Eeprom, EepromS6000 classes to accommodate the plugin-specific methods.

**- How to verify it**

- Use 'decode-syseeprom' command to list the system EEPROM details.
- Wrote a python script to load chassis class and call the appropriate methods.

UT Logs: [S6000_eeprom_logs.txt](https://github.com/Azure/sonic-buildimage/files/4735515/S6000_eeprom_logs.txt), [S6000-ON_eeprom_logs.txt](https://github.com/Azure/sonic-buildimage/files/4735461/S6000-ON_eeprom_logs.txt)
Test script: [eeprom_test_py.txt](https://github.com/Azure/sonic-buildimage/files/4735509/eeprom_test_py.txt)
This commit is contained in:
Arun Saravanan Balachandran 2020-06-09 21:38:15 +05:30 committed by GitHub
parent 9505bdb910
commit 54b284f4b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 292 additions and 23 deletions

View File

@ -10,13 +10,21 @@
#############################################################################
try:
from sonic_eeprom import eeprom_tlvinfo
except ImportError, e:
raise ImportError (str(e) + "- required module not found")
from sonic_eeprom.eeprom_tlvinfo import TlvInfoDecoder
from sonic_platform.eeprom import EepromS6000
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class board(eeprom_tlvinfo.TlvInfoDecoder):
class board(object):
def __init__(self, name, path, cpld_root, ro):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-10/10-0053/eeprom"
super(board, self).__init__(self.eeprom_path, 0, '', True)
def __new__(cls, name, path, cpld_root, ro):
eeprom_path = "/sys/class/i2c-adapter/i2c-10/10-0053/eeprom"
with open("/sys/class/dmi/id/product_name", "r") as fd:
board_type = fd.read()
if 'S6000-ON' in board_type:
return TlvInfoDecoder(eeprom_path, 0, '', True)
else:
return EepromS6000(is_plugin=True)

View File

@ -15,7 +15,7 @@ try:
import subprocess
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.sfp import Sfp
from sonic_platform.eeprom import Eeprom
from sonic_platform.eeprom import Eeprom, EepromS6000
from sonic_platform.fan import Fan
from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal
@ -68,7 +68,14 @@ class Chassis(ChassisBase):
# Get Transceiver status
self.modprs_register = self._get_transceiver_status()
self._eeprom = Eeprom()
with open("/sys/class/dmi/id/product_name", "r") as fd:
board_type = fd.read()
if 'S6000-ON' in board_type:
self._eeprom = Eeprom()
else:
self._eeprom = EepromS6000()
for i in range(MAX_S6000_FAN):
fan = Fan(i)
self._fan_list.append(fan)
@ -138,7 +145,7 @@ class Chassis(ChassisBase):
Returns:
string: The name of the chassis
"""
return self._eeprom.modelstr()
return self._eeprom.get_model()
def get_presence(self):
"""
@ -154,7 +161,7 @@ class Chassis(ChassisBase):
Returns:
string: Model/part number of chassis
"""
return self._eeprom.part_number_str()
return self._eeprom.get_part_number()
def get_serial(self):
"""
@ -162,7 +169,7 @@ class Chassis(ChassisBase):
Returns:
string: Serial number of chassis
"""
return self._eeprom.serial_str()
return self._eeprom.get_serial()
def get_status(self):
"""
@ -181,7 +188,7 @@ class Chassis(ChassisBase):
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
return self._eeprom.base_mac_addr()
return self._eeprom.get_base_mac()
def get_serial_number(self):
"""
@ -191,7 +198,7 @@ class Chassis(ChassisBase):
A string containing the hardware serial number for this
chassis.
"""
return self._eeprom.serial_number_str()
return self._eeprom.get_serial_number()
def get_system_eeprom_info(self):
"""

View File

@ -17,12 +17,17 @@
try:
import binascii
import os
import redis
import struct
from collections import OrderedDict
from sonic_platform_base.sonic_eeprom.eeprom_base import EepromDecoder
from sonic_platform_base.sonic_eeprom.eeprom_tlvinfo import TlvInfoDecoder
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
STATE_DB_INDEX = 6
# PSU eeprom fields in format required by EepromDecoder
psu_eeprom_format = [
@ -200,13 +205,13 @@ class Eeprom(TlvInfoDecoder):
return (False, None)
def serial_number_str(self):
def get_serial_number(self):
"""
Returns the serial number.
"""
return self.serial_number
def part_number_str(self):
def get_part_number(self):
"""
Returns the part number.
"""
@ -222,19 +227,19 @@ class Eeprom(TlvInfoDecoder):
return int(self.fan_type.encode('hex'), 16)
# System EEPROM specific methods
def base_mac_addr(self):
def get_base_mac(self):
"""
Returns the base MAC address found in the system EEPROM.
"""
return self.base_mac
def modelstr(self):
def get_model(self):
"""
Returns the Model name.
"""
return self.model_str
def serial_str(self):
def get_serial(self):
"""
Returns the servicetag number.
"""
@ -247,3 +252,252 @@ class Eeprom(TlvInfoDecoder):
found in the system EEPROM.
"""
return self.eeprom_tlv_dict
class EepromS6000(EepromDecoder):
_EEPROM_MAX_LEN = 128
_BLK_HDR_LEN = 6
_BLK_HDR_MAGIC = '\x3a\x29'
_BLK_HDR_REVID = 1
_BLK_CODE_MFG = 0x20
_BLK_CODE_SW = 0x1F
_BLK_CODE_MAC = 0x21
_BLK_MFG_FORMAT = [
("PPID", 20), ("DPN Rev", 3), ("Service Tag", 7), ("Part Number", 10),
("Part Number Rev", 3), ("Mfg Test Results", 2)
]
_BLK_SW_FORMAT = [("Card ID", 4), ("Module ID", 2)]
_BLK_MAC_FORMAT = [("Base MAC address", 6)]
_BLK_INFO = OrderedDict([
(_BLK_CODE_MFG, {"name": "MFG BLOCK", "size": 64,
"offset": 0x0, "format": _BLK_MFG_FORMAT}),
(_BLK_CODE_SW, {"name": "SW BLOCK", "size": 48,
"offset": 0x40, "format": _BLK_SW_FORMAT}),
(_BLK_CODE_MAC, {"name": "MAC BLOCK", "size": 16,
"offset": 0x70, "format": _BLK_MAC_FORMAT})
])
def __init__(self, is_plugin=False):
self.eeprom_path = "/sys/bus/i2c/devices/i2c-10/10-0053/eeprom"
super(EepromS6000, self).__init__(self.eeprom_path, None, 0, '', True)
if not is_plugin:
self.eeprom_data = self.read_eeprom()
def _is_valid_block_checksum(self, e):
crc = self.compute_dell_crc(e[:-2])
return crc == struct.unpack('<H', e[-2:])[0]
def _is_valid_block(self, e, blk_code):
return (e[:2] == self._BLK_HDR_MAGIC
and struct.unpack('<H', e[2:4])[0] == self._BLK_INFO[blk_code]["size"]
and ord(e[4]) == blk_code
and ord(e[5]) == self._BLK_HDR_REVID)
def _get_eeprom_field(self, e, blk_code, field_name):
"""
For a field name specified in the EEPROM format, returns the
presence of the field and the value for the same.
"""
blk_start = self._BLK_INFO[blk_code]["offset"]
blk_end = blk_start + self._BLK_INFO[blk_code]["size"]
if not self._is_valid_block(e[blk_start:blk_end], blk_code):
return (False, None)
field_start = blk_start + self._BLK_HDR_LEN
for field in self._BLK_INFO[blk_code]["format"]:
field_end = field_start + field[1]
if field[0] == field_name:
return (True, e[field_start:field_end])
field_start = field_end
return (False, None)
def decode_eeprom(self, e):
"""
Decode and print out the contents of the EEPROM.
"""
print " Field Name Len Value"
print "-------------------- --- --------------------"
for blk_code in self._BLK_INFO.keys():
blk_start = self._BLK_INFO[blk_code]["offset"]
blk_end = blk_start + self._BLK_INFO[blk_code]["size"]
if not self._is_valid_block(e[blk_start:blk_end], blk_code):
print "Invalid Block starting at EEPROM offset %d" % (blk_start)
return
offset = blk_start + self._BLK_HDR_LEN
for f in self._BLK_INFO[blk_code]["format"]:
if f[0] == "Num MACs" or f[0] == "Module ID":
data = str(struct.unpack('<H', e[offset:offset+f[1]])[0])
elif f[0] == "Card ID":
data = hex(struct.unpack('<I', e[offset:offset+f[1]])[0])
elif f[0] == "Base MAC address":
data = ":".join([binascii.b2a_hex(T) for T in e[offset:offset+f[1]]]).upper()
else:
data = e[offset:offset+f[1]]
print "{:<20s} {:>3d} {:<s}".format(f[0], f[1], data)
offset += f[1]
if not self._is_valid_block_checksum(e[blk_start:blk_end]):
print "(*** block checksum invalid)"
def read_eeprom(self):
"""
Read the EEPROM contents.
"""
return self.read_eeprom_bytes(self._EEPROM_MAX_LEN)
def read_eeprom_db(self):
"""
Print out the contents of the EEPROM from database.
"""
client = redis.Redis(db=STATE_DB_INDEX)
db_state = client.hget('EEPROM_INFO|State', 'Initialized')
if db_state != '1':
return -1
print " Field Name Len Value"
print "-------------------- --- --------------------"
for blk_code in self._BLK_INFO.keys():
blk_name = self._BLK_INFO[blk_code]["name"]
blk_start = self._BLK_INFO[blk_code]["offset"]
is_valid = client.hget('EEPROM_INFO|{}'.format(blk_name), 'Valid')
if is_valid == '0':
print "Invalid Block starting at EEPROM offset %d" % (blk_start)
break
for f in self._BLK_INFO[blk_code]["format"]:
data = client.hget('EEPROM_INFO|{}'.format(f[0]), 'Value')
print "{:<20s} {:>3d} {:<s}".format(f[0], f[1], data)
is_checksum_valid = client.hget('EEPROM_INFO|{}'.format(blk_name), 'Checksum_Valid')
if is_checksum_valid == '0':
print "(*** block checksum invalid)"
return 0
def update_eeprom_db(self, e):
"""
Decode the contents of the EEPROM and update the contents to database
"""
client = redis.Redis(db=STATE_DB_INDEX)
for blk_code in self._BLK_INFO.keys():
blk_name = self._BLK_INFO[blk_code]["name"]
blk_start = self._BLK_INFO[blk_code]["offset"]
blk_end = blk_start + self._BLK_INFO[blk_code]["size"]
if not self._is_valid_block(e[blk_start:blk_end], blk_code):
client.hset('EEPROM_INFO|{}'.format(blk_name), 'Valid', '0')
print "Invalid Block starting at EEPROM offset %d" % (blk_start)
break
else:
client.hset('EEPROM_INFO|{}'.format(blk_name), 'Valid', '1')
offset = blk_start + self._BLK_HDR_LEN
for f in self._BLK_INFO[blk_code]["format"]:
if f[0] == "Num MACs" or f[0] == "Module ID":
data = str(struct.unpack('<H', e[offset:offset+f[1]])[0])
elif f[0] == "Card ID":
data = hex(struct.unpack('<I', e[offset:offset+f[1]])[0])
elif f[0] == "Base MAC address":
data = ":".join([binascii.b2a_hex(T) for T in e[offset:offset+f[1]]]).upper()
else:
data = e[offset:offset+f[1]]
client.hset('EEPROM_INFO|{}'.format(f[0]), 'Value', data)
offset += f[1]
if not self._is_valid_block_checksum(e[blk_start:blk_end]):
client.hset('EEPROM_INFO|{}'.format(blk_name), 'Checksum_Valid', '0')
else:
client.hset('EEPROM_INFO|{}'.format(blk_name), 'Checksum_Valid', '1')
client.hset('EEPROM_INFO|State', 'Initialized', '1')
return 0
def get_base_mac(self):
"""
Returns the base MAC address found in the system EEPROM.
"""
(valid, data) = self._get_eeprom_field(self.eeprom_data,
self._BLK_CODE_MAC, "Base MAC address")
if valid:
return ":".join([binascii.b2a_hex(T) for T in data]).upper()
else:
return 'NA'
def get_model(self):
"""
Returns the Model name.
"""
return 'S6000'
def get_part_number(self):
"""
Returns the part number.
"""
(valid, data) = self._get_eeprom_field(self.eeprom_data,
self._BLK_CODE_MFG, "Part Number")
if valid:
return data
else:
return 'NA'
def get_serial(self):
"""
Returns the Service tag.
"""
(valid, data) = self._get_eeprom_field(self.eeprom_data,
self._BLK_CODE_MFG, "Service Tag")
if valid:
return data
else:
return 'NA'
def get_serial_number(self):
"""
Returns the serial number.
"""
(valid, data) = self._get_eeprom_field(self.eeprom_data,
self._BLK_CODE_MFG, "PPID")
if valid:
return data
else:
return 'NA'
# EEPROM Plugin specific methods
def is_checksum_valid(self, e):
# Checksum is already calculated before
return (True, 0)
def mgmtaddrstr(self, e):
"""
Returns the base MAC address.
"""
(valid, data) = self._get_eeprom_field(e, self._BLK_CODE_MAC, "Base MAC address")
if valid:
return ":".join([binascii.b2a_hex(T) for T in data]).upper()
else:
return 'NA'
def modelstr(self, e):
"""
Returns the Model name.
"""
return 'S6000'
def serial_number_str(self, e):
"""
Returns the Service Tag.
"""
(valid, data) = self._get_eeprom_field(e, self._BLK_CODE_MFG, "Service Tag")
if valid:
return data
else:
return 'NA'

View File

@ -186,7 +186,7 @@ class Fan(FanBase):
string: Part number of Fan
"""
if not self.is_psu_fan:
return self.eeprom.part_number_str()
return self.eeprom.get_part_number()
else:
return 'NA'
@ -199,7 +199,7 @@ class Fan(FanBase):
"""
# Sample Serial number format "US-01234D-54321-25A-0123-A00"
if not self.is_psu_fan:
return self.eeprom.serial_number_str()
return self.eeprom.get_serial_number()
else:
return 'NA'

View File

@ -146,7 +146,7 @@ class Psu(PsuBase):
Returns:
string: Part number of PSU
"""
return self.eeprom.part_number_str()
return self.eeprom.get_part_number()
def get_serial(self):
"""
@ -156,7 +156,7 @@ class Psu(PsuBase):
string: Serial number of PSU
"""
# Sample Serial number format "US-01234D-54321-25A-0123-A00"
return self.eeprom.serial_number_str()
return self.eeprom.get_serial_number()
def get_status(self):
"""