Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[show][interface] Add changes for show interface errors command #3721

Merged
merged 13 commits into from
Jan 23, 2025
68 changes: 68 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
@@ -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
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
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 ...)
#
120 changes: 120 additions & 0 deletions tests/interfaces_test.py
Original file line number Diff line number Diff line change
@@ -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")
27 changes: 27 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
@@ -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": ""
}
}