Skip to content
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
12 changes: 12 additions & 0 deletions clear/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ def queuecounters():
command = ["queuestat", "-c"]
run_command(command)

@cli.command()
def fabriccountersqueue():
"""Clear fabric queue counters"""
command = ["fabricstat", "-C", "-q"]
run_command(command)

@cli.command()
def fabriccountersport():
"""Clear fabric port counters"""
command = ["fabricstat", "-C"]
run_command(command)

@cli.command()
def pfccounters():
"""Clear pfc counters"""
Expand Down
136 changes: 129 additions & 7 deletions scripts/fabricstat
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import argparse
from collections import OrderedDict, namedtuple
import json
import os
import sys

from utilities_common import constants
from utilities_common.cli import json_serial, UserCache
from utilities_common.netstat import format_number_with_comma, table_as_json, ns_diff, format_prate
from natsort import natsorted
from tabulate import tabulate
from sonic_py_common import multi_asic
Expand All @@ -32,6 +35,10 @@ FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
FABRIC_PORT_STATUS_FIELD = "STATUS"
STATUS_NA = 'N/A'

cnstat_dir = 'N/A'
cnstat_fqn_file_port = 'N/A'
cnstat_fqn_file_queue = 'N/A'

class FabricStat(object):
def __init__(self, namespace):
self.db = None
Expand Down Expand Up @@ -78,6 +85,12 @@ class FabricStat(object):
"""
assert False, 'Need to override this method'

def save_fresh_stats(self):
"""
Get stat for each port and save.
"""
assert False, 'Need to override this method'

def cnstat_print(self, cnstat_dict, errors_only=False):
"""
Print the counter stat.
Expand Down Expand Up @@ -115,6 +128,25 @@ class FabricPortStat(FabricStat):
cnstat_dict[port_name] = PortStat._make(cntr)
return cnstat_dict

def save_fresh_stats(self):
# Get stat for each port and save
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
if counter_port_name_map is None:
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

counter_port_name_map

I do not see how counter_port_name_map is used in this function. Is it really needed. Do you want to print some info before return? #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print a message before the return.

print("No counters require cleaning")
return
cnstat_dict = self.get_cnstat()
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)
try:
cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
json.dump(cnstat_dict, open(cnstat_fqn_file_port_name, 'w'), default=json_serial)
except IOError as e:
print(e.errno, e)
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print

print to stderr. #WontFix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change it, just that I see other commands print the same IOError like this, so I was trying to get the same style as those. Please let me know if we still want to print to stderr or like this. thank you

sys.exit(e.errno)
else:
print("Clear and update saved counters port")

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
Expand All @@ -127,19 +159,44 @@ class FabricPortStat(FabricStat):
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)

cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
cnstat_cached_dict = {}
if os.path.isfile(cnstat_fqn_file_port_name):
try:
cnstat_cached_dict = json.load(open(cnstat_fqn_file_port_name, 'r'))
except IOError as e:
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except

When exception is caught, do you want to exit earlier? #WontFix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file not exist, I think we can still print out the counters ( with no cleaned ) , instead of print out nothing .
I read the clean code for other counters ( not exactly the same code piece as what I wrote ), but when they don't have the cached file, it still print out the uncleared counters. Please suggest if this behavior is ok or you want to print out nothing in this case. thank you

print(e.errno, e)
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print

print to stderr. #WontFix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change it, just that I see other commands print the same IOError like this, so I was trying to get the same style as those. Please let me know if we still want to print to stderr or like this. thank you


for key, data in cnstat_dict.items():
port_id = key[len(PORT_NAME_PREFIX):]
port_name = "PORT" + port_id
# The content in the for each port:
# "IN_CELL, IN_OCTET, OUT_CELL, OUT_OCTET, CRC, FEC_CORRECTABLE, FEC_UNCORRECTABL, SYMBOL_ERR"
# e.g. PORT76 ['0', '0', '36', '6669', '0', '13', '302626', '3']
# Now, set default saved values to 0
diff_cached = ['0', '0', '0', '0', '0', '0', '0', '0']
if port_name in cnstat_cached_dict:
diff_cached = cnstat_cached_dict.get(port_name)

if errors_only:
header = portstat_header_errors_only
table.append((asic_name, port_id, self.get_port_state(key),
data.crc, data.fec_correctable, data.fec_uncorrectable,
data.symbol_err))
ns_diff(data.crc, diff_cached[4]),
ns_diff(data.fec_correctable, diff_cached[5]),
ns_diff(data.fec_uncorrectable, diff_cached[6]),
ns_diff(data.symbol_err, diff_cached[7])))
else:
header = portstat_header_all
table.append((asic_name, port_id, self.get_port_state(key),
data.in_cell, data.in_octet, data.out_cell, data.out_octet,
data.crc, data.fec_correctable, data.fec_uncorrectable,
data.symbol_err))
ns_diff(data.in_cell, diff_cached[0]),
ns_diff(data.in_octet, diff_cached[1]),
ns_diff(data.out_cell, diff_cached[2]),
ns_diff(data.out_octet, diff_cached[3]),
ns_diff(data.crc, diff_cached[4]),
ns_diff(data.fec_correctable, diff_cached[5]),
ns_diff(data.fec_uncorrectable, diff_cached[6]),
ns_diff(data.symbol_err, diff_cached[7])))

print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()
Expand All @@ -166,6 +223,25 @@ class FabricQueueStat(FabricStat):
cnstat_dict[port_queue_name] = QueueStat._make(cntr)
return cnstat_dict

def save_fresh_stats(self):
# Get stat for each port and save
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
if counter_port_name_map is None:
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

counter_port_name_map

not used. is it needed? if yes, do you want to print some info before return? #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printed out a message before the return

print("No counters require cleaning")
return
cnstat_dict = self.get_cnstat()
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)
try:
cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
json.dump(cnstat_dict, open(cnstat_fqn_file_queue_name, 'w'), default=json_serial)
except IOError as e:
print(e.errno, e)
sys.exit(e.errno)
else:
print("Clear and update saved counters queue")

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
Expand All @@ -177,11 +253,29 @@ class FabricQueueStat(FabricStat):
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)

cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
cnstat_cached_dict={}
if os.path.isfile(cnstat_fqn_file_queue_name):
try:
cnstat_cached_dict = json.load(open(cnstat_fqn_file_queue_name, 'r'))
except IOError as e:
Copy link
Copy Markdown
Contributor

@qiluo-msft qiluo-msft Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except

do you want to return earlier if exception caught? #WontFix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file not exist, I think we can still print out the counters ( with no cleaned ) , instead of print out nothing .
I read the clean code for other counters ( not exactly the same code piece as what I wrote ), but when they don't have the cached file, it still print out the uncleared counters. Please suggest if this behavior is ok or you want to print out nothing in this case. thank you

print(e.errno, e)

for key, data in cnstat_dict.items():
port_name, queue_id = key.split(':')
# The content of saved counters queue for each port:
# portName:queueId CURRENT_LEVEL, WATERMARK_LEVEL, CURRENT_BYTE
# e.g. PORT90:0 ['N/A', 'N/A', 'N/A']
# Now, set default saved values to 0
diff_cached = ['0', '0', '0']
if key in cnstat_cached_dict:
diff_cached = cnstat_cached_dict.get(key)
port_id = port_name[len(PORT_NAME_PREFIX):]
table.append((asic_name, port_id, self.get_port_state(port_name), queue_id,
data.curbyte, data.curlevel, data.watermarklevel))
ns_diff(data.curbyte, diff_cached[2]),
ns_diff(data.curlevel, diff_cached[0]),
ns_diff(data.watermarklevel, diff_cached[1])))

print(tabulate(table, queuestat_header, tablefmt='simple', stralign='right'))
print()
Expand Down Expand Up @@ -214,6 +308,10 @@ class FabricReachability(FabricStat):
return

def main():
global cnstat_dir
global cnstat_fqn_file_port
global cnstat_fqn_file_queue

parser = argparse.ArgumentParser(description='Display the fabric port state and counters',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Expand All @@ -223,19 +321,40 @@ Examples:
fabricstat -p -n asic0 -e
fabricstat -q
fabricstat -q -n asic0
fabricstat -C
fabricstat -D
""")

parser.add_argument('-q','--queue', action='store_true', help='Display fabric queue stat, otherwise port stat')
parser.add_argument('-r','--reachability', action='store_true', help='Display reachability, otherwise port stat')
parser.add_argument('-n','--namespace', default=None, help='Display fabric ports counters for specific namespace')
parser.add_argument('-e', '--errors', action='store_true', help='Display errors')
parser.add_argument('-C','--clear', action='store_true', help='Copy & clear fabric counters')
parser.add_argument('-D','--delete', action='store_true', help='Delete saved stats')

args = parser.parse_args()
queue = args.queue
reachability = args.reachability
namespace = args.namespace
errors_only = args.errors

save_fresh_stats = args.clear
delete_stats = args.delete

cache = UserCache()

cnstat_dir = cache.get_directory()

cnstat_file = "fabricstatport"
cnstat_fqn_file_port = os.path.join(cnstat_dir, cnstat_file)

cnstat_file = "fabricstatqueue"
cnstat_fqn_file_queue = os.path.join(cnstat_dir, cnstat_file)

if delete_stats:
cache.remove()
sys.exit(0)

def nsStat(ns, errors_only):
if queue:
stat = FabricQueueStat(ns)
Expand All @@ -246,7 +365,10 @@ Examples:
else:
stat = FabricPortStat(ns)
cnstat_dict = stat.get_cnstat_dict()
stat.cnstat_print(cnstat_dict, errors_only)
if save_fresh_stats:
stat.save_fresh_stats()
else:
stat.cnstat_print(cnstat_dict, errors_only)

if namespace is None:
# All asics or all fabric asics
Expand Down
Loading