From dec5cd87752e7d80129b34d8fd8afed2459a7843 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Tue, 21 Apr 2020 17:33:16 +0300 Subject: [PATCH 1/7] [portstat, intfstat] update rates and utilization Signed-off-by: Mykola Faryma --- config/main.py | 30 +++++++++- scripts/intfstat | 51 ++++++++++++----- scripts/portstat | 111 ++++++++++++------------------------ tests/intfstat_test.py | 8 --- utilities_common/netstat.py | 48 +++++++++++++++- 5 files changed, 151 insertions(+), 97 deletions(-) diff --git a/config/main.py b/config/main.py index 8b9443b20b..51cf3ac39d 100755 --- a/config/main.py +++ b/config/main.py @@ -2946,7 +2946,35 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, verbose): # -# 'pfc' group ('config interface pfc ...') +# 'rate' group ('config rate ...') +# + +@config.group() +def rate(): + """Set port rates configuration.""" + pass + + +@rate.command() +@click.argument('interval', metavar='', type=click.IntRange(min=1, max=1000), required=True) +@click.argument('rates_type', type=click.Choice(['all', 'port', 'rif']), default='all') +def smoothing_interval(interval, rates_type): + """Set rates smoothing interval """ + counters_db = swsssdk.SonicV2Connector() + counters_db.connect('COUNTERS_DB') + + alpha = 2.0/(interval + 1) + + if rates_type in ['port', 'all']: + counters_db.set('COUNTERS_DB', 'RATES:PORT', 'PORT_SMOOTH_INTERVAL', interval) + counters_db.set('COUNTERS_DB', 'RATES:PORT', 'PORT_ALPHA', alpha) + if rates_type in ['rif', 'all']: + counters_db.set('COUNTERS_DB', 'RATES:RIF', 'RIF_SMOOTH_INTERVAL', interval) + counters_db.set('COUNTERS_DB', 'RATES:RIF', 'RIF_ALPHA', alpha) + + +# +# 'pfc' group ('config pfc ...') # @interface.group(cls=clicommon.AbbreviationGroup) diff --git a/scripts/intfstat b/scripts/intfstat index f38f67e55a..266d34235e 100755 --- a/scripts/intfstat +++ b/scripts/intfstat @@ -29,10 +29,11 @@ except KeyError: from collections import namedtuple, OrderedDict from natsort import natsorted from tabulate import tabulate -from utilities_common.netstat import ns_diff, ns_brate, ns_prate, table_as_json, STATUS_NA +from utilities_common.netstat import ns_diff, ns_brate, ns_prate, table_as_json, STATUS_NA, format_brate, format_prate, format_util -NStats = namedtuple("NStats", "rx_b_ok, rx_p_ok, tx_b_ok, tx_p_ok,\ - rx_b_err, rx_p_err, tx_b_err, tx_p_err,") +nstat_fields = ("rx_b_ok", "rx_p_ok", "tx_b_ok", "tx_p_ok",\ + "rx_b_err", "rx_p_err", "tx_b_err", "tx_p_err") +NStats = namedtuple("NStats", nstat_fields) header = ['IFACE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_ERR', 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_ERR'] @@ -75,7 +76,7 @@ class Intfstat(object): """ Get the counters from specific table. """ - fields = [STATUS_NA] * (len(header) - 1) + fields = [STATUS_NA] * len(nstat_fields) for pos, counter_name in enumerate(counter_names): full_table_id = COUNTER_TABLE_PREFIX + table_id counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name) @@ -126,6 +127,24 @@ class Intfstat(object): else: return STATUS_NA + def get_intf_speed(self, port_name): + """ + Get the Intf speed + """ + full_table_id = PORT_STATUS_TABLE_PREFIX + port_name + admin_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_ADMIN_STATUS_FIELD) + oper_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_OPER_STATUS_FIELD) + if admin_state is None or oper_state is None: + return STATUS_NA + elif admin_state.upper() == PORT_STATUS_VALUE_DOWN: + return PORT_STATE_DISABLED + elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_UP: + return PORT_STATE_UP + elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_DOWN: + return PORT_STATE_DOWN + else: + return STATUS_NA + def cnstat_print(self, cnstat_dict, use_json): """ Print the cnstat. @@ -172,15 +191,21 @@ class Intfstat(object): ns_prate(cntr.tx_p_ok, old_cntr.tx_p_ok, time_gap), ns_diff(cntr.tx_p_err, old_cntr.tx_p_err))) else: - table.append((key, - cntr.rx_p_ok, - STATUS_NA, - STATUS_NA, - cntr.rx_p_err, - cntr.tx_p_ok, - STATUS_NA, - STATUS_NA, - cntr.tx_p_err)) + old_cntr = NStats._make([0] * len(nstat_fields)) + + rates = ratestat_dict.get(key, RateStats._make([STATUS_NA]*len(ratestat_fields))) + + table.append((key, + ns_diff(cntr.rx_p_ok, old_cntr.rx_p_ok), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, speed), + ns_diff(cntr.rx_p_err, old_cntr.rx_p_err), + ns_diff(cntr.tx_p_ok, old_cntr.tx_p_ok), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, speed), + ns_diff(cntr.tx_p_err, old_cntr.tx_p_err))) if use_json: print(table_as_json(table, header)) else: diff --git a/scripts/portstat b/scripts/portstat index b34191d0cd..1b0340c48b 100644 --- a/scripts/portstat +++ b/scripts/portstat @@ -17,7 +17,7 @@ import time from collections import namedtuple, OrderedDict from natsort import natsorted from tabulate import tabulate -from utilities_common.netstat import ns_diff, ns_brate, ns_prate, ns_util, table_as_json +from utilities_common.netstat import ns_diff, table_as_json, format_brate, format_prate, format_util from utilities_common.intf_filter import parse_interface_in_filter PORT_RATE = 40 @@ -180,51 +180,32 @@ class Portstat(object): header = None for key, cntr in cnstat_new_dict.iteritems(): - if key == 'time': - time_gap = cnstat_new_dict.get('time') - cnstat_old_dict.get('time') - time_gap = time_gap.total_seconds() - continue old_cntr = None if key in cnstat_old_dict: old_cntr = cnstat_old_dict.get(key) + else: + old_cntr = NStats._make([0] * BUCKET_NUM) + rates = ratestat_dict.get(key, RateStats._make([STATUS_NA] * len(ratestat_fields))) if intf_list and key not in intf_list: continue port_speed = self.get_port_speed(key) if print_all: - header = header_all - if old_cntr is not None: - table.append((key, self.get_port_state(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, port_speed), - 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, port_speed), - 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_state(key), - cntr.rx_ok, - STATUS_NA, - STATUS_NA, - STATUS_NA, - cntr.rx_err, - cntr.rx_drop, - cntr.rx_ovr, - cntr.tx_ok, - STATUS_NA, - STATUS_NA, - STATUS_NA, - cntr.tx_err, - cntr.tx_drop, - cntr.tx_ovr)) + table.append((key, self.get_port_state(key), + ns_diff(cntr.rx_ok, old_cntr.rx_ok), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed), + 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), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed), + 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))) elif errors_only: header = header_errors_only table.append((key, self.get_port_state(key), @@ -238,43 +219,27 @@ class Portstat(object): header = header_rates_only table.append((key, self.get_port_state(key), ns_diff(cntr.rx_ok, old_cntr.rx_ok), - STATUS_NA, - STATUS_NA, - STATUS_NA, + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed), ns_diff(cntr.tx_ok, old_cntr.tx_ok), - STATUS_NA, - STATUS_NA, - STATUS_NA)) + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed))) else: - header = header_std - if old_cntr is not None: - table.append((key, self.get_port_state(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_state(key), - cntr.rx_ok, - STATUS_NA, - STATUS_NA, - cntr.rx_err, - cntr.rx_drop, - cntr.rx_ovr, - cntr.tx_ok, - STATUS_NA, - STATUS_NA, - cntr.tx_err, - cntr.tx_drop, - cntr.tx_ovr)) + table.append((key, self.get_port_state(key), + ns_diff(cntr.rx_ok, old_cntr.rx_ok), + format_brate(rates.rx_bps), + format_util(rates.rx_bps, port_speed), + 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), + format_brate(rates.tx_bps), + format_util(rates.tx_bps, port_speed), + 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 use_json: print(table_as_json(table, header)) diff --git a/tests/intfstat_test.py b/tests/intfstat_test.py index fcf20620e8..f8a41c928b 100644 --- a/tests/intfstat_test.py +++ b/tests/intfstat_test.py @@ -109,17 +109,9 @@ def test_clear(self): assert result.exit_code == 0 result = runner.invoke(show.cli.commands["interfaces"].commands["counters"].commands["rif"], []) print(result.stdout) - expected = ["Ethernet20 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0", - "PortChannel0001 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0", - "PortChannel0002 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0", - "PortChannel0003 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0", - "PortChannel0004 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0", - "Vlan1000 0 0.00 B/s 0.00/s 0 0 0.00 B/s 0.00/s 0"] # remove the counters snapshot show.run_command("intfstat -D") - for line in expected: - assert line in result.output def test_alias_mode(self): os.environ["SONIC_CLI_IFACE_MODE"] = "alias" diff --git a/utilities_common/netstat.py b/utilities_common/netstat.py index 9bf266e580..3a9f6dabee 100755 --- a/utilities_common/netstat.py +++ b/utilities_common/netstat.py @@ -1,10 +1,19 @@ # network statistics utility functions # import json +from collections import namedtuple STATUS_NA = 'N/A' PORT_RATE = 40 +rates_key_list = [ 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_BPS', 'TX_PPS', 'TX_UTIL' ] +ratestat_fields = ("rx_bps", "rx_pps", "rx_util", "tx_bps", "tx_pps", "tx_util") +RateStats = namedtuple("RateStats", ratestat_fields) + +COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" +COUNTERS_RIF_NAME_MAP = "COUNTERS_RIF_NAME_MAP" +RATES_TABLE_PREFIX = "RATES:" + def ns_diff(newstr, oldstr): """ Calculate the diff. @@ -24,9 +33,9 @@ def ns_brate(newstr, oldstr, delta): else: rate = int(ns_diff(newstr, oldstr).replace(',',''))/delta if rate > 1024*1024*10: - rate = "{:.2f}".format(rate/1024/1024)+' MB' + rate = "{:.2f}".format(rate/1024/1024.0)+' MB' elif rate > 1024*10: - rate = "{:.2f}".format(rate/1024)+' KB' + rate = "{:.2f}".format(rate/1024.0)+' KB' else: rate = "{:.2f}".format(rate)+' B' return rate+'/s' @@ -52,6 +61,41 @@ def ns_util(newstr, oldstr, delta, port_rate=PORT_RATE): util = rate/(port_rate*1024*1024*1024/8.0)*100 return "{:.2f}%".format(util) +def format_brate(rate): + """ + Show the byte rate. + """ + if rate == STATUS_NA: + return STATUS_NA + else: + rate = float(rate) + if rate > 1024*1024*10: + rate = "{:.2f}".format(rate/1024/1024.0)+' MB' + elif rate > 1024*10: + rate = "{:.2f}".format(rate/1024.0)+' KB' + else: + rate = "{:.2f}".format(rate)+' B' + return rate+'/s' + +def format_prate(rate): + """ + Show the packet rate. + """ + if rate == STATUS_NA: + return STATUS_NA + else: + return "{:.2f}".format(float(rate))+'/s' + +def format_util(brate, port_rate=PORT_RATE): + """ + Calculate the util. + """ + if newstr == STATUS_NA or oldstr == STATUS_NA: + return STATUS_NA + else: + util = brate/(float(port_rate)*1024*1024*1024/8.0)*100 + return "{:.2f}%".format(util) + def table_as_json(table, header): """ Print table as json format. From 47e355c965363403e0c01f0215dfa1e5612359a4 Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Tue, 18 Aug 2020 17:28:09 +0300 Subject: [PATCH 2/7] fix get_rates Signed-off-by: Mykola Faryma --- scripts/portstat | 66 +++++++++++++++++++++++++++++-------- utilities_common/netstat.py | 2 +- 2 files changed, 54 insertions(+), 14 deletions(-) mode change 100644 => 100755 scripts/portstat diff --git a/scripts/portstat b/scripts/portstat old mode 100644 new mode 100755 index 1b0340c48b..e16ae8439d --- a/scripts/portstat +++ b/scripts/portstat @@ -17,9 +17,23 @@ import time from collections import namedtuple, OrderedDict from natsort import natsorted from tabulate import tabulate -from utilities_common.netstat import ns_diff, table_as_json, format_brate, format_prate, format_util +from utilities_common.netstat import ns_diff, table_as_json, format_brate, format_prate, format_util, rates_key_list, RateStats from utilities_common.intf_filter import parse_interface_in_filter + +try: + if os.environ["UTILITIES_UNIT_TESTING"] == "1": + modules_path = os.path.join(os.path.dirname(__file__), "..") + test_path = os.path.join(modules_path, "tests") + sys.path.insert(0, modules_path) + sys.path.insert(0, test_path) + import mock_tables.dbconnector +except KeyError: + pass + +from collections import namedtuple, OrderedDict + + PORT_RATE = 40 NStats = namedtuple("NStats", "rx_ok, rx_err, rx_drop, rx_ovr, tx_ok,\ @@ -49,6 +63,7 @@ counter_bucket_dict = { STATUS_NA = 'N/A' COUNTER_TABLE_PREFIX = "COUNTERS:" +RATES_TABLE_PREFIX = "RATES:" COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" @@ -87,16 +102,33 @@ class Portstat(object): cntr = NStats._make(fields) return cntr + def get_rates(table_id): + """ + Get the rates from specific table. + """ + fields = ["0","0","0","0","0","0"] + for pos, name in enumerate(rates_key_list): + full_table_id = RATES_TABLE_PREFIX + table_id + counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, name) + if counter_data is None: + fields[pos] = STATUS_NA + elif fields[pos] != STATUS_NA: + fields[pos] = float(counter_data) + cntr = RateStats._make(fields) + return cntr + # Get the info from database counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP); # Build a dictionary of the stats cnstat_dict = OrderedDict() + ratestat_dict = OrderedDict() cnstat_dict['time'] = datetime.datetime.now() if counter_port_name_map is None: return cnstat_dict for port in natsorted(counter_port_name_map): cnstat_dict[port] = get_counters(counter_port_name_map[port]) - return cnstat_dict + ratestat_dict[port] = get_rates(counter_port_name_map[port]) + return cnstat_dict, ratestat_dict def get_port_speed(self, port_name): """ @@ -129,7 +161,7 @@ class Portstat(object): else: return STATUS_NA - def cnstat_print(self, cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only): + def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only): """ Print the cnstat. """ @@ -141,6 +173,8 @@ class Portstat(object): continue if intf_list and key not in intf_list: continue + port_speed = self.get_port_speed(key) + rates = ratestat_dict.get(key, RateStats._make([STATUS_NA] * len(rates_key_list))) if print_all: header = header_all table.append((key, self.get_port_state(key), @@ -156,8 +190,14 @@ class Portstat(object): elif rates_only: header = header_rates_only table.append((key, self.get_port_state(key), - data.rx_ok, STATUS_NA, STATUS_NA, STATUS_NA, - data.tx_ok, STATUS_NA, STATUS_NA, STATUS_NA)) + data.rx_ok, + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed), + data.tx_ok, + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed))) else: header = header_std table.append((key, self.get_port_state(key), @@ -171,7 +211,7 @@ class Portstat(object): else: print(tabulate(table, header, tablefmt='simple', stralign='right')) - def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list, use_json, print_all, errors_only, rates_only): + def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only): """ Print the difference between two cnstat results. """ @@ -325,7 +365,7 @@ Examples: portstat = Portstat() # The cnstat_dict just give an ordered dict of all output. - cnstat_dict = portstat.get_cnstat() + cnstat_dict, ratestat_dict = portstat.get_cnstat() # Now decide what information to display if raw_stats: @@ -355,8 +395,8 @@ Examples: if os.path.isfile(cnstat_fqn_file): 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, intf_list, use_json, print_all, errors_only, rates_only) + print "Last cached time was " + str(cnstat_cached_dict.get('time')) + portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only) except IOError as e: print(e.errno, e) else: @@ -364,13 +404,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, intf_list, use_json, print_all, errors_only, rates_only) + portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only) 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, intf_list, use_json, print_all, errors_only, rates_only) + print "The rates are calculated within %s seconds period" % wait_time_in_seconds + cnstat_new_dict, ratestat_dict = portstat.get_cnstat() + portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only) if __name__ == "__main__": main() diff --git a/utilities_common/netstat.py b/utilities_common/netstat.py index 3a9f6dabee..af3ed219f7 100755 --- a/utilities_common/netstat.py +++ b/utilities_common/netstat.py @@ -90,7 +90,7 @@ def format_util(brate, port_rate=PORT_RATE): """ Calculate the util. """ - if newstr == STATUS_NA or oldstr == STATUS_NA: + if brate == STATUS_NA: return STATUS_NA else: util = brate/(float(port_rate)*1024*1024*1024/8.0)*100 From 93e2cd0210cefd88a3df677dc813ab419f7eca9e Mon Sep 17 00:00:00 2001 From: Mykola Faryma Date: Tue, 18 Aug 2020 17:28:52 +0300 Subject: [PATCH 3/7] [UT] add rates_test.py Signed-off-by: Mykola Faryma --- tests/mock_tables/counters_db.json | 18 ++++++++++++++ tests/rates_test.py | 39 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/rates_test.py diff --git a/tests/mock_tables/counters_db.json b/tests/mock_tables/counters_db.json index 2b2b600280..5fcd1c68f9 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -59,6 +59,24 @@ "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" }, + "RATES:oid:0x1000000000002": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000004": { + "RX_BPS": "204800", + "RX_PPS": "200", + "TX_BPS": "204848", + "TX_PPS": "201" + }, + "RATES:oid:0x1000000000006": { + "RX_BPS": "1.35e6", + "RX_PPS": "9000", + "TX_BPS": "13.37e6", + "TX_PPS": "9000" + }, "COUNTERS_RIF_NAME_MAP": { "Ethernet20": "oid:0x600000000065f", "PortChannel0001": "oid:0x60000000005a1", diff --git a/tests/rates_test.py b/tests/rates_test.py new file mode 100644 index 0000000000..ad07a9409f --- /dev/null +++ b/tests/rates_test.py @@ -0,0 +1,39 @@ +import sys +import os +from click.testing import CliRunner + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + +import mock_tables.dbconnector +import show.main as show + +expected_rates = """ IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL TX_OK TX_BPS TX_PPS TX_UTIL +--------- ------- ------- ------------ --------- --------- ------- ----------- --------- --------- +Ethernet0 D N/A 0.00 B/s 0.00/s 0.00% N/A 0.00 B/s 0.00/s 0.00% +Ethernet4 N/A N/A 200.00 KB/s 200.00/s 0.00% N/A 200.05 KB/s 201.00/s 0.00% +Ethernet8 N/A N/A 1318.36 KB/s 9000.00/s 0.03% N/A 12.75 MB/s 9000.00/s 0.25% +""" + + +class TestRates(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "1" + + def test_rates(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["interfaces"].commands["counters"].commands["rates"], []) + print(result.output) + assert result.output == expected_rates + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0" From 0f98cc99a50fe62d0fc209451a3a89ea3df5f547 Mon Sep 17 00:00:00 2001 From: Volodymyr Samotiy Date: Tue, 20 Oct 2020 20:23:26 +0300 Subject: [PATCH 4/7] [portstat, intfstat] update rates and utilization * Fix review comments Signed-off-by: Volodymyr Samotiy --- scripts/portstat | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/portstat b/scripts/portstat index 1e38a24a3f..789b490b24 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -22,9 +22,8 @@ from sonic_py_common import multi_asic from utilities_common import constants from utilities_common.intf_filter import parse_interface_in_filter import utilities_common.multi_asic as multi_asic_util -from utilities_common.netstat import (ns_brate, ns_diff, ns_prate, ns_util, table_as_json, - table_as_json, format_brate, format_prate, - format_util, rates_key_list, RateStats) +from utilities_common.netstat import (ns_diff, table_as_json,table_as_json, format_brate, + format_prate, format_util, rates_key_list, RateStats) # mock the redis for unit test purposes # try: @@ -422,7 +421,7 @@ Examples: # Now decide what information to display if raw_stats: - portstat.cnstat_print(cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only) + portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only) sys.exit(0) # At this point, either we'll create a file or open an existing one. From 3344cbe9df12b20c75daed7a2eb7658504b8b0d1 Mon Sep 17 00:00:00 2001 From: Volodymyr Samotiy Date: Fri, 18 Jun 2021 14:49:20 +0300 Subject: [PATCH 5/7] [portstat, intfstat] update rates and utilization * Fix "wrong number of args" issue after merge from upstream Signed-off-by: Volodymyr Samotiy --- scripts/portstat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/portstat b/scripts/portstat index 3e1427ee02..9783a55650 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -353,7 +353,7 @@ class Portstat(object): print("Time Since Counters Last Cleared............... " + str(cnstat_old_dict.get('time'))) - def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only): + def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail=False): """ Print the difference between two cnstat results. """ From 978b7c9c21cb67a3000dd283840b7756799d09d6 Mon Sep 17 00:00:00 2001 From: Vadym Hlushko Date: Mon, 26 Jul 2021 08:45:14 +0000 Subject: [PATCH 6/7] Fixed portstat script code Signed-off-by: Vadym Hlushko --- scripts/portstat | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/portstat b/scripts/portstat index 9783a55650..1743ff72fa 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -38,7 +38,8 @@ from utilities_common import constants from utilities_common.intf_filter import parse_interface_in_filter import utilities_common.multi_asic as multi_asic_util from utilities_common.netstat import (ns_diff, table_as_json, format_brate, - format_prate, format_util, rates_key_list, RateStats) + format_prate, format_util, rates_key_list, + RateStats, ratestat_fields) PORT_RATE = 40 @@ -134,11 +135,12 @@ class Portstat(object): self.db = None self.multi_asic = multi_asic_util.MultiAsic(display_option, namespace) - def get_cnstat_dict(self): + def get_cnstat_ratestat_dict(self): self.cnstat_dict = OrderedDict() self.cnstat_dict['time'] = datetime.datetime.now() + self.ratestat_dict = OrderedDict() self.collect_stat() - return self.cnstat_dict + return self.cnstat_dict, self.ratestat_dict @multi_asic_util.run_on_multi_asic def collect_stat(self): @@ -146,9 +148,11 @@ class Portstat(object): Collect the statisitics from all the asics present on the device and store in a dict """ - self.cnstat_dict.update(self.get_cnstat()) + cnstat_dict, ratestat_dict = self.get_cnstat_ratestat() + self.cnstat_dict.update(cnstat_dict) + self.ratestat_dict.update(ratestat_dict) - def get_cnstat(self): + def get_cnstat_ratestat(self): """ Get the counters info from database. """ @@ -192,7 +196,7 @@ class Portstat(object): ratestat_dict = OrderedDict() cnstat_dict['time'] = datetime.datetime.now() if counter_port_name_map is None: - return cnstat_dict + return cnstat_dict, ratestat_dict for port in natsorted(counter_port_name_map): port_name = port.split(":")[0] if self.multi_asic.skip_display(constants.PORT_OBJ, port_name): @@ -381,6 +385,7 @@ class Portstat(object): continue port_speed = self.get_port_speed(key) if print_all: + header = header_all table.append((key, self.get_port_state(key), ns_diff(cntr.rx_ok, old_cntr.rx_ok), format_brate(rates.rx_bps), @@ -417,6 +422,7 @@ class Portstat(object): format_prate(rates.tx_pps), format_util(rates.tx_bps, port_speed))) else: + header = header_std table.append((key, self.get_port_state(key), ns_diff(cntr.rx_ok, old_cntr.rx_ok), format_brate(rates.rx_bps), @@ -528,7 +534,7 @@ Examples: display_option = constants.DISPLAY_ALL portstat = Portstat(namespace, display_option) - cnstat_dict, ratestat_dict = portstat.get_cnstat_dict() + cnstat_dict, ratestat_dict = portstat.get_cnstat_ratestat_dict() # Now decide what information to display if raw_stats: @@ -572,8 +578,8 @@ Examples: 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, ratestat_dict = portstat.get_cnstat() + print("The rates are calculated within %s seconds period" % wait_time_in_seconds) + cnstat_new_dict, ratestat_dict = portstat.get_cnstat_ratestat() portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail) if __name__ == "__main__": From f4e83db0e07419bd8b2febf1b26f9aa00fb9f638 Mon Sep 17 00:00:00 2001 From: Vadym Hlushko Date: Mon, 26 Jul 2021 08:48:40 +0000 Subject: [PATCH 7/7] Fixed UT Signed-off-by: Vadym Hlushko --- tests/mock_tables/asic0/counters_db.json | 24 ++++++++++++++++++++++++ tests/mock_tables/asic1/counters_db.json | 12 ++++++++++++ tests/mock_tables/asic2/counters_db.json | 24 ++++++++++++++++++++++++ tests/mock_tables/counters_db.json | 6 +++--- tests/portstat_test.py | 24 ++++++++++-------------- tests/rates_test.py | 11 ++++++----- 6 files changed, 79 insertions(+), 22 deletions(-) diff --git a/tests/mock_tables/asic0/counters_db.json b/tests/mock_tables/asic0/counters_db.json index 167805e313..a28c002e66 100644 --- a/tests/mock_tables/asic0/counters_db.json +++ b/tests/mock_tables/asic0/counters_db.json @@ -1584,6 +1584,30 @@ "Ethernet-BP0": "oid:0x1000000000006", "Ethernet-BP4": "oid:0x1000000000008" }, + "RATES:oid:0x1000000000002": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000004": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000006": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000008": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, "COUNTERS_LAG_NAME_MAP": { "PortChannel0001": "oid:0x60000000005a1", "PortChannel0002": "oid:0x60000000005a2", diff --git a/tests/mock_tables/asic1/counters_db.json b/tests/mock_tables/asic1/counters_db.json index 279963b81b..fe111711a5 100644 --- a/tests/mock_tables/asic1/counters_db.json +++ b/tests/mock_tables/asic1/counters_db.json @@ -182,6 +182,18 @@ "Ethernet-BP256": "oid:0x1000000000b06", "Ethernet-BP260": "oid:0x1000000000b08" }, + "RATES:oid:0x1000000000b06": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000b08": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, "COUNTERS_LAG_NAME_MAP": { "PortChannel0001": "oid:0x60000000005a1", "PortChannel0002": "oid:0x60000000005a2", diff --git a/tests/mock_tables/asic2/counters_db.json b/tests/mock_tables/asic2/counters_db.json index 3286a0a752..65b9a2d329 100644 --- a/tests/mock_tables/asic2/counters_db.json +++ b/tests/mock_tables/asic2/counters_db.json @@ -1584,6 +1584,30 @@ "Ethernet-BP0": "oid:0x1000000000006", "Ethernet-BP4": "oid:0x1000000000008" }, + "RATES:oid:0x1000000000002": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000004": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000006": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000008": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, "COUNTERS_LAG_NAME_MAP": { "PortChannel0001": "oid:0x60000000005a1", "PortChannel0002": "oid:0x60000000005a2", diff --git a/tests/mock_tables/counters_db.json b/tests/mock_tables/counters_db.json index 10a7f16312..131ebcdfcd 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -468,19 +468,19 @@ "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" }, - "RATES:oid:0x1000000000002": { + "RATES:oid:0x1000000000012": { "RX_BPS": "0", "RX_PPS": "0", "TX_BPS": "0", "TX_PPS": "0" }, - "RATES:oid:0x1000000000004": { + "RATES:oid:0x1000000000013": { "RX_BPS": "204800", "RX_PPS": "200", "TX_BPS": "204848", "TX_PPS": "201" }, - "RATES:oid:0x1000000000006": { + "RATES:oid:0x1000000000014": { "RX_BPS": "1.35e6", "RX_PPS": "9000", "TX_BPS": "13.37e6", diff --git a/tests/portstat_test.py b/tests/portstat_test.py index b0f60b1e8a..c06dcd7e0b 100644 --- a/tests/portstat_test.py +++ b/tests/portstat_test.py @@ -35,19 +35,19 @@ intf_counters_period = """\ The rates are calculated within 3 seconds period - 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 ---------- ------- ------- -------- --------- -------- -------- -------- ------- -------- --------- -------- -------- -------- -Ethernet0 D 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A -Ethernet4 N/A 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A -Ethernet8 N/A 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A + 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 +--------- ------- ------- ------------ --------- -------- -------- -------- ------- ----------- --------- -------- -------- -------- +Ethernet0 D 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A +Ethernet4 N/A 0 200.00 KB/s 0.00% 0 0 N/A 0 200.05 KB/s 0.00% N/A N/A N/A +Ethernet8 N/A 0 1318.36 KB/s 0.03% 0 0 N/A 0 12.75 MB/s 0.25% N/A N/A N/A """ intf_counter_after_clear = """\ - 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 ---------- ------- ------- -------- --------- -------- -------- -------- ------- -------- --------- -------- -------- -------- -Ethernet0 D 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A -Ethernet4 N/A 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A -Ethernet8 N/A 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A""" + 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 +--------- ------- ------- ------------ --------- -------- -------- -------- ------- ----------- --------- -------- -------- -------- +Ethernet0 D 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A +Ethernet4 N/A 0 200.00 KB/s 0.00% 0 0 N/A 0 200.05 KB/s 0.00% N/A N/A N/A +Ethernet8 N/A 0 1318.36 KB/s 0.03% 0 0 N/A 0 12.75 MB/s 0.25% N/A N/A N/A""" clear_counter = """\ Cleared counters""" @@ -116,10 +116,6 @@ The rates are calculated within 3 seconds period 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 -------------- ------- ------- -------- --------- -------- -------- -------- ------- -------- --------- -------- -------- -------- - Ethernet0 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A - Ethernet4 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A - Ethernet-BP0 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A - Ethernet-BP4 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A Ethernet-BP256 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A Ethernet-BP260 U 0 0.00 B/s 0.00% 0 0 N/A 0 0.00 B/s 0.00% N/A N/A N/A """ diff --git a/tests/rates_test.py b/tests/rates_test.py index ad07a9409f..0a0b109fd2 100644 --- a/tests/rates_test.py +++ b/tests/rates_test.py @@ -11,11 +11,12 @@ import mock_tables.dbconnector import show.main as show -expected_rates = """ IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL TX_OK TX_BPS TX_PPS TX_UTIL +expected_rates = """\ + IFACE STATE RX_OK RX_BPS RX_PPS RX_UTIL TX_OK TX_BPS TX_PPS TX_UTIL --------- ------- ------- ------------ --------- --------- ------- ----------- --------- --------- -Ethernet0 D N/A 0.00 B/s 0.00/s 0.00% N/A 0.00 B/s 0.00/s 0.00% -Ethernet4 N/A N/A 200.00 KB/s 200.00/s 0.00% N/A 200.05 KB/s 201.00/s 0.00% -Ethernet8 N/A N/A 1318.36 KB/s 9000.00/s 0.03% N/A 12.75 MB/s 9000.00/s 0.25% +Ethernet0 D 8 0.00 B/s 0.00/s 0.00% 10 0.00 B/s 0.00/s 0.00% +Ethernet4 N/A 4 200.00 KB/s 200.00/s 0.00% 40 200.05 KB/s 201.00/s 0.00% +Ethernet8 N/A 6 1318.36 KB/s 9000.00/s 0.03% 60 12.75 MB/s 9000.00/s 0.25% """ @@ -24,7 +25,7 @@ class TestRates(object): def setup_class(cls): print("SETUP") os.environ["PATH"] += os.pathsep + scripts_path - os.environ["UTILITIES_UNIT_TESTING"] = "1" + os.environ["UTILITIES_UNIT_TESTING"] = "2" def test_rates(self): runner = CliRunner()