diff --git a/config/main.py b/config/main.py index e680f65274..a92eb8814a 100644 --- a/config/main.py +++ b/config/main.py @@ -4675,7 +4675,35 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos # -# '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 a4858019c4..79087f5852 100755 --- a/scripts/intfstat +++ b/scripts/intfstat @@ -28,12 +28,12 @@ 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 from swsscommon.swsscommon import SonicV2Connector - -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'] @@ -76,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) @@ -127,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. @@ -173,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 7fa37108cf..1743ff72fa 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -37,8 +37,9 @@ except KeyError: 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) +from utilities_common.netstat import (ns_diff, table_as_json, format_brate, + format_prate, format_util, rates_key_list, + RateStats, ratestat_fields) PORT_RATE = 40 @@ -114,6 +115,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:" @@ -133,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): @@ -145,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. """ @@ -169,18 +174,36 @@ 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 + 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): continue 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): """ @@ -218,7 +241,7 @@ class Portstat(object): return STATUS_NA - def cnstat_print(self, cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail=False): + def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail=False): """ Print the cnstat. """ @@ -235,6 +258,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), @@ -250,8 +275,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), @@ -265,7 +296,6 @@ class Portstat(object): else: print(tabulate(table, header, tablefmt='simple', stralign='right')) - def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list): """ Print the difference between two cnstat results for interface. @@ -326,8 +356,8 @@ 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, intf_list, use_json, print_all, errors_only, rates_only, detail=False): + + 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. """ @@ -347,44 +377,30 @@ class Portstat(object): 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), @@ -398,43 +414,28 @@ 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, 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_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, - 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)) @@ -533,11 +534,11 @@ Examples: display_option = constants.DISPLAY_ALL portstat = Portstat(namespace, display_option) - cnstat_dict = portstat.get_cnstat_dict() + cnstat_dict, ratestat_dict = portstat.get_cnstat_ratestat_dict() # 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. @@ -565,7 +566,7 @@ Examples: cnstat_cached_dict = pickle.load(open(cnstat_fqn_file, 'rb')) if not detail: 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, detail) + portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail) except IOError as e: print(e.errno, e) else: @@ -573,13 +574,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, detail) + portstat.cnstat_print(cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail) 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_dict() - portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only, detail) + 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__": main() diff --git a/tests/intfstat_test.py b/tests/intfstat_test.py index 2f63c7aeaf..c220993188 100644 --- a/tests/intfstat_test.py +++ b/tests/intfstat_test.py @@ -110,17 +110,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/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 1d4ce33a02..131ebcdfcd 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -468,6 +468,24 @@ "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS": "0", "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS": "0" }, + "RATES:oid:0x1000000000012": { + "RX_BPS": "0", + "RX_PPS": "0", + "TX_BPS": "0", + "TX_PPS": "0" + }, + "RATES:oid:0x1000000000013": { + "RX_BPS": "204800", + "RX_PPS": "200", + "TX_BPS": "204848", + "TX_PPS": "201" + }, + "RATES:oid:0x1000000000014": { + "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/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 new file mode 100644 index 0000000000..0a0b109fd2 --- /dev/null +++ b/tests/rates_test.py @@ -0,0 +1,40 @@ +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 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% +""" + + +class TestRates(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "2" + + 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" diff --git a/utilities_common/netstat.py b/utilities_common/netstat.py index 9bb203af67..0e903eb769 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. @@ -28,9 +37,9 @@ def ns_brate(newstr, oldstr, delta): else: rate = int(ns_diff(newstr, oldstr).replace(',',''))/delta if rate > 1000*1000*10: - rate = "{:.2f}".format(rate/1000/1000)+' MB' + rate = "{:.2f}".format(rate/1000/1000.0)+' MB' elif rate > 1000*10: - rate = "{:.2f}".format(rate/1000)+' KB' + rate = "{:.2f}".format(rate/1000.0)+' KB' else: rate = "{:.2f}".format(rate)+' B' return rate+'/s' @@ -56,6 +65,41 @@ def ns_util(newstr, oldstr, delta, port_rate=PORT_RATE): util = rate/(port_rate*1000*1000*1000/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 brate == 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.