[Mellanox] Read EEPROM data from DB if possible (#7808)

- Why I did it
Remove EEPROM cache file and use DB instead

- How I did it
Read EEPROM data from DB if possible
If data is not ready in DB, read from hardware using a visitor pattern

- How to verify it
Manual test and regression
This commit is contained in:
Junchao-Mellanox 2021-06-20 22:58:11 +08:00 committed by GitHub
parent 4c4799ed41
commit f294096eb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 119 deletions

View File

@ -6,15 +6,8 @@
# #
############################################################################# #############################################################################
import os import os
import sys
import re
import time import time
if sys.version_info.major == 3:
from io import StringIO
else:
from cStringIO import StringIO
from sonic_py_common.logger import Logger from sonic_py_common.logger import Logger
try: try:
@ -22,16 +15,9 @@ try:
except ImportError as e: except ImportError as e:
raise ImportError (str(e) + "- required module not found") raise ImportError (str(e) + "- required module not found")
logger = Logger() from .utils import default_return
# logger = Logger()
# CACHE_XXX stuffs are supposted to be moved to the base classes
# since they are common for all vendors
# they are defined in decode-syseeprom which might be removed in the future
# currently we just copy them here
#
CACHE_ROOT = '/var/cache/sonic/decode-syseeprom'
CACHE_FILE = 'syseeprom_cache'
# #
# this is mlnx-specific # this is mlnx-specific
@ -41,10 +27,6 @@ EEPROM_SYMLINK = "/var/run/hw-management/eeprom/vpd_info"
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder): class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
RETRIES = 3 RETRIES = 3
EEPROM_DECODE_HEADLINES = 6
EEPROM_DECODE_MAXITEM = 3
EEPROM_DECODE_OFFSET = 0
EEPROM_DECODE_CONTENT = 2
def __init__(self): def __init__(self):
for attempt in range(self.RETRIES): for attempt in range(self.RETRIES):
@ -53,95 +35,15 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
else: else:
break break
if not (os.path.exists(EEPROM_SYMLINK) \ if not os.path.exists(EEPROM_SYMLINK):
or os.path.isfile(os.path.join(CACHE_ROOT, CACHE_FILE))): logger.log_error("Nowhere to read syseeprom from! No symlink or cache file found")
log_error("Nowhere to read syseeprom from! No symlink or cache file found")
raise RuntimeError("No syseeprom symlink or cache file found") raise RuntimeError("No syseeprom symlink or cache file found")
self.eeprom_path = EEPROM_SYMLINK self.eeprom_path = EEPROM_SYMLINK
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True) super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
self._eeprom_loaded = False self._eeprom_info_dict = None
self._load_eeprom()
self._eeprom_loaded = True
def _load_eeprom(self):
cache_file = os.path.join(CACHE_ROOT, CACHE_FILE)
if not os.path.exists(CACHE_ROOT):
try:
os.makedirs(CACHE_ROOT)
except:
pass
else:
try:
# Make sure first time always read eeprom data from hardware
if os.path.exists(cache_file):
os.remove(cache_file)
except Exception as e:
logger.log_error('Failed to remove cache file {} - {}'.format(cache_file, repr(e)))
try:
self.set_cache_name(cache_file)
except:
pass
eeprom = self.read_eeprom()
if eeprom is None :
return 0
try:
self.update_cache(eeprom)
except:
pass
self._base_mac = self.mgmtaddrstr(eeprom)
if self._base_mac is None:
self._base_mac = "Undefined."
else:
self._base_mac = self._base_mac.strip('\0')
self._serial_str = self.serial_number_str(eeprom)
if self._serial_str is None:
self._serial_str = "Undefined."
else:
self._serial_str = self._serial_str.strip('\0')
self._product_name = self.modelstr(eeprom)
if self._product_name is None:
self._product_name = "Undefined."
else:
self._product_name = self._product_name.strip('\0')
self._part_number = self.part_number_str(eeprom)
if self._part_number is None:
self._part_number = "Undefined."
else:
self._part_number = self._part_number.strip('\0')
original_stdout = sys.stdout
sys.stdout = StringIO()
self.decode_eeprom(eeprom)
decode_output = sys.stdout.getvalue()
sys.stdout = original_stdout
#parse decode_output into a dictionary
decode_output.replace('\0', '')
lines = decode_output.split('\n')
lines = lines[self.EEPROM_DECODE_HEADLINES:]
self._eeprom_info_dict = dict()
for line in lines:
try:
match = re.search('(0x[0-9a-fA-F]{2})([\s]+[\S]+[\s]+)([\S]+[\s]*[\S]*)', line)
if match is not None:
idx = match.group(1)
value = match.group(3).rstrip('\0')
self._eeprom_info_dict[idx] = value
except:
pass
return 0
@default_return(return_value='Undefined.')
def get_base_mac(self): def get_base_mac(self):
""" """
Retrieves the base MAC address for the chassis Retrieves the base MAC address for the chassis
@ -150,10 +52,9 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
A string containing the MAC address in the format A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX' 'XX:XX:XX:XX:XX:XX'
""" """
if not self._eeprom_loaded: return self._get_eeprom_value(self._TLV_CODE_MAC_BASE)
self._load_eeprom()
return self._base_mac
@default_return(return_value='Undefined.')
def get_serial_number(self): def get_serial_number(self):
""" """
Retrieves the hardware serial number for the chassis Retrieves the hardware serial number for the chassis
@ -161,10 +62,9 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
Returns: Returns:
A string containing the hardware serial number for this chassis. A string containing the hardware serial number for this chassis.
""" """
if not self._eeprom_loaded: return self._get_eeprom_value(self._TLV_CODE_SERIAL_NUMBER)
self._load_eeprom()
return self._serial_str
@default_return(return_value='Undefined.')
def get_product_name(self): def get_product_name(self):
""" """
Retrieves the hardware product name for the chassis Retrieves the hardware product name for the chassis
@ -172,10 +72,9 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
Returns: Returns:
A string containing the hardware product name for this chassis. A string containing the hardware product name for this chassis.
""" """
if not self._eeprom_loaded: return self._get_eeprom_value(self._TLV_CODE_PRODUCT_NAME)
self._load_eeprom()
return self._product_name
@default_return(return_value='Undefined.')
def get_part_number(self): def get_part_number(self):
""" """
Retrieves the hardware part number for the chassis Retrieves the hardware part number for the chassis
@ -183,10 +82,9 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
Returns: Returns:
A string containing the hardware part number for this chassis. A string containing the hardware part number for this chassis.
""" """
if not self._eeprom_loaded: return self._get_eeprom_value(self._TLV_CODE_PART_NUMBER)
self._load_eeprom()
return self._part_number
@default_return({})
def get_system_eeprom_info(self): def get_system_eeprom_info(self):
""" """
Retrieves the full content of system EEPROM information for the chassis Retrieves the full content of system EEPROM information for the chassis
@ -196,6 +94,72 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
OCP ONIE TlvInfo EEPROM format and values are their corresponding OCP ONIE TlvInfo EEPROM format and values are their corresponding
values. values.
""" """
if not self._eeprom_loaded: if self._eeprom_info_dict is None:
self._load_eeprom() self._eeprom_info_dict = {}
# Try get from DB first
db_initialized = self._redis_hget('EEPROM_INFO|State', 'Initialized')
if db_initialized == '1':
code = self._TLV_CODE_PRODUCT_NAME
while code <= self._TLV_CODE_SERVICE_TAG:
value = self._redis_hget('EEPROM_INFO|{}'.format(hex(code)), 'Value')
if value:
self._eeprom_info_dict[hex(code)] = value
code += 1
# Handle vendor extension TLV
vendor_extension_tlv_code = hex(self._TLV_CODE_VENDOR_EXT)
try:
vendor_extension_num = int(self._redis_hget('EEPROM_INFO|{}'.format(vendor_extension_tlv_code), 'Num_vendor_ext'))
except (ValueError, TypeError):
vendor_extension_num = 0
if vendor_extension_num != 0:
for i in range(vendor_extension_num):
value = self._redis_hget('EEPROM_INFO|{}'.format(vendor_extension_tlv_code), 'Value_{}'.format(i))
if value:
if vendor_extension_tlv_code not in self._eeprom_info_dict:
self._eeprom_info_dict[vendor_extension_tlv_code] = [value]
else:
self._eeprom_info_dict[vendor_extension_tlv_code].append(value)
# Get CRC
value = self._redis_hget('EEPROM_INFO|{}'.format(hex(self._TLV_CODE_CRC_32)), 'Value')
if value:
self._eeprom_info_dict[hex(self._TLV_CODE_CRC_32)] = value
else:
eeprom = self.read_eeprom()
visitor = EepromContentVisitor(self._eeprom_info_dict)
self.visit_eeprom(eeprom, visitor)
return self._eeprom_info_dict return self._eeprom_info_dict
def _get_eeprom_value(self, code):
"""Helper function to help get EEPROM data by code
Args:
code (int): EEPROM TLV code
Returns:
str: value of EEPROM TLV
"""
eeprom_info_dict = self.get_system_eeprom_info()
return eeprom_info_dict[hex(code)]
class EepromContentVisitor(eeprom_tlvinfo.EepromDefaultVisitor):
def __init__(self, content):
self.content = content
def visit_tlv(self, name, code, length, value):
if code != Eeprom._TLV_CODE_VENDOR_EXT:
self.content[hex(code)] = value.rstrip('\0')
else:
if value:
value = value.rstrip('\0')
if value:
code = hex(code)
if code not in self.content:
self.content[code] = [value]
else:
self.content[code].append(value)

View File

@ -1,3 +1,4 @@
import functools
import subprocess import subprocess
# flags to indicate whether this process is running in docker or host # flags to indicate whether this process is running in docker or host
@ -89,3 +90,15 @@ def is_host():
pass pass
return _is_host return _is_host
def default_return(return_value):
def wrapper(method):
@functools.wraps(method)
def _impl(*args, **kwargs):
try:
return method(*args, **kwargs)
except:
return return_value
return _impl
return wrapper