diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index f8889e6c32..2c7081fa81 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -414,6 +414,74 @@ def mpls(ctx, interfacename, namespace, display): interfaces.add_command(portchannel.portchannel) + +def get_all_port_errors(interfacename): + + port_operr_table = {} + db = SonicV2Connector(host=REDIS_HOSTIP) + db.connect(db.STATE_DB) + + # Retrieve the errors data from the PORT_OPERR_TABLE + port_operr_table = db.get_all(db.STATE_DB, 'PORT_OPERR_TABLE|{}'.format(interfacename)) + db.close(db.STATE_DB) + + return port_operr_table + + +@interfaces.command() +@click.argument('interfacename', required=True) +@click.pass_context +def errors(ctx, interfacename): + """Show Interface Erorrs """ + # Try to convert interface name from alias + interfacename = try_convert_interfacename_from_alias(click.get_current_context(), interfacename) + + port_operr_table = get_all_port_errors(interfacename) + + # Define a list of all potential errors + ALL_PORT_ERRORS = [ + "oper_error_status", + "mac_local_fault", + "mac_remote_fault", + "fec_sync_loss", + "fec_alignment_loss", + "high_ser_error", + "high_ber_error", + "data_unit_crc_error", + "data_unit_misalignment_error", + "signal_local_error", + "crc_rate", + "data_unit_size", + "code_group_error", + "no_rx_reachability" + ] + + # Prepare the table headers and body + header = ['Port Errors', 'Count', 'Last timestamp(UTC)'] + body = [] + + # Populate the table body with all errors, defaulting missing ones to 0 and Never + for error in ALL_PORT_ERRORS: + count_key = f"{error}_count" + time_key = f"{error}_time" + + if port_operr_table is not None: + count = port_operr_table.get(count_key, "0") # Default count to '0' + timestamp = port_operr_table.get(time_key, "Never") # Default timestamp to 'Never' + else: + count = "0" + timestamp = "Never" + + # Add to table + body.append([error.replace('_', ' '), count, timestamp]) + + # Sort the body for consistent display + body.sort(key=lambda x: x[0]) + + # Display the formatted table + click.echo(tabulate(body, header)) + + # # transceiver group (show interfaces trasceiver ...) # diff --git a/tests/interfaces_test.py b/tests/interfaces_test.py index 6acdb505ac..ae24b1bd4c 100644 --- a/tests/interfaces_test.py +++ b/tests/interfaces_test.py @@ -263,6 +263,82 @@ PortChannel1001 trunk 4000 """ +intf_errors_Ethernet64 = """\ +Port Errors Count Last timestamp(UTC) +---------------------------- ------- --------------------- +code group error 0 Never +crc rate 0 Never +data unit crc error 0 Never +data unit misalignment error 0 Never +data unit size 0 Never +fec alignment loss 0 Never +fec sync loss 0 Never +high ber error 0 Never +high ser error 0 Never +mac local fault 26 2025-01-17 18:40:56 +mac remote fault 14483 2025-01-17 19:51:12 +no rx reachability 0 Never +oper error status 0 Never +signal local error 0 Never +""" +intf_errors_Ethernet16 = """\ +Port Errors Count Last timestamp(UTC) +---------------------------- ------- --------------------- +code group error 0 Never +crc rate 0 Never +data unit crc error 0 Never +data unit misalignment error 0 Never +data unit size 0 Never +fec alignment loss 0 Never +fec sync loss 0 Never +high ber error 0 Never +high ser error 0 Never +mac local fault 0 Never +mac remote fault 0 Never +no rx reachability 0 Never +oper error status 0 Never +signal local error 0 Never +""" + +intf_errors_Ethernet32 = """\ +Port Errors Count Last timestamp(UTC) +---------------------------- ------- --------------------- +code group error 0 Never +crc rate 0 Never +data unit crc error 0 Never +data unit misalignment error 0 Never +data unit size 0 Never +fec alignment loss 0 Never +fec sync loss 3 2025-01-16 13:45:20 +high ber error 1 2025-01-16 14:30:10 +high ser error 0 Never +mac local fault 5 2025-01-16 12:05:34 +mac remote fault 0 +no rx reachability 0 Never +oper error status 0 Never +signal local error 0 Never +""" + +intf_errors_Ethernet48 = """\ +Port Errors Count Last timestamp(UTC) +---------------------------- ------- --------------------- +code group error 0 Never +crc rate 0 Never +data unit crc error 0 Never +data unit misalignment error 0 Never +data unit size 0 Never +fec alignment loss 0 Never +fec sync loss 0 Never +high ber error 0 Never +high ser error 0 Never +mac local fault 0 +mac remote fault 0 +no rx reachability 0 Never +oper error status 0 Never +signal local error 0 Never +""" + + class TestInterfaces(object): @classmethod @@ -496,6 +572,50 @@ def test_show_interfaces_switchport_config_in_alias_mode(self): assert result.exit_code == 0 assert result.output == show_interfaces_switchport_config_in_alias_mode_output + def test_show_intf_errors_filled_data(self): + """Test case for an interface with filled error data.""" + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["errors"], ["Ethernet64"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_errors_Ethernet64 + + def test_show_intf_errors_empty_data(self): + """Test case for an interface with no error data.""" + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["errors"], ["Ethernet16"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_errors_Ethernet16 + + def test_show_intf_errors_partial_data(self): + """Test case for an interface with partial error data.""" + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["errors"], ["Ethernet32"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_errors_Ethernet32 + + def test_show_intf_errors_default_values(self): + """Test case for an interface with default values.""" + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["errors"], ["Ethernet48"] + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_errors_Ethernet48 + @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index bad7882cb6..a4f0bd72c3 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -1684,5 +1684,32 @@ }, "STP_TABLE|GLOBAL": { "max_stp_inst": "510" + }, + "PORT_OPERR_TABLE|Ethernet72": { + }, + "PORT_OPERR_TABLE|Ethernet64": { + "oper_error_status": "2", + "mac_remote_fault_count": "14483", + "mac_remote_fault_time": "2025-01-17 19:51:12", + "mac_local_fault_count": "26", + "mac_local_fault_time": "2025-01-17 18:40:56" + }, + "PORT_OPERR_TABLE|Ethernet32": { + "oper_error_status": "0", + "mac_remote_fault_count": "0", + "mac_remote_fault_time": "", + "mac_local_fault_count": "5", + "mac_local_fault_time": "2025-01-16 12:05:34", + "fec_sync_loss_count": "3", + "fec_sync_loss_time": "2025-01-16 13:45:20", + "high_ber_error_count": "1", + "high_ber_error_time": "2025-01-16 14:30:10" + }, + "PORT_OPERR_TABLE|Ethernet48": { + "oper_error_status": "0", + "mac_remote_fault_count": "0", + "mac_remote_fault_time": "", + "mac_local_fault_count": "0", + "mac_local_fault_time": "" } }