Skip to content

Commit 4133ef5

Browse files
[chassis][voq] Added support for Voq Counters(SAI_SWITCH_STAT_PACKET_INTEGRITY_DROP,SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS) for Voq/Fabric switches (#3322)
What I did Added cli support to show SAI_SWITCH_STAT_PACKET_INTEGRITY_DROP counter in show dropcounter counts command and show SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS counters in show queue counter --voq command. How I did it Modified the dropstat and queuestat cli commands to show these new counters How to verify it Simulated the Packet integrity (CRC, RQP errors) and Credit Watchdog delete drops (disabled the TX for the ports and simulated the credit watchdog deletes) and verified that the show commands are showing the correct output from COUNTERS_DB. Previous command output (if the output of a command-line utility has changed) New command output (if the output of a command-line utility has changed) 1)show dropcounter counts 2)show queue counter --voq Signed-off-by: saksarav <[email protected]>
1 parent f14ed66 commit 4133ef5

File tree

6 files changed

+376
-174
lines changed

6 files changed

+376
-174
lines changed

scripts/dropconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class DropConfig(object):
105105
if supported_reasons and int(capabilities.get('count', 0)) > 0:
106106
print('\n{}'.format(counter))
107107
for reason in supported_reasons:
108-
print('\t{}'.format(reason))
108+
print(' {}'.format(reason))
109109

110110
def create_counter(self, counter_name, alias, group, counter_type,
111111
description, reasons):

scripts/dropstat

+76-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ DEBUG_COUNTER_PORT_STAT_MAP = 'COUNTERS_DEBUG_NAME_PORT_STAT_MAP'
4343
DEBUG_COUNTER_SWITCH_STAT_MAP = 'COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP'
4444
COUNTERS_PORT_NAME_MAP = 'COUNTERS_PORT_NAME_MAP'
4545
COUNTER_TABLE_PREFIX = 'COUNTERS:'
46+
SWITCH_LEVEL_COUNTER_PREFIX = 'SWITCH_STD_DROP_COUNTER-'
4647

4748
# ASIC_DB Tables
4849
ASIC_SWITCH_INFO_PREFIX = 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH:'
@@ -79,6 +80,10 @@ std_port_headers_map = {
7980
# Standard Switch-Level Headers
8081
std_switch_description_header = ['DEVICE']
8182

83+
std_switch_dflt_drop_headers= [ 'SWITCH-ID']
84+
std_switch_drop_headers_map = {
85+
'SAI_SWITCH_STAT_PACKET_INTEGRITY_DROP': 'PKT_INTEGRITY_ERR'
86+
}
8287

8388
def get_dropstat_dir():
8489
return UserCache().get_directory()
@@ -93,10 +98,12 @@ class DropStat(object):
9398
self.db.connect(self.db.COUNTERS_DB)
9499
self.db.connect(self.db.ASIC_DB)
95100
self.db.connect(self.db.APPL_DB)
101+
self.db.connect(self.db.CONFIG_DB)
96102

97103
dropstat_dir = get_dropstat_dir()
98104
self.port_drop_stats_file = os.path.join(dropstat_dir, 'port-stats')
99105
self.switch_drop_stats_file = os.path.join(dropstat_dir + 'switch-stats')
106+
self.switch_std_drop_stats_file = os.path.join(dropstat_dir, 'switch-std-drop-stats')
100107

101108
self.stat_lookup = {}
102109
self.reverse_stat_lookup = {}
@@ -107,6 +114,7 @@ class DropStat(object):
107114
switch-level.
108115
"""
109116

117+
self.show_switch_std_drop_counts(group, counter_type)
110118
self.show_port_drop_counts(group, counter_type)
111119
print('')
112120
self.show_switch_drop_counts(group, counter_type)
@@ -119,13 +127,68 @@ class DropStat(object):
119127
try:
120128
json.dump(self.get_counts_table(self.gather_counters(std_port_rx_counters + std_port_tx_counters, DEBUG_COUNTER_PORT_STAT_MAP), COUNTERS_PORT_NAME_MAP),
121129
open(self.port_drop_stats_file, 'w+'))
122-
json.dump(self.get_counts(self.gather_counters([], DEBUG_COUNTER_SWITCH_STAT_MAP), self.get_switch_id()),
123-
open(self.switch_drop_stats_file, 'w+'))
130+
counters = self.gather_counters([], DEBUG_COUNTER_SWITCH_STAT_MAP)
131+
if counters:
132+
json.dump(self.get_counts(counters, self.get_switch_id()), open(self.switch_drop_stats_file, 'w+'))
133+
134+
counters = self.get_configured_counters(DEBUG_COUNTER_SWITCH_STAT_MAP, True)
135+
if counters:
136+
json.dump(self.get_counts(counters, self.get_switch_id()), open(self.switch_std_drop_stats_file, 'w+'))
124137
except IOError as e:
125138
print(e)
126139
sys.exit(e.errno)
127140
print("Cleared drop counters")
128141

142+
def show_switch_std_drop_counts(self, group, counter_type):
143+
"""
144+
Prints out the standard drop counts (packet integrity drop etc) at the switch level, if such counts exist.
145+
"""
146+
147+
if group is not None or counter_type is not None:
148+
return
149+
150+
#Currently the switch drop counter (packet integrity) is supported only for chassis.
151+
if os.environ.get("VOQ_DROP_COUNTER_TESTING", "0") == "1":
152+
#fake the switch_type for mock-test code coverage
153+
switch_type = "voq"
154+
else:
155+
switch_type = self.db.get(self.db.CONFIG_DB, "DEVICE_METADATA|localhost", "switch_type")
156+
157+
if switch_type is None:
158+
return
159+
if switch_type != "fabric" and switch_type != "voq":
160+
return
161+
162+
switch_std_drop_ckpt = {}
163+
164+
# Grab the latest clear checkpoint, if it exists
165+
if os.path.isfile(self.switch_std_drop_stats_file):
166+
switch_std_drop_ckpt = json.load(open(self.switch_std_drop_stats_file, 'r'))
167+
168+
counters = self.get_configured_counters(DEBUG_COUNTER_SWITCH_STAT_MAP, True)
169+
if not counters:
170+
return
171+
switch_id = self.get_switch_id()
172+
switch_std_stats = self.get_counts(counters, switch_id)
173+
174+
if not switch_std_stats:
175+
return
176+
177+
if os.environ.get("VOQ_DROP_COUNTER_TESTING", "0") == "1":
178+
row = [socket.gethostname()]
179+
else:
180+
cfg_switch_id = self.db.get(self.db.CONFIG_DB, "DEVICE_METADATA|localhost", "switch_id")
181+
row = [cfg_switch_id]
182+
183+
headers = std_switch_dflt_drop_headers
184+
for cntr in counters:
185+
if cntr in std_switch_drop_headers_map:
186+
row.append(switch_std_stats.get(cntr, 0) - switch_std_drop_ckpt.get(cntr, 0))
187+
headers.append(std_switch_drop_headers_map[cntr])
188+
if row:
189+
print(tabulate([row], headers, tablefmt='simple', stralign='right'))
190+
print('')
191+
129192
def show_port_drop_counts(self, group, counter_type):
130193
"""
131194
Prints out the drop counts at the port level, if such counts exist.
@@ -189,7 +252,7 @@ class DropStat(object):
189252
the group or not the right counter type.
190253
"""
191254

192-
configured_counters = self.get_configured_counters(object_stat_map)
255+
configured_counters = self.get_configured_counters(object_stat_map, False)
193256
counters = std_counters + configured_counters
194257
return [ctr for ctr in counters
195258
if self.in_group(ctr, object_stat_map, group) and
@@ -282,7 +345,7 @@ class DropStat(object):
282345

283346
return self.reverse_stat_lookup[object_stat_map]
284347

285-
def get_configured_counters(self, object_stat_map):
348+
def get_configured_counters(self, object_stat_map, std_switch_cntr=False):
286349
"""
287350
Returns the list of counters that have been configured to
288351
track packet drops.
@@ -294,6 +357,15 @@ class DropStat(object):
294357
if not counters:
295358
return configured_counters
296359

360+
#Switch level standard drop counters are added by default and added to DEBUG_COUNTER_SWITCH_STAT_MAP table,
361+
#so remove it from configrued counters
362+
if object_stat_map == DEBUG_COUNTER_SWITCH_STAT_MAP:
363+
if std_switch_cntr:
364+
new_cntrs = {k:counters[k] for k in counters if SWITCH_LEVEL_COUNTER_PREFIX in k}
365+
else:
366+
new_cntrs = {k:counters[k] for k in counters if not SWITCH_LEVEL_COUNTER_PREFIX in k}
367+
return list(new_cntrs.values())
368+
297369
return list(counters.values())
298370

299371
def get_counter_name(self, object_stat_map, counter_stat):

scripts/queuestat

+79-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python3
1+
#!/usr/bin/python3
22

33
#####################################################################
44
#
@@ -38,15 +38,19 @@ from utilities_common import constants
3838
import utilities_common.multi_asic as multi_asic_util
3939

4040
QueueStats = namedtuple("QueueStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes")
41+
VoqStats = namedtuple("VoqStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes, creditWDpkts")
4142
header = ['Port', 'TxQ', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes']
42-
voq_header = ['Port', 'Voq', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes']
43+
voq_header = ['Port', 'Voq', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes', 'Credit-WD-Del/pkts']
4344

4445
counter_bucket_dict = {
4546
'SAI_QUEUE_STAT_PACKETS': 2,
4647
'SAI_QUEUE_STAT_BYTES': 3,
4748
'SAI_QUEUE_STAT_DROPPED_PACKETS': 4,
4849
'SAI_QUEUE_STAT_DROPPED_BYTES': 5,
4950
}
51+
voq_counter_bucket_dict = {
52+
'SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS': 6
53+
}
5054

5155
from utilities_common.cli import json_dump
5256
from utilities_common.netstat import ns_diff, STATUS_NA
@@ -73,15 +77,24 @@ cnstat_dir = 'N/A'
7377
cnstat_fqn_file = 'N/A'
7478

7579

76-
def build_json(port, cnstat):
80+
def build_json(port, cnstat, voq=False):
7781
def ports_stats(k):
7882
p = {}
79-
p[k[1]] = {
80-
"totalpacket": k[2],
81-
"totalbytes": k[3],
82-
"droppacket": k[4],
83-
"dropbytes": k[5]
84-
}
83+
if voq:
84+
p[k[1]] = {
85+
"totalpacket": k[2],
86+
"totalbytes": k[3],
87+
"droppacket": k[4],
88+
"dropbytes": k[5],
89+
"creditWDPkts": k[6]
90+
}
91+
else:
92+
p[k[1]] = {
93+
"totalpacket": k[2],
94+
"totalbytes": k[3],
95+
"droppacket": k[4],
96+
"dropbytes": k[5]
97+
}
8598
return p
8699

87100
out = {}
@@ -175,18 +188,30 @@ class Queuestat(object):
175188
print("Queue Type is invalid:", table_id, queue_type)
176189
sys.exit(1)
177190

178-
fields = ["0","0","0","0","0","0"]
191+
if self.voq:
192+
fields = ["0","0","0","0","0","0","0"]
193+
else:
194+
fields = ["0","0","0","0","0","0"]
179195
fields[0] = get_queue_index(table_id)
180196
fields[1] = get_queue_type(table_id)
181197

182-
for counter_name, pos in counter_bucket_dict.items():
198+
counter_dict = {}
199+
counter_dict.update(counter_bucket_dict)
200+
if self.voq:
201+
counter_dict.update(voq_counter_bucket_dict)
202+
203+
for counter_name, pos in counter_dict.items():
183204
full_table_id = COUNTER_TABLE_PREFIX + table_id
184205
counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name)
185206
if counter_data is None:
186207
fields[pos] = STATUS_NA
187208
elif fields[pos] != STATUS_NA:
188209
fields[pos] = str(int(counter_data))
189-
cntr = QueueStats._make(fields)._asdict()
210+
211+
if self.voq:
212+
cntr = VoqStats._make(fields)._asdict()
213+
else:
214+
cntr = QueueStats._make(fields)._asdict()
190215
return cntr
191216

192217
# Build a dictionary of the stats
@@ -211,14 +236,21 @@ class Queuestat(object):
211236
if json_opt:
212237
json_output[port][key] = data
213238
continue
214-
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
215-
data['droppacket'] != '0' or data['dropbytes'] != '0':
216-
table.append((port, data['queuetype'] + str(data['queueindex']),
217-
data['totalpacket'], data['totalbytes'],
218-
data['droppacket'], data['dropbytes']))
239+
if self.voq:
240+
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
241+
data['droppacket'] != '0' or data['dropbytes'] != '0' or data['creditWDpkts'] != '0':
242+
table.append((port, data['queuetype'] + str(data['queueindex']),
243+
data['totalpacket'], data['totalbytes'],
244+
data['droppacket'], data['dropbytes'], data['creditWDpkts']))
245+
else:
246+
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
247+
data['droppacket'] != '0' or data['dropbytes'] != '0':
248+
table.append((port, data['queuetype'] + str(data['queueindex']),
249+
data['totalpacket'], data['totalbytes'],
250+
data['droppacket'], data['dropbytes']))
219251

220252
if json_opt:
221-
json_output[port].update(build_json(port, table))
253+
json_output[port].update(build_json(port, table, self.voq))
222254
return json_output
223255
else:
224256
hdr = voq_header if self.voq else header
@@ -242,25 +274,42 @@ class Queuestat(object):
242274
old_cntr = None
243275
if key in cnstat_old_dict:
244276
old_cntr = cnstat_old_dict.get(key)
245-
246277
if old_cntr is not None:
247-
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
278+
if self.voq:
279+
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
280+
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \
281+
ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \
282+
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0' or \
283+
ns_diff(cntr['creditWDpkts'], old_cntr['creditWDpkts']) != '0':
284+
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
285+
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
286+
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
287+
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
288+
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']),
289+
ns_diff(cntr['creditWDpkts'], old_cntr['creditWDpkts'])))
290+
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
291+
cntr['droppacket'] != '0' or cntr['dropbytes'] != '0' or cntr['creditWDpkts'] != '0':
292+
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
293+
cntr['totalpacket'], cntr['totalbytes'],
294+
cntr['droppacket'], cntr['dropbytes'], cntr['creditWDpkts']))
295+
else:
296+
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
248297
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \
249298
ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \
250299
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0':
251-
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
252-
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
253-
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
254-
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
255-
ns_diff(cntr['dropbytes'], old_cntr['dropbytes'])))
256-
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
300+
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
301+
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
302+
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
303+
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
304+
ns_diff(cntr['dropbytes'], old_cntr['dropbytes'])))
305+
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
257306
cntr['droppacket'] != '0' or cntr['dropbytes'] != '0':
258-
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
259-
cntr['totalpacket'], cntr['totalbytes'],
260-
cntr['droppacket'], cntr['dropbytes']))
307+
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
308+
cntr['totalpacket'], cntr['totalbytes'],
309+
cntr['droppacket'], cntr['dropbytes']))
261310

262311
if json_opt:
263-
json_output[port].update(build_json(port, table))
312+
json_output[port].update(build_json(port, table, self.voq))
264313
return json_output
265314
else:
266315
hdr = voq_header if self.voq else header

tests/drops_group_test.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
SWITCH_EGRESS_DROPS 2
2121
2222
PORT_INGRESS_DROPS
23-
IP_HEADER_ERROR
24-
NO_L3_HEADER
23+
IP_HEADER_ERROR
24+
NO_L3_HEADER
2525
2626
SWITCH_EGRESS_DROPS
27-
ACL_ANY
28-
L2_ANY
29-
L3_ANY
27+
ACL_ANY
28+
L2_ANY
29+
L3_ANY
3030
"""
3131

3232
expected_counter_configuration = """\
@@ -56,6 +56,21 @@
5656
sonic_drops_test 1000 0
5757
"""
5858

59+
expected_counts_voq = """\
60+
SWITCH-ID PKT_INTEGRITY_ERR
61+
---------------- -------------------
62+
sonic_drops_test 500
63+
64+
IFACE STATE RX_ERR RX_DROPS TX_ERR TX_DROPS DEBUG_0 DEBUG_2
65+
--------- ------- -------- ---------- -------- ---------- --------- ---------
66+
Ethernet0 D 10 100 0 0 80 20
67+
Ethernet4 N/A 0 1000 0 0 800 100
68+
Ethernet8 N/A 100 10 0 0 10 0
69+
70+
DEVICE SWITCH_DROPS lowercase_counter
71+
---------------- -------------- -------------------
72+
sonic_drops_test 1000 0
73+
"""
5974
expected_counts_with_group = """
6075
DEVICE SWITCH_DROPS
6176
---------------- --------------
@@ -117,6 +132,14 @@ def test_show_counts(self):
117132
print(result.output)
118133
assert result.output == expected_counts
119134

135+
def test_show_counts_voq(self):
136+
runner = CliRunner()
137+
os.environ["VOQ_DROP_COUNTER_TESTING"] = "1"
138+
result = runner.invoke(show.cli.commands["dropcounters"].commands["counts"], [])
139+
os.environ["VOQ_DROP_COUNTER_TESTING"] = "0"
140+
print(result.output)
141+
assert result.output == expected_counts_voq
142+
120143
def test_show_counts_with_group(self):
121144
runner = CliRunner()
122145
result = runner.invoke(show.cli.commands["dropcounters"].commands["counts"], ["-g", "PACKET_DROPS"])

0 commit comments

Comments
 (0)