Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mellanox] [202012] Backport 'Read EEPROM data from DB if possible'(7808) to 202012 #81

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 83 additions & 119 deletions platform/mellanox/mlnx-platform-api/sonic_platform/eeprom.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,18 @@
#
#############################################################################
import os
import sys
import re
import time

if sys.version_info.major == 3:
from io import StringIO
else:
from cStringIO import StringIO

from sonic_py_common.logger import Logger

try:
from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo
except ImportError as e:
raise ImportError (str(e) + "- required module not found")

logger = Logger()
from .utils import default_return

#
# 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'
logger = Logger()

#
# this is mlnx-specific
Expand All @@ -41,10 +27,6 @@

class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
RETRIES = 3
EEPROM_DECODE_HEADLINES = 6
EEPROM_DECODE_MAXITEM = 3
EEPROM_DECODE_OFFSET = 0
EEPROM_DECODE_CONTENT = 2

def __init__(self):
for attempt in range(self.RETRIES):
Expand All @@ -53,95 +35,15 @@ def __init__(self):
else:
break

if not (os.path.exists(EEPROM_SYMLINK) \
or os.path.isfile(os.path.join(CACHE_ROOT, CACHE_FILE))):
log_error("Nowhere to read syseeprom from! No symlink or cache file found")
if not os.path.exists(EEPROM_SYMLINK):
logger.log_error("Nowhere to read syseeprom from! No symlink or cache file found")
raise RuntimeError("No syseeprom symlink or cache file found")

self.eeprom_path = EEPROM_SYMLINK
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
self._eeprom_loaded = False
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
self._eeprom_info_dict = None

@default_return(return_value='Undefined.')
def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
Expand All @@ -150,43 +52,39 @@ def get_base_mac(self):
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
if not self._eeprom_loaded:
self._load_eeprom()
return self._base_mac

return self._get_eeprom_value(self._TLV_CODE_MAC_BASE)

@default_return(return_value='Undefined.')
def get_serial_number(self):
"""
Retrieves the hardware serial number for the chassis

Returns:
A string containing the hardware serial number for this chassis.
"""
if not self._eeprom_loaded:
self._load_eeprom()
return self._serial_str
return self._get_eeprom_value(self._TLV_CODE_SERIAL_NUMBER)

@default_return(return_value='Undefined.')
def get_product_name(self):
"""
Retrieves the hardware product name for the chassis

Returns:
A string containing the hardware product name for this chassis.
"""
if not self._eeprom_loaded:
self._load_eeprom()
return self._product_name
return self._get_eeprom_value(self._TLV_CODE_PRODUCT_NAME)

@default_return(return_value='Undefined.')
def get_part_number(self):
"""
Retrieves the hardware part number for the chassis

Returns:
A string containing the hardware part number for this chassis.
"""
if not self._eeprom_loaded:
self._load_eeprom()
return self._part_number
return self._get_eeprom_value(self._TLV_CODE_PART_NUMBER)

@default_return({})
def get_system_eeprom_info(self):
"""
Retrieves the full content of system EEPROM information for the chassis
Expand All @@ -196,6 +94,72 @@ def get_system_eeprom_info(self):
OCP ONIE TlvInfo EEPROM format and values are their corresponding
values.
"""
if not self._eeprom_loaded:
self._load_eeprom()
if self._eeprom_info_dict is None:
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

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)

47 changes: 47 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import functools
import subprocess

# flags to indicate whether this process is running in docker or host
_is_host = None


def read_str_from_file(file_path, default='', raise_exception=False):
"""
Read string content from file
Expand Down Expand Up @@ -55,3 +62,43 @@ def write_file(file_path, content, raise_exception=False):
else:
raise e
return True


def is_host():
"""
Test whether current process is running on the host or an docker
return True for host and False for docker
"""
global _is_host
if _is_host is not None:
return _is_host

_is_host = False
try:
proc = subprocess.Popen("docker --version 2>/dev/null",
stdout=subprocess.PIPE,
shell=True,
stderr=subprocess.STDOUT,
universal_newlines=True)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
if result != '':
_is_host = True

except OSError as e:
pass

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