diff --git a/scripts/portstat b/scripts/portstat index 85f611fe5..6224b97e5 100755 --- a/scripts/portstat +++ b/scripts/portstat @@ -27,6 +27,13 @@ try: sys.path.insert(0, modules_path) sys.path.insert(0, tests_path) import mock_tables.dbconnector + + if os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] == "1": + import mock + import sonic_py_common + from swsscommon.swsscommon import SonicV2Connector + sonic_py_common.device_info.is_supervisor = mock.MagicMock(return_value=True) + SonicV2Connector.delete_all_by_pattern = mock.MagicMock() if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": import mock_tables.mock_multi_asic mock_tables.dbconnector.load_namespace_config() @@ -36,526 +43,11 @@ 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_diff, table_as_json, format_brate, format_prate, format_util, format_number_with_comma +from utilities_common.portstat import Portstat from utilities_common.cli import UserCache -""" -The order and count of statistics mentioned below needs to be in sync with the values in portstat script -So, any fields added/deleted in here should be reflected in portstat script also -""" -NStats = namedtuple("NStats", "rx_ok, rx_err, rx_drop, rx_ovr, tx_ok,\ - tx_err, tx_drop, tx_ovr, rx_byt, tx_byt,\ - rx_64, rx_65_127, rx_128_255, rx_256_511, rx_512_1023, rx_1024_1518, rx_1519_2047, rx_2048_4095, rx_4096_9216, rx_9217_16383,\ - rx_uca, rx_mca, rx_bca, rx_all,\ - tx_64, tx_65_127, tx_128_255, tx_256_511, tx_512_1023, tx_1024_1518, tx_1519_2047, tx_2048_4095, tx_4096_9216, tx_9217_16383,\ - tx_uca, tx_mca, tx_bca, tx_all,\ - rx_jbr, rx_frag, rx_usize, rx_ovrrun,\ - fec_corr, fec_uncorr, fec_symbol_err") -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_std = ['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'] -header_errors_only = ['IFACE', 'STATE', 'RX_ERR', 'RX_DRP', 'RX_OVR', 'TX_ERR', 'TX_DRP', 'TX_OVR'] -header_fec_only = ['IFACE', 'STATE', 'FEC_CORR', 'FEC_UNCORR', 'FEC_SYMBOL_ERR'] -header_rates_only = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL'] - -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) - -""" -The order and count of statistics mentioned below needs to be in sync with the values in portstat script -So, any fields added/deleted in here should be reflected in portstat script also -""" -BUCKET_NUM = 45 -counter_bucket_dict = { - 0:['SAI_PORT_STAT_IF_IN_UCAST_PKTS', 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS'], - 1:['SAI_PORT_STAT_IF_IN_ERRORS'], - 2:['SAI_PORT_STAT_IF_IN_DISCARDS'], - 3:['SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS'], - 4:['SAI_PORT_STAT_IF_OUT_UCAST_PKTS', 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS'], - 5:['SAI_PORT_STAT_IF_OUT_ERRORS'], - 6:['SAI_PORT_STAT_IF_OUT_DISCARDS'], - 7:['SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS'], - 8:['SAI_PORT_STAT_IF_IN_OCTETS'], - 9:['SAI_PORT_STAT_IF_OUT_OCTETS'], - 10:['SAI_PORT_STAT_ETHER_IN_PKTS_64_OCTETS'], - 11:['SAI_PORT_STAT_ETHER_IN_PKTS_65_TO_127_OCTETS'], - 12:['SAI_PORT_STAT_ETHER_IN_PKTS_128_TO_255_OCTETS'], - 13:['SAI_PORT_STAT_ETHER_IN_PKTS_256_TO_511_OCTETS'], - 14:['SAI_PORT_STAT_ETHER_IN_PKTS_512_TO_1023_OCTETS'], - 15:['SAI_PORT_STAT_ETHER_IN_PKTS_1024_TO_1518_OCTETS'], - 16:['SAI_PORT_STAT_ETHER_IN_PKTS_1519_TO_2047_OCTETS'], - 17:['SAI_PORT_STAT_ETHER_IN_PKTS_2048_TO_4095_OCTETS'], - 18:['SAI_PORT_STAT_ETHER_IN_PKTS_4096_TO_9216_OCTETS'], - 19:['SAI_PORT_STAT_ETHER_IN_PKTS_9217_TO_16383_OCTETS'], - 20:['SAI_PORT_STAT_IF_IN_UCAST_PKTS'], - 21:['SAI_PORT_STAT_IF_IN_MULTICAST_PKTS'], - 22:['SAI_PORT_STAT_IF_IN_BROADCAST_PKTS'], - 23:['SAI_PORT_STAT_IF_IN_UCAST_PKTS', 'SAI_PORT_STAT_IF_IN_MULTICAST_PKTS', 'SAI_PORT_STAT_IF_IN_BROADCAST_PKTS'], - 24:['SAI_PORT_STAT_ETHER_OUT_PKTS_64_OCTETS'], - 25:['SAI_PORT_STAT_ETHER_OUT_PKTS_65_TO_127_OCTETS'], - 26:['SAI_PORT_STAT_ETHER_OUT_PKTS_128_TO_255_OCTETS'], - 27:['SAI_PORT_STAT_ETHER_OUT_PKTS_256_TO_511_OCTETS'], - 28:['SAI_PORT_STAT_ETHER_OUT_PKTS_512_TO_1023_OCTETS'], - 29:['SAI_PORT_STAT_ETHER_OUT_PKTS_1024_TO_1518_OCTETS'], - 30:['SAI_PORT_STAT_ETHER_OUT_PKTS_1519_TO_2047_OCTETS'], - 31:['SAI_PORT_STAT_ETHER_OUT_PKTS_2048_TO_4095_OCTETS'], - 32:['SAI_PORT_STAT_ETHER_OUT_PKTS_4096_TO_9216_OCTETS'], - 33:['SAI_PORT_STAT_ETHER_OUT_PKTS_9217_TO_16383_OCTETS'], - 34:['SAI_PORT_STAT_IF_OUT_UCAST_PKTS'], - 35:['SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS'], - 36:['SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS'], - 37:['SAI_PORT_STAT_IF_OUT_UCAST_PKTS', 'SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS', 'SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS'], - 38:['SAI_PORT_STAT_ETHER_STATS_JABBERS'], - 39:['SAI_PORT_STAT_ETHER_STATS_FRAGMENTS'], - 40:['SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS'], - 41:['SAI_PORT_STAT_IP_IN_RECEIVES'], - 42:['SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES'], - 43:['SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES'], - 44:['SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS'] -} - -STATUS_NA = 'N/A' - -RATES_TABLE_PREFIX = "RATES:" - -COUNTER_TABLE_PREFIX = "COUNTERS:" -COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" - -PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" -PORT_STATE_TABLE_PREFIX = "PORT_TABLE|" -PORT_OPER_STATUS_FIELD = "oper_status" -PORT_ADMIN_STATUS_FIELD = "admin_status" -PORT_STATUS_VALUE_UP = 'UP' -PORT_STATUS_VALUE_DOWN = 'DOWN' -PORT_SPEED_FIELD = "speed" - -PORT_STATE_UP = 'U' -PORT_STATE_DOWN = 'D' -PORT_STATE_DISABLED = 'X' - - -class Portstat(object): - def __init__(self, namespace, display_option): - self.db = None - self.multi_asic = multi_asic_util.MultiAsic(display_option, namespace) - - def get_cnstat_dict(self): - self.cnstat_dict = OrderedDict() - self.cnstat_dict['time'] = datetime.datetime.now() - self.ratestat_dict = OrderedDict() - self.collect_stat() - return self.cnstat_dict, self.ratestat_dict - - @multi_asic_util.run_on_multi_asic - def collect_stat(self): - """ - Collect the statisitics from all the asics present on the - device and store in a dict - """ - - cnstat_dict, ratestat_dict = self.get_cnstat() - self.cnstat_dict.update(cnstat_dict) - self.ratestat_dict.update(ratestat_dict) - - def get_cnstat(self): - """ - Get the counters info from database. - """ - def get_counters(table_id): - """ - Get the counters from specific table. - """ - fields = ["0"]*BUCKET_NUM - - for pos, cntr_list in counter_bucket_dict.items(): - for counter_name in cntr_list: - full_table_id = COUNTER_TABLE_PREFIX + table_id - counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name) - if counter_data is None: - fields[pos] = STATUS_NA - elif fields[pos] != STATUS_NA: - fields[pos] = str(int(fields[pos]) + int(counter_data)) - - 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() - cnstat_dict['time'] = datetime.datetime.now() - ratestat_dict = OrderedDict() - if counter_port_name_map is None: - 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]) - ratestat_dict[port] = get_rates(counter_port_name_map[port]) - return cnstat_dict, ratestat_dict - - def get_port_speed(self, port_name): - """ - Get the port speed - """ - # Get speed from APPL_DB - state_db_table_id = PORT_STATE_TABLE_PREFIX + port_name - app_db_table_id = PORT_STATUS_TABLE_PREFIX + port_name - for ns in self.multi_asic.get_ns_list_based_on_options(): - self.db = multi_asic.connect_to_all_dbs_for_ns(ns) - speed = self.db.get(self.db.STATE_DB, state_db_table_id, PORT_SPEED_FIELD) - oper_status = self.db.get(self.db.APPL_DB, app_db_table_id, PORT_OPER_STATUS_FIELD) - if speed is None or speed == STATUS_NA or oper_status != "up": - speed = self.db.get(self.db.APPL_DB, app_db_table_id, PORT_SPEED_FIELD) - if speed is not None: - return int(speed) - return STATUS_NA - - def get_port_state(self, port_name): - """ - Get the port state - """ - full_table_id = PORT_STATUS_TABLE_PREFIX + port_name - for ns in self.multi_asic.get_ns_list_based_on_options(): - self.db = multi_asic.connect_to_all_dbs_for_ns(ns) - 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: - continue - if 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 - return STATUS_NA - - - def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, detail=False): - """ - Print the cnstat. - """ - - if intf_list and detail: - self.cnstat_intf_diff_print(cnstat_dict, {}, intf_list) - return None - table = [] - header = None - - for key, data in cnstat_dict.items(): - if key == 'time': - 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), - format_number_with_comma(data.rx_ok), - format_brate(rates.rx_bps), - format_prate(rates.rx_pps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(data.rx_err), - format_number_with_comma(data.rx_drop), - format_number_with_comma(data.rx_ovr), - format_number_with_comma(data.tx_ok), - format_brate(rates.tx_bps), - format_prate(rates.tx_pps), - format_util(rates.tx_bps, port_speed), - format_number_with_comma(data.tx_err), - format_number_with_comma(data.tx_drop), - format_number_with_comma(data.tx_ovr))) - elif errors_only: - header = header_errors_only - table.append((key, self.get_port_state(key), - format_number_with_comma(data.rx_err), - format_number_with_comma(data.rx_drop), - format_number_with_comma(data.rx_ovr), - format_number_with_comma(data.tx_err), - format_number_with_comma(data.tx_drop), - format_number_with_comma(data.tx_ovr))) - elif fec_stats_only: - header = header_fec_only - table.append((key, self.get_port_state(key), - format_number_with_comma(data.fec_corr), - format_number_with_comma(data.fec_uncorr), - format_number_with_comma(data.fec_symbol_err))) - elif rates_only: - header = header_rates_only - table.append((key, self.get_port_state(key), - format_number_with_comma(data.rx_ok), - format_brate(rates.rx_bps), - format_prate(rates.rx_pps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(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), - format_number_with_comma(data.rx_ok), - format_brate(rates.rx_bps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(data.rx_err), - format_number_with_comma(data.rx_drop), - format_number_with_comma(data.rx_ovr), - format_number_with_comma(data.tx_ok), - format_brate(rates.tx_bps), - format_util(rates.tx_bps, port_speed), - format_number_with_comma(data.tx_err), - format_number_with_comma(data.tx_drop), - format_number_with_comma(data.tx_ovr))) - if table: - if use_json: - print(table_as_json(table, header)) - else: - print(tabulate(table, header, tablefmt='simple', stralign='right')) - if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json: - print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n") - - def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list): - """ - Print the difference between two cnstat results for interface. - """ - - for key, cntr in cnstat_new_dict.items(): - if key == 'time': - continue - - if key in cnstat_old_dict: - old_cntr = cnstat_old_dict.get(key) - else: - old_cntr = NStats._make([0] * BUCKET_NUM) - - if intf_list and key not in intf_list: - continue - - print("Packets Received 64 Octets..................... {}".format(ns_diff(cntr.rx_64, old_cntr.rx_64))) - print("Packets Received 65-127 Octets................. {}".format(ns_diff(cntr.rx_65_127, old_cntr.rx_65_127))) - print("Packets Received 128-255 Octets................ {}".format(ns_diff(cntr.rx_128_255, old_cntr.rx_128_255))) - print("Packets Received 256-511 Octets................ {}".format(ns_diff(cntr.rx_256_511, old_cntr.rx_256_511))) - print("Packets Received 512-1023 Octets............... {}".format(ns_diff(cntr.rx_512_1023, old_cntr.rx_512_1023))) - print("Packets Received 1024-1518 Octets.............. {}".format(ns_diff(cntr.rx_1024_1518, old_cntr.rx_1024_1518))) - print("Packets Received 1519-2047 Octets.............. {}".format(ns_diff(cntr.rx_1519_2047, old_cntr.rx_1519_2047))) - print("Packets Received 2048-4095 Octets.............. {}".format(ns_diff(cntr.rx_2048_4095, old_cntr.rx_2048_4095))) - print("Packets Received 4096-9216 Octets.............. {}".format(ns_diff(cntr.rx_4096_9216, old_cntr.rx_4096_9216))) - print("Packets Received 9217-16383 Octets............. {}".format(ns_diff(cntr.rx_9217_16383, old_cntr.rx_9217_16383))) - - print("") - print("Total Packets Received Without Errors.......... {}".format(ns_diff(cntr.rx_all, old_cntr.rx_all))) - print("Unicast Packets Received....................... {}".format(ns_diff(cntr.rx_uca, old_cntr.rx_uca))) - print("Multicast Packets Received..................... {}".format(ns_diff(cntr.rx_mca, old_cntr.rx_mca))) - print("Broadcast Packets Received..................... {}".format(ns_diff(cntr.rx_bca, old_cntr.rx_bca))) - - print("") - print("Jabbers Received............................... {}".format(ns_diff(cntr.rx_jbr, old_cntr.rx_jbr))) - print("Fragments Received............................. {}".format(ns_diff(cntr.rx_frag, old_cntr.rx_frag))) - print("Undersize Received............................. {}".format(ns_diff(cntr.rx_usize, old_cntr.rx_usize))) - print("Overruns Received.............................. {}".format(ns_diff(cntr.rx_ovrrun, old_cntr.rx_ovrrun))) - - print("") - print("Packets Transmitted 64 Octets.................. {}".format(ns_diff(cntr.tx_64, old_cntr.tx_64))) - print("Packets Transmitted 65-127 Octets.............. {}".format(ns_diff(cntr.tx_65_127, old_cntr.tx_65_127))) - print("Packets Transmitted 128-255 Octets............. {}".format(ns_diff(cntr.tx_128_255, old_cntr.tx_128_255))) - print("Packets Transmitted 256-511 Octets............. {}".format(ns_diff(cntr.tx_256_511, old_cntr.tx_256_511))) - print("Packets Transmitted 512-1023 Octets............ {}".format(ns_diff(cntr.tx_512_1023, old_cntr.tx_512_1023))) - print("Packets Transmitted 1024-1518 Octets........... {}".format(ns_diff(cntr.tx_1024_1518, old_cntr.tx_1024_1518))) - print("Packets Transmitted 1519-2047 Octets........... {}".format(ns_diff(cntr.tx_1519_2047, old_cntr.tx_1519_2047))) - print("Packets Transmitted 2048-4095 Octets........... {}".format(ns_diff(cntr.tx_2048_4095, old_cntr.tx_2048_4095))) - print("Packets Transmitted 4096-9216 Octets........... {}".format(ns_diff(cntr.tx_4096_9216, old_cntr.tx_4096_9216))) - print("Packets Transmitted 9217-16383 Octets.......... {}".format(ns_diff(cntr.tx_9217_16383, old_cntr.tx_9217_16383))) - - print("") - print("Total Packets Transmitted Successfully......... {}".format(ns_diff(cntr.tx_all, old_cntr.tx_all))) - print("Unicast Packets Transmitted.................... {}".format(ns_diff(cntr.tx_uca, old_cntr.tx_uca))) - print("Multicast Packets Transmitted.................. {}".format(ns_diff(cntr.tx_mca, old_cntr.tx_mca))) - print("Broadcast Packets Transmitted.................. {}".format(ns_diff(cntr.tx_bca, old_cntr.tx_bca))) - - 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, fec_stats_only, - rates_only, detail=False): - """ - Print the difference between two cnstat results. - """ - - if intf_list and detail: - self.cnstat_intf_diff_print(cnstat_new_dict, cnstat_old_dict, intf_list) - return None - - table = [] - header = None - - for key, cntr in cnstat_new_dict.items(): - if key == 'time': - continue - old_cntr = None - if key in cnstat_old_dict: - old_cntr = cnstat_old_dict.get(key) - - 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), - 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))) - else: - table.append((key, self.get_port_state(key), - format_number_with_comma(cntr.rx_ok), - format_brate(rates.rx_bps), - format_prate(rates.rx_pps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(cntr.rx_err), - format_number_with_comma(cntr.rx_drop), - format_number_with_comma(cntr.rx_ovr), - format_number_with_comma(cntr.tx_ok), - format_brate(rates.tx_bps), - format_prate(rates.tx_pps), - format_util(rates.tx_bps, port_speed), - format_number_with_comma(cntr.tx_err), - format_number_with_comma(cntr.tx_drop), - format_number_with_comma(cntr.tx_ovr))) - elif errors_only: - header = header_errors_only - if old_cntr is not None: - table.append((key, self.get_port_state(key), - 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_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), - format_number_with_comma(cntr.rx_err), - format_number_with_comma(cntr.rx_drop), - format_number_with_comma(cntr.rx_ovr), - format_number_with_comma(cntr.tx_err), - format_number_with_comma(cntr.tx_drop), - format_number_with_comma(cntr.tx_ovr))) - elif fec_stats_only: - header = header_fec_only - if old_cntr is not None: - table.append((key, self.get_port_state(key), - ns_diff(cntr.fec_corr, old_cntr.fec_corr), - ns_diff(cntr.fec_uncorr, old_cntr.fec_uncorr), - ns_diff(cntr.fec_symbol_err, old_cntr.fec_symbol_err))) - else: - table.append((key, self.get_port_state(key), - format_number_with_comma(cntr.fec_corr), - format_number_with_comma(cntr.fec_uncorr), - format_number_with_comma(cntr.fec_symbol_err))) - - elif rates_only: - header = header_rates_only - if old_cntr is not None: - 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.tx_ok, old_cntr.tx_ok), - format_brate(rates.tx_bps), - format_prate(rates.tx_pps), - format_util(rates.tx_bps, port_speed))) - else: - table.append((key, - self.get_port_state(key), - format_number_with_comma(cntr.rx_ok), - format_brate(rates.rx_bps), - format_prate(rates.rx_pps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(cntr.tx_ok), - 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), - 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))) - else: - table.append((key, - self.get_port_state(key), - format_number_with_comma(cntr.rx_ok), - format_brate(rates.rx_bps), - format_util(rates.rx_bps, port_speed), - format_number_with_comma(cntr.rx_err), - format_number_with_comma(cntr.rx_drop), - format_number_with_comma(cntr.rx_ovr), - format_number_with_comma(cntr.tx_ok), - format_brate(rates.tx_bps), - format_util(rates.tx_bps, port_speed), - format_number_with_comma(cntr.tx_err), - format_number_with_comma(cntr.tx_drop), - format_number_with_comma(cntr.tx_ovr))) - if table: - if use_json: - print(table_as_json(table, header)) - else: - print(tabulate(table, header, tablefmt='simple', stralign='right')) - if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json: - print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n") def main(): parser = argparse.ArgumentParser(description='Display the ports state and counters', diff --git a/tests/mock_tables/chassis_state_db.json b/tests/mock_tables/chassis_state_db.json index 6af9e19da..365cbf80c 100644 --- a/tests/mock_tables/chassis_state_db.json +++ b/tests/mock_tables/chassis_state_db.json @@ -7,6 +7,62 @@ }, "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": { "module_hostname": "sonic-lc3" + }, + "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc1": { + "timestamp": "2020-07-01 00:00:00" + }, + "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc3": { + "timestamp": "2020-07-01 00:00:00" + }, + "LINECARD_PORT_STAT_TABLE|Ethernet1/1": { + "state": "U", + "rx_ok": 100, + "rx_bps": 10, + "rx_pps": 1, + "rx_util": 0, + "rx_err": 0, + "rx_drop": 0, + "rx_ovr": 0, + "tx_ok": 100, + "tx_bps": 10, + "tx_pps": 1, + "tx_util": 0, + "tx_err": 0, + "tx_drop": 0, + "tx_ovr": 0 + }, + "LINECARD_PORT_STAT_TABLE|Ethernet2/1": { + "state": "U", + "rx_ok": 100, + "rx_bps": 10, + "rx_pps": 1, + "rx_util": 0, + "rx_err": 0, + "rx_drop": 0, + "rx_ovr": 0, + "tx_ok": 100, + "tx_bps": 10, + "tx_pps": 1, + "tx_util": 0, + "tx_err": 0, + "tx_drop": 0, + "tx_ovr": 0 + }, + "LINECARD_PORT_STAT_TABLE|Ethernet11/1": { + "state": "U", + "rx_ok": 100, + "rx_bps": 10, + "rx_pps": 1, + "rx_util": 0, + "rx_err": 0, + "rx_drop": 0, + "rx_ovr": 0, + "tx_ok": 100, + "tx_bps": 10, + "tx_pps": 1, + "tx_util": 0, + "tx_err": 0, + "tx_drop": 0, + "tx_ovr": 0 } - } \ No newline at end of file diff --git a/tests/portstat_db/on_sup_no_counters/chassis_state_db.json b/tests/portstat_db/on_sup_no_counters/chassis_state_db.json new file mode 100644 index 000000000..5c380954c --- /dev/null +++ b/tests/portstat_db/on_sup_no_counters/chassis_state_db.json @@ -0,0 +1,11 @@ +{ + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD0": { + "module_hostname": "sonic-lc1" + }, + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD1": { + "module_hostname": "sonic-lc2" + }, + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": { + "module_hostname": "sonic-lc3" + } +} \ No newline at end of file diff --git a/tests/portstat_db/on_sup_partial_lc/chassis_state_db.json b/tests/portstat_db/on_sup_partial_lc/chassis_state_db.json new file mode 100644 index 000000000..6040a8077 --- /dev/null +++ b/tests/portstat_db/on_sup_partial_lc/chassis_state_db.json @@ -0,0 +1,48 @@ +{ + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD0": { + "module_hostname": "sonic-lc1" + }, + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD1": { + "module_hostname": "sonic-lc2" + }, + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": { + "module_hostname": "sonic-lc3" + }, + "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc1": { + "timestamp": "2020-07-01 00:00:00" + }, + "LINECARD_PORT_STAT_TABLE|Ethernet1/1": { + "state": "U", + "rx_ok": 100, + "rx_bps": 10, + "rx_pps": 1, + "rx_util": 0, + "rx_err": 0, + "rx_drop": 0, + "rx_ovr": 0, + "tx_ok": 100, + "tx_bps": 10, + "tx_pps": 1, + "tx_util": 0, + "tx_err": 0, + "tx_drop": 0, + "tx_ovr": 0 + }, + "LINECARD_PORT_STAT_TABLE|Ethernet2/1": { + "state": "U", + "rx_ok": 100, + "rx_bps": 10, + "rx_pps": 1, + "rx_util": 0, + "rx_err": 0, + "rx_drop": 0, + "rx_ovr": 0, + "tx_ok": 100, + "tx_bps": 10, + "tx_pps": 1, + "tx_util": 0, + "tx_err": 0, + "tx_drop": 0, + "tx_ovr": 0 + } +} \ No newline at end of file diff --git a/tests/portstat_test.py b/tests/portstat_test.py index d418a16fe..b664612d9 100644 --- a/tests/portstat_test.py +++ b/tests/portstat_test.py @@ -8,8 +8,8 @@ from .utils import get_result_and_return_code from utilities_common.cli import UserCache -root_path = os.path.dirname(os.path.abspath(__file__)) -modules_path = os.path.dirname(root_path) +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) scripts_path = os.path.join(modules_path, "scripts") intf_counters_before_clear = """\ @@ -235,6 +235,23 @@ Time Since Counters Last Cleared............... None """ +intf_counters_on_sup = """\ + 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 +------------ ------- ------- --------- --------- -------- -------- -------- ------- --------- ---------\ + -------- -------- -------- + Ethernet1/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\ + 0 0 0 + Ethernet2/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\ + 0 0 0 +Ethernet11/1 U 100 10.00 B/s 0.00% 0 0 0 100 10.00 B/s 0.00%\ + 0 0 0 +""" + +intf_counters_on_sup_no_counters = "Linecard Counter Table is not available.\n" + +intf_counters_on_sup_partial_lc = "Not all linecards have published their counter values.\n" + TEST_PERIOD = 3 @@ -398,12 +415,79 @@ def test_clear_intf_counters(self): assert return_code == 0 verify_after_clear(result, intf_counter_after_clear) + def test_show_intf_counters_on_sup(self): + remove_tmp_cnstat_file() + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1" + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_counters_on_sup + + return_code, result = get_result_and_return_code(['portstat']) + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_counters_on_sup + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0" + + def test_show_intf_counters_on_sup_no_counters(self): + remove_tmp_cnstat_file() + os.system("cp {} /tmp/".format(os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + os.system("cp {} {}".format(os.path.join(test_path, "portstat_db/on_sup_no_counters/chassis_state_db.json"), + os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1" + + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_counters_on_sup_no_counters + + return_code, result = get_result_and_return_code(['portstat']) + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_counters_on_sup_no_counters + + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0" + os.system("cp /tmp/chassis_state_db.json {}".format(os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + + def test_show_intf_counters_on_sup_partial_lc(self): + remove_tmp_cnstat_file() + os.system("cp {} /tmp/".format(os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + os.system("cp {} {}".format(os.path.join(test_path, "portstat_db/on_sup_partial_lc/chassis_state_db.json"), + os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1" + + runner = CliRunner() + result = runner.invoke( + show.cli.commands["interfaces"].commands["counters"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == intf_counters_on_sup_partial_lc + + return_code, result = get_result_and_return_code(['portstat']) + print("return_code: {}".format(return_code)) + print("result = {}".format(result)) + assert return_code == 0 + assert result == intf_counters_on_sup_partial_lc + + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0" + os.system("cp /tmp/chassis_state_db.json {}".format(os.path.join(test_path, "mock_tables/chassis_state_db.json"))) + @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" + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0" remove_tmp_cnstat_file() diff --git a/utilities_common/netstat.py b/utilities_common/netstat.py index 5f17c1f4c..21b1a0fae 100755 --- a/utilities_common/netstat.py +++ b/utilities_common/netstat.py @@ -118,3 +118,12 @@ def format_util(brate, port_rate): util = brate/(float(port_rate)*1000*1000/8.0)*100 return "{:.2f}%".format(util) + +def format_util_directly(util): + """ + Format the util without calculation. + """ + if util == STATUS_NA: + return STATUS_NA + else: + return "{:.2f}%".format(float(util)) diff --git a/utilities_common/portstat.py b/utilities_common/portstat.py new file mode 100644 index 000000000..5dae085aa --- /dev/null +++ b/utilities_common/portstat.py @@ -0,0 +1,665 @@ +import datetime +import time +from collections import OrderedDict, namedtuple + +from natsort import natsorted +from tabulate import tabulate +from sonic_py_common import multi_asic +from sonic_py_common import device_info +from swsscommon.swsscommon import SonicV2Connector, CounterTable, PortCounter + +from utilities_common import constants +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, format_number_with_comma, format_util_directly + +""" +The order and count of statistics mentioned below needs to be in sync with the values in portstat script +So, any fields added/deleted in here should be reflected in portstat script also +""" +NStats = namedtuple("NStats", "rx_ok, rx_err, rx_drop, rx_ovr, tx_ok,\ + tx_err, tx_drop, tx_ovr, rx_byt, tx_byt,\ + rx_64, rx_65_127, rx_128_255, rx_256_511, rx_512_1023,\ + rx_1024_1518, rx_1519_2047, rx_2048_4095, rx_4096_9216, rx_9217_16383,\ + rx_uca, rx_mca, rx_bca, rx_all,\ + tx_64, tx_65_127, tx_128_255, tx_256_511, tx_512_1023, tx_1024_1518,\ + tx_1519_2047, tx_2048_4095, tx_4096_9216, tx_9217_16383,\ + tx_uca, tx_mca, tx_bca, tx_all,\ + rx_jbr, rx_frag, rx_usize, rx_ovrrun,\ + fec_corr, fec_uncorr, fec_symbol_err") +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_std = ['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'] +header_errors_only = ['IFACE', 'STATE', 'RX_ERR', 'RX_DRP', 'RX_OVR', 'TX_ERR', 'TX_DRP', 'TX_OVR'] +header_fec_only = ['IFACE', 'STATE', 'FEC_CORR', 'FEC_UNCORR', 'FEC_SYMBOL_ERR'] +header_rates_only = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL'] + +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) + +""" +The order and count of statistics mentioned below needs to be in sync with the values in portstat script +So, any fields added/deleted in here should be reflected in portstat script also +""" +BUCKET_NUM = 45 +counter_bucket_dict = { + 0: ['SAI_PORT_STAT_IF_IN_UCAST_PKTS', 'SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS'], + 1: ['SAI_PORT_STAT_IF_IN_ERRORS'], + 2: ['SAI_PORT_STAT_IF_IN_DISCARDS'], + 3: ['SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS'], + 4: ['SAI_PORT_STAT_IF_OUT_UCAST_PKTS', 'SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS'], + 5: ['SAI_PORT_STAT_IF_OUT_ERRORS'], + 6: ['SAI_PORT_STAT_IF_OUT_DISCARDS'], + 7: ['SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS'], + 8: ['SAI_PORT_STAT_IF_IN_OCTETS'], + 9: ['SAI_PORT_STAT_IF_OUT_OCTETS'], + 10: ['SAI_PORT_STAT_ETHER_IN_PKTS_64_OCTETS'], + 11: ['SAI_PORT_STAT_ETHER_IN_PKTS_65_TO_127_OCTETS'], + 12: ['SAI_PORT_STAT_ETHER_IN_PKTS_128_TO_255_OCTETS'], + 13: ['SAI_PORT_STAT_ETHER_IN_PKTS_256_TO_511_OCTETS'], + 14: ['SAI_PORT_STAT_ETHER_IN_PKTS_512_TO_1023_OCTETS'], + 15: ['SAI_PORT_STAT_ETHER_IN_PKTS_1024_TO_1518_OCTETS'], + 16: ['SAI_PORT_STAT_ETHER_IN_PKTS_1519_TO_2047_OCTETS'], + 17: ['SAI_PORT_STAT_ETHER_IN_PKTS_2048_TO_4095_OCTETS'], + 18: ['SAI_PORT_STAT_ETHER_IN_PKTS_4096_TO_9216_OCTETS'], + 19: ['SAI_PORT_STAT_ETHER_IN_PKTS_9217_TO_16383_OCTETS'], + 20: ['SAI_PORT_STAT_IF_IN_UCAST_PKTS'], + 21: ['SAI_PORT_STAT_IF_IN_MULTICAST_PKTS'], + 22: ['SAI_PORT_STAT_IF_IN_BROADCAST_PKTS'], + 23: ['SAI_PORT_STAT_IF_IN_UCAST_PKTS', 'SAI_PORT_STAT_IF_IN_MULTICAST_PKTS', + 'SAI_PORT_STAT_IF_IN_BROADCAST_PKTS'], + 24: ['SAI_PORT_STAT_ETHER_OUT_PKTS_64_OCTETS'], + 25: ['SAI_PORT_STAT_ETHER_OUT_PKTS_65_TO_127_OCTETS'], + 26: ['SAI_PORT_STAT_ETHER_OUT_PKTS_128_TO_255_OCTETS'], + 27: ['SAI_PORT_STAT_ETHER_OUT_PKTS_256_TO_511_OCTETS'], + 28: ['SAI_PORT_STAT_ETHER_OUT_PKTS_512_TO_1023_OCTETS'], + 29: ['SAI_PORT_STAT_ETHER_OUT_PKTS_1024_TO_1518_OCTETS'], + 30: ['SAI_PORT_STAT_ETHER_OUT_PKTS_1519_TO_2047_OCTETS'], + 31: ['SAI_PORT_STAT_ETHER_OUT_PKTS_2048_TO_4095_OCTETS'], + 32: ['SAI_PORT_STAT_ETHER_OUT_PKTS_4096_TO_9216_OCTETS'], + 33: ['SAI_PORT_STAT_ETHER_OUT_PKTS_9217_TO_16383_OCTETS'], + 34: ['SAI_PORT_STAT_IF_OUT_UCAST_PKTS'], + 35: ['SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS'], + 36: ['SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS'], + 37: ['SAI_PORT_STAT_IF_OUT_UCAST_PKTS', 'SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS', + 'SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS'], + 38: ['SAI_PORT_STAT_ETHER_STATS_JABBERS'], + 39: ['SAI_PORT_STAT_ETHER_STATS_FRAGMENTS'], + 40: ['SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS'], + 41: ['SAI_PORT_STAT_IP_IN_RECEIVES'], + 42: ['SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES'], + 43: ['SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES'], + 44: ['SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS'] +} + +STATUS_NA = 'N/A' + +RATES_TABLE_PREFIX = "RATES:" + +COUNTER_TABLE_PREFIX = "COUNTERS:" +COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" + +PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" +PORT_STATE_TABLE_PREFIX = "PORT_TABLE|" +PORT_OPER_STATUS_FIELD = "oper_status" +PORT_ADMIN_STATUS_FIELD = "admin_status" +PORT_STATUS_VALUE_UP = 'UP' +PORT_STATUS_VALUE_DOWN = 'DOWN' +PORT_SPEED_FIELD = "speed" + +PORT_STATE_UP = 'U' +PORT_STATE_DOWN = 'D' +PORT_STATE_DISABLED = 'X' + +LINECARD_PORT_STAT_TABLE = 'LINECARD_PORT_STAT_TABLE' +LINECARD_PORT_STAT_MARK_TABLE = 'LINECARD_PORT_STAT_MARK_TABLE' +CHASSIS_MIDPLANE_INFO_TABLE = 'CHASSIS_MIDPLANE_TABLE' + + +class Portstat(object): + def __init__(self, namespace, display_option): + self.db = None + self.multi_asic = multi_asic_util.MultiAsic(display_option, namespace) + if device_info.is_supervisor(): + self.db = SonicV2Connector(use_unix_socket_path=False) + self.db.connect(self.db.CHASSIS_STATE_DB, False) + + def get_cnstat_dict(self): + self.cnstat_dict = OrderedDict() + self.cnstat_dict['time'] = datetime.datetime.now() + self.ratestat_dict = OrderedDict() + if device_info.is_supervisor(): + self.collect_stat_from_lc() + else: + self.collect_stat() + return self.cnstat_dict, self.ratestat_dict + + def collect_stat_from_lc(self): + # Retrieve the current counter values from all LCs + + # Clear stale records + self.db.delete_all_by_pattern(self.db.CHASSIS_STATE_DB, LINECARD_PORT_STAT_TABLE + "*") + self.db.delete_all_by_pattern(self.db.CHASSIS_STATE_DB, LINECARD_PORT_STAT_MARK_TABLE + "*") + + # Check how many linecards are connected + tempdb = SonicV2Connector(use_unix_socket_path=False) + tempdb.connect(tempdb.STATE_DB, False) + linecard_midplane_keys = tempdb.keys(tempdb.STATE_DB, CHASSIS_MIDPLANE_INFO_TABLE + "*") + lc_count = 0 + if not linecard_midplane_keys: + # LC has not published it's Counter which could be due to chassis_port_counter_monitor.service not running + print("No linecards are connected!") + return + else: + for key in linecard_midplane_keys: + linecard_status = tempdb.get(tempdb.STATE_DB, key, "access") + if linecard_status == "True": + lc_count += 1 + + # Notify the Linecards to publish their counter values instantly + self.db.set(self.db.CHASSIS_STATE_DB, "GET_LINECARD_COUNTER|pull", "enable", "true") + time.sleep(2) + + # Check if all LCs have published counters + linecard_names = self.db.keys(self.db.CHASSIS_STATE_DB, LINECARD_PORT_STAT_MARK_TABLE + "*") + linecard_port_aliases = self.db.keys(self.db.CHASSIS_STATE_DB, LINECARD_PORT_STAT_TABLE + "*") + if not linecard_port_aliases: + # LC has not published it's Counter which could be due to chassis_port_counter_monitor.service not running + print("Linecard Counter Table is not available.") + return + if len(linecard_names) != lc_count: + print("Not all linecards have published their counter values.") + return + + # Create the dictornaries to store the counter values + cnstat_dict = OrderedDict() + cnstat_dict['time'] = datetime.datetime.now() + ratestat_dict = OrderedDict() + + # Get the counter values from CHASSIS_STATE_DB + for key in linecard_port_aliases: + rx_ok = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_ok") + rx_bps = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_bps") + rx_pps = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_pps") + rx_util = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_util") + rx_err = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_err") + rx_drop = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_drop") + rx_ovr = self.db.get(self.db.CHASSIS_STATE_DB, key, "rx_ovr") + tx_ok = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_ok") + tx_bps = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_bps") + tx_pps = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_pps") + tx_util = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_util") + tx_err = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_err") + tx_drop = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_drop") + tx_ovr = self.db.get(self.db.CHASSIS_STATE_DB, key, "tx_ovr") + port_alias = key.split("|")[-1] + cnstat_dict[port_alias] = NStats._make([rx_ok, rx_err, rx_drop, rx_ovr, tx_ok, tx_err, tx_drop, tx_ovr] + + [STATUS_NA] * (len(NStats._fields) - 8))._asdict() + ratestat_dict[port_alias] = RateStats._make([rx_bps, rx_pps, rx_util, tx_bps, tx_pps, tx_util]) + self.cnstat_dict.update(cnstat_dict) + self.ratestat_dict.update(ratestat_dict) + + @multi_asic_util.run_on_multi_asic + def collect_stat(self): + """ + Collect the statisitics from all the asics present on the + device and store in a dict + """ + + cnstat_dict, ratestat_dict = self.get_cnstat() + self.cnstat_dict.update(cnstat_dict) + self.ratestat_dict.update(ratestat_dict) + + def get_cnstat(self): + """ + Get the counters info from database. + """ + def get_counters(table_id): + """ + Get the counters from specific table. + """ + fields = ["0"]*BUCKET_NUM + + for pos, cntr_list in counter_bucket_dict.items(): + for counter_name in cntr_list: + full_table_id = COUNTER_TABLE_PREFIX + table_id + counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name) + if counter_data is None: + fields[pos] = STATUS_NA + elif fields[pos] != STATUS_NA: + fields[pos] = str(int(fields[pos]) + int(counter_data)) + + cntr = NStats._make(fields)._asdict() + 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() + cnstat_dict['time'] = datetime.datetime.now() + ratestat_dict = OrderedDict() + if counter_port_name_map is None: + 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]) + ratestat_dict[port] = get_rates(counter_port_name_map[port]) + return cnstat_dict, ratestat_dict + + def get_port_speed(self, port_name): + """ + Get the port speed + """ + # Get speed from APPL_DB + state_db_table_id = PORT_STATE_TABLE_PREFIX + port_name + app_db_table_id = PORT_STATUS_TABLE_PREFIX + port_name + for ns in self.multi_asic.get_ns_list_based_on_options(): + self.db = multi_asic.connect_to_all_dbs_for_ns(ns) + speed = self.db.get(self.db.STATE_DB, state_db_table_id, PORT_SPEED_FIELD) + oper_status = self.db.get(self.db.APPL_DB, app_db_table_id, PORT_OPER_STATUS_FIELD) + if speed is None or speed == STATUS_NA or oper_status != "up": + speed = self.db.get(self.db.APPL_DB, app_db_table_id, PORT_SPEED_FIELD) + if speed is not None: + return int(speed) + return STATUS_NA + + def get_port_state(self, port_name): + """ + Get the port state + """ + if device_info.is_supervisor(): + self.db.connect(self.db.CHASSIS_STATE_DB, False) + return self.db.get(self.db.CHASSIS_STATE_DB, LINECARD_PORT_STAT_TABLE + "|" + port_name, "state") + + full_table_id = PORT_STATUS_TABLE_PREFIX + port_name + for ns in self.multi_asic.get_ns_list_based_on_options(): + self.db = multi_asic.connect_to_all_dbs_for_ns(ns) + 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: + continue + if 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 + return STATUS_NA + + def cnstat_print(self, cnstat_dict, ratestat_dict, intf_list, use_json, print_all, + errors_only, fec_stats_only, rates_only, detail=False): + """ + Print the cnstat. + """ + + if intf_list and detail: + self.cnstat_intf_diff_print(cnstat_dict, {}, intf_list) + return None + + table = [] + header = None + + for key in natsorted(cnstat_dict.keys()): + if key == 'time': + continue + if intf_list and key not in intf_list: + continue + port_speed = self.get_port_speed(key) + data = cnstat_dict[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), + format_number_with_comma(data["rx_ok"]), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(data["rx_err"]), + format_number_with_comma(data["rx_drop"]), + format_number_with_comma(data["rx_ovr"]), + format_number_with_comma(data["tx_ok"]), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + format_number_with_comma(data["tx_err"]), + format_number_with_comma(data["tx_drop"]), + format_number_with_comma(data["tx_ovr"]))) + elif errors_only: + header = header_errors_only + table.append((key, self.get_port_state(key), + format_number_with_comma(data["rx_err"]), + format_number_with_comma(data["rx_drop"]), + format_number_with_comma(data["rx_ovr"]), + format_number_with_comma(data["tx_err"]), + format_number_with_comma(data["tx_drop"]), + format_number_with_comma(data["tx_ovr"]))) + elif fec_stats_only: + header = header_fec_only + table.append((key, self.get_port_state(key), + format_number_with_comma(data['fec_corr']), + format_number_with_comma(data['fec_uncorr']), + format_number_with_comma(data['fec_symbol_err']))) + elif rates_only: + header = header_rates_only + table.append((key, self.get_port_state(key), + format_number_with_comma(data["rx_ok"]), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(data["tx_ok"]), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util))) + else: + header = header_std + table.append((key, self.get_port_state(key), + format_number_with_comma(data["rx_ok"]), + format_brate(rates.rx_bps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(data["rx_err"]), + format_number_with_comma(data["rx_drop"]), + format_number_with_comma(data["rx_ovr"]), + format_number_with_comma(data["tx_ok"]), + format_brate(rates.tx_bps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + format_number_with_comma(data["tx_err"]), + format_number_with_comma(data["tx_drop"]), + format_number_with_comma(data["tx_ovr"]))) + if table: + if use_json: + print(table_as_json(table, header)) + else: + print(tabulate(table, header, tablefmt='simple', stralign='right')) + if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json: + print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n") + + def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list): + """ + Print the difference between two cnstat results for interface. + """ + + for key in natsorted(cnstat_new_dict.keys()): + cntr = cnstat_new_dict.get(key) + if key == 'time': + continue + + if key in cnstat_old_dict: + old_cntr = cnstat_old_dict.get(key) + else: + old_cntr = NStats._make([0] * BUCKET_NUM)._asdict() + + if intf_list and key not in intf_list: + continue + + print("Packets Received 64 Octets..................... {}".format(ns_diff(cntr['rx_64'], + old_cntr['rx_64']))) + print("Packets Received 65-127 Octets................. {}".format(ns_diff(cntr['rx_65_127'], + old_cntr['rx_65_127']))) + print("Packets Received 128-255 Octets................ {}".format(ns_diff(cntr['rx_128_255'], + old_cntr['rx_128_255']))) + print("Packets Received 256-511 Octets................ {}".format(ns_diff(cntr['rx_256_511'], + old_cntr['rx_256_511']))) + print("Packets Received 512-1023 Octets............... {}".format(ns_diff(cntr['rx_512_1023'], + old_cntr['rx_512_1023']))) + print("Packets Received 1024-1518 Octets.............. {}".format(ns_diff(cntr['rx_1024_1518'], + old_cntr['rx_1024_1518']))) + print("Packets Received 1519-2047 Octets.............. {}".format(ns_diff(cntr['rx_1519_2047'], + old_cntr['rx_1519_2047']))) + print("Packets Received 2048-4095 Octets.............. {}".format(ns_diff(cntr['rx_2048_4095'], + old_cntr['rx_2048_4095']))) + print("Packets Received 4096-9216 Octets.............. {}".format(ns_diff(cntr['rx_4096_9216'], + old_cntr['rx_4096_9216']))) + print("Packets Received 9217-16383 Octets............. {}".format(ns_diff(cntr['rx_9217_16383'], + old_cntr['rx_9217_16383']))) + + print("") + print("Total Packets Received Without Errors.......... {}".format(ns_diff(cntr['rx_all'], + old_cntr['rx_all']))) + print("Unicast Packets Received....................... {}".format(ns_diff(cntr['rx_uca'], + old_cntr['rx_uca']))) + print("Multicast Packets Received..................... {}".format(ns_diff(cntr['rx_mca'], + old_cntr['rx_mca']))) + print("Broadcast Packets Received..................... {}".format(ns_diff(cntr['rx_bca'], + old_cntr['rx_bca']))) + + print("") + print("Jabbers Received............................... {}".format(ns_diff(cntr['rx_jbr'], + old_cntr['rx_jbr']))) + print("Fragments Received............................. {}".format(ns_diff(cntr['rx_frag'], + old_cntr['rx_frag']))) + print("Undersize Received............................. {}".format(ns_diff(cntr['rx_usize'], + old_cntr['rx_usize']))) + print("Overruns Received.............................. {}".format(ns_diff(cntr["rx_ovrrun"], + old_cntr["rx_ovrrun"]))) + + print("") + print("Packets Transmitted 64 Octets.................. {}".format(ns_diff(cntr['tx_64'], + old_cntr['tx_64']))) + print("Packets Transmitted 65-127 Octets.............. {}".format(ns_diff(cntr['tx_65_127'], + old_cntr['tx_65_127']))) + print("Packets Transmitted 128-255 Octets............. {}".format(ns_diff(cntr['tx_128_255'], + old_cntr['tx_128_255']))) + print("Packets Transmitted 256-511 Octets............. {}".format(ns_diff(cntr['tx_256_511'], + old_cntr['tx_256_511']))) + print("Packets Transmitted 512-1023 Octets............ {}".format(ns_diff(cntr['tx_512_1023'], + old_cntr['tx_512_1023']))) + print("Packets Transmitted 1024-1518 Octets........... {}".format(ns_diff(cntr['tx_1024_1518'], + old_cntr['tx_1024_1518']))) + print("Packets Transmitted 1519-2047 Octets........... {}".format(ns_diff(cntr['tx_1519_2047'], + old_cntr['tx_1519_2047']))) + print("Packets Transmitted 2048-4095 Octets........... {}".format(ns_diff(cntr['tx_2048_4095'], + old_cntr['tx_2048_4095']))) + print("Packets Transmitted 4096-9216 Octets........... {}".format(ns_diff(cntr['tx_4096_9216'], + old_cntr['tx_4096_9216']))) + print("Packets Transmitted 9217-16383 Octets.......... {}".format(ns_diff(cntr['tx_9217_16383'], + old_cntr['tx_9217_16383']))) + + print("") + print("Total Packets Transmitted Successfully......... {}".format(ns_diff(cntr['tx_all'], + old_cntr['tx_all']))) + print("Unicast Packets Transmitted.................... {}".format(ns_diff(cntr['tx_uca'], + old_cntr['tx_uca']))) + print("Multicast Packets Transmitted.................. {}".format(ns_diff(cntr['tx_mca'], + old_cntr['tx_mca']))) + print("Broadcast Packets Transmitted.................. {}".format(ns_diff(cntr['tx_bca'], + old_cntr['tx_bca']))) + + 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, fec_stats_only, + rates_only, detail=False): + """ + Print the difference between two cnstat results. + """ + + if intf_list and detail: + self.cnstat_intf_diff_print(cnstat_new_dict, cnstat_old_dict, intf_list) + return None + + table = [] + header = None + + for key in natsorted(cnstat_new_dict.keys()): + cntr = cnstat_new_dict.get(key) + if key == 'time': + continue + old_cntr = None + if key in cnstat_old_dict: + old_cntr = cnstat_old_dict.get(key) + + 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"]), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + 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) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + 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), + format_number_with_comma(cntr["rx_ok"]), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(cntr["rx_err"]), + format_number_with_comma(cntr["rx_drop"]), + format_number_with_comma(cntr["rx_ovr"]), + format_number_with_comma(cntr["tx_ok"]), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + format_number_with_comma(cntr["tx_err"]), + format_number_with_comma(cntr["tx_drop"]), + format_number_with_comma(cntr["tx_ovr"]))) + elif errors_only: + header = header_errors_only + if old_cntr is not None: + table.append((key, self.get_port_state(key), + 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_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), + format_number_with_comma(cntr["rx_err"]), + format_number_with_comma(cntr["rx_drop"]), + format_number_with_comma(cntr["rx_ovr"]), + format_number_with_comma(cntr["tx_err"]), + format_number_with_comma(cntr["tx_drop"]), + format_number_with_comma(cntr["tx_ovr"]))) + elif fec_stats_only: + header = header_fec_only + if old_cntr is not None: + table.append((key, self.get_port_state(key), + ns_diff(cntr['fec_corr'], old_cntr['fec_corr']), + ns_diff(cntr['fec_uncorr'], old_cntr['fec_uncorr']), + ns_diff(cntr['fec_symbol_err'], old_cntr['fec_symbol_err']))) + else: + table.append((key, self.get_port_state(key), + format_number_with_comma(cntr['fec_corr']), + format_number_with_comma(cntr['fec_uncorr']), + format_number_with_comma(cntr['fec_symbol_err']))) + + elif rates_only: + header = header_rates_only + if old_cntr is not None: + 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) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + 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) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util))) + else: + table.append((key, + self.get_port_state(key), + format_number_with_comma(cntr["rx_ok"]), + format_brate(rates.rx_bps), + format_prate(rates.rx_pps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(cntr["tx_ok"]), + format_brate(rates.tx_bps), + format_prate(rates.tx_pps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util))) + 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"]), + format_brate(rates.rx_bps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + 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) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + 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), + format_number_with_comma(cntr["rx_ok"]), + format_brate(rates.rx_bps), + format_util(rates.rx_bps, port_speed) + if rates.rx_util == STATUS_NA else format_util_directly(rates.rx_util), + format_number_with_comma(cntr["rx_err"]), + format_number_with_comma(cntr["rx_drop"]), + format_number_with_comma(cntr["rx_ovr"]), + format_number_with_comma(cntr["tx_ok"]), + format_brate(rates.tx_bps), + format_util(rates.tx_bps, port_speed) + if rates.tx_util == STATUS_NA else format_util_directly(rates.tx_util), + format_number_with_comma(cntr["tx_err"]), + format_number_with_comma(cntr["tx_drop"]), + format_number_with_comma(cntr["tx_ovr"]))) + if table: + if use_json: + print(table_as_json(table, header)) + else: + print(tabulate(table, header, tablefmt='simple', stralign='right')) + if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json: + print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n")