Skip to content

Commit

Permalink
Enable show interfacess counters on chassis supervisor (sonic-net#3488)
Browse files Browse the repository at this point in the history
What I did
I modify the portstat script and the implementation of Class Porstat to enable the Chassis Supervisor to collect port counters from linecards. By doing so, "show interfaces counters" on the Chassis Supervisor can now collect port counters from linecards and display them on the CLI.

How I did it
I made the Class Portstat aware of chassis environment and added logic to collect port counters from linecards through "GET_LINECARD_COUNTER|pull" signal in CHASSIS_STATE_DB. And I also added an agent on every linecard to collect port counters and send them to CHASSIS_STATE_DB on the Chassis Supervisor.

Note: the agent is not part of this PR. The current agent used in my test is only for internal use in MSFT.

How to verify it
Run it on a SONiC Chassis Supervisor and make sure the Linecards are equipped with agents to publish port counters to CHASSIS_STATE_DB.
  • Loading branch information
BYGX-wcr authored Aug 28, 2024
1 parent e4df80a commit c4ce5ae
Show file tree
Hide file tree
Showing 7 changed files with 890 additions and 530 deletions.
536 changes: 9 additions & 527 deletions scripts/portstat

Large diffs are not rendered by default.

58 changes: 57 additions & 1 deletion tests/mock_tables/chassis_state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,62 @@
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": {
"module_hostname": "sonic-lc3"
},
"LINECARD_PORT_STAT_MARK_TABLE|sonic-lc1": {
"timestamp": "2020-07-01 00:00:00"
},
"LINECARD_PORT_STAT_MARK_TABLE|sonic-lc3": {
"timestamp": "2020-07-01 00:00:00"
},
"LINECARD_PORT_STAT_TABLE|Ethernet1/1": {
"state": "U",
"rx_ok": 100,
"rx_bps": 10,
"rx_pps": 1,
"rx_util": 0,
"rx_err": 0,
"rx_drop": 0,
"rx_ovr": 0,
"tx_ok": 100,
"tx_bps": 10,
"tx_pps": 1,
"tx_util": 0,
"tx_err": 0,
"tx_drop": 0,
"tx_ovr": 0
},
"LINECARD_PORT_STAT_TABLE|Ethernet2/1": {
"state": "U",
"rx_ok": 100,
"rx_bps": 10,
"rx_pps": 1,
"rx_util": 0,
"rx_err": 0,
"rx_drop": 0,
"rx_ovr": 0,
"tx_ok": 100,
"tx_bps": 10,
"tx_pps": 1,
"tx_util": 0,
"tx_err": 0,
"tx_drop": 0,
"tx_ovr": 0
},
"LINECARD_PORT_STAT_TABLE|Ethernet11/1": {
"state": "U",
"rx_ok": 100,
"rx_bps": 10,
"rx_pps": 1,
"rx_util": 0,
"rx_err": 0,
"rx_drop": 0,
"rx_ovr": 0,
"tx_ok": 100,
"tx_bps": 10,
"tx_pps": 1,
"tx_util": 0,
"tx_err": 0,
"tx_drop": 0,
"tx_ovr": 0
}

}
11 changes: 11 additions & 0 deletions tests/portstat_db/on_sup_no_counters/chassis_state_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD0": {
"module_hostname": "sonic-lc1"
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD1": {
"module_hostname": "sonic-lc2"
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": {
"module_hostname": "sonic-lc3"
}
}
48 changes: 48 additions & 0 deletions tests/portstat_db/on_sup_partial_lc/chassis_state_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD0": {
"module_hostname": "sonic-lc1"
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD1": {
"module_hostname": "sonic-lc2"
},
"CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": {
"module_hostname": "sonic-lc3"
},
"LINECARD_PORT_STAT_MARK_TABLE|sonic-lc1": {
"timestamp": "2020-07-01 00:00:00"
},
"LINECARD_PORT_STAT_TABLE|Ethernet1/1": {
"state": "U",
"rx_ok": 100,
"rx_bps": 10,
"rx_pps": 1,
"rx_util": 0,
"rx_err": 0,
"rx_drop": 0,
"rx_ovr": 0,
"tx_ok": 100,
"tx_bps": 10,
"tx_pps": 1,
"tx_util": 0,
"tx_err": 0,
"tx_drop": 0,
"tx_ovr": 0
},
"LINECARD_PORT_STAT_TABLE|Ethernet2/1": {
"state": "U",
"rx_ok": 100,
"rx_bps": 10,
"rx_pps": 1,
"rx_util": 0,
"rx_err": 0,
"rx_drop": 0,
"rx_ovr": 0,
"tx_ok": 100,
"tx_bps": 10,
"tx_pps": 1,
"tx_util": 0,
"tx_err": 0,
"tx_drop": 0,
"tx_ovr": 0
}
}
92 changes: 90 additions & 2 deletions tests/portstat_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from .utils import get_result_and_return_code
from utilities_common.cli import UserCache

root_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(root_path)
test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")

intf_counters_before_clear = """\
Expand Down Expand Up @@ -234,6 +234,23 @@
Time Since Counters Last Cleared............... None
"""

intf_counters_on_sup = """\
IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL\
TX_ERR TX_DRP TX_OVR
------------ ------- ------- --------- --------- -------- -------- -------- ------- --------- ---------\
-------- -------- --------
Ethernet1/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\
0 0 0
Ethernet2/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\
0 0 0
Ethernet11/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\
0 0 0
"""

intf_counters_on_sup_no_counters = "Linecard Counter Table is not available.\n"

intf_counters_on_sup_partial_lc = "Not all linecards have published their counter values.\n"

TEST_PERIOD = 3


Expand Down Expand Up @@ -397,13 +414,84 @@ def test_clear_intf_counters(self):
assert return_code == 0
verify_after_clear(result, intf_counter_after_clear)

def test_show_intf_counters_on_sup(self):
remove_tmp_cnstat_file()
os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1"
runner = CliRunner()
result = runner.invoke(
show.cli.commands["interfaces"].commands["counters"], [])
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == intf_counters_on_sup

return_code, result = get_result_and_return_code(['portstat'])
print("return_code: {}".format(return_code))
print("result = {}".format(result))
assert return_code == 0
assert result == intf_counters_on_sup
os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0"

def test_show_intf_counters_on_sup_no_counters(self):
remove_tmp_cnstat_file()
os.system("cp {} /tmp/".format(os.path.join(test_path, "mock_tables/chassis_state_db.json")))
os.system("cp {} {}".format(os.path.join(test_path, "portstat_db/on_sup_no_counters/chassis_state_db.json"),
os.path.join(test_path, "mock_tables/chassis_state_db.json")))
os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1"

runner = CliRunner()
result = runner.invoke(
show.cli.commands["interfaces"].commands["counters"], [])
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == intf_counters_on_sup_no_counters

return_code, result = get_result_and_return_code(['portstat'])
print("return_code: {}".format(return_code))
print("result = {}".format(result))
assert return_code == 0
assert result == intf_counters_on_sup_no_counters

os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0"
os.system("cp /tmp/chassis_state_db.json {}"
.format(os.path.join(test_path, "mock_tables/chassis_state_db.json")))

def test_show_intf_counters_on_sup_partial_lc(self):
remove_tmp_cnstat_file()
os.system("cp {} /tmp/".format(os.path.join(test_path, "mock_tables/chassis_state_db.json")))
os.system("cp {} {}".format(os.path.join(test_path, "portstat_db/on_sup_partial_lc/chassis_state_db.json"),
os.path.join(test_path, "mock_tables/chassis_state_db.json")))
os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1"

runner = CliRunner()
result = runner.invoke(
show.cli.commands["interfaces"].commands["counters"], [])
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == intf_counters_on_sup_partial_lc

return_code, result = get_result_and_return_code(['portstat'])
print("return_code: {}".format(return_code))
print("result = {}".format(result))
assert return_code == 0
assert result == intf_counters_on_sup_partial_lc

os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0"
os.system("cp /tmp/chassis_state_db.json {}"
.format(os.path.join(test_path, "mock_tables/chassis_state_db.json")))

@classmethod
def teardown_class(cls):
print("TEARDOWN")
os.environ["PATH"] = os.pathsep.join(
os.environ["PATH"].split(os.pathsep)[:-1])
os.environ["UTILITIES_UNIT_TESTING"] = "0"
os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0"
remove_tmp_cnstat_file()
os.system("cp /tmp/chassis_state_db.json {}"
.format(os.path.join(test_path, "mock_tables/chassis_state_db.json")))


class TestMultiAsicPortStat(object):
Expand Down
9 changes: 9 additions & 0 deletions utilities_common/netstat.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,12 @@ def format_util(brate, port_rate):
util = brate/(float(port_rate)*1000*1000/8.0)*100
return "{:.2f}%".format(util)


def format_util_directly(util):
"""
Format the util without calculation.
"""
if util == STATUS_NA:
return STATUS_NA
else:
return "{:.2f}%".format(float(util))
Loading

0 comments on commit c4ce5ae

Please sign in to comment.