Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <interfacename>"""
# 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])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vdahiya12 can you double check if there is enough space the columns as displayed in the CLI command document?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked with the sample test output, we should be good


# Display the formatted table
click.echo(tabulate(body, header))


#
# transceiver group (show interfaces trasceiver ...)
#
Expand Down
120 changes: 120 additions & 0 deletions tests/interfaces_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
27 changes: 27 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": ""
}
}