22
33import argparse
44from collections import OrderedDict , namedtuple
5+ import json
56import os
67import sys
78
89from utilities_common import constants
10+ from utilities_common .cli import json_serial , UserCache
11+ from utilities_common .netstat import format_number_with_comma , table_as_json , ns_diff , format_prate
912from natsort import natsorted
1013from tabulate import tabulate
1114from sonic_py_common import multi_asic
@@ -32,6 +35,10 @@ FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
3235FABRIC_PORT_STATUS_FIELD = "STATUS"
3336STATUS_NA = 'N/A'
3437
38+ cnstat_dir = 'N/A'
39+ cnstat_fqn_file_port = 'N/A'
40+ cnstat_fqn_file_queue = 'N/A'
41+
3542class FabricStat (object ):
3643 def __init__ (self , namespace ):
3744 self .db = None
@@ -78,6 +85,12 @@ class FabricStat(object):
7885 """
7986 assert False , 'Need to override this method'
8087
88+ def save_fresh_stats (self ):
89+ """
90+ Get stat for each port and save.
91+ """
92+ assert False , 'Need to override this method'
93+
8194 def cnstat_print (self , cnstat_dict , errors_only = False ):
8295 """
8396 Print the counter stat.
@@ -115,6 +128,24 @@ class FabricPortStat(FabricStat):
115128 cnstat_dict [port_name ] = PortStat ._make (cntr )
116129 return cnstat_dict
117130
131+ def save_fresh_stats (self ):
132+ # Get stat for each port and save
133+ counter_port_name_map = self .db .get_all (self .db .COUNTERS_DB , COUNTERS_FABRIC_PORT_NAME_MAP )
134+ if counter_port_name_map is None :
135+ return
136+ cnstat_dict = self .get_cnstat ()
137+ asic_name = '0'
138+ if self .namespace :
139+ asic_name = multi_asic .get_asic_id_from_name (self .namespace )
140+ try :
141+ cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
142+ json .dump (cnstat_dict , open (cnstat_fqn_file_port_name , 'w' ), default = json_serial )
143+ except IOError as e :
144+ print (e .errno , e )
145+ sys .exit (e .errno )
146+ else :
147+ print ("Clear and update saved counters port" )
148+
118149 def cnstat_print (self , cnstat_dict , errors_only = False ):
119150 if len (cnstat_dict ) == 0 :
120151 print ("Counters %s empty" % self .namespace )
@@ -127,19 +158,44 @@ class FabricPortStat(FabricStat):
127158 asic_name = '0'
128159 if self .namespace :
129160 asic_name = multi_asic .get_asic_id_from_name (self .namespace )
161+
162+ cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
163+ cnstat_cached_dict = {}
164+ if os .path .isfile (cnstat_fqn_file_port_name ):
165+ try :
166+ cnstat_cached_dict = json .load (open (cnstat_fqn_file_port_name , 'r' ))
167+ except IOError as e :
168+ print (e .errno , e )
169+
130170 for key , data in cnstat_dict .items ():
131171 port_id = key [len (PORT_NAME_PREFIX ):]
172+ port_name = "PORT" + port_id
173+ # The content in the for each port:
174+ # "IN_CELL, IN_OCTET, OUT_CELL, OUT_OCTET, CRC, FEC_CORRECTABLE, FEC_UNCORRECTABL, SYMBOL_ERR"
175+ # e.g. PORT76 ['0', '0', '36', '6669', '0', '13', '302626', '3']
176+ # Now, set default saved values to 0
177+ diff_cached = ['0' , '0' , '0' , '0' , '0' , '0' , '0' , '0' ]
178+ if port_name in cnstat_cached_dict :
179+ diff_cached = cnstat_cached_dict .get (port_name )
180+
132181 if errors_only :
133182 header = portstat_header_errors_only
134183 table .append ((asic_name , port_id , self .get_port_state (key ),
135- data .crc , data .fec_correctable , data .fec_uncorrectable ,
136- data .symbol_err ))
184+ ns_diff (data .crc , diff_cached [4 ]),
185+ ns_diff (data .fec_correctable , diff_cached [5 ]),
186+ ns_diff (data .fec_uncorrectable , diff_cached [6 ]),
187+ ns_diff (data .symbol_err , diff_cached [7 ])))
137188 else :
138189 header = portstat_header_all
139190 table .append ((asic_name , port_id , self .get_port_state (key ),
140- data .in_cell , data .in_octet , data .out_cell , data .out_octet ,
141- data .crc , data .fec_correctable , data .fec_uncorrectable ,
142- data .symbol_err ))
191+ ns_diff (data .in_cell , diff_cached [0 ]),
192+ ns_diff (data .in_octet , diff_cached [1 ]),
193+ ns_diff (data .out_cell , diff_cached [2 ]),
194+ ns_diff (data .out_octet , diff_cached [3 ]),
195+ ns_diff (data .crc , diff_cached [4 ]),
196+ ns_diff (data .fec_correctable , diff_cached [5 ]),
197+ ns_diff (data .fec_uncorrectable , diff_cached [6 ]),
198+ ns_diff (data .symbol_err , diff_cached [7 ])))
143199
144200 print (tabulate (table , header , tablefmt = 'simple' , stralign = 'right' ))
145201 print ()
@@ -166,6 +222,24 @@ class FabricQueueStat(FabricStat):
166222 cnstat_dict [port_queue_name ] = QueueStat ._make (cntr )
167223 return cnstat_dict
168224
225+ def save_fresh_stats (self ):
226+ # Get stat for each port and save
227+ counter_port_name_map = self .db .get_all (self .db .COUNTERS_DB , COUNTERS_FABRIC_PORT_NAME_MAP )
228+ if counter_port_name_map is None :
229+ return
230+ cnstat_dict = self .get_cnstat ()
231+ asic_name = '0'
232+ if self .namespace :
233+ asic_name = multi_asic .get_asic_id_from_name (self .namespace )
234+ try :
235+ cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
236+ json .dump (cnstat_dict , open (cnstat_fqn_file_queue_name , 'w' ), default = json_serial )
237+ except IOError as e :
238+ print (e .errno , e )
239+ sys .exit (e .errno )
240+ else :
241+ print ("Clear and update saved counters queue" )
242+
169243 def cnstat_print (self , cnstat_dict , errors_only = False ):
170244 if len (cnstat_dict ) == 0 :
171245 print ("Counters %s empty" % self .namespace )
@@ -177,11 +251,29 @@ class FabricQueueStat(FabricStat):
177251 asic_name = '0'
178252 if self .namespace :
179253 asic_name = multi_asic .get_asic_id_from_name (self .namespace )
254+
255+ cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
256+ cnstat_cached_dict = {}
257+ if os .path .isfile (cnstat_fqn_file_queue_name ):
258+ try :
259+ cnstat_cached_dict = json .load (open (cnstat_fqn_file_queue_name , 'r' ))
260+ except IOError as e :
261+ print (e .errno , e )
262+
180263 for key , data in cnstat_dict .items ():
181264 port_name , queue_id = key .split (':' )
265+ # The content of saved counters queue for each port:
266+ # portName:queueId CURRENT_LEVEL, WATERMARK_LEVEL, CURRENT_BYTE
267+ # e.g. PORT90:0 ['N/A', 'N/A', 'N/A']
268+ # Now, set default saved values to 0
269+ diff_cached = ['0' , '0' , '0' ]
270+ if key in cnstat_cached_dict :
271+ diff_cached = cnstat_cached_dict .get (key )
182272 port_id = port_name [len (PORT_NAME_PREFIX ):]
183273 table .append ((asic_name , port_id , self .get_port_state (port_name ), queue_id ,
184- data .curbyte , data .curlevel , data .watermarklevel ))
274+ ns_diff (data .curbyte , diff_cached [2 ]),
275+ ns_diff (data .curlevel , diff_cached [0 ]),
276+ ns_diff (data .watermarklevel , diff_cached [1 ])))
185277
186278 print (tabulate (table , queuestat_header , tablefmt = 'simple' , stralign = 'right' ))
187279 print ()
@@ -214,6 +306,10 @@ class FabricReachability(FabricStat):
214306 return
215307
216308def main ():
309+ global cnstat_dir
310+ global cnstat_fqn_file_port
311+ global cnstat_fqn_file_queue
312+
217313 parser = argparse .ArgumentParser (description = 'Display the fabric port state and counters' ,
218314 formatter_class = argparse .RawTextHelpFormatter ,
219315 epilog = """
@@ -223,19 +319,40 @@ Examples:
223319 fabricstat -p -n asic0 -e
224320 fabricstat -q
225321 fabricstat -q -n asic0
322+ fabricstat -C
323+ fabricstat -D
226324""" )
227325
228326 parser .add_argument ('-q' ,'--queue' , action = 'store_true' , help = 'Display fabric queue stat, otherwise port stat' )
229327 parser .add_argument ('-r' ,'--reachability' , action = 'store_true' , help = 'Display reachability, otherwise port stat' )
230328 parser .add_argument ('-n' ,'--namespace' , default = None , help = 'Display fabric ports counters for specific namespace' )
231329 parser .add_argument ('-e' , '--errors' , action = 'store_true' , help = 'Display errors' )
330+ parser .add_argument ('-C' ,'--clear' , action = 'store_true' , help = 'Copy & clear fabric counters' )
331+ parser .add_argument ('-D' ,'--delete' , action = 'store_true' , help = 'Delete saved stats' )
232332
233333 args = parser .parse_args ()
234334 queue = args .queue
235335 reachability = args .reachability
236336 namespace = args .namespace
237337 errors_only = args .errors
238338
339+ save_fresh_stats = args .clear
340+ delete_stats = args .delete
341+
342+ cache = UserCache ()
343+
344+ cnstat_dir = cache .get_directory ()
345+
346+ cnstat_file = "fabricstatport"
347+ cnstat_fqn_file_port = os .path .join (cnstat_dir , cnstat_file )
348+
349+ cnstat_file = "fabricstatqueue"
350+ cnstat_fqn_file_queue = os .path .join (cnstat_dir , cnstat_file )
351+
352+ if delete_stats :
353+ cache .remove ()
354+ sys .exit (0 )
355+
239356 def nsStat (ns , errors_only ):
240357 if queue :
241358 stat = FabricQueueStat (ns )
@@ -246,7 +363,10 @@ Examples:
246363 else :
247364 stat = FabricPortStat (ns )
248365 cnstat_dict = stat .get_cnstat_dict ()
249- stat .cnstat_print (cnstat_dict , errors_only )
366+ if save_fresh_stats :
367+ stat .save_fresh_stats ()
368+ else :
369+ stat .cnstat_print (cnstat_dict , errors_only )
250370
251371 if namespace is None :
252372 # All asics or all fabric asics
0 commit comments