Skip to content

Commit ccb663c

Browse files
[Mellanox] [202012] Backport 'Read EEPROM data from DB if possible'(7808) to 202012 (#7928)
- 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
1 parent 7d14a60 commit ccb663c

File tree

2 files changed

+130
-119
lines changed

2 files changed

+130
-119
lines changed

platform/mellanox/mlnx-platform-api/sonic_platform/eeprom.py

+83-119
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,18 @@
66
#
77
#############################################################################
88
import os
9-
import sys
10-
import re
119
import time
1210

13-
if sys.version_info.major == 3:
14-
from io import StringIO
15-
else:
16-
from cStringIO import StringIO
17-
1811
from sonic_py_common.logger import Logger
1912

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

25-
logger = Logger()
18+
from .utils import default_return
2619

27-
#
28-
# CACHE_XXX stuffs are supposted to be moved to the base classes
29-
# since they are common for all vendors
30-
# they are defined in decode-syseeprom which might be removed in the future
31-
# currently we just copy them here
32-
#
33-
CACHE_ROOT = '/var/cache/sonic/decode-syseeprom'
34-
CACHE_FILE = 'syseeprom_cache'
20+
logger = Logger()
3521

3622
#
3723
# this is mlnx-specific
@@ -41,10 +27,6 @@
4127

4228
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
4329
RETRIES = 3
44-
EEPROM_DECODE_HEADLINES = 6
45-
EEPROM_DECODE_MAXITEM = 3
46-
EEPROM_DECODE_OFFSET = 0
47-
EEPROM_DECODE_CONTENT = 2
4830

4931
def __init__(self):
5032
for attempt in range(self.RETRIES):
@@ -53,95 +35,15 @@ def __init__(self):
5335
else:
5436
break
5537

56-
if not (os.path.exists(EEPROM_SYMLINK) \
57-
or os.path.isfile(os.path.join(CACHE_ROOT, CACHE_FILE))):
58-
log_error("Nowhere to read syseeprom from! No symlink or cache file found")
38+
if not os.path.exists(EEPROM_SYMLINK):
39+
logger.log_error("Nowhere to read syseeprom from! No symlink or cache file found")
5940
raise RuntimeError("No syseeprom symlink or cache file found")
6041

6142
self.eeprom_path = EEPROM_SYMLINK
6243
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
63-
self._eeprom_loaded = False
64-
self._load_eeprom()
65-
self._eeprom_loaded = True
66-
67-
def _load_eeprom(self):
68-
cache_file = os.path.join(CACHE_ROOT, CACHE_FILE)
69-
if not os.path.exists(CACHE_ROOT):
70-
try:
71-
os.makedirs(CACHE_ROOT)
72-
except:
73-
pass
74-
else:
75-
try:
76-
# Make sure first time always read eeprom data from hardware
77-
if os.path.exists(cache_file):
78-
os.remove(cache_file)
79-
except Exception as e:
80-
logger.log_error('Failed to remove cache file {} - {}'.format(cache_file, repr(e)))
81-
82-
try:
83-
self.set_cache_name(cache_file)
84-
except:
85-
pass
86-
87-
eeprom = self.read_eeprom()
88-
if eeprom is None :
89-
return 0
90-
91-
try:
92-
self.update_cache(eeprom)
93-
except:
94-
pass
95-
96-
self._base_mac = self.mgmtaddrstr(eeprom)
97-
if self._base_mac is None:
98-
self._base_mac = "Undefined."
99-
else:
100-
self._base_mac = self._base_mac.strip('\0')
101-
102-
self._serial_str = self.serial_number_str(eeprom)
103-
if self._serial_str is None:
104-
self._serial_str = "Undefined."
105-
else:
106-
self._serial_str = self._serial_str.strip('\0')
107-
108-
self._product_name = self.modelstr(eeprom)
109-
if self._product_name is None:
110-
self._product_name = "Undefined."
111-
else:
112-
self._product_name = self._product_name.strip('\0')
113-
114-
self._part_number = self.part_number_str(eeprom)
115-
if self._part_number is None:
116-
self._part_number = "Undefined."
117-
else:
118-
self._part_number = self._part_number.strip('\0')
119-
120-
original_stdout = sys.stdout
121-
sys.stdout = StringIO()
122-
self.decode_eeprom(eeprom)
123-
decode_output = sys.stdout.getvalue()
124-
sys.stdout = original_stdout
125-
126-
#parse decode_output into a dictionary
127-
decode_output.replace('\0', '')
128-
lines = decode_output.split('\n')
129-
lines = lines[self.EEPROM_DECODE_HEADLINES:]
130-
self._eeprom_info_dict = dict()
131-
132-
for line in lines:
133-
try:
134-
match = re.search('(0x[0-9a-fA-F]{2})([\s]+[\S]+[\s]+)([\S]+[\s]*[\S]*)', line)
135-
if match is not None:
136-
idx = match.group(1)
137-
value = match.group(3).rstrip('\0')
138-
139-
self._eeprom_info_dict[idx] = value
140-
except:
141-
pass
142-
143-
return 0
44+
self._eeprom_info_dict = None
14445

46+
@default_return(return_value='Undefined.')
14547
def get_base_mac(self):
14648
"""
14749
Retrieves the base MAC address for the chassis
@@ -150,43 +52,39 @@ def get_base_mac(self):
15052
A string containing the MAC address in the format
15153
'XX:XX:XX:XX:XX:XX'
15254
"""
153-
if not self._eeprom_loaded:
154-
self._load_eeprom()
155-
return self._base_mac
156-
55+
return self._get_eeprom_value(self._TLV_CODE_MAC_BASE)
56+
57+
@default_return(return_value='Undefined.')
15758
def get_serial_number(self):
15859
"""
15960
Retrieves the hardware serial number for the chassis
16061
16162
Returns:
16263
A string containing the hardware serial number for this chassis.
16364
"""
164-
if not self._eeprom_loaded:
165-
self._load_eeprom()
166-
return self._serial_str
65+
return self._get_eeprom_value(self._TLV_CODE_SERIAL_NUMBER)
16766

67+
@default_return(return_value='Undefined.')
16868
def get_product_name(self):
16969
"""
17070
Retrieves the hardware product name for the chassis
17171
17272
Returns:
17373
A string containing the hardware product name for this chassis.
17474
"""
175-
if not self._eeprom_loaded:
176-
self._load_eeprom()
177-
return self._product_name
75+
return self._get_eeprom_value(self._TLV_CODE_PRODUCT_NAME)
17876

77+
@default_return(return_value='Undefined.')
17978
def get_part_number(self):
18079
"""
18180
Retrieves the hardware part number for the chassis
18281
18382
Returns:
18483
A string containing the hardware part number for this chassis.
18584
"""
186-
if not self._eeprom_loaded:
187-
self._load_eeprom()
188-
return self._part_number
85+
return self._get_eeprom_value(self._TLV_CODE_PART_NUMBER)
18986

87+
@default_return({})
19088
def get_system_eeprom_info(self):
19189
"""
19290
Retrieves the full content of system EEPROM information for the chassis
@@ -196,6 +94,72 @@ def get_system_eeprom_info(self):
19694
OCP ONIE TlvInfo EEPROM format and values are their corresponding
19795
values.
19896
"""
199-
if not self._eeprom_loaded:
200-
self._load_eeprom()
97+
if self._eeprom_info_dict is None:
98+
self._eeprom_info_dict = {}
99+
100+
# Try get from DB first
101+
db_initialized = self._redis_hget('EEPROM_INFO|State', 'Initialized')
102+
if db_initialized == '1':
103+
code = self._TLV_CODE_PRODUCT_NAME
104+
while code <= self._TLV_CODE_SERVICE_TAG:
105+
value = self._redis_hget('EEPROM_INFO|{}'.format(hex(code)), 'Value')
106+
if value:
107+
self._eeprom_info_dict[hex(code)] = value
108+
code += 1
109+
110+
# Handle vendor extension TLV
111+
vendor_extension_tlv_code = hex(self._TLV_CODE_VENDOR_EXT)
112+
try:
113+
vendor_extension_num = int(self._redis_hget('EEPROM_INFO|{}'.format(vendor_extension_tlv_code), 'Num_vendor_ext'))
114+
except (ValueError, TypeError):
115+
vendor_extension_num = 0
116+
117+
if vendor_extension_num != 0:
118+
for i in range(vendor_extension_num):
119+
value = self._redis_hget('EEPROM_INFO|{}'.format(vendor_extension_tlv_code), 'Value_{}'.format(i))
120+
if value:
121+
if vendor_extension_tlv_code not in self._eeprom_info_dict:
122+
self._eeprom_info_dict[vendor_extension_tlv_code] = [value]
123+
else:
124+
self._eeprom_info_dict[vendor_extension_tlv_code].append(value)
125+
126+
# Get CRC
127+
value = self._redis_hget('EEPROM_INFO|{}'.format(hex(self._TLV_CODE_CRC_32)), 'Value')
128+
if value:
129+
self._eeprom_info_dict[hex(self._TLV_CODE_CRC_32)] = value
130+
else:
131+
eeprom = self.read_eeprom()
132+
visitor = EepromContentVisitor(self._eeprom_info_dict)
133+
self.visit_eeprom(eeprom, visitor)
201134
return self._eeprom_info_dict
135+
136+
def _get_eeprom_value(self, code):
137+
"""Helper function to help get EEPROM data by code
138+
139+
Args:
140+
code (int): EEPROM TLV code
141+
142+
Returns:
143+
str: value of EEPROM TLV
144+
"""
145+
eeprom_info_dict = self.get_system_eeprom_info()
146+
return eeprom_info_dict[hex(code)]
147+
148+
149+
class EepromContentVisitor(eeprom_tlvinfo.EepromDefaultVisitor):
150+
def __init__(self, content):
151+
self.content = content
152+
153+
def visit_tlv(self, name, code, length, value):
154+
if code != Eeprom._TLV_CODE_VENDOR_EXT:
155+
self.content[hex(code)] = value.rstrip('\0')
156+
else:
157+
if value:
158+
value = value.rstrip('\0')
159+
if value:
160+
code = hex(code)
161+
if code not in self.content:
162+
self.content[code] = [value]
163+
else:
164+
self.content[code].append(value)
165+

platform/mellanox/mlnx-platform-api/sonic_platform/utils.py

+47
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import functools
2+
import subprocess
3+
4+
# flags to indicate whether this process is running in docker or host
5+
_is_host = None
6+
7+
18
def read_str_from_file(file_path, default='', raise_exception=False):
29
"""
310
Read string content from file
@@ -55,3 +62,43 @@ def write_file(file_path, content, raise_exception=False):
5562
else:
5663
raise e
5764
return True
65+
66+
67+
def is_host():
68+
"""
69+
Test whether current process is running on the host or an docker
70+
return True for host and False for docker
71+
"""
72+
global _is_host
73+
if _is_host is not None:
74+
return _is_host
75+
76+
_is_host = False
77+
try:
78+
proc = subprocess.Popen("docker --version 2>/dev/null",
79+
stdout=subprocess.PIPE,
80+
shell=True,
81+
stderr=subprocess.STDOUT,
82+
universal_newlines=True)
83+
stdout = proc.communicate()[0]
84+
proc.wait()
85+
result = stdout.rstrip('\n')
86+
if result != '':
87+
_is_host = True
88+
89+
except OSError as e:
90+
pass
91+
92+
return _is_host
93+
94+
95+
def default_return(return_value):
96+
def wrapper(method):
97+
@functools.wraps(method)
98+
def _impl(*args, **kwargs):
99+
try:
100+
return method(*args, **kwargs)
101+
except:
102+
return return_value
103+
return _impl
104+
return wrapper

0 commit comments

Comments
 (0)