Skip to content

Commit 25fda26

Browse files
authored
[chassis]Add fabric counter cli commands (sonic-net#1860)
Add fabric counters CLI commands. Commands are: show fabric counters port show fabric counters port -n asic4 show fabric counters port -n asic5 -e show fabric counters queue show fabric counters queue -n asic4 Signed-off-by: Maxime Lorrillere <[email protected]>
1 parent ae97e59 commit 25fda26

File tree

10 files changed

+756
-12
lines changed

10 files changed

+756
-12
lines changed

scripts/fabricstat

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
from collections import OrderedDict, namedtuple
5+
import os
6+
import sys
7+
8+
from utilities_common import constants
9+
from natsort import natsorted
10+
from tabulate import tabulate
11+
from sonic_py_common import multi_asic
12+
from swsscommon.swsscommon import APP_FABRIC_PORT_TABLE_NAME, COUNTERS_TABLE, COUNTERS_FABRIC_PORT_NAME_MAP, COUNTERS_FABRIC_QUEUE_NAME_MAP
13+
import utilities_common.multi_asic as multi_asic_util
14+
15+
# mock the redis for unit test purposes #
16+
try:
17+
if os.environ["UTILITIES_UNIT_TESTING"] == "2":
18+
modules_path = os.path.join(os.path.dirname(__file__), "..")
19+
tests_path = os.path.join(modules_path, "tests")
20+
sys.path.insert(0, modules_path)
21+
sys.path.insert(0, tests_path)
22+
import mock_tables.dbconnector
23+
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic":
24+
import mock_tables.mock_multi_asic
25+
mock_tables.dbconnector.load_namespace_config()
26+
except KeyError:
27+
pass
28+
29+
PORT_NAME_PREFIX = 'PORT'
30+
COUNTER_TABLE_PREFIX = COUNTERS_TABLE+":"
31+
FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
32+
FABRIC_PORT_STATUS_FIELD = "STATUS"
33+
STATUS_NA = 'N/A'
34+
35+
class FabricStat(object):
36+
def __init__(self, namespace):
37+
self.db = None
38+
self.namespace = namespace
39+
self.multi_asic = multi_asic_util.MultiAsic(constants.DISPLAY_ALL, namespace)
40+
41+
def get_cnstat_dict(self):
42+
self.cnstat_dict = OrderedDict()
43+
self.collect_stat()
44+
return self.cnstat_dict
45+
46+
@multi_asic_util.run_on_multi_asic
47+
def collect_stat(self):
48+
"""
49+
Collect the statisitics from all the asics present on the
50+
device and store in a dict
51+
"""
52+
self.cnstat_dict.update(self.get_cnstat())
53+
54+
def get_port_state(self, port_name):
55+
"""
56+
Get the port state
57+
"""
58+
full_table_id = FABRIC_PORT_STATUS_TABLE_PREFIX + port_name
59+
oper_state = self.db.get(self.db.STATE_DB, full_table_id, FABRIC_PORT_STATUS_FIELD)
60+
if oper_state is not None:
61+
return oper_state
62+
return STATUS_NA
63+
64+
def get_counters(self, counter_bucket_dict, table_id):
65+
fields = ["0"] * len(counter_bucket_dict)
66+
for pos, counter_name in counter_bucket_dict.items():
67+
full_table_id = COUNTER_TABLE_PREFIX + table_id
68+
counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name)
69+
if counter_data is None:
70+
fields[pos] = STATUS_NA
71+
elif fields[pos] != STATUS_NA:
72+
fields[pos] = str(int(fields[pos]) + int(counter_data))
73+
return fields
74+
75+
def get_cnstat(self):
76+
"""
77+
Get the counters info from database.
78+
"""
79+
assert False, 'Need to override this method'
80+
81+
def cnstat_print(self, cnstat_dict, errors_only=False):
82+
"""
83+
Print the counter stat.
84+
"""
85+
assert False, 'Need to override this method'
86+
87+
PortStat = namedtuple("PortStat", "in_cell, in_octet, out_cell, out_octet,\
88+
crc, fec_correctable, fec_uncorrectable, symbol_err")
89+
port_counter_bucket_list = [
90+
'SAI_PORT_STAT_IF_IN_FABRIC_DATA_UNITS',
91+
'SAI_PORT_STAT_IF_IN_OCTETS',
92+
'SAI_PORT_STAT_IF_OUT_FABRIC_DATA_UNITS',
93+
'SAI_PORT_STAT_IF_OUT_OCTETS',
94+
'SAI_PORT_STAT_IF_IN_ERRORS',
95+
'SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES',
96+
'SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES',
97+
'SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS',
98+
]
99+
port_counter_bucket_dict = {k : v for k, v in enumerate(port_counter_bucket_list)}
100+
101+
portstat_header_all = ['ASIC', 'PORT', 'STATE',
102+
'IN_CELL', 'IN_OCTET', 'OUT_CELL', 'OUT_OCTET',
103+
'CRC', 'FEC_CORRECTABLE', 'FEC_UNCORRECTABLE', 'SYMBOL_ERR']
104+
portstat_header_errors_only = ['ASIC', 'PORT', 'STATE',
105+
'CRC', 'FEC_CORRECTABLE', 'FEC_UNCORRECTABLE', 'SYMBOL_ERR']
106+
107+
class FabricPortStat(FabricStat):
108+
def get_cnstat(self):
109+
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
110+
cnstat_dict = OrderedDict()
111+
if counter_port_name_map is None:
112+
return cnstat_dict
113+
for port_name in natsorted(counter_port_name_map):
114+
cntr = self.get_counters(port_counter_bucket_dict, counter_port_name_map[port_name])
115+
cnstat_dict[port_name] = PortStat._make(cntr)
116+
return cnstat_dict
117+
118+
def cnstat_print(self, cnstat_dict, errors_only=False):
119+
if len(cnstat_dict) == 0:
120+
print("Counters %s empty" % self.namespace)
121+
return
122+
123+
table = []
124+
header = None
125+
asic = multi_asic.get_asic_id_from_name(self.namespace)
126+
for key, data in cnstat_dict.items():
127+
port_id = key[len(PORT_NAME_PREFIX):]
128+
if errors_only:
129+
header = portstat_header_errors_only
130+
table.append((asic, port_id, self.get_port_state(key),
131+
data.crc, data.fec_correctable, data.fec_uncorrectable,
132+
data.symbol_err))
133+
else:
134+
header = portstat_header_all
135+
table.append((asic, port_id, self.get_port_state(key),
136+
data.in_cell, data.in_octet, data.out_cell, data.out_octet,
137+
data.crc, data.fec_correctable, data.fec_uncorrectable,
138+
data.symbol_err))
139+
140+
print(tabulate(table, header, tablefmt='simple', stralign='right'))
141+
print()
142+
143+
QueueStat = namedtuple("QueueStat", "curlevel, watermarklevel, curbyte")
144+
145+
queue_counter_bucket_list = [
146+
'SAI_QUEUE_STAT_CURR_OCCUPANCY_LEVEL',
147+
'SAI_QUEUE_STAT_WATERMARK_LEVEL',
148+
'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES',
149+
]
150+
queue_counter_bucket_dict = {k : v for k, v in enumerate(queue_counter_bucket_list)}
151+
152+
queuestat_header = ['ASIC', 'PORT', 'STATE', 'QUEUE_ID', 'CURRENT_BYTE', 'CURRENT_LEVEL', 'WATERMARK_LEVEL']
153+
154+
class FabricQueueStat(FabricStat):
155+
def get_cnstat(self):
156+
counter_queue_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_QUEUE_NAME_MAP)
157+
cnstat_dict = OrderedDict()
158+
if counter_queue_name_map is None:
159+
return cnstat_dict
160+
for port_queue_name in natsorted(counter_queue_name_map):
161+
cntr = self.get_counters(queue_counter_bucket_dict, counter_queue_name_map[port_queue_name])
162+
cnstat_dict[port_queue_name] = QueueStat._make(cntr)
163+
return cnstat_dict
164+
165+
def cnstat_print(self, cnstat_dict, errors_only=False):
166+
if len(cnstat_dict) == 0:
167+
print("Counters %s empty" % self.namespace)
168+
return
169+
170+
table = []
171+
asic = multi_asic.get_asic_id_from_name(self.namespace)
172+
for key, data in cnstat_dict.items():
173+
port_name, queue_id = key.split(':')
174+
port_id = port_name[len(PORT_NAME_PREFIX):]
175+
table.append((asic, port_id, self.get_port_state(port_name), queue_id,
176+
data.curbyte, data.curlevel, data.watermarklevel))
177+
178+
print(tabulate(table, queuestat_header, tablefmt='simple', stralign='right'))
179+
print()
180+
181+
def main():
182+
parser = argparse.ArgumentParser(description='Display the fabric port state and counters',
183+
formatter_class=argparse.RawTextHelpFormatter,
184+
epilog="""
185+
Examples:
186+
fabricstat
187+
fabricstat --namespace asic0
188+
fabricstat -p -n asic0 -e
189+
fabricstat -q
190+
fabricstat -q -n asic0
191+
""")
192+
193+
parser.add_argument('-q','--queue', action='store_true', help='Display fabric queue stat, otherwise port stat')
194+
parser.add_argument('-n','--namespace', default=None, help='Display fabric ports counters for specific namespace')
195+
parser.add_argument('-e', '--errors', action='store_true', help='Display errors')
196+
197+
args = parser.parse_args()
198+
queue = args.queue
199+
namespace = args.namespace
200+
errors_only = args.errors
201+
202+
def nsStat(ns, errors_only):
203+
stat = FabricQueueStat(ns) if queue else FabricPortStat(ns)
204+
cnstat_dict = stat.get_cnstat_dict()
205+
stat.cnstat_print(cnstat_dict, errors_only)
206+
207+
if namespace is None:
208+
# All asics or all fabric asics
209+
multi_asic = multi_asic_util.MultiAsic()
210+
for ns in multi_asic.get_ns_list_based_on_options():
211+
nsStat(ns, errors_only)
212+
else:
213+
# Asic with namespace
214+
nsStat(namespace, errors_only)
215+
216+
if __name__ == "__main__":
217+
main()

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
'scripts/dropstat',
9898
'scripts/dump_nat_entries.py',
9999
'scripts/ecnconfig',
100+
'scripts/fabricstat',
100101
'scripts/fanshow',
101102
'scripts/fast-reboot',
102103
'scripts/fast-reboot-dump.py',

show/fabric.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import click
2+
3+
import utilities_common.multi_asic as multi_asic_util
4+
import utilities_common.cli as clicommon
5+
6+
@click.group(cls=clicommon.AliasedGroup)
7+
def fabric():
8+
"""Show fabric information"""
9+
pass
10+
11+
@fabric.group(invoke_without_command=True)
12+
def counters():
13+
"""Show fabric port counters"""
14+
pass
15+
16+
@counters.command()
17+
@multi_asic_util.multi_asic_click_option_namespace
18+
@click.option('-e', '--errors', is_flag=True)
19+
def port(namespace, errors):
20+
"""Show fabric port stat"""
21+
cmd = "fabricstat"
22+
if namespace is not None:
23+
cmd += " -n {}".format(namespace)
24+
if errors:
25+
cmd += " -e"
26+
clicommon.run_command(cmd)
27+
28+
@counters.command()
29+
@multi_asic_util.multi_asic_click_option_namespace
30+
def queue(namespace):
31+
"""Show fabric queue stat"""
32+
cmd = "fabricstat -q"
33+
if namespace is not None:
34+
cmd += " -n {}".format(namespace)
35+
clicommon.run_command(cmd)

show/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from . import bgp_common
4242
from . import chassis_modules
4343
from . import dropcounters
44+
from . import fabric
4445
from . import feature
4546
from . import fgnhg
4647
from . import flow_counters
@@ -263,6 +264,7 @@ def cli(ctx):
263264
cli.add_command(acl.acl)
264265
cli.add_command(chassis_modules.chassis)
265266
cli.add_command(dropcounters.dropcounters)
267+
cli.add_command(fabric.fabric)
266268
cli.add_command(feature.feature)
267269
cli.add_command(fgnhg.fgnhg)
268270
cli.add_command(flow_counters.flowcnt_route)

0 commit comments

Comments
 (0)