Skip to content

Commit

Permalink
Support new API sfp.dump_eeprom
Browse files Browse the repository at this point in the history
  • Loading branch information
Junchao-Mellanox committed Sep 26, 2023
1 parent 086a88c commit ef486c5
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 16 deletions.
12 changes: 12 additions & 0 deletions sonic_platform_base/sfp_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,18 @@ def get_error_description(self):
"""
raise NotImplementedError

def dump_eeprom(self, page=None):
"""
Dump all EEPROM data for this SFP
Args:
page: EEPROM page number, dump all pages if page is None
Returns:
A string contains the hex format EEPROM data
"""
raise NotImplementedError

def refresh_xcvr_api(self):
"""
Updates the XcvrApi associated with this SFP
Expand Down
11 changes: 7 additions & 4 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,10 +763,10 @@ def get_media_lane_count(self, appl=1):
'''
if self.is_flat_memory():
return 0

if (appl <= 0):
return 0

appl_advt = self.get_application_advertisement()
return appl_advt[appl]['media_lane_count'] if len(appl_advt) >= appl else 0

Expand Down Expand Up @@ -795,10 +795,10 @@ def get_media_lane_assignment_option(self, appl=1):
'''
if self.is_flat_memory():
return 'N/A'

if (appl <= 0):
return 0

appl_advt = self.get_application_advertisement()
return appl_advt[appl]['media_lane_assignment_options'] if len(appl_advt) >= appl else 0

Expand Down Expand Up @@ -2438,4 +2438,7 @@ def get_error_description(self):

return None

def _get_valid_eeprom_pages(self):
return (0, 1, 2, 16, 17) if not self.is_flat_memory() else (0,)

# TODO: other XcvrApi methods
3 changes: 3 additions & 0 deletions sonic_platform_base/sonic_xcvr/api/public/sff8436.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,6 @@ def get_lpmode(self):
# Since typically optics come up by default set to high power, in this case,
# power_override not being set, function will return high power mode.
return power_set and power_override

def _get_valid_eeprom_pages(self):
return (0, 1, 2, 3) if not self.is_flat_memory() else (0,)
24 changes: 23 additions & 1 deletion sonic_platform_base/sonic_xcvr/api/public/sff8472.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def get_transceiver_info(self):
if len > 0:
cable_len = len
cable_type = type

xcvr_info = {
"type": serial_id[consts.ID_FIELD],
"type_abbrv_name": serial_id[consts.ID_ABBRV_FIELD],
Expand Down Expand Up @@ -295,3 +295,25 @@ def get_lpmode_support(self):

def get_power_override_support(self):
return False

def is_active_cable(self):
return self.xcvr_eeprom.read(consts.SFP_CABLE_TECH_FIELD) == 'Active Cable'

def _get_valid_eeprom_pages(self):
return (0, 1) if self.is_active_cable() else (0,)

def _dump_eeprom_pages(self, pages):
indent = ' ' * 8
lines = []
for page in pages:
if page == 0:
if self.is_active_cable():
lines.append(f'{indent}A0h dump')
lines.append(self._dump_eeprom(0, 256, 0, indent))
else:
lines.append(f'{indent}A0h dump')
lines.append(self._dump_eeprom(0, 128, 0, indent))
elif page == 1:
lines.append(f'\n{indent}A2h dump (lower 128 bytes)')
lines.append(self._dump_eeprom(256, 128, 0, indent))
return '\n'.join(lines)
3 changes: 3 additions & 0 deletions sonic_platform_base/sonic_xcvr/api/public/sff8636.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,6 @@ def get_lpmode(self):
# Since typically optics come up by default set to high power, in this case,
# power_override not being set, function will return high power mode.
return power_set and power_override

def _get_valid_eeprom_pages(self):
return (0, 1, 2, 3) if not self.is_flat_memory() else (0,)
67 changes: 67 additions & 0 deletions sonic_platform_base/sonic_xcvr/api/xcvr_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,70 @@ def get_error_description(self):
"""
raise NotImplementedError

def convert_byte_to_valid_ascii_char(self, byte):
if byte < 32 or 126 < byte:
return '.'
else:
return chr(byte)

def hexdump(self, indent, data, mem_address):
size = len(data)
offset = 0
lines = []
while size > 0:
offset_str = "{}{:08x}".format(indent, mem_address)
if size >= 16:
first_half = ' '.join("{:02x}".format(x) for x in data[offset:offset + 8])
second_half = ' '.join("{:02x}".format(x) for x in data[offset + 8:offset + 16])
ascii_str = ''.join(self.convert_byte_to_valid_ascii_char(x) for x in data[offset:offset + 16])
lines.append(f'{offset_str} {first_half} {second_half} |{ascii_str}|')
elif size > 8:
first_half = ' '.join("{:02x}".format(x) for x in data[offset:offset + 8])
second_half = ' '.join("{:02x}".format(x) for x in data[offset + 8:offset + size])
padding = ' ' * (16 - size)
ascii_str = ''.join(self.convert_byte_to_valid_ascii_char(x) for x in data[offset:offset + size])
lines.append(f'{offset_str} {first_half} {second_half}{padding} |{ascii_str}|')
break
else:
hex_part = ' '.join("{:02x}".format(x) for x in data[offset:offset + size])
padding = ' ' * (16 - size)
ascii_str = ''.join(self.convert_byte_to_valid_ascii_char(x) for x in data[offset:offset + size])
lines.append(f'{offset_str} {hex_part} {padding} |{ascii_str}|')
break
size -= 16
offset += 16
mem_address += 16
return '\n'.join(lines)

def dump_eeprom(self, page):
pages = self._get_valid_eeprom_pages()
if page is not None:
if page not in pages:
raise ValueError(f"Invalid page {page}")
pages = (page,)
return self._dump_eeprom_pages(pages)

def _dump_eeprom_pages(self, pages):
indent = ' ' * 8
lines = []
for page in pages:
if page == 0:
lines.append(f'{indent}Lower page 0h')
lines.append(self._dump_eeprom(0, 128, 0, indent))
lines.append(f'\n{indent}Upper page 0h')
lines.append(self._dump_eeprom(128, 128, 128, indent))
else:
lines.append(f'\n{indent}Page {page}h')
lines.append(self._dump_eeprom(256 + (page - 1) * 128, 128, 128, indent))
return '\n'.join(lines)

def _get_valid_eeprom_pages(self):
raise NotImplementedError

def _dump_eeprom(self, overall_offset, size, page_offset, indent):
page_dump = self.xcvr_eeprom.read_raw(overall_offset, size, return_raw=True)
if page_dump is None:
return f'{indent}N/A'

output = self.hexdump(indent, page_dump, page_offset)
return output
13 changes: 13 additions & 0 deletions sonic_platform_base/sonic_xcvr/sfp_optoe_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,16 @@ def get_error_description(self):
"""
api = self.get_xcvr_api()
return api.get_error_description() if api is not None else None

def dump_eeprom(self, page=None):
"""
Dump all EEPROM data for this SFP
Args:
page: EEPROM page number, dump all pages if page is None
Returns:
A string contains the hex format EEPROM data
"""
api = self.get_xcvr_api()
return api.dump_eeprom(page) if api is not None else None
51 changes: 51 additions & 0 deletions tests/sonic_xcvr/test_cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2371,3 +2371,54 @@ def test_get_transceiver_info_firmware_versions_negative_tests(self):
self.api.get_module_fw_info.side_effect = {'result': TypeError}
result = self.api.get_transceiver_info_firmware_versions()
assert result == ["N/A", "N/A"]

def test_dump_eeprom(self):
with pytest.raises(ValueError):
self.api.dump_eeprom(-1)

self.api.xcvr_eeprom.read_raw = MagicMock()
self.api.xcvr_eeprom.read_raw.side_effect = [bytearray([x for x in range(128)]), bytearray([x for x in range(128, 256)])]
output = self.api.dump_eeprom(0)
expected_output = """ Lower page 0h
00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
00000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|
00000020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !"#$%&'()*+,-./|
00000030 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|
00000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
00000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\]^_|
00000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|
00000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
Upper page 0h
00000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f |................|
00000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f |................|
000000a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af |................|
000000b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf |................|
000000c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf |................|
000000d0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df |................|
000000e0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef |................|
000000f0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff |................|"""
assert output == expected_output

self.api.xcvr_eeprom.read_raw.reset_mock(return_value=True, side_effect=True)
self.api.xcvr_eeprom.read_raw.return_value = bytearray([x for x in range(128)])
output = self.api.dump_eeprom(1)
expected_output = """
Page 1h
00000080 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
00000090 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|
000000a0 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !"#$%&'()*+,-./|
000000b0 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|
000000c0 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
000000d0 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\]^_|
000000e0 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|
000000f0 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|"""
assert output == expected_output

self.api.xcvr_eeprom.read_raw.return_value = None
output = self.api.dump_eeprom(1)
print(output)
expected_output = """
Page 1h
N/A"""
assert output == expected_output
Loading

0 comments on commit ef486c5

Please sign in to comment.