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

[action] [PR:3322] [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) #3366

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading