diff --git a/scripts/portstat b/scripts/portstat index eae18d39b9b1..9a29fa14e514 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -27,8 +27,11 @@ PORT_RATE = 40 NStats = namedtuple("NStats", "rx_ok, rx_err, rx_drop, rx_ovr, tx_ok,\ tx_err, tx_drop, tx_ovr, rx_byt, tx_byt") -header = ['Iface', 'RX_OK', 'RX_RATE', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR', - 'TX_OK', 'TX_RATE', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR'] +header_all = ['Iface', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR', + 'TX_OK', 'TX_BPS', 'Tx_PPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR'] + +header = ['Iface', 'STATE', 'RX_OK', 'RX_BPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR', + 'TX_OK', 'TX_BPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR'] counter_bucket_dict = { 'SAI_PORT_STAT_IF_IN_UCAST_PKTS': 0, @@ -47,11 +50,14 @@ counter_bucket_dict = { COUNTER_TABLE_PREFIX = "COUNTERS:" COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" +PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" +PORT_STATUS_FIELD = "oper_status" class Portstat(object): def __init__(self): self.db = swsssdk.SonicV2Connector() self.db.connect(self.db.COUNTERS_DB) + self.db.connect(self.db.APPL_DB) def get_cnstat(self): """ @@ -68,7 +74,7 @@ class Portstat(object): if counter_data is None: fields[pos] = "N/A" elif fields[pos] != "N/A": - fields[pos] = str(int(fields[pos]) + int(self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name))) + fields[pos] = str(int(fields[pos]) + int(counter_data)) cntr = NStats._make(fields) return cntr @@ -78,38 +84,76 @@ class Portstat(object): cnstat_dict = OrderedDict() cnstat_dict['time'] = datetime.datetime.now() for port in natsorted(counter_port_name_map): - cnstat_dict[port] = get_counters(counter_port_name_map[port]) - + cnstat_dict[port] = get_counters(counter_port_name_map[port]) return cnstat_dict - def table_as_json(self, table): + def get_port_status(self, port_name): + """ + Get the port status + """ + full_table_id = PORT_STATUS_TABLE_PREFIX + port_name + status = self.db.get(self.db.APPL_DB, full_table_id, PORT_STATUS_FIELD) + if status is None: + return "N/A" + elif status.lower() == 'up': + return "U" + elif status.lower() == 'down': + return "D" + else: + return "N/A" + + def table_as_json(self, table, print_all): """ Print table as json format. """ output = {} - for line in table: - if_name = line[0] - - # Build a dictionary where the if_name is the key and the value is - # a dictionary that holds MTU, TX_DRP, etc - output[if_name] = { - header[1] : line[1], - header[2] : line[2], - header[3] : line[3], - header[4] : line[4], - header[5] : line[5], - header[6] : line[6], - header[7] : line[7], - header[8] : line[8], - header[9] : line[9], - header[10] : line[10], - header[11] : line[11], - header[12] : line[12] + if print_all: + for line in table: + if_name = line[0] + + # Build a dictionary where the if_name is the key and the value is + # a dictionary that holds MTU, TX_DRP, etc + output[if_name] = { + header_all[1] : line[1], + header_all[2] : line[2], + header_all[3] : line[3], + header_all[4] : line[4], + header_all[5] : line[5], + header_all[6] : line[6], + header_all[7] : line[7], + header_all[8] : line[8], + header_all[9] : line[9], + header_all[10] : line[10], + header_all[11] : line[11], + header_all[12] : line[12], + header_all[13] : line[13], + header_all[14] : line[14] + } + else: + for line in table: + if_name = line[0] + + # Build a dictionary where the if_name is the key and the value is + # a dictionary that holds MTU, TX_DRP, etc + output[if_name] = { + header[1] : line[1], + header[2] : line[2], + header[3] : line[3], + header[4] : line[4], + header[5] : line[5], + header[6] : line[6], + header[7] : line[7], + header[8] : line[8], + header[9] : line[9], + header[10] : line[10], + header[11] : line[11], + header[12] : line[12], } + return json.dumps(output, indent=4, sort_keys=True) - def cnstat_print(self, cnstat_dict, use_json): + def cnstat_print(self, cnstat_dict, use_json, print_all): """ Print the cnstat. """ @@ -118,19 +162,22 @@ class Portstat(object): for key, data in cnstat_dict.iteritems(): if key == 'time': continue - table.append((key, - data.rx_ok, "N/A", "N/A", data.rx_err, + table.append((key, self.get_port_status(key), + data.rx_ok, "N/A", "N/A", "N/A", data.rx_err, data.rx_drop, data.rx_ovr, - data.tx_ok, "N/A", "N/A", data.tx_err, + data.tx_ok, "N/A", "N/A", "N/A", data.tx_err, data.tx_drop, data.tx_ovr)) if use_json: - print self.table_as_json(table) + print self.table_as_json(table, print_all) else: - print tabulate(table, header, tablefmt='simple', stralign='right') + if print_all: + print tabulate(table, header_all, tablefmt='simple', stralign='right') + else: + print tabulate(table, header, tablefmt='simple', stralign='right') - def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, use_json): + def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, use_json, print_all): """ Print the difference between two cnstat results. """ @@ -144,9 +191,9 @@ class Portstat(object): new, old = int(newstr), int(oldstr) return '{:,}'.format(new - old) - def ns_rate(newstr, oldstr, delta): + def ns_brate(newstr, oldstr, delta): """ - Calculate the rate. + Calculate the byte rate. """ if newstr == "N/A" or oldstr == "N/A": return "N/A" @@ -160,6 +207,16 @@ class Portstat(object): rate = "{:.2f}".format(rate)+' B' return rate+'/s' + def ns_prate(newstr, oldstr, delta): + """ + Calculate the packet rate. + """ + if newstr == "N/A" or oldstr == "N/A": + return "N/A" + else: + rate = int(ns_diff(newstr, oldstr).replace(',',''))/delta + return "{:.2f}".format(rate)+'/s' + def ns_util(newstr, oldstr, delta): """ Calculate the util. @@ -182,39 +239,77 @@ class Portstat(object): if key in cnstat_old_dict: old_cntr = cnstat_old_dict.get(key) - if old_cntr is not None: - table.append((key, - ns_diff(cntr.rx_ok, old_cntr.rx_ok), - ns_rate(cntr.rx_byt, old_cntr.rx_byt, time_gap), - ns_util(cntr.rx_byt, old_cntr.rx_byt, time_gap), - ns_diff(cntr.rx_err, old_cntr.rx_err), - ns_diff(cntr.rx_drop, old_cntr.rx_drop), - ns_diff(cntr.rx_ovr, old_cntr.rx_ovr), - ns_diff(cntr.tx_ok, old_cntr.tx_ok), - ns_rate(cntr.tx_byt, old_cntr.tx_byt, time_gap), - ns_util(cntr.tx_byt, old_cntr.tx_byt, time_gap), - ns_diff(cntr.tx_err, old_cntr.tx_err), - ns_diff(cntr.tx_drop, old_cntr.tx_drop), - ns_diff(cntr.tx_ovr, old_cntr.tx_ovr))) + if print_all: + if old_cntr is not None: + table.append((key, self.get_port_status(key), + ns_diff(cntr.rx_ok, old_cntr.rx_ok), + ns_brate(cntr.rx_byt, old_cntr.rx_byt, time_gap), + ns_prate(cntr.rx_ok, old_cntr.rx_ok, time_gap), + ns_util(cntr.rx_byt, old_cntr.rx_byt, time_gap), + ns_diff(cntr.rx_err, old_cntr.rx_err), + ns_diff(cntr.rx_drop, old_cntr.rx_drop), + ns_diff(cntr.rx_ovr, old_cntr.rx_ovr), + ns_diff(cntr.tx_ok, old_cntr.tx_ok), + ns_brate(cntr.tx_byt, old_cntr.tx_byt, time_gap), + ns_prate(cntr.tx_ok, old_cntr.tx_ok, time_gap), + ns_util(cntr.tx_byt, old_cntr.tx_byt, time_gap), + ns_diff(cntr.tx_err, old_cntr.tx_err), + ns_diff(cntr.tx_drop, old_cntr.tx_drop), + ns_diff(cntr.tx_ovr, old_cntr.tx_ovr))) + else: + table.append((key, self.get_port_status(key), + cntr.rx_ok, + "N/A", + "N/A", + "N/A", + cntr.rx_err, + cntr.rx_drop, + cntr.rx_ovr, + cntr.tx_ok, + "N/A", + "N/A", + "N/A", + cntr.tx_err, + cntr.tx_drop, + cntr.tx_err)) else: - table.append((key, - cntr.rx_ok, - "N/A", - "N/A", - cntr.rx_err, - cntr.rx_drop, - cntr.rx_ovr, - cntr.tx_ok, - "N/A", - "N/A", - cntr.tx_err, - cntr.tx_drop, - cntr.tx_err)) + if old_cntr is not None: + table.append((key, self.get_port_status(key), + ns_diff(cntr.rx_ok, old_cntr.rx_ok), + ns_brate(cntr.rx_byt, old_cntr.rx_byt, time_gap), + ns_util(cntr.rx_byt, old_cntr.rx_byt, time_gap), + ns_diff(cntr.rx_err, old_cntr.rx_err), + ns_diff(cntr.rx_drop, old_cntr.rx_drop), + ns_diff(cntr.rx_ovr, old_cntr.rx_ovr), + ns_diff(cntr.tx_ok, old_cntr.tx_ok), + ns_brate(cntr.tx_byt, old_cntr.tx_byt, time_gap), + ns_util(cntr.tx_byt, old_cntr.tx_byt, time_gap), + ns_diff(cntr.tx_err, old_cntr.tx_err), + ns_diff(cntr.tx_drop, old_cntr.tx_drop), + ns_diff(cntr.tx_ovr, old_cntr.tx_ovr))) + else: + table.append((key, self.get_port_status(key), + cntr.rx_ok, + "N/A", + "N/A", + cntr.rx_err, + cntr.rx_drop, + cntr.rx_ovr, + cntr.tx_ok, + "N/A", + "N/A", + cntr.tx_err, + cntr.tx_drop, + cntr.tx_err)) if use_json: - print self.table_as_json(table) + print self.table_as_json(table, print_all) else: - print tabulate(table, header, tablefmt='simple', stralign='right') + if print_all: + print tabulate(table, header_all, tablefmt='simple', stralign='right') + else: + print tabulate(table, header, tablefmt='simple', stralign='right') + def main(): parser = argparse.ArgumentParser(description='Wrapper for netstat', @@ -227,6 +322,7 @@ Examples: portstat -d -t test portstat portstat -r + portstat -a portstat -p 20 """) @@ -235,6 +331,7 @@ Examples: parser.add_argument('-D', '--delete-all', action='store_true', help='Delete all saved stats') parser.add_argument('-j', '--json', action='store_true', help='Display in JSON format') parser.add_argument('-r', '--raw', action='store_true', help='Raw stats (unmodified output of netstat)') + parser.add_argument('-a', '--all', action='store_true', help='Display all the stats counters') parser.add_argument('-t', '--tag', type=str, help='Save stats with name TAG', default=None) parser.add_argument('-p', '--period', type=int, help='Display stats over a specified period (in seconds).', default=0) args = parser.parse_args() @@ -247,6 +344,7 @@ Examples: tag_name = args.tag uid = str(os.getuid()) wait_time_in_seconds = args.period + print_all = args.all if not os.geteuid() == 0: raise RuntimeError("must be root to run") @@ -287,7 +385,7 @@ Examples: # Now decide what information to display if raw_stats: - portstat.cnstat_print(cnstat_dict, use_json) + portstat.cnstat_print(cnstat_dict, use_json, print_all) sys.exit(0) # At this point, either we'll create a file or open an existing one. @@ -314,7 +412,7 @@ Examples: try: cnstat_cached_dict = pickle.load(open(cnstat_fqn_file, 'r')) print "Last cached time was " + str(cnstat_cached_dict.get('time')) - portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, use_json) + portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, use_json, print_all) except IOError as e: print e.errno, e else: @@ -322,12 +420,13 @@ Examples: print "\nFile '%s' does not exist" % cnstat_fqn_file print "Did you run 'portstat -c -t %s' to record the counters via tag %s?\n" % (tag_name, tag_name) else: - portstat.cnstat_print(cnstat_dict, use_json) + portstat.cnstat_print(cnstat_dict, use_json, print_all) else: #wait for the specified time and then gather the new stats and output the difference. time.sleep(wait_time_in_seconds) + print "The rates are calculated within %s seconds period" % wait_time_in_seconds cnstat_new_dict = portstat.get_cnstat() - portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, use_json) + portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, use_json, print_all) if __name__ == "__main__": main() diff --git a/sonic_cli/main.py b/sonic_cli/main.py index 68b9b5b89b1b..55324b87f96e 100644 --- a/sonic_cli/main.py +++ b/sonic_cli/main.py @@ -158,7 +158,7 @@ def default(interfacename, sfp): @interfaces.command() def counters(): """Show interface counters""" - run_command("portstat", pager=True) + run_command("portstat -a -p 30", pager=True) # 'portchannel' subcommand @interfaces.command()