Skip to content

Commit

Permalink
[chassis][voq] Added support for Voq Counters(SAI_SWITCH_STAT_PACKET_…
Browse files Browse the repository at this point in the history
…INTEGRITY_DROP,SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS) for Voq/Fabric switches (sonic-net#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 <sakthivadivu.saravanaraj@nokia.com>
  • Loading branch information
saksarav-nokia authored and mssonicbld committed Jun 12, 2024
1 parent 676ebe4 commit bb1441b
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 174 deletions.
2 changes: 1 addition & 1 deletion scripts/dropconfig
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class DropConfig(object):
if supported_reasons and int(capabilities.get('count', 0)) > 0:
print('\n{}'.format(counter))
for reason in supported_reasons:
print('\t{}'.format(reason))
print(' {}'.format(reason))

def create_counter(self, counter_name, alias, group, counter_type,
description, reasons):
Expand Down
80 changes: 76 additions & 4 deletions scripts/dropstat
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ DEBUG_COUNTER_PORT_STAT_MAP = 'COUNTERS_DEBUG_NAME_PORT_STAT_MAP'
DEBUG_COUNTER_SWITCH_STAT_MAP = 'COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP'
COUNTERS_PORT_NAME_MAP = 'COUNTERS_PORT_NAME_MAP'
COUNTER_TABLE_PREFIX = 'COUNTERS:'
SWITCH_LEVEL_COUNTER_PREFIX = 'SWITCH_STD_DROP_COUNTER-'

# ASIC_DB Tables
ASIC_SWITCH_INFO_PREFIX = 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH:'
Expand Down Expand Up @@ -79,6 +80,10 @@ std_port_headers_map = {
# Standard Switch-Level Headers
std_switch_description_header = ['DEVICE']

std_switch_dflt_drop_headers= [ 'SWITCH-ID']
std_switch_drop_headers_map = {
'SAI_SWITCH_STAT_PACKET_INTEGRITY_DROP': 'PKT_INTEGRITY_ERR'
}

def get_dropstat_dir():
return UserCache().get_directory()
Expand All @@ -93,10 +98,12 @@ class DropStat(object):
self.db.connect(self.db.COUNTERS_DB)
self.db.connect(self.db.ASIC_DB)
self.db.connect(self.db.APPL_DB)
self.db.connect(self.db.CONFIG_DB)

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

self.stat_lookup = {}
self.reverse_stat_lookup = {}
Expand All @@ -107,6 +114,7 @@ class DropStat(object):
switch-level.
"""

self.show_switch_std_drop_counts(group, counter_type)
self.show_port_drop_counts(group, counter_type)
print('')
self.show_switch_drop_counts(group, counter_type)
Expand All @@ -119,13 +127,68 @@ class DropStat(object):
try:
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),
open(self.port_drop_stats_file, 'w+'))
json.dump(self.get_counts(self.gather_counters([], DEBUG_COUNTER_SWITCH_STAT_MAP), self.get_switch_id()),
open(self.switch_drop_stats_file, 'w+'))
counters = self.gather_counters([], DEBUG_COUNTER_SWITCH_STAT_MAP)
if counters:
json.dump(self.get_counts(counters, self.get_switch_id()), open(self.switch_drop_stats_file, 'w+'))

counters = self.get_configured_counters(DEBUG_COUNTER_SWITCH_STAT_MAP, True)
if counters:
json.dump(self.get_counts(counters, self.get_switch_id()), open(self.switch_std_drop_stats_file, 'w+'))
except IOError as e:
print(e)
sys.exit(e.errno)
print("Cleared drop counters")

def show_switch_std_drop_counts(self, group, counter_type):
"""
Prints out the standard drop counts (packet integrity drop etc) at the switch level, if such counts exist.
"""

if group is not None or counter_type is not None:
return

#Currently the switch drop counter (packet integrity) is supported only for chassis.
if os.environ.get("VOQ_DROP_COUNTER_TESTING", "0") == "1":
#fake the switch_type for mock-test code coverage
switch_type = "voq"
else:
switch_type = self.db.get(self.db.CONFIG_DB, "DEVICE_METADATA|localhost", "switch_type")

if switch_type is None:
return
if switch_type != "fabric" and switch_type != "voq":
return

switch_std_drop_ckpt = {}

# Grab the latest clear checkpoint, if it exists
if os.path.isfile(self.switch_std_drop_stats_file):
switch_std_drop_ckpt = json.load(open(self.switch_std_drop_stats_file, 'r'))

counters = self.get_configured_counters(DEBUG_COUNTER_SWITCH_STAT_MAP, True)
if not counters:
return
switch_id = self.get_switch_id()
switch_std_stats = self.get_counts(counters, switch_id)

if not switch_std_stats:
return

if os.environ.get("VOQ_DROP_COUNTER_TESTING", "0") == "1":
row = [socket.gethostname()]
else:
cfg_switch_id = self.db.get(self.db.CONFIG_DB, "DEVICE_METADATA|localhost", "switch_id")
row = [cfg_switch_id]

headers = std_switch_dflt_drop_headers
for cntr in counters:
if cntr in std_switch_drop_headers_map:
row.append(switch_std_stats.get(cntr, 0) - switch_std_drop_ckpt.get(cntr, 0))
headers.append(std_switch_drop_headers_map[cntr])
if row:
print(tabulate([row], headers, tablefmt='simple', stralign='right'))
print('')

def show_port_drop_counts(self, group, counter_type):
"""
Prints out the drop counts at the port level, if such counts exist.
Expand Down Expand Up @@ -189,7 +252,7 @@ class DropStat(object):
the group or not the right counter type.
"""

configured_counters = self.get_configured_counters(object_stat_map)
configured_counters = self.get_configured_counters(object_stat_map, False)
counters = std_counters + configured_counters
return [ctr for ctr in counters
if self.in_group(ctr, object_stat_map, group) and
Expand Down Expand Up @@ -282,7 +345,7 @@ class DropStat(object):

return self.reverse_stat_lookup[object_stat_map]

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

#Switch level standard drop counters are added by default and added to DEBUG_COUNTER_SWITCH_STAT_MAP table,
#so remove it from configrued counters
if object_stat_map == DEBUG_COUNTER_SWITCH_STAT_MAP:
if std_switch_cntr:
new_cntrs = {k:counters[k] for k in counters if SWITCH_LEVEL_COUNTER_PREFIX in k}
else:
new_cntrs = {k:counters[k] for k in counters if not SWITCH_LEVEL_COUNTER_PREFIX in k}
return list(new_cntrs.values())

return list(counters.values())

def get_counter_name(self, object_stat_map, counter_stat):
Expand Down
109 changes: 79 additions & 30 deletions scripts/queuestat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/python3

#####################################################################
#
Expand Down Expand Up @@ -38,15 +38,19 @@ from utilities_common import constants
import utilities_common.multi_asic as multi_asic_util

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

counter_bucket_dict = {
'SAI_QUEUE_STAT_PACKETS': 2,
'SAI_QUEUE_STAT_BYTES': 3,
'SAI_QUEUE_STAT_DROPPED_PACKETS': 4,
'SAI_QUEUE_STAT_DROPPED_BYTES': 5,
}
voq_counter_bucket_dict = {
'SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS': 6
}

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


def build_json(port, cnstat):
def build_json(port, cnstat, voq=False):
def ports_stats(k):
p = {}
p[k[1]] = {
"totalpacket": k[2],
"totalbytes": k[3],
"droppacket": k[4],
"dropbytes": k[5]
}
if voq:
p[k[1]] = {
"totalpacket": k[2],
"totalbytes": k[3],
"droppacket": k[4],
"dropbytes": k[5],
"creditWDPkts": k[6]
}
else:
p[k[1]] = {
"totalpacket": k[2],
"totalbytes": k[3],
"droppacket": k[4],
"dropbytes": k[5]
}
return p

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

fields = ["0","0","0","0","0","0"]
if self.voq:
fields = ["0","0","0","0","0","0","0"]
else:
fields = ["0","0","0","0","0","0"]
fields[0] = get_queue_index(table_id)
fields[1] = get_queue_type(table_id)

for counter_name, pos in counter_bucket_dict.items():
counter_dict = {}
counter_dict.update(counter_bucket_dict)
if self.voq:
counter_dict.update(voq_counter_bucket_dict)

for counter_name, pos in counter_dict.items():
full_table_id = COUNTER_TABLE_PREFIX + table_id
counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name)
if counter_data is None:
fields[pos] = STATUS_NA
elif fields[pos] != STATUS_NA:
fields[pos] = str(int(counter_data))
cntr = QueueStats._make(fields)._asdict()

if self.voq:
cntr = VoqStats._make(fields)._asdict()
else:
cntr = QueueStats._make(fields)._asdict()
return cntr

# Build a dictionary of the stats
Expand All @@ -211,14 +236,21 @@ class Queuestat(object):
if json_opt:
json_output[port][key] = data
continue
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
data['droppacket'] != '0' or data['dropbytes'] != '0':
table.append((port, data['queuetype'] + str(data['queueindex']),
data['totalpacket'], data['totalbytes'],
data['droppacket'], data['dropbytes']))
if self.voq:
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
data['droppacket'] != '0' or data['dropbytes'] != '0' or data['creditWDpkts'] != '0':
table.append((port, data['queuetype'] + str(data['queueindex']),
data['totalpacket'], data['totalbytes'],
data['droppacket'], data['dropbytes'], data['creditWDpkts']))
else:
if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \
data['droppacket'] != '0' or data['dropbytes'] != '0':
table.append((port, data['queuetype'] + str(data['queueindex']),
data['totalpacket'], data['totalbytes'],
data['droppacket'], data['dropbytes']))

if json_opt:
json_output[port].update(build_json(port, table))
json_output[port].update(build_json(port, table, self.voq))
return json_output
else:
hdr = voq_header if self.voq else header
Expand All @@ -242,25 +274,42 @@ class Queuestat(object):
old_cntr = None
if key in cnstat_old_dict:
old_cntr = cnstat_old_dict.get(key)

if old_cntr is not None:
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
if self.voq:
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \
ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0' or \
ns_diff(cntr['creditWDpkts'], old_cntr['creditWDpkts']) != '0':
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']),
ns_diff(cntr['creditWDpkts'], old_cntr['creditWDpkts'])))
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
cntr['droppacket'] != '0' or cntr['dropbytes'] != '0' or cntr['creditWDpkts'] != '0':
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
cntr['totalpacket'], cntr['totalbytes'],
cntr['droppacket'], cntr['dropbytes'], cntr['creditWDpkts']))
else:
if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \
ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \
ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0':
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
ns_diff(cntr['dropbytes'], old_cntr['dropbytes'])))
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
ns_diff(cntr['totalpacket'], old_cntr['totalpacket']),
ns_diff(cntr['totalbytes'], old_cntr['totalbytes']),
ns_diff(cntr['droppacket'], old_cntr['droppacket']),
ns_diff(cntr['dropbytes'], old_cntr['dropbytes'])))
elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \
cntr['droppacket'] != '0' or cntr['dropbytes'] != '0':
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
cntr['totalpacket'], cntr['totalbytes'],
cntr['droppacket'], cntr['dropbytes']))
table.append((port, cntr['queuetype'] + str(cntr['queueindex']),
cntr['totalpacket'], cntr['totalbytes'],
cntr['droppacket'], cntr['dropbytes']))

if json_opt:
json_output[port].update(build_json(port, table))
json_output[port].update(build_json(port, table, self.voq))
return json_output
else:
hdr = voq_header if self.voq else header
Expand Down
33 changes: 28 additions & 5 deletions tests/drops_group_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
SWITCH_EGRESS_DROPS 2
PORT_INGRESS_DROPS
IP_HEADER_ERROR
NO_L3_HEADER
IP_HEADER_ERROR
NO_L3_HEADER
SWITCH_EGRESS_DROPS
ACL_ANY
L2_ANY
L3_ANY
ACL_ANY
L2_ANY
L3_ANY
"""

expected_counter_configuration = """\
Expand Down Expand Up @@ -56,6 +56,21 @@
sonic_drops_test 1000 0
"""

expected_counts_voq = """\
SWITCH-ID PKT_INTEGRITY_ERR
---------------- -------------------
sonic_drops_test 500
IFACE STATE RX_ERR RX_DROPS TX_ERR TX_DROPS DEBUG_0 DEBUG_2
--------- ------- -------- ---------- -------- ---------- --------- ---------
Ethernet0 D 10 100 0 0 80 20
Ethernet4 N/A 0 1000 0 0 800 100
Ethernet8 N/A 100 10 0 0 10 0
DEVICE SWITCH_DROPS lowercase_counter
---------------- -------------- -------------------
sonic_drops_test 1000 0
"""
expected_counts_with_group = """
DEVICE SWITCH_DROPS
---------------- --------------
Expand Down Expand Up @@ -117,6 +132,14 @@ def test_show_counts(self):
print(result.output)
assert result.output == expected_counts

def test_show_counts_voq(self):
runner = CliRunner()
os.environ["VOQ_DROP_COUNTER_TESTING"] = "1"
result = runner.invoke(show.cli.commands["dropcounters"].commands["counts"], [])
os.environ["VOQ_DROP_COUNTER_TESTING"] = "0"
print(result.output)
assert result.output == expected_counts_voq

def test_show_counts_with_group(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["dropcounters"].commands["counts"], ["-g", "PACKET_DROPS"])
Expand Down
Loading

0 comments on commit bb1441b

Please sign in to comment.