From cb0fd4280bff26b7a6e8d691a0c555155b02138b Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Fri, 19 Jan 2024 00:31:59 +0800 Subject: [PATCH] [202311] Collect module EEPROM data in dump (#3009) (#3124) * Collect module EEPROM data in dump --- doc/Command-Reference.md | 179 +++++++++++++++++++ scripts/generate_dump | 1 + sfputil/main.py | 361 +++++++++++++++++++++++++++------------ tests/sfputil_test.py | 152 ++++++++++++++++- 4 files changed, 582 insertions(+), 111 deletions(-) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index f9c6233551..30f317be80 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -202,6 +202,10 @@ * [MACsec config command](#macsec-config-command) * [MACsec show command](#macsec-show-command) * [MACsec clear command](#macsec-clear-command) +* [SFP Utilities Commands](#sfp-utilities-commands) + * [SFP Utilities show commands](#sfp-utilities-show-commands) + * [SFP Utilities read command](#sfp-utilities-read-command) + * [SFP Utilities write command](#sfp-utilities-write-command) * [Static DNS Commands](#static-dns-commands) * [Static DNS config command](#static-dns-config-command) * [Static DNS show command](#static-dns-show-command) @@ -12937,6 +12941,181 @@ Clear MACsec counters which is to reset all MACsec counters to ZERO. Go Back To [Beginning of the document](#) or [Beginning of this section](#macsec-commands) +# SFP Utilities Commands + This sub-section explains the list of commands available for SFP utilities feature. + +## SFP Utilities show commands + +- Show SFP EEPROM hex dump + +``` +admin@sonic:~$ sfputil show eeprom-hexdump --help +Usage: sfputil show eeprom-hexdump [OPTIONS] + Display EEPROM hexdump of SFP transceiver(s) +Options: + -p, --port Display SFP EEPROM hexdump for port + -n, --page Display SFP EEEPROM hexdump for + + --help Show this message and exit. +``` + +``` +admin@sonic:~$ sfputil show eeprom-hexdump --port Ethernet0 --page 0 +EEPROM hexdump for port Ethernet0 page 0h + Lower page 0h + 00000000 18 30 80 03 00 00 00 00 00 00 00 00 00 00 00 00 |.0..............| + 00000010 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 |................| + 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000050 00 00 00 00 00 03 1d 01 88 01 1c 01 44 11 1b 01 |............D...| + 00000060 22 55 1a 01 44 11 18 01 11 ff 17 01 44 11 16 01 |"U..D.......D...| + 00000070 11 ff 01 01 11 ff 00 00 00 00 00 00 00 00 00 00 |................| + + Upper page 0h + 00000080 18 4d 65 6c 6c 61 6e 6f 78 20 20 20 20 20 20 20 |.Mellanox | + 00000090 20 00 02 c9 4d 43 50 31 36 36 30 2d 57 30 30 41 | ...MCP1660-W00A| + 000000a0 45 33 30 20 41 32 4d 54 32 30 31 39 56 53 30 34 |E30 A2MT2019VS04| + 000000b0 37 39 35 20 20 20 32 30 30 35 30 37 20 20 00 00 |795 200507 ..| + 000000c0 00 00 00 00 00 00 00 00 00 01 05 23 04 05 07 15 |...........#....| + 000000d0 00 00 00 02 0a 00 00 00 00 00 00 00 00 00 77 00 |..............w.| + 000000e0 33 30 33 33 30 4b 34 33 34 31 30 44 00 00 00 00 |30330K43410D....| + 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + +admin@sonic:~$ sfputil show eeprom-hexdump --port Ethernet0 --page 1 +EEPROM hexdump for port Ethernet0 page 1h + Lower page 0h + 00000000 11 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 01 08 00 |................| + 00000070 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + + Upper page 1h + 00000080 11 00 23 88 00 00 04 00 00 00 00 08 ff 00 00 00 |..#.............| + 00000090 00 00 01 a0 4d 65 6c 6c 61 6e 6f 78 20 20 20 20 |....Mellanox | + 000000a0 20 20 20 20 00 00 02 c9 4d 43 50 31 36 35 30 2d | ....MCP1650-| + 000000b0 56 30 30 31 45 33 30 20 41 32 02 03 05 07 46 c5 |V001E30 A2....F.| + 000000c0 40 00 00 00 4d 54 32 30 31 30 56 53 30 38 33 32 |@...MT2010VS0832| + 000000d0 39 20 20 20 32 30 30 33 30 32 20 20 00 00 6a 84 |9 200302 ..j.| + 000000e0 31 39 32 32 39 33 31 43 41 31 43 54 00 1e 00 00 |1922931CA1CT....| + 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 |.............0..| + +admin@sonic:~$ sfputil show eeprom-hexdump +EEPROM hexdump for port Ethernet0 + Lower page 0h + 00000000 11 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 01 08 00 |................| + 00000070 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + + Upper page 0h + 00000080 11 00 23 88 00 00 04 00 00 00 00 08 ff 00 00 00 |..#.............| + 00000090 00 00 01 a0 4d 65 6c 6c 61 6e 6f 78 20 20 20 20 |....Mellanox | + 000000a0 20 20 20 20 00 00 02 c9 4d 43 50 31 36 35 30 2d | ....MCP1650-| + 000000b0 56 30 30 31 45 33 30 20 41 32 02 03 05 07 46 c5 |V001E30 A2....F.| + 000000c0 40 00 00 00 4d 54 32 30 31 30 56 53 30 38 33 32 |@...MT2010VS0832| + 000000d0 39 20 20 20 32 30 30 33 30 32 20 20 00 00 6a 84 |9 200302 ..j.| + 000000e0 31 39 32 32 39 33 31 43 41 31 43 54 00 1e 00 00 |1922931CA1CT....| + 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 |.............0..| + +EEPROM hexdump for port Ethernet8 + Lower page 0h + 00000000 18 30 80 03 00 00 00 00 00 00 00 00 00 00 00 00 |.0..............| + 00000010 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 |................| + 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000050 00 00 00 00 00 03 1d 01 88 01 1c 01 44 11 1b 01 |............D...| + 00000060 22 55 1a 01 44 11 18 01 11 ff 17 01 44 11 16 01 |"U..D.......D...| + 00000070 11 ff 01 01 11 ff 00 00 00 00 00 00 00 00 00 00 |................| + + Upper page 0h + 00000080 18 4d 65 6c 6c 61 6e 6f 78 20 20 20 20 20 20 20 |.Mellanox | + 00000090 20 00 02 c9 4d 43 50 31 36 36 30 2d 57 30 30 41 | ...MCP1660-W00A| + 000000a0 45 33 30 20 41 32 4d 54 32 30 31 39 56 53 30 34 |E30 A2MT2019VS04| + 000000b0 37 39 35 20 20 20 32 30 30 35 30 37 20 20 00 00 |795 200507 ..| + 000000c0 00 00 00 00 00 00 00 00 00 01 05 23 04 05 07 15 |...........#....| + 000000d0 00 00 00 02 0a 00 00 00 00 00 00 00 00 00 77 00 |..............w.| + 000000e0 33 30 33 33 30 4b 34 33 34 31 30 44 00 00 00 00 |30330K43410D....| + 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +``` + +# SFP Utilities read command + +- Read SFP EEPROM data + +``` +admin@sonic:~$ sfputil read-eeprom --help +Usage: sfputil read-eeprom [OPTIONS] + + Read SFP EEPROM data + +Options: + -p, --port Logical port name [required] + -n, --page EEPROM page number [required] + -o, --offset EEPROM offset within the page [required] + -s, --size Size of byte to be read [required] + --no-format Display non formatted data + --wire-addr TEXT Wire address of sff8472 + --help Show this message and exit. +``` + +``` +admin@sonic:~$ sfputil read-eeprom -p Ethernet0 -n 0 -o 100 -s 2 + 00000064 4a 44 |..| + +admin@sonic:~$ sfputil read-eeprom --port Ethernet0 --page 0 --offset 0 --size 32 + 00000000 11 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + +admin@sonic:~$ sfputil read-eeprom --port Ethernet0 --page 0 --offset 100 --size 2 --no-format +4a44 +``` + +# SFP Utilities write command + +- Write SFP EEPROM data + +``` +admin@sonic:~$ sfputil write-eeprom --help +Usage: sfputil write-eeprom [OPTIONS] + + Write SFP EEPROM data + +Options: + -p, --port Logical port name [required] + -n, --page EEPROM page number [required] + -o, --offset EEPROM offset within the page [required] + -d, --data Hex string EEPROM data [required] + --wire-addr TEXT Wire address of sff8472 + --verify Verify the data by reading back + --help Show this message and exit. +``` + +- Write success +``` +admin@sonic:~$ sfputil write-eeprom -p Ethernet0 -n 0 -o 100 -d 4a44 + +admin@sonic:~$ sfputil write-eeprom --port Etherent0 --page 0 --offset 100 --data 0000 --verify + +``` + +- Write fail +``` +admin@sonic:~$ sfputil write-eeprom -p Etherent0 -n 0 -o 100 -d 4a44 --verify +Error: Write data failed! Write: 4a44, read: 0000. +``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#sfp-utilities-commands) + # Static DNS Commands This sub-section explains the list of the configuration options available for static DNS feature. diff --git a/scripts/generate_dump b/scripts/generate_dump index 5fa0f605d3..6b46333da3 100755 --- a/scripts/generate_dump +++ b/scripts/generate_dump @@ -1853,6 +1853,7 @@ main() { save_cmd "show interface transceiver presence" "interface.xcvrs.presence" & save_cmd "show interface transceiver eeprom --dom" "interface.xcvrs.eeprom" & save_cmd "show ip interface -d all" "ip.interface" & + save_cmd "sfputil show eeprom-hexdump" "interface.xcvrs.eeprom.raw" & wait save_cmd "lldpctl" "lldpctl" & diff --git a/sfputil/main.py b/sfputil/main.py index e324963e82..013c8d7c6f 100644 --- a/sfputil/main.py +++ b/sfputil/main.py @@ -5,6 +5,7 @@ # Command-line utility for interacting with SFP transceivers within SONiC # +import copy import os import sys import natsort @@ -39,6 +40,7 @@ ERROR_PORT_CONFIG_LOAD = 4 ERROR_NOT_IMPLEMENTED = 5 ERROR_INVALID_PORT = 6 +ERROR_INVALID_PAGE = 7 SMBUS_BLOCK_WRITE_SIZE = 32 # Default host password as per CMIS spec: # http://www.qsfp-dd.com/wp-content/uploads/2021/05/CMIS5p0.pdf @@ -50,6 +52,19 @@ PAGE_OFFSET = 128 SFF8472_A0_SIZE = 256 +MAX_EEPROM_PAGE = 255 +MAX_EEPROM_OFFSET = 255 +MIN_OFFSET_FOR_NON_PAGE0 = 128 +MAX_OFFSET_FOR_A0H_UPPER_PAGE = 255 +MAX_OFFSET_FOR_A0H_LOWER_PAGE = 127 +MAX_OFFSET_FOR_A2H = 255 +PAGE_SIZE_FOR_A0H = 256 +SFF8636_MODULE_PAGES = [0, 1, 2, 3] +SFF8472_MODULE_PAGES = [0, 1, 2] +CMIS_MODULE_PAGES = [0, 1, 2, 16, 17] +CMIS_COHERENT_MODULE_PAGES = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a, 0x3b] + +EEPROM_DUMP_INDENT = ' ' * 8 # TODO: We should share these maps and the formatting functions between sfputil and sfpshow QSFP_DD_DATA_MAP = { @@ -678,120 +693,241 @@ def eeprom(port, dump_dom, namespace): click.echo(output) + # 'eeprom-hexdump' subcommand @show.command() -@click.option('-p', '--port', metavar='', required=True, help="Display SFP EEPROM hexdump for port ") -@click.option('-n', '--page', metavar='', help="Display SFP EEEPROM hexdump for ") +@click.option('-p', '--port', metavar='', help="Display SFP EEPROM hexdump for port ") +@click.option('-n', '--page', metavar='', type=click.IntRange(0, MAX_EEPROM_PAGE), help="Display SFP EEEPROM hexdump for ") def eeprom_hexdump(port, page): - """Display EEPROM hexdump of SFP transceiver(s) for a given port name and page number""" - output = "" + """Display EEPROM hexdump of SFP transceiver(s)""" + if port: + if page is None: + page = 0 + return_code, output = eeprom_hexdump_single_port(port, page) + click.echo(output) + sys.exit(return_code) + else: + logical_port_list = natsorted(platform_sfputil.logical) + lines = [] + for logical_port_name in logical_port_list: + return_code, output = eeprom_hexdump_single_port(logical_port_name, page) + if return_code != 0: + lines.append(f'EEPROM hexdump for port {logical_port_name}') + lines.append(f'{EEPROM_DUMP_INDENT}{output}\n') + continue + lines.append(output) + click.echo('\n'.join(lines)) - if platform_sfputil.is_logical_port(port) == 0: - click.echo("Error: invalid port {}".format(port)) - print_all_valid_port_values() - sys.exit(ERROR_INVALID_PORT) - if page is None: - page = '0' +def eeprom_hexdump_single_port(logical_port_name, page): + """ + Dump EEPROM for a single logical port in hex format. + Args: + logical_port_name: logical port name + page: page to be dumped - logical_port_name = port - physical_port = logical_port_to_physical_port_index(logical_port_name) + Returns: + tuple(0, dump string) if success else tuple(error_code, error_message) + """ + if platform_sfputil.is_logical_port(logical_port_name) == 0: + print_all_valid_port_values() + return ERROR_INVALID_PORT, f'Error: invalid port {logical_port_name}' if is_port_type_rj45(logical_port_name): - click.echo("{}: SFP EEPROM Hexdump is not applicable for RJ45 port".format(port)) - sys.exit(ERROR_INVALID_PORT) + return ERROR_INVALID_PORT, f'{logical_port_name}: SFP EEPROM Hexdump is not applicable for RJ45 port' + physical_port = logical_port_to_physical_port_index(logical_port_name) try: - presence = platform_chassis.get_sfp(physical_port).get_presence() + sfp = platform_chassis.get_sfp(physical_port) + presence = sfp.get_presence() except NotImplementedError: - click.echo("Sfp.get_presence() is currently not implemented for this platform") - sys.exit(ERROR_NOT_IMPLEMENTED) + return ERROR_NOT_IMPLEMENTED, 'Sfp.get_presence() is currently not implemented for this platform' if not presence: - click.echo("SFP EEPROM not detected") - sys.exit(ERROR_NOT_IMPLEMENTED) - else: - try: - id = platform_chassis.get_sfp(physical_port).read_eeprom(0, 1) - if id is None: - click.echo("Error: Failed to read EEPROM for offset 0!") - sys.exit(ERROR_NOT_IMPLEMENTED) - except NotImplementedError: - click.echo("Sfp.read_eeprom() is currently not implemented for this platform") - sys.exit(ERROR_NOT_IMPLEMENTED) + return ERROR_NOT_IMPLEMENTED, 'SFP EEPROM not detected' - if id[0] == 0x3: - output = eeprom_hexdump_sff8472(port, physical_port, page) + try: + api = sfp.get_xcvr_api() + if not api: + return ERROR_NOT_IMPLEMENTED, 'Error: Failed to read EEPROM for offset 0!' + + from sonic_platform_base.sonic_xcvr.api.public import sff8636, sff8436, cmis, sff8472 + from sonic_platform_base.sonic_xcvr.fields import consts + if isinstance(api, cmis.CmisApi): + if page is None: # print all possible pages + if api.is_flat_memory(): + pages = [0] + else: + pages = copy.deepcopy(CMIS_MODULE_PAGES) + if api.is_coherent_module(): + pages.extend(CMIS_COHERENT_MODULE_PAGES) + cdb_support = api.xcvr_eeprom.read(consts.CDB_SUPPORT) + if cdb_support != 0: + pages.append(0x9f) + else: + pages = [0] + if page not in pages: + pages.append(page) + return eeprom_hexdump_pages_general(logical_port_name, pages, page) + elif isinstance(api, sff8636.Sff8636Api) or isinstance(api, sff8436.Sff8436Api): + if page is None: + if api.is_flat_memory(): + pages = [0] + else: + pages = copy.deepcopy(SFF8636_MODULE_PAGES) + else: + pages = [0] + if page not in pages: + pages.append(page) + return eeprom_hexdump_pages_general(logical_port_name, pages, page) + elif isinstance(api, sff8472.Sff8472Api): + if page is None: + if not api.is_copper(): + pages = copy.deepcopy(SFF8472_MODULE_PAGES) + else: + pages = [0] + else: + pages = copy.deepcopy(SFF8472_MODULE_PAGES) if not api.is_copper() else [0] + if page not in pages: + pages.append(page) + return eeprom_hexdump_pages_sff8472(logical_port_name, pages, page) else: - output = eeprom_hexdump_sff8636(port, physical_port, page) - - output += '\n' + return ERROR_NOT_IMPLEMENTED, 'Cable type is not supported' + except NotImplementedError: + return ERROR_NOT_IMPLEMENTED, 'Sfp.read_eeprom() is currently not implemented for this platform' - click.echo(output) -def eeprom_hexdump_sff8472(port, physical_port, page): - try: - output = "" - indent = ' ' * 8 - output += 'EEPROM hexdump for port {} page {}h'.format(port, page) - output += '\n{}A0h dump'.format(indent) - page_dump = platform_chassis.get_sfp(physical_port).read_eeprom(0, SFF8472_A0_SIZE) - if page_dump is None: - click.echo("Error: Failed to read EEPROM for A0h!") - sys.exit(ERROR_NOT_IMPLEMENTED) +def eeprom_hexdump_pages_general(logical_port_name, pages, target_page): + """ + Dump module EEPROM for given pages in hex format. This function is designed for cable type other than SFF8472. + Args: + logical_port_name: logical port name + pages: a list of pages to be dumped. The list always include a default page list and the target_page input by + user + target_page: user input page number, optional. target_page is only for display purpose - output += hexdump(indent, page_dump, 0) - page_dump = platform_chassis.get_sfp(physical_port).read_eeprom(SFF8472_A0_SIZE, PAGE_SIZE) - if page_dump is None: - click.echo("Error: Failed to read EEPROM for A2h!") - sys.exit(ERROR_NOT_IMPLEMENTED) + Returns: + tuple(0, dump string) if success else tuple(error_code, error_message) + """ + if target_page is not None: + lines = [f'EEPROM hexdump for port {logical_port_name} page {target_page}h'] + else: + lines = [f'EEPROM hexdump for port {logical_port_name}'] + physical_port = logical_port_to_physical_port_index(logical_port_name) + for page in pages: + if page == 0: + lines.append(f'{EEPROM_DUMP_INDENT}Lower page 0h') + return_code, output = eeprom_dump_general(physical_port, page, 0, PAGE_SIZE, 0) + if return_code != 0: + return return_code, output + lines.append(output) + + lines.append(f'\n{EEPROM_DUMP_INDENT}Upper page 0h') + return_code, output = eeprom_dump_general(physical_port, page, PAGE_OFFSET, PAGE_SIZE, PAGE_OFFSET) + if return_code != 0: + return return_code, output + lines.append(output) else: - output += '\n\n{}A2h dump (lower 128 bytes)'.format(indent) - output += hexdump(indent, page_dump, 0) + lines.append(f'\n{EEPROM_DUMP_INDENT}Upper page {page:x}h') + return_code, output = eeprom_dump_general(physical_port, page, page * PAGE_SIZE + PAGE_OFFSET, PAGE_SIZE, PAGE_OFFSET) + if return_code != 0: + return return_code, output + lines.append(output) - page_dump = platform_chassis.get_sfp(physical_port).read_eeprom(SFF8472_A0_SIZE + PAGE_OFFSET + (int(page, base=16) * PAGE_SIZE), PAGE_SIZE) - if page_dump is None: - click.echo("Error: Failed to read EEPROM for A2h upper page!") - sys.exit(ERROR_NOT_IMPLEMENTED) - else: - output += '\n\n{}A2h dump (upper 128 bytes) page {}h'.format(indent, page) - output += hexdump(indent, page_dump, PAGE_OFFSET) - except NotImplementedError: - click.echo("Sfp.read_eeprom() is currently not implemented for this platform") - sys.exit(ERROR_NOT_IMPLEMENTED) - except ValueError: - click.echo("Please enter a numeric page number") - sys.exit(ERROR_NOT_IMPLEMENTED) + lines.append('') # add a new line + return 0, '\n'.join(lines) - return output -def eeprom_hexdump_sff8636(port, physical_port, page): - try: - output = "" - indent = ' ' * 8 - output += 'EEPROM hexdump for port {} page {}h'.format(port, page) - output += '\n{}Lower page 0h'.format(indent) - page_dump = platform_chassis.get_sfp(physical_port).read_eeprom(0, PAGE_SIZE) - if page_dump is None: - click.echo("Error: Failed to read EEPROM for page 0!") - sys.exit(ERROR_NOT_IMPLEMENTED) +def eeprom_hexdump_pages_sff8472(logical_port_name, pages, target_page): + """ + Dump module EEPROM for given pages in hex format. This function is designed for SFF8472 only. + Args: + logical_port_name: logical port name + pages: a list of pages to be dumped. The list always include a default page list and the target_page input by + user + target_page: user input page number, optional. target_page is only for display purpose - output += hexdump(indent, page_dump, 0) - page_dump = platform_chassis.get_sfp(physical_port).read_eeprom(int(page, base=16) * PAGE_SIZE + PAGE_OFFSET, PAGE_SIZE) - if page_dump is None: - click.echo("Error: Failed to read EEPROM!") - sys.exit(ERROR_NOT_IMPLEMENTED) + Returns: + tuple(0, dump string) if success else tuple(error_code, error_message) + """ + if target_page is not None: + lines = [f'EEPROM hexdump for port {logical_port_name} page {target_page}h'] + else: + lines = [f'EEPROM hexdump for port {logical_port_name}'] + physical_port = logical_port_to_physical_port_index(logical_port_name) + api = platform_chassis.get_sfp(physical_port).get_xcvr_api() + is_flat_memory = api.is_flat_memory() + for page in pages: + if page == 0: + lines.append(f'{EEPROM_DUMP_INDENT}A0h dump') + if not is_flat_memory: + return_code, output = eeprom_dump_general(physical_port, page, 0, SFF8472_A0_SIZE, 0) + else: + return_code, output = eeprom_dump_general(physical_port, page, 0, PAGE_SIZE, 0) + if return_code != 0: + return return_code, 'Error: Failed to read EEPROM for A0h!' + lines.append(output) + elif page == 1: + lines.append(f'\n{EEPROM_DUMP_INDENT}A2h dump (lower 128 bytes)') + return_code, output = eeprom_dump_general(physical_port, page, SFF8472_A0_SIZE, PAGE_SIZE, 0) + if return_code != 0: + return ERROR_NOT_IMPLEMENTED, 'Error: Failed to read EEPROM for A2h!' + lines.append(output) else: - output += '\n\n{}Upper page {}h'.format(indent, page) - output += hexdump(indent, page_dump, PAGE_OFFSET) - except NotImplementedError: - click.echo("Sfp.read_eeprom() is currently not implemented for this platform") - sys.exit(ERROR_NOT_IMPLEMENTED) - except ValueError: - click.echo("Please enter a numeric page number") - sys.exit(ERROR_NOT_IMPLEMENTED) + lines.append(f'\n{EEPROM_DUMP_INDENT}A2h dump (upper 128 bytes) page {page - 2:x}h') + return_code, output = eeprom_dump_general(physical_port, page, SFF8472_A0_SIZE + PAGE_OFFSET + page * PAGE_SIZE, PAGE_SIZE, PAGE_SIZE) + if return_code != 0: + return ERROR_NOT_IMPLEMENTED, 'Error: Failed to read EEPROM for A2h upper page!' + lines.append(output) + + lines.append('') # add a new line + return 0, '\n'.join(lines) + + +def eeprom_dump_general(physical_port, page, flat_offset, size, page_offset, no_format=False): + """ + Dump module EEPROM. + Args: + physical_port: physical port index + page: module EEPROM page number + flat_offset: overall offset in flat memory + size: size of bytes to be dumped + page_offset: offset within a page, only for print purpose + no_format: False if dump with hex format else dump with flat hex string. Default False. + + Returns: + tuple(0, dump string) if success else tuple(error_code, error_message) + """ + sfp = platform_chassis.get_sfp(physical_port) + page_dump = sfp.read_eeprom(flat_offset, size) + if page_dump is None: + return ERROR_NOT_IMPLEMENTED, f'Error: Failed to read EEPROM for page {page:x}h, flat_offset {flat_offset}, page_offset {page_offset}, size {size}!' + if not no_format: + return 0, hexdump(EEPROM_DUMP_INDENT, page_dump, page_offset, start_newline=False) + else: + return 0, ''.join('{:02x}'.format(x) for x in page_dump) + + +def eeprom_dump_general(physical_port, page, flat_offset, size, page_offset, no_format=False): + """ + Dump module EEPROM for given pages in hex format. + Args: + logical_port_name: logical port name + pages: a list of pages to be dumped. The list always include a default page list and the target_page input by + user + target_page: user input page number, optional. target_page is only for display purpose + Returns: + tuple(0, dump string) if success else tuple(error_code, error_message) + """ + sfp = platform_chassis.get_sfp(physical_port) + page_dump = sfp.read_eeprom(flat_offset, size) + if page_dump is None: + return ERROR_NOT_IMPLEMENTED, f'Error: Failed to read EEPROM for page {page:x}h, flat_offset {flat_offset}, page_offset {page_offset}, size {size}!' + if not no_format: + return 0, hexdump(EEPROM_DUMP_INDENT, page_dump, page_offset, start_newline=False) + else: + return 0, ''.join('{:02x}'.format(x) for x in page_dump) - return output def convert_byte_to_valid_ascii_char(byte): if byte < 32 or 126 < byte: @@ -799,27 +935,36 @@ def convert_byte_to_valid_ascii_char(byte): else: return chr(byte) -def hexdump(indent, data, mem_address): - ascii_string = '' - result = '' - for byte in data: - ascii_string = ascii_string + convert_byte_to_valid_ascii_char(byte) - byte_string = "{:02x}".format(byte) - if mem_address % 16 == 0: - mem_address_string = "{:08x}".format(mem_address) - result += '\n{}{} '.format(indent, mem_address_string) - result += '{} '.format(byte_string) - elif mem_address % 16 == 15: - result += '{} '.format(byte_string) - result += '|{}|'.format(ascii_string) - ascii_string = "" - elif mem_address % 16 == 8: - result += ' {} '.format(byte_string) + +def hexdump(indent, data, mem_address, start_newline=True): + size = len(data) + offset = 0 + lines = [''] if start_newline else [] + 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(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(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: - result += '{} '.format(byte_string) - mem_address += 1 + hex_part = ' '.join("{:02x}".format(x) for x in data[offset:offset + size]) + padding = ' ' * (16 - size) + ascii_str = ''.join(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) - return result # 'presence' subcommand @show.command() diff --git a/tests/sfputil_test.py b/tests/sfputil_test.py index 0e1f8b9969..2b74423ad9 100644 --- a/tests/sfputil_test.py +++ b/tests/sfputil_test.py @@ -594,14 +594,14 @@ def test_show_eeprom_RJ45(self, mock_chassis): def test_show_eeprom_hexdump_invalid_port(self, mock_chassis): runner = CliRunner() result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ["-p", "Ethernet"]) - assert result.exit_code == ERROR_INVALID_PORT + assert result.exit_code != 0 @patch('sfputil.main.platform_chassis') @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1))) def test_show_eeprom_hexdump_invalid_page(self, mock_chassis): runner = CliRunner() result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ["-p", "Ethernet1", "-n", "INVALID"]) - assert result.exit_code == ERROR_NOT_IMPLEMENTED + assert result.exit_code != 0 @patch('sfputil.main.platform_chassis') @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) @@ -650,7 +650,7 @@ def test_show_eeprom_hexdump_read_eeprom_failure(self, mock_chassis): mock_sfp = MagicMock() mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) mock_sfp.get_presence.return_value = True - mock_sfp.read_eeprom = MagicMock(return_value=None) + mock_sfp.get_xcvr_api = MagicMock(return_value=None) runner = CliRunner() result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ["-p", "Ethernet16"]) assert result.exit_code == ERROR_NOT_IMPLEMENTED @@ -661,6 +661,7 @@ def test_show_eeprom_hexdump_read_eeprom_failure(self, mock_chassis): @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1))) @patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=False)) + @patch('sfputil.main.isinstance', MagicMock(return_value=True)) def test_show_eeprom_hexdump_read_eeprom_not_implemented(self, mock_chassis): mock_sfp = MagicMock() mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) @@ -676,6 +677,7 @@ def test_show_eeprom_hexdump_read_eeprom_not_implemented(self, mock_chassis): @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1))) @patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=False)) + @patch('sfputil.main.isinstance', MagicMock(return_value=True)) def test_show_eeprom_hexdump_sff8636_page(self, mock_chassis): lower_page_bytearray = bytearray([13, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) upper_page0_bytearray = bytearray([13, 0, 35, 8, 0, 0, 0, 65, 128, 128, 245, 0, 0, 0, 0, 0, 0, 0, 1, 160, 77, 111, 108, 101, 120, 32, 73, 110, 99, 46, 32, 32, 32, 32, 32, 32, 7, 0, 9, 58, 49, 49, 49, 48, 52, 48, 49, 48, 53, 52, 32, 32, 32, 32, 32, 32, 32, 32, 3, 4, 0, 0, 70, 196, 0, 0, 0, 0, 54, 49, 49, 48, 51, 48, 57, 50, 57, 32, 32, 32, 32, 32, 32, 32, 49, 54, 48, 52, 49, 57, 32, 32, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) @@ -719,6 +721,7 @@ def side_effect(offset, num_bytes): @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1))) @patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=False)) + @patch('sfputil.main.isinstance', MagicMock(side_effect=[False, False, False, True])) def test_show_eeprom_hexdump_sff8472_page(self, mock_chassis): a0h_bytearray = bytearray([3, 4, 7, 16, 0, 0, 0, 0, 0, 0, 0, 6, 103, 0, 0, 0, 8, 3, 0, 30, 70, 73, 78, 73, 83, 65, 82, 32, 67, 79, 82, 80, 46, 32, 32, 32, 0, 0, 144, 101, 70, 84, 76, 88, 56, 53, 55, 49, 68, 51, 66, 67, 76, 32, 32, 32, 65, 32, 32, 32, 3, 82, 0, 72, 0, 26, 0, 0, 65, 85, 74, 48, 82, 67, 74, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 53, 49, 48, 50, 57, 32, 32, 104, 240, 3, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) a2h_lower_bytearray = bytearray([78, 0, 243, 0, 73, 0, 248, 0, 144, 136, 113, 72, 140, 160, 117, 48, 25, 200, 7, 208, 24, 156, 9, 196, 39, 16, 9, 208, 31, 7, 12, 90, 39, 16, 0, 100, 31, 7, 0, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 128, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 27, 20, 2, 129, 177, 13, 90, 23, 165, 21, 135, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 1]) @@ -775,11 +778,154 @@ def side_effect(offset, num_bytes): mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) mock_sfp.get_presence.return_value = True mock_sfp.read_eeprom = MagicMock(side_effect=side_effect) + mock_api = MagicMock() + mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api) + mock_api.is_copper = MagicMock(return_value=False) runner = CliRunner() result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ["-p", "Ethernet256", "-n", "0"]) assert result.exit_code == 0 assert result.output == expected_output + @patch('sfputil.main.platform_chassis') + @patch('sfputil.main.platform_sfputil') + @patch('sfputil.main.isinstance', MagicMock(side_effect=[True, False, False, False, True])) + def test_eeprom_hexdump_all_falure(self, mock_sfputil, mock_chassis): + mock_sfputil.logical = ['Ethernet4', 'Ethernet0'] + mock_sfp = MagicMock() + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + mock_sfp.get_presence.return_value = True + mock_sfp.read_eeprom = MagicMock(return_value=None) + + runner = CliRunner() + result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump']) + assert result.exit_code == 0 + expected_output = """EEPROM hexdump for port Ethernet0 + Error: Failed to read EEPROM for page 0h, flat_offset 0, page_offset 0, size 128! + +EEPROM hexdump for port Ethernet4 + Error: Failed to read EEPROM for A0h! + +""" + assert result.output == expected_output + + @patch('sfputil.main.platform_chassis') + @patch('sfputil.main.platform_sfputil') + @patch('sfputil.main.isinstance', MagicMock(side_effect=[True, False, False, False, True])) + def test_eeprom_hexdump_all(self, mock_sfputil, mock_chassis): + mock_sfputil.logical = ['Ethernet4', 'Ethernet0'] + mock_sfp = MagicMock() + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + mock_sfp.get_presence.return_value = True + mock_sfp.read_eeprom = MagicMock(return_value=None) + + def mock_read_eeprom(offset, num_bytes): + return bytearray([x for x in range(num_bytes)]) + + mock_sfp.read_eeprom.side_effect = mock_read_eeprom + + runner = CliRunner() + result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump']) + assert result.exit_code == 0 + expected_output = """EEPROM hexdump for port Ethernet0 + 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 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{|}~.| + +EEPROM hexdump for port Ethernet4 + A0h dump + 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{|}~.| + +""" + assert expected_output == result.output + + def test_test_eeprom_hexdump_all_invalid_page(self): + runner = CliRunner() + result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ['--page', '-1']) + assert result.exit_code != 0 + + result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ['--page', '256']) + assert result.exit_code != 0 + + result = runner.invoke(sfputil.cli.commands['show'].commands['eeprom-hexdump'], ['--page', 'invalid_number']) + assert result.exit_code != 0 + + @patch('sfputil.main.platform_chassis') + @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) + @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1))) + @patch('sfputil.main.eeprom_hexdump_pages_general') + @patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=False)) + @patch('sfputil.main.isinstance') + def test_eeprom_hexdump_single_port(self, mock_isinstance, mock_dump, mock_chassis): + mock_isinstance.return_value = True + mock_sfp = MagicMock() + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + mock_sfp.get_presence.return_value = True + mock_api = MagicMock() + mock_sfp.get_xcvr_api.return_value = mock_api + sfputil.eeprom_hexdump_single_port('Ethernet0', 0) + mock_dump.assert_called_with('Ethernet0', [0], 0) + + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', 1) + mock_dump.assert_called_with('Ethernet0', [0, 1], 1) + + mock_api.is_flat_memory.return_value = False + mock_api.is_coherent_module.return_value = False + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', None) + mock_dump.assert_called_with('Ethernet0', [0, 1, 2, 16, 17, 159], None) + + mock_api.is_coherent_module.return_value = True + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', None) + mock_dump.assert_called_with('Ethernet0', [0, 1, 2, 16, 17, 48, 49, 50, 51, 52, 53, 56, 57, 58, 59, 159], None) + + mock_api.is_flat_memory.return_value = True + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', None) + mock_dump.assert_called_with('Ethernet0', [0], None) + + mock_isinstance.side_effect = [False, True] + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', None) + mock_dump.assert_called_with('Ethernet0', [0], None) + + mock_api.is_flat_memory.return_value = False + mock_isinstance.side_effect = [False, True] + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', None) + mock_dump.assert_called_with('Ethernet0', [0, 1, 2, 3], None) + + mock_isinstance.side_effect = [False, True] + mock_dump.reset_mock() + sfputil.eeprom_hexdump_single_port('Ethernet0', 3) + mock_dump.assert_called_with('Ethernet0', [0, 3], 3) + + + @patch('sfputil.main.logical_port_name_to_physical_port_list', MagicMock(return_value=1)) @patch('sfputil.main.is_port_type_rj45', MagicMock(return_value=True)) @patch('sfputil.main.platform_sfputil', MagicMock(is_logical_port=MagicMock(return_value=1)))