diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 9d91cf5e1c..aaf6ca7535 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -560,7 +560,7 @@ This command displays the current date and time configured on the system ``` **config clock date** - + This command will set the date-time of the systetm, given strings with date-time format - Usage: @@ -578,7 +578,7 @@ This command will set the date-time of the systetm, given strings with date-time ``` **config clock timezone** - + This command will set the timezone of the systetm, given a string of a valid timezone. - Usage: @@ -588,13 +588,13 @@ This command will set the timezone of the systetm, given a string of a valid tim - Parameters: - _timezone_: valid timezone to be configured - - + + - Example: ``` admin@sonic:~$ config clock timezone Africa/Accra - + **show clock timezones** This command Will display list of all valid timezones to be configured. @@ -2571,7 +2571,7 @@ Once enabled, BGP will not advertise routes which aren't yet offloaded. admin@sonic:~$ sudo config suppress-fib-pending enabled ``` ``` - admin@sonic:~$ sudo config suppress-fib-pending disabled + admin@sonic:~$ sudo config suppress-fib-pending disabled ``` Go Back To [Beginning of the document](#) or [Beginning of this section](#bgp) @@ -2851,7 +2851,7 @@ This command is used for downloading firmware tp upgrade the transciever module. ``` **sfputil firmware run** -This command is used to start and run a downloaded image. This command transfers control from the currently running firmware to a new firmware. +This command is used to start and run a downloaded image. This command transfers control from the currently running firmware to a new firmware. - Usage: ``` @@ -2876,7 +2876,7 @@ This command is used to start and run a downloaded image. This command transfers **sfputil firmware commit** -This command to commit the running image so that the module will boot from it on future boots. +This command to commit the running image so that the module will boot from it on future boots. - Usage: ``` @@ -4597,7 +4597,7 @@ This command is to display the FEC status of the selected interfaces. If **inter show interfaces fec status [] ``` -- Example: +- Example: ``` admin@sonic:~$ show interfaces fec status Interface FEC Oper FEC Admin @@ -6849,7 +6849,7 @@ in order to detemine whether the health of the cable is Ok the following are checked - the vendor name is correct able to be read - the FW is correctly loaded for SerDes by reading the appropriate register val -- the Counters for UART are displaying healthy status +- the Counters for UART are displaying healthy status i.e Error Counters , retry Counters for UART or internal xfer protocols are below a threshold @@ -6905,7 +6905,7 @@ the result will be displayed like this, each item in the dictionary shows the he { "uart_stat1": "2", "uart_stat2": "1", - + } ``` @@ -12811,16 +12811,16 @@ Usage: sfputil read-eeprom [OPTIONS] Options: --no-format Display non formatted data. --wire-addr Wire address of sff8472. - --help Show this + --help Show this ``` ``` admin@sonic:~$ sfputil read-eeprom Ethernet0 0 100 2 -00000064 4a 44 |..| + 00000064 4a 44 |..| admin@sonic:~$ sfputil read-eeprom Ethernet0 0 0 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 |................| + 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 Ethernet0 0 100 2 --no-format 4a44 diff --git a/sfputil/main.py b/sfputil/main.py index 1eeb10eb51..47d20368f0 100644 --- a/sfputil/main.py +++ b/sfputil/main.py @@ -48,8 +48,17 @@ PAGE_SIZE = 128 PAGE_OFFSET = 128 - SFF8472_A0_SIZE = 256 +MAX_EEPROM_PAGE = 255 +MAX_EEPROM_OFFSET = 255 +MIN_OFFSET_FOR_PAGE0 = 0 +MIN_OFFSET_FOR_OTHER_PAGE = 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 + +EEPROM_DUMP_INDENT = ' ' * 8 # TODO: We should share these maps and the formatting functions between sfputil and sfpshow QSFP_DD_DATA_MAP = { @@ -793,6 +802,28 @@ def eeprom_hexdump_sff8636(port, physical_port, page): return output + +def eeprom_dump_general(physical_port, page, overall_offset, size, page_offset, no_format=False): + """ + 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 + 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(overall_offset, size) + if page_dump is None: + return ERROR_NOT_IMPLEMENTED, f'Error: Failed to read EEPROM for page {page:x}h, overall_offset {overall_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 convert_byte_to_valid_ascii_char(byte): if byte < 32 or 126 < byte: return '.' @@ -1260,10 +1291,10 @@ def is_fw_switch_done(port_name): status = -1 # Abnormal status. elif (ImageARunning == 1) and (ImageACommitted == 0): # ImageA is running, but not committed. click.echo("FW images switch successful : ImageA is running") - status = 1 # run_firmware is done. + status = 1 # run_firmware is done. elif (ImageBRunning == 1) and (ImageBCommitted == 0): # ImageB is running, but not committed. click.echo("FW images switch successful : ImageB is running") - status = 1 # run_firmware is done. + status = 1 # run_firmware is done. else: # No image is running, or running and committed image is same. click.echo("FW info error : Failed to switch into uncommitted image!") status = -1 # Failure for Switching images. @@ -1527,6 +1558,50 @@ def unlock(port_name, password): else: click.echo("CDB: Host password NOT accepted! status = {}".format(status)) +# 'version' subcommand +@cli.command() +def version(): + """Display version info""" + click.echo("sfputil version {0}".format(VERSION)) + +# 'target' subcommand +@firmware.command() +@click.argument('port_name', required=True, default=None) +@click.argument('target', type=click.IntRange(0, 2), required=True, default=None) +def target(port_name, target): + """Select target end for firmware download 0-(local) \n + 1-(remote-A) \n + 2-(remote-B) + """ + physical_port = logical_port_to_physical_port_index(port_name) + sfp = platform_chassis.get_sfp(physical_port) + + if is_port_type_rj45(port_name): + click.echo("{}: This functionality is not applicable for RJ45 port".format(port_name)) + sys.exit(EXIT_FAIL) + + if not is_sfp_present(port_name): + click.echo("{}: SFP EEPROM not detected\n".format(port_name)) + sys.exit(EXIT_FAIL) + + try: + api = sfp.get_xcvr_api() + except NotImplementedError: + click.echo("{}: This functionality is currently not implemented for this module".format(port_name)) + sys.exit(ERROR_NOT_IMPLEMENTED) + + try: + status = api.set_firmware_download_target_end(target) + except AttributeError: + click.echo("{}: This functionality is not applicable for this module".format(port_name)) + sys.exit(ERROR_NOT_IMPLEMENTED) + + if status: + click.echo("Target Mode set to {}". format(target)) + else: + click.echo("Target Mode set failed!") + sys.exit(EXIT_FAIL) + # 'read-eeprom' subcommand @cli.command() @@ -1538,35 +1613,40 @@ def unlock(port_name, password): @click.option('--wire-addr', help="Wire address of sff8472") def read_eeprom(port_name, page, offset, size, no_format, wire_addr): """Read SFP EEPROM data""" - if platform_sfputil.is_logical_port(port_name) == 0: - click.echo("Error: invalid port {}".format(port_name)) - print_all_valid_port_values() - sys.exit(ERROR_INVALID_PORT) - - if is_port_type_rj45(port_name): - click.echo("This functionality is not applicable for RJ45 port {}.".format(port_name)) - sys.exit(EXIT_FAIL) + try: + if platform_sfputil.is_logical_port(port_name) == 0: + click.echo("Error: invalid port {}".format(port_name)) + print_all_valid_port_values() + sys.exit(ERROR_INVALID_PORT) - physical_port = logical_port_to_physical_port_index(port_name) - sfp = platform_chassis.get_sfp(physical_port) - if not sfp.get_presence(): - click.echo("{}: SFP EEPROM not detected\n".format(port_name)) - sys.exit(EXIT_FAIL) + if is_port_type_rj45(port_name): + click.echo("This functionality is not applicable for RJ45 port {}.".format(port_name)) + sys.exit(EXIT_FAIL) - try: - data = sfp.read_eeprom_by_page(page, offset, size, wire_addr) - if data is None: - click.echo("Error: Failed to read EEPROM!") - sys.exit(ERROR_NOT_IMPLEMENTED) - if no_format: - click.echo(''.join('{:02x}'.format(x) for x in data)) + physical_port = logical_port_to_physical_port_index(port_name) + sfp = platform_chassis.get_sfp(physical_port) + if not sfp.get_presence(): + click.echo("{}: SFP EEPROM not detected\n".format(port_name)) + sys.exit(EXIT_FAIL) + + from sonic_platform_base.sonic_xcvr.api.public import sff8472 + api = sfp.get_xcvr_api() + if api is None: + click.echo('Error: SFP EEPROM not detected!') + if not isinstance(api, sff8472.Sff8472Api): + overall_offset = get_overall_offset_general(api, page, offset, size) else: - click.echo(hexdump('', data, offset)) + overall_offset = get_overall_offset_sff8472(api, page, offset, size, wire_addr) + return_code, output = eeprom_dump_general(physical_port, page, overall_offset, size, offset, no_format) + if return_code != 0: + click.echo("Error: Failed to read EEPROM!") + sys.exit(return_code) + click.echo(output) except NotImplementedError: click.echo("This functionality is currently not implemented for this platform") sys.exit(ERROR_NOT_IMPLEMENTED) except ValueError as e: - click.echo("Error: {}".format(e)) + click.echo(f"Error: {e}") sys.exit(EXIT_FAIL) @@ -1580,34 +1660,44 @@ def read_eeprom(port_name, page, offset, size, no_format, wire_addr): @click.option('--verify', is_flag=True, help="Verify the data by reading back") def write_eeprom(port_name, page, offset, data, wire_addr, verify): """Write SFP EEPROM data""" - if platform_sfputil.is_logical_port(port_name) == 0: - click.echo("Error: invalid port {}".format(port_name)) - print_all_valid_port_values() - sys.exit(ERROR_INVALID_PORT) + try: + if platform_sfputil.is_logical_port(port_name) == 0: + click.echo("Error: invalid port {}".format(port_name)) + print_all_valid_port_values() + sys.exit(ERROR_INVALID_PORT) - if is_port_type_rj45(port_name): - click.echo("This functionality is not applicable for RJ45 port {}.".format(port_name)) - sys.exit(EXIT_FAIL) + if is_port_type_rj45(port_name): + click.echo("This functionality is not applicable for RJ45 port {}.".format(port_name)) + sys.exit(EXIT_FAIL) - physical_port = logical_port_to_physical_port_index(port_name) - sfp = platform_chassis.get_sfp(physical_port) - if not sfp.get_presence(): - click.echo("{}: SFP EEPROM not detected\n".format(port_name)) - sys.exit(EXIT_FAIL) + physical_port = logical_port_to_physical_port_index(port_name) + sfp = platform_chassis.get_sfp(physical_port) + if not sfp.get_presence(): + click.echo("{}: SFP EEPROM not detected\n".format(port_name)) + sys.exit(EXIT_FAIL) - try: - bytes = bytearray.fromhex(data) - except ValueError: - click.echo("Error: Data must be a hex string of even length!") - sys.exit(EXIT_FAIL) + try: + bytes = bytearray.fromhex(data) + except ValueError: + click.echo("Error: Data must be a hex string of even length!") + sys.exit(EXIT_FAIL) - try: - success = sfp.write_eeprom_by_page(page, offset, bytes, wire_addr) + from sonic_platform_base.sonic_xcvr.api.public import sff8472 + api = sfp.get_xcvr_api() + if api is None: + click.echo('Error: SFP EEPROM not detected!') + sys.exit(EXIT_FAIL) + + if not isinstance(api, sff8472.Sff8472Api): + overall_offset = get_overall_offset_general(api, page, offset, len(bytes)) + else: + overall_offset = get_overall_offset_sff8472(api, page, offset, len(bytes), wire_addr) + success = sfp.write_eeprom(overall_offset, len(bytes), bytes) if not success: click.echo("Error: Failed to write EEPROM!") sys.exit(ERROR_NOT_IMPLEMENTED) if verify: - read_data = sfp.read_eeprom_by_page(page, offset, len(bytes), wire_addr) + read_data = sfp.read_eeprom(overall_offset, len(bytes)) if read_data != bytes: click.echo(f"Error: Write data failed! Write: {''.join('{:02x}'.format(x) for x in bytes)}, read: {''.join('{:02x}'.format(x) for x in read_data)}") sys.exit(EXIT_FAIL) @@ -1619,49 +1709,79 @@ def write_eeprom(port_name, page, offset, data, wire_addr, verify): sys.exit(EXIT_FAIL) -# 'version' subcommand -@cli.command() -def version(): - """Display version info""" - click.echo("sfputil version {0}".format(VERSION)) +def get_overall_offset_general(api, page, offset, size): + """ + Validate input parameter page, offset, size and translate them to overall offset + Args: + api: cable API object + page: module EEPROM page number. + offset: module EEPROM page offset. + size: number bytes of the data to be read/write -# 'target' subcommand -@firmware.command() -@click.argument('port_name', required=True, default=None) -@click.argument('target', type=click.IntRange(0, 2), required=True, default=None) -def target(port_name, target): - """Select target end for firmware download 0-(local) \n - 1-(remote-A) \n - 2-(remote-B) + Returns: + The overall offset """ - physical_port = logical_port_to_physical_port_index(port_name) - sfp = platform_chassis.get_sfp(physical_port) + max_page = 0 if api.is_flat_memory() else MAX_EEPROM_PAGE + if max_page == 0 and page != 0: + raise ValueError(f'Invalid page number {page}, only page 0 is supported') - if is_port_type_rj45(port_name): - click.echo("{}: This functionality is not applicable for RJ45 port".format(port_name)) - sys.exit(EXIT_FAIL) + if page < 0 or page > max_page: + raise ValueError(f'Invalid page number {page}, valid range: [0, {max_page}]') - if not is_sfp_present(port_name): - click.echo("{}: SFP EEPROM not detected\n".format(port_name)) - sys.exit(EXIT_FAIL) + if page == 0: + if offset < MIN_OFFSET_FOR_PAGE0 or offset > MAX_EEPROM_OFFSET: + raise ValueError(f'Invalid offset {offset} for page 0, valid range: [0, 255]') + else: + if offset < MIN_OFFSET_FOR_OTHER_PAGE or offset > MAX_EEPROM_OFFSET: + raise ValueError(f'Invalid offset {offset} for page {page}, valid range: [128, 255]') - try: - api = sfp.get_xcvr_api() - except NotImplementedError: - click.echo("{}: This functionality is currently not implemented for this module".format(port_name)) - sys.exit(ERROR_NOT_IMPLEMENTED) + if size <= 0 or size + offset - 1 > MAX_EEPROM_OFFSET: + raise ValueError(f'Invalid size {size}, valid range: [1, {255 - offset + 1}]') - try: - status = api.set_firmware_download_target_end(target) - except AttributeError: - click.echo("{}: This functionality is not applicable for this module".format(port_name)) - sys.exit(ERROR_NOT_IMPLEMENTED) + return page * PAGE_SIZE + offset - if status: - click.echo("Target Mode set to {}". format(target)) + +def get_overall_offset_sff8472(api, page, offset, size, wire_addr): + """ + Validate input parameter page, offset, size, wire_addr and translate them to overall offset + Args: + api: cable API object + page: module EEPROM page number. + offset: module EEPROM page offset. + size: number bytes of the data to be read/write + wire_addr: case-insensitive wire address string. Only valid for sff8472, a0h or a2h. + + Returns: + The overall offset + """ + if not wire_addr: + raise ValueError("Invalid wire address for sff8472, must a0h or a2h") + + is_active_cable = not api.is_copper() + valid_wire_address = ('a0h', 'a2h') if is_active_cable else ('a0h',) + wire_addr = wire_addr.lower() + if wire_addr not in valid_wire_address: + raise ValueError(f"Invalid wire address {wire_addr} for sff8472, must be {' or '.join(valid_wire_address)}") + + if wire_addr == 'a0h': + if page != 0: + raise ValueError(f'Invalid page number {page} for wire address {wire_addr}, only page 0 is supported') + max_offset = MAX_OFFSET_FOR_A0H_UPPER_PAGE if is_active_cable else MAX_OFFSET_FOR_A0H_LOWER_PAGE + if offset < 0 or offset > max_offset: + raise ValueError(f'Invalid offset {offset} for wire address {wire_addr}, valid range: [0, {max_offset}]') + if size <= 0 or size + offset - 1 > max_offset: + raise ValueError( + f'Invalid size {size} for wire address {wire_addr}, valid range: [1, {max_offset - offset + 1}]') + return offset else: - click.echo("Target Mode set failed!") - sys.exit(EXIT_FAIL) + if page < 0 or page > MAX_EEPROM_PAGE: + raise ValueError(f'Invalid page number {page} for wire address {wire_addr}, valid range: [0, 255]') + if offset < 0 or offset > MAX_OFFSET_FOR_A2H: + raise ValueError(f'Invalid offset {offset} for wire address {wire_addr}, valid range: [0, 255]') + if size <= 0 or size + offset - 1 > MAX_OFFSET_FOR_A2H: + raise ValueError(f'Invalid size {size} for wire address {wire_addr}, valid range: [1, {255 - offset + 1}]') + return page * PAGE_SIZE + offset + PAGE_SIZE_FOR_A0H + if __name__ == '__main__': cli() diff --git a/tests/sfputil_test.py b/tests/sfputil_test.py index d3a76935b9..c7ad00a68b 100644 --- a/tests/sfputil_test.py +++ b/tests/sfputil_test.py @@ -256,7 +256,7 @@ def test_convert_sfp_info_to_output_string(self, sfp_info_dict, expected_output) Vcc: 3.2577Volts ModuleThresholdValues: ''' - ), + ), ( 'QSFP-DD Double Density 8X Pluggable Transceiver', { @@ -982,28 +982,27 @@ def test_read_eeprom(self, mock_chassis): assert result.exit_code == EXIT_FAIL mock_sfp.get_presence.return_value = True - mock_sfp.read_eeprom_by_page = MagicMock(return_value=None) + mock_sfp.read_eeprom = MagicMock(return_value=None) result = runner.invoke(sfputil.cli.commands['read-eeprom'], ["Ethernet0", '0', '0', '1']) assert result.exit_code == ERROR_NOT_IMPLEMENTED - mock_sfp.read_eeprom_by_page.return_value = bytearray([0x00, 0x01]) + mock_sfp.read_eeprom.return_value = bytearray([0x00, 0x01]) result = runner.invoke(sfputil.cli.commands['read-eeprom'], ["Ethernet0", '0', '0', '2', '--no-format']) assert result.exit_code == 0 assert result.output == '0001\n' result = runner.invoke(sfputil.cli.commands['read-eeprom'], ["Ethernet0", '0', '5', '2']) assert result.exit_code == 0 - expected_output = """ -00000005 00 01 |..| + expected_output = """ 00000005 00 01 |..| """ print(result.output) assert result.output == expected_output - mock_sfp.read_eeprom_by_page.side_effect = NotImplementedError + mock_sfp.read_eeprom.side_effect = NotImplementedError result = runner.invoke(sfputil.cli.commands['read-eeprom'], ["Ethernet0", '0', '5', '2']) assert result.exit_code == ERROR_NOT_IMPLEMENTED - mock_sfp.read_eeprom_by_page.side_effect = ValueError + mock_sfp.read_eeprom.side_effect = ValueError result = runner.invoke(sfputil.cli.commands['read-eeprom'], ["Ethernet0", '0', '5', '2']) assert result.exit_code == EXIT_FAIL @@ -1030,33 +1029,33 @@ def test_write_eeprom(self, mock_chassis): assert result.exit_code == EXIT_FAIL # write failed - mock_sfp.write_eeprom_by_page = MagicMock(return_value=False) + mock_sfp.write_eeprom = MagicMock(return_value=False) result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '10']) print(result.output) assert result.exit_code == ERROR_NOT_IMPLEMENTED # write success - mock_sfp.write_eeprom_by_page.return_value = True + mock_sfp.write_eeprom.return_value = True result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '10']) assert result.exit_code == 0 # write verify success - mock_sfp.read_eeprom_by_page = MagicMock(return_value=bytearray([16])) + mock_sfp.read_eeprom = MagicMock(return_value=bytearray([16])) result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '10', '--verify']) assert result.exit_code == 0 # write verify failed - mock_sfp.read_eeprom_by_page = MagicMock(return_value=bytearray([10])) + mock_sfp.read_eeprom = MagicMock(return_value=bytearray([10])) result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '11', '--verify']) assert result.exit_code != 0 # Not implemented - mock_sfp.write_eeprom_by_page.side_effect = NotImplementedError + mock_sfp.write_eeprom.side_effect = NotImplementedError result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '10']) assert result.exit_code == ERROR_NOT_IMPLEMENTED # Value error - mock_sfp.write_eeprom_by_page.side_effect = ValueError + mock_sfp.write_eeprom.side_effect = ValueError result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '10']) assert result.exit_code == EXIT_FAIL @@ -1086,6 +1085,80 @@ def test_write_eeprom_rj45(self): result = runner.invoke(sfputil.cli.commands['write-eeprom'], ["Ethernet0", '0', '0', '00']) assert result.exit_code == EXIT_FAIL + def test_get_overall_offset_general(self): + api = MagicMock() + api.is_flat_memory = MagicMock(return_value=False) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, -1, 0, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 256, 0, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 0, -1, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 0, 256, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 1, 127, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 1, 256, 1) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 0, 0, 0) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_general(api, 0, 0, 257) + + assert sfputil.get_overall_offset_general(api, 0, 1, 1) == 1 + + def test_get_overall_offset_sff8472(self): + api = MagicMock() + api.is_copper = MagicMock(return_value=False) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 1, None) + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 1, wire_addr='invalid') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 1, 0, 1, wire_addr='a0h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, -1, 1, wire_addr='A0h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 256, 1, wire_addr='A0h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 0, wire_addr='A0h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 257, wire_addr='A0h') + + assert sfputil.get_overall_offset_sff8472(api, 0, 2, 2, wire_addr='A0h') == 2 + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, -1, 0, 1, wire_addr='a2h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 256, 0, 1, wire_addr='a2h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, -1, 1, wire_addr='a2h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 0, wire_addr='A2h') + + with pytest.raises(ValueError): + sfputil.get_overall_offset_sff8472(api, 0, 0, 257, wire_addr='A2h') + + assert sfputil.get_overall_offset_sff8472(api, 0, 2, 2, wire_addr='A2h') == 258 + @patch('sfputil.main.platform_chassis') @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) def test_target_firmware(self, mock_chassis):