diff --git a/scripts/queuestat b/scripts/queuestat index d18d8ee91d..3a74e5cc57 100755 --- a/scripts/queuestat +++ b/scripts/queuestat @@ -15,7 +15,9 @@ import sys from collections import namedtuple, OrderedDict from natsort import natsorted from tabulate import tabulate -from sonic_py_common import multi_asic +from sonic_py_common import multi_asic, device_info +from redis import Redis, exceptions +from swsscommon import swsscommon # mock the redis for unit test purposes # try: @@ -25,6 +27,11 @@ try: sys.path.insert(0, modules_path) sys.path.insert(0, tests_path) import mock_tables.dbconnector # lgtm [py/unused-import] + + if os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] == "1": + import mock + device_info.is_supervisor = mock.MagicMock(return_value=True) + if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic": import mock_tables.mock_multi_asic mock_tables.dbconnector.load_namespace_config() @@ -82,6 +89,39 @@ cnstat_dir = 'N/A' cnstat_fqn_file = 'N/A' +def get_redis_ips(db): + db.connect(db.STATE_DB) + redis_ips = [] + chassis_midplane_table = db.keys(db.STATE_DB, "CHASSIS_MIDPLANE_TABLE*") + lc_metadata = [] + for lc in chassis_midplane_table: + lc_metadata.append(db.get_all(db.STATE_DB, lc)) + + db.connect(db.CHASSIS_STATE_DB) + for lc in lc_metadata: + # skip if LC is offline + if lc['access'] == "False": + continue + + slot_id = int(lc['ip_address'].split(".")[2]) - 1 + num_asics = db.get(db.CHASSIS_STATE_DB, f"CHASSIS_MODULE_TABLE|LINE-CARD{slot_id}", 'num_asics') + + # Skip if pmon hasn't started on LC yet + if num_asics == None: + continue + + # No namespace in single ASIC LC + if num_asics == "1": + redis_ips.append(lc['ip_address']) + else: + prefix, _ = lc['ip_address'].rsplit(".", maxsplit=1) + for i in range(int(num_asics)): + prefix, _, _ = lc['ip_address'].rpartition(".") + redis_ips.append(f"{prefix}.{10+i}") + + return redis_ips + + def build_json(port, cnstat, all=False, trim=False, voq=False): def ports_stats(k): p = {} @@ -120,6 +160,18 @@ def build_json(port, cnstat, all=False, trim=False, voq=False): out.update(ports_stats(k)) return out +def run_queuestat(save_fresh_stats, port_to_show_stats, json_opt, non_zero, ns, db, voq, trim, all_): + queuestat = Queuestat(ns, db, all_, trim, voq) + if save_fresh_stats: + queuestat.save_fresh_stats() + return + + if port_to_show_stats != None: + queuestat.get_print_port_stat(port_to_show_stats, json_opt, non_zero) + else: + queuestat.get_print_all_stat(json_opt, non_zero) + + class QueuestatWrapper(object): """A wrapper to execute queuestat cmd over the correct namespaces""" def __init__(self, namespace, all, trim, voq): @@ -134,16 +186,8 @@ class QueuestatWrapper(object): @multi_asic_util.run_on_multi_asic def run(self, save_fresh_stats, port_to_show_stats, json_opt, non_zero): - queuestat = Queuestat(self.multi_asic.current_namespace, self.db, self.all, self.trim, self.voq) - if save_fresh_stats: - queuestat.save_fresh_stats() - return - - if port_to_show_stats != None: - queuestat.get_print_port_stat(port_to_show_stats, json_opt, non_zero) - else: - queuestat.get_print_all_stat(json_opt, non_zero) - + run_queuestat(save_fresh_stats, port_to_show_stats, json_opt, non_zero, \ + self.multi_asic.current_namespace, self.db, self.voq, self.trim, self.all) class Queuestat(object): def __init__(self, namespace, db, all=False, trim=False, voq=False): @@ -151,7 +195,11 @@ class Queuestat(object): self.all = all self.trim = trim self.voq = voq + self.voq_stats = {} self.namespace = namespace + if namespace is None: + self.db = SonicV2Connector(use_unix_socket_path=False) + self.db.connect(self.db.COUNTERS_DB) self.namespace_str = f" for {namespace}" if namespace else '' def get_queue_port(table_id): @@ -164,7 +212,9 @@ class Queuestat(object): # Get all ports if voq: - self.counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_SYSTEM_PORT_NAME_MAP) + # counter_port_name_map is assigned later for supervisor as a list + self.counter_port_name_map = [] if device_info.is_supervisor() else \ + self.db.get_all(self.db.COUNTERS_DB, COUNTERS_SYSTEM_PORT_NAME_MAP) else: self.counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP) @@ -179,6 +229,16 @@ class Queuestat(object): self.port_queues_map[port] = {} self.port_name_map[self.counter_port_name_map[port]] = port + if self.voq: + counter_bucket_dict.update(voq_counter_bucket_dict) + else: + counter_bucket_dict.update(trim_counter_bucket_dict) + + if device_info.is_supervisor(): + self.aggregate_voq_stats() + self.counter_port_name_map = self.voq_stats.keys() + return + counter_queue_name_map = None # Get Queues for each port if voq: @@ -194,6 +254,44 @@ class Queuestat(object): port = self.port_name_map[get_queue_port(counter_queue_name_map[queue])] self.port_queues_map[port][queue] = counter_queue_name_map[queue] + def aggregate_voq_stats(self): + redis_ips = get_redis_ips(self.db) + self.voq_stats = {} + + for ip in redis_ips: + asic_counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, ip, 6379, 0) + try: + counters_voq_name_map = asic_counters_db.hgetall(COUNTERS_VOQ_NAME_MAP) + if counters_voq_name_map is None: + continue + for voq in counters_voq_name_map: + # key LINECARD|ASIC|EthernetXXX:INDEX + sysPort, idx = voq.split(":") + for counter_name in counter_bucket_dict: + self.voq_stats.setdefault(sysPort, {}).setdefault(idx, {}).setdefault(counter_name, 0) + oid = counters_voq_name_map[voq] + counter_data = asic_counters_db.hget("COUNTERS:"+oid, counter_name) + if counter_data is not None: + self.voq_stats[sysPort][idx][counter_name] += int(counter_data) + + except exceptions.ConnectionError as e: + # Skip further operations for this redis-instance + continue + + def get_aggregate_port_stats(self, port): + # Build a dictionary of stats + cnstat_dict = OrderedDict() + cnstat_dict['time'] = datetime.datetime.now() + for idx in sorted(self.voq_stats[port].keys()): + fields = ["0"]*len(voq_header) + fields[0] = idx + fields[1] = QUEUE_TYPE_VOQ + for counter_name, pos in counter_bucket_dict.items(): + fields[pos] = str(self.voq_stats[port][idx][counter_name]) + cntr = VoqStats._make(fields)._asdict() + cnstat_dict[port+":"+idx] = cntr + return cnstat_dict + def get_cnstat(self, queue_map): """ Get the counters info from database. @@ -230,11 +328,6 @@ class Queuestat(object): counter_dict = { **counter_bucket_dict } fields = [ get_queue_index(table_id), get_queue_type(table_id) ] - if self.voq: - counter_dict.update(voq_counter_bucket_dict) - else: - counter_dict.update(trim_counter_bucket_dict) - # Layout is per QueueStats/VoqStats type definition fields.extend(["0"]*len(counter_dict)) @@ -325,7 +418,8 @@ class Queuestat(object): hdr = std_header if table: - print(f"For namespace {self.namespace}:") + if not device_info.is_supervisor(): + print(f"For namespace {self.namespace}:") print(tabulate(table, hdr, tablefmt='simple', stralign='right')) print() @@ -428,7 +522,11 @@ class Queuestat(object): json_output = {} for port in natsorted(self.counter_port_name_map): json_output[port] = {} - cnstat_dict = self.get_cnstat(self.port_queues_map[port]) + if self.voq and device_info.is_supervisor(): + cnstat_dict = self.get_aggregate_port_stats(port) + else: + cnstat_dict = self.get_cnstat(self.port_queues_map[port]) + cache_ns = '' if self.voq and self.namespace is not None: cache_ns = '-' + self.namespace + '-' @@ -457,12 +555,16 @@ class Queuestat(object): Get stat for the port If JSON option is True print data in JSON format """ - if not port in self.port_queues_map: + if port not in self.port_queues_map and port not in self.voq_stats: print("Port doesn't exist!", port) sys.exit(1) # Get stat for the port queried - cnstat_dict = self.get_cnstat(self.port_queues_map[port]) + + if self.voq and device_info.is_supervisor(): + cnstat_dict = self.get_aggregate_port_stats(port) + else: + cnstat_dict = self.get_cnstat(self.port_queues_map[port]) cache_ns = '' if self.voq and self.namespace is not None: cache_ns = '-' + self.namespace + '-' @@ -542,8 +644,13 @@ def main(port, clear, delete, json_opt, all, trim, voq, non_zero, namespace): if delete_stats: cache.remove() - queuestat_wrapper = QueuestatWrapper(namespace, all, trim, voq) - queuestat_wrapper.run(save_fresh_stats, port_to_show_stats, json_opt, non_zero) + + if device_info.is_supervisor() and namespace is None: + run_queuestat(save_fresh_stats, port_to_show_stats, json_opt, non_zero, namespace, None, voq, trim, all) + else: + queuestat_wrapper = QueuestatWrapper(namespace, all, trim, voq) + queuestat_wrapper.run(save_fresh_stats, port_to_show_stats, json_opt, non_zero) + sys.exit(0) diff --git a/tests/chassis_modules_test.py b/tests/chassis_modules_test.py index c1fd653ecc..305d26b380 100755 --- a/tests/chassis_modules_test.py +++ b/tests/chassis_modules_test.py @@ -46,11 +46,12 @@ """ show_chassis_midplane_output="""\ - Name IP-Address Reachability ----------- ------------- -------------- -LINE-CARD0 192.168.1.100 True -LINE-CARD1 192.168.1.2 False -LINE-CARD2 192.168.1.1 True + Name IP-Address Reachability +---------- ------------ -------------- +LINE-CARD0 192.168.3.1 True +LINE-CARD1 192.168.4.1 False +LINE-CARD2 192.168.5.1 True +LINE-CARD3 192.168.6.1 True """ show_chassis_system_ports_output_asic0="""\ @@ -346,7 +347,7 @@ def test_midplane_show_all_count_lines(self): result = runner.invoke(show.cli.commands["chassis"].commands["modules"].commands["midplane-status"], []) print(result.output) result_lines = result.output.strip('\n').split('\n') - modules = ["LINE-CARD0", "LINE-CARD1", "LINE-CARD2"] + modules = ["LINE-CARD0", "LINE-CARD1", "LINE-CARD2", "LINE-CARD3"] for i, module in enumerate(modules): assert module in result_lines[i + warning_lines + header_lines] assert len(result_lines) == warning_lines + header_lines + len(modules) diff --git a/tests/mock_tables/asic0/counters_db.json b/tests/mock_tables/asic0/counters_db.json index c61a00e192..7de2015c41 100644 --- a/tests/mock_tables/asic0/counters_db.json +++ b/tests/mock_tables/asic0/counters_db.json @@ -2788,5 +2788,23 @@ "PERSISTENT_WATERMARKS:oid:100000000b0ff": { "SAI_INGRESS_PRIORITY_GROUP_STAT_SHARED_WATERMARK_BYTES": "507", "SAI_INGRESS_PRIORITY_GROUP_STAT_XOFF_ROOM_WATERMARK_BYTES": "507" + }, + "COUNTERS_VOQ_NAME_MAP": { + "sonic-lc1|asic0|Ethernet0:0": "oid:0x150000000005c2", + "sonic-lc2|asic0|Ethernet4:1": "oid:0x1500000000047a" + }, + "COUNTERS:oid:0x150000000005c2": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "122", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "1", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "122", + "SAI_QUEUE_STAT_PACKETS": "1" + }, + "COUNTERS:oid:0x1500000000047a": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "244", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "2", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "366", + "SAI_QUEUE_STAT_PACKETS": "3" } } diff --git a/tests/mock_tables/asic1/counters_db.json b/tests/mock_tables/asic1/counters_db.json index 1455f069c0..254221020b 100644 --- a/tests/mock_tables/asic1/counters_db.json +++ b/tests/mock_tables/asic1/counters_db.json @@ -1675,5 +1675,23 @@ }, "USER_WATERMARKS:oid:0x100000000b115": { "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "2" + }, + "COUNTERS_VOQ_NAME_MAP": { + "sonic-lc1|asic0|Ethernet0:0": "oid:0x1500000000059d", + "sonic-lc2|asic0|Ethernet4:1": "oid:0x15000000000571" + }, + "COUNTERS:oid:0x1500000000059d": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "1098", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "9", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "854", + "SAI_QUEUE_STAT_PACKETS": "7" + }, + "COUNTERS:oid:0x15000000000571": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "732", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "6", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "610", + "SAI_QUEUE_STAT_PACKETS": "5" } } diff --git a/tests/mock_tables/asic2/counters_db.json b/tests/mock_tables/asic2/counters_db.json index 66875f8245..5498c4a10c 100644 --- a/tests/mock_tables/asic2/counters_db.json +++ b/tests/mock_tables/asic2/counters_db.json @@ -1699,5 +1699,23 @@ }, "COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP": { "DEBUG_1": "SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE" + }, + "COUNTERS_VOQ_NAME_MAP": { + "sonic-lc1|asic0|Ethernet0:0": "oid:0x15000000000532", + "sonic-lc2|asic0|Ethernet4:1": "oid:0x15000000000521" + }, + "COUNTERS:oid:0x15000000000532": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "0", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "0", + "SAI_QUEUE_STAT_PACKETS": "0" + }, + "COUNTERS:oid:0x15000000000521": { + "SAI_QUEUE_STAT_DROPPED_BYTES": "976", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "8", + "SAI_QUEUE_STAT_CREDIT_WD_DELETED_PACKETS": "0", + "SAI_QUEUE_STAT_BYTES": "1220", + "SAI_QUEUE_STAT_PACKETS": "10" } } diff --git a/tests/mock_tables/chassis_state_db.json b/tests/mock_tables/chassis_state_db.json index 365cbf80cd..09fd357ad5 100644 --- a/tests/mock_tables/chassis_state_db.json +++ b/tests/mock_tables/chassis_state_db.json @@ -8,12 +8,28 @@ "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD2": { "module_hostname": "sonic-lc3" }, + "CHASSIS_MODULE_HOSTNAME_TABLE|LINE-CARD3": { + "module_hostname": "sonic-lc4" + }, + "CHASSIS_MODULE_TABLE|LINE-CARD2": { + "slot": "3", + "hostname": "sonic-lc1", + "num_asics": "2" + }, + "CHASSIS_MODULE_TABLE|LINE-CARD4": { + "slot": "5", + "hostname": "sonic-lc2", + "num_asics": "1" + }, "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_MARK_TABLE|sonic-lc4": { + "timestamp": "2020-07-01 00:00:00" + }, "LINECARD_PORT_STAT_TABLE|Ethernet1/1": { "state": "U", "rx_ok": 100, @@ -64,5 +80,22 @@ "tx_err": 0, "tx_drop": 0, "tx_ovr": 0 + }, + "LINECARD_PORT_STAT_TABLE|Ethernet12/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/mock_tables/dbconnector.py b/tests/mock_tables/dbconnector.py index 379c4e75cd..23e9ee1737 100644 --- a/tests/mock_tables/dbconnector.py +++ b/tests/mock_tables/dbconnector.py @@ -3,6 +3,7 @@ import os import sys import re +import ipaddress from unittest import mock import mockredis @@ -228,6 +229,43 @@ def get(self, counter, name): return True, tuple(self.db.get("COUNTERS:" + key).items()) +class DBConnector: + + def __init__(self, *args): + + self.data = None + ip_to_asic = { + "192.168.3.10": "asic0", + "192.168.3.11": "asic1", + "192.168.5.1": "asic2", + "192.168.6.1": "asic3" + } + + redis_kwargs = {} + + # Check if IP is being used to connect to redis + if len(args) == 4: + try: + ip = args[1] + ipaddress.ip_address(args[1]) + redis_kwargs['namespace'] = ip_to_asic[ip] if ip is not None else ip + except ValueError: + redis_kwargs['namespace'] = None + + redis_kwargs['db_name'] = 'counters_db' + redis_kwargs['topo'] = None + redis_kwargs['unix_socket_path'] = None + redis_kwargs['decode_responses'] = True + self.swsssyncclient = SwssSyncClient(**redis_kwargs) + + def hgetall(self, key): + return self.swsssyncclient.hgetall(key) + + def hget(self, key, attr): + return self.swsssyncclient.hget(key, attr) + + +swsscommon.DBConnector = DBConnector swsssdk.interface.DBInterface._subscribe_keyspace_notification = _subscribe_keyspace_notification swsssdk.interface.DBInterface.close = mock_close mockredis.MockRedis.config_set = config_set diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 69f3056a1f..35d7f1a682 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -1393,17 +1393,21 @@ "max_priority_groups": "8" }, "CHASSIS_MIDPLANE_TABLE|LINE-CARD0": { - "ip_address": "192.168.1.100", + "ip_address": "192.168.3.1", "access": "True" }, "CHASSIS_MIDPLANE_TABLE|LINE-CARD2": { - "ip_address": "192.168.1.1", + "ip_address": "192.168.5.1", "access": "True" }, "CHASSIS_MIDPLANE_TABLE|LINE-CARD1": { - "ip_address": "192.168.1.2", + "ip_address": "192.168.4.1", "access": "False" }, + "CHASSIS_MIDPLANE_TABLE|LINE-CARD3": { + "ip_address": "192.168.6.1", + "access": "True" + }, "PORT_TABLE|Ethernet0": { "rmt_adv_speeds" : "10,100,1000", "speed" : "100000", diff --git a/tests/multi_asic_queue_counter_test.py b/tests/multi_asic_queue_counter_test.py index af57fa75e5..2058697ed8 100644 --- a/tests/multi_asic_queue_counter_test.py +++ b/tests/multi_asic_queue_counter_test.py @@ -255,6 +255,7 @@ class TestQueueMultiAsic(object): def setup_class(cls): os.environ["PATH"] += os.pathsep + scripts_path os.environ['UTILITIES_UNIT_TESTING'] = "2" + os.environ['UTILITIES_UNIT_TESTING_IS_SUP'] = "0" os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic" print("SETUP") diff --git a/tests/portstat_db/on_sup_na/chassis_state_db.json b/tests/portstat_db/on_sup_na/chassis_state_db.json index d2e5771098..6f66efac69 100644 --- a/tests/portstat_db/on_sup_na/chassis_state_db.json +++ b/tests/portstat_db/on_sup_na/chassis_state_db.json @@ -14,6 +14,9 @@ "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc3": { "timestamp": "2020-07-01 00:00:00" }, + "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc4": { + "timestamp": "2020-07-01 00:00:00" + }, "LINECARD_PORT_STAT_TABLE|Ethernet1/1": { "state": "U", "rx_ok": 100, @@ -64,5 +67,22 @@ "tx_err": "N/A", "tx_drop": "N/A", "tx_ovr": "N/A" + }, + "LINECARD_PORT_STAT_TABLE|Ethernet12/1": { + "state": "N/A", + "rx_ok": "N/A", + "rx_bps": "N/A", + "rx_pps": "N/A", + "rx_util": "N/A", + "rx_err": "N/A", + "rx_drop": "N/A", + "rx_ovr": "N/A", + "tx_ok": "N/A", + "tx_bps": "N/A", + "tx_pps": "N/A", + "tx_util": "N/A", + "tx_err": "N/A", + "tx_drop": "N/A", + "tx_ovr": "N/A" } -} \ No newline at end of file +} diff --git a/tests/portstat_db/on_sup_packet_chassis/chassis_state_db.json b/tests/portstat_db/on_sup_packet_chassis/chassis_state_db.json index 34353b6f52..c3371f26e3 100644 --- a/tests/portstat_db/on_sup_packet_chassis/chassis_state_db.json +++ b/tests/portstat_db/on_sup_packet_chassis/chassis_state_db.json @@ -14,6 +14,9 @@ "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc3": { "timestamp": "2020-07-01 00:00:00" }, + "LINECARD_PORT_STAT_MARK_TABLE|sonic-lc4": { + "timestamp": "2020-07-01 00:00:00" + }, "LINECARD_PORT_STAT_TABLE|HundredGigE0/1/0/1": { "state": "U", "rx_ok": 100, diff --git a/tests/portstat_test.py b/tests/portstat_test.py index e5f32f5823..070441b127 100644 --- a/tests/portstat_test.py +++ b/tests/portstat_test.py @@ -309,6 +309,8 @@ 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 +Ethernet12/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" @@ -326,6 +328,8 @@ 0 0 0 Ethernet11/1 N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A\ N/A N/A N/A +Ethernet12/1 N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A\ + N/A N/A N/A """ intf_counters_on_sup_packet_chassis = """\ diff --git a/tests/queue_counter_test.py b/tests/queue_counter_test.py index 508550b9c8..5f8d54954b 100644 --- a/tests/queue_counter_test.py +++ b/tests/queue_counter_test.py @@ -2245,6 +2245,7 @@ class TestQueue(object): def setup_class(cls): os.environ["PATH"] += os.pathsep + scripts_path os.environ['UTILITIES_UNIT_TESTING'] = "2" + os.environ['UTILITIES_UNIT_TESTING_IS_SUP'] = "0" print("SETUP") def test_queue_counters(self): diff --git a/tests/remote_cli_test.py b/tests/remote_cli_test.py index 57a220be1e..519a39debf 100644 --- a/tests/remote_cli_test.py +++ b/tests/remote_cli_test.py @@ -17,6 +17,8 @@ hello world ======== LINE-CARD2|sonic-lc3 output: ======== hello world +======== LINE-CARD3|sonic-lc4 output: ======== +hello world ''' REXEC_HELP = '''Usage: cli [OPTIONS] LINECARD_NAMES... diff --git a/tests/test_aggregate_voq_counters.py b/tests/test_aggregate_voq_counters.py new file mode 100644 index 0000000000..4e5557e571 --- /dev/null +++ b/tests/test_aggregate_voq_counters.py @@ -0,0 +1,98 @@ +from click.testing import CliRunner +import show.main as show +import os +import json + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") + +show_queue_counters_voq = """\ + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes Credit-WD-Del/pkts +------------------------- ----- -------------- --------------- ----------- ------------ -------------------- +sonic-lc1|asic0|Ethernet0 VOQ0 8 976 10 1220 0 + + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes Credit-WD-Del/pkts +------------------------- ----- -------------- --------------- ----------- ------------ -------------------- +sonic-lc2|asic0|Ethernet4 VOQ1 18 2196 16 1952 0 + +""" + +show_queue_counters_voq_sys_port = """\ + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes Credit-WD-Del/pkts +------------------------- ----- -------------- --------------- ----------- ------------ -------------------- +sonic-lc2|asic0|Ethernet4 VOQ1 18 2196 16 1952 0 + +""" + +show_queue_counters_voq_json = { + "sonic-lc1|asic0|Ethernet0": { + "VOQ0": { + "creditWDPkts": "0", + "dropbytes": "1220", + "droppacket": "10", + "totalbytes": "976", + "totalpacket": "8" + }, + "time": "2025-04-07T15:57:11.881430" + }, + "sonic-lc2|asic0|Ethernet4": { + "VOQ1": { + "creditWDPkts": "0", + "dropbytes": "1952", + "droppacket": "16", + "totalbytes": "2196", + "totalpacket": "18" + }, + "time": "2025-04-07T15:57:11.881496" + } +} + + +class TestAggVoq(object): + @classmethod + def setup_class(cls): + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "1" + os.environ["UTILITIES_UNIT_TESTING"] = "2" + print("SETUP") + + def test_queue_voq_counters_aggregate(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--voq"] + ) + assert result.exit_code == 0 + assert result.output == show_queue_counters_voq + + def test_queue_voq_counters_aggregate_json(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--voq", "--json"] + ) + res = result.output + res = json.loads(res) + assert result.exit_code == 0 + for lc in show_queue_counters_voq_json: + for voq in show_queue_counters_voq_json[lc]: + if voq != "time": + for counter in show_queue_counters_voq_json[lc][voq]: + assert res[lc][voq][counter] == show_queue_counters_voq_json[lc][voq][counter] + + def test_queue_voq_counters_aggregate_sys_port(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["sonic-lc2|asic0|Ethernet4", "--voq"] + ) + assert result.exit_code == 0 + assert result.output == show_queue_counters_voq_sys_port + + @classmethod + def teardown_class(cls): + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING_IS_SUP"] = "0" + os.environ["UTILITIES_UNIT_TESTING"] = "0" + print("TEARDOWN")