diff --git a/clear/main.py b/clear/main.py index ff0bc0d6cb..011b2d1230 100755 --- a/clear/main.py +++ b/clear/main.py @@ -4,6 +4,10 @@ import sys import click +from natsort import natsorted +from swsssdk import ConfigDBConnector +from swsssdk import SonicV2Connector +COUNTERS_BUFFER_POOL_NAME_MAP = 'COUNTERS_BUFFER_POOL_NAME_MAP' # This is from the aliases example: @@ -235,6 +239,94 @@ def clear_pwm_pg_shared(): command = 'watermarkstat -c -p -t pg_shared' run_command(command) +@cli.group(name='buffer-pool') +def buffer_pool(): + """Clear Buffer pool WM""" + pass + +@buffer_pool.command('threshold') +def clear_buffer_pool_threshold(): + + # connect to COUNTERS DB + counters_db = SonicV2Connector(host='127.0.0.1') + counters_db.connect(counters_db.COUNTERS_DB) + + # connect to CONFIG DB + config_db = ConfigDBConnector() + config_db.connect() + + buffer_pool_name_to_oid_map = counters_db.get_all(counters_db.COUNTERS_DB, COUNTERS_BUFFER_POOL_NAME_MAP) + + if buffer_pool_name_to_oid_map is None: + print("Buffer pools are empty!!. Not yet created") + return + + # Delete buffer pool table entries from threshold table + for buf_pool,buf_oid in natsorted(buffer_pool_name_to_oid_map.items()): + key = buf_pool + entry = config_db.get_entry('THRESHOLD_BUFFERPOOL_TABLE', key) + if entry: + config_db.set_entry('THRESHOLD_BUFFERPOOL_TABLE', key, None) + +def interface_name_is_valid(interface_name): + """Check if the interface name is valid + """ + config_db = ConfigDBConnector() + config_db.connect() + port_dict = config_db.get_table('PORT') + port_channel_dict = config_db.get_table('PORTCHANNEL') + + if interface_name is not None: + if not port_dict: + click.echo("port_dict is None!") + raise click.Abort() + for port_name in port_dict.keys(): + if interface_name == port_name: + return True + if port_channel_dict: + for port_channel_name in port_channel_dict.keys(): + if interface_name == port_channel_name: + return True + return False + + +@priority_group.command('threshold') +@click.argument('port_name', metavar='', required=False) +@click.argument('pg_index', metavar='', required=False, type=int) +@click.argument('threshold_type', required=False, type=click.Choice(['shared', 'headroom'])) +@click.pass_context +def threshold(ctx, port_name, pg_index, threshold_type): + """ Clear priority group threshold """ + # If no params are provided, clear all priority-group entries. + config_db = ConfigDBConnector() + config_db.connect() + + all = False + + if port_name is None and pg_index is None and threshold_type is None: + # clear all entries. + key = 'priority-group' + all = True + elif port_name is None or pg_index is None or threshold_type is None: + ctx.fail("port_name, pg_index and threshold_type are mandatory parameters.") + else: + if pg_index not in range(0, 8): + ctx.fail("priority-group must be in range 0-7") + if interface_name_is_valid(port_name) is False: + ctx.fail("Interface name is invalid!!") + key = 'priority-group' + '|' + threshold_type + '|' + port_name + '|' + str(pg_index) + + if all is True: + entry_table = config_db.get_keys('THRESHOLD_TABLE') + # Clear data for all keys + for k in natsorted(entry_table): + if k[0] == 'priority-group': + config_db.set_entry('THRESHOLD_TABLE', k, None) + else: + entry = config_db.get_entry('THRESHOLD_TABLE', key) + if entry: + config_db.set_entry('THRESHOLD_TABLE', key, None) + @cli.group() def queue(): @@ -301,6 +393,77 @@ def persistent_watermark(): command = 'watermarkstat -c -p -t headroom_pool' run_command(command) +@queue.command('threshold') +@click.argument('port_name', metavar='', required=False) +@click.argument('queue_index', metavar='', required=False, type=int) +@click.argument('queue_type', required=False, type=click.Choice(['unicast', 'multicast', 'cpu'])) +@click.pass_context +def threshold(ctx, port_name, queue_index, queue_type): + """ Clear queue threshold for a queue on a port """ + # If no params are provided, clear all priority-group entries. + config_db = ConfigDBConnector() + config_db.connect() + + all = False + + if port_name is None and queue_index is None and queue_type is None: + # clear all entries. + key = 'queue' + all = True + elif port_name is None or queue_index is None or queue_type is None: + ctx.fail("port_name, queue_index and queue_type are mandatory parameters.") + else: + if queue_index not in range(0, 8): + ctx.fail("queue index must be in range 0-7") + if interface_name_is_valid(port_name) is False: + ctx.fail("Interface name is invalid!!") + ## Validate if queue index is supported. + counters_db = SonicV2Connector(host='127.0.0.1') + counters_db.connect(counters_db.COUNTERS_DB) + + counters_queue_name_map = counters_db.get_all(counters_db.COUNTERS_DB, "COUNTERS_QUEUE_NAME_MAP") + counters_queue_type_map = counters_db.get_all(counters_db.COUNTERS_DB, "COUNTERS_QUEUE_TYPE_MAP") + + if counters_queue_name_map is None or counters_queue_type_map is None: + ctx.fail("Queue maps not generated yet.") + + num_uc_queues = 0 + num_mc_queues = 0 + + for queue in counters_queue_name_map: + portstr = queue.split(':') + port = portstr[0] + if port == port_name: + if counters_queue_type_map[counters_queue_name_map[queue]] == "SAI_QUEUE_TYPE_MULTICAST": + num_mc_queues += 1 + elif counters_queue_type_map[counters_queue_name_map[queue]] == "SAI_QUEUE_TYPE_UNICAST": + num_uc_queues += 1 + + if queue_type == 'unicast': + if queue_index not in range(0, num_uc_queues): + ctx.fail("Invalid queue index provided. Only {} unicast queues supported on port.".format(num_uc_queues)) + elif queue_type == 'multicast': + if queue_index not in range(0, num_mc_queues): + ctx.fail("Invalid queue index provided. Only {} multicast queues supported on port.". format(num_mc_queues)) + + key = 'queue' + '|' + queue_type + '|' + port_name + '|' + str(queue_index) + if queue_type == 'cpu': + key = 'queue' + '|' + queue_type + '|' + 'CPU' + '|' + str(queue_index) + else: + key = 'queue' + '|' + queue_type + '|' + port_name + '|' + str(queue_index) + + + if all is True: + entry_table = config_db.get_keys('THRESHOLD_TABLE') + # Clear data for all keys + for k in natsorted(entry_table): + if k[0] == 'queue': + config_db.set_entry('THRESHOLD_TABLE', k, None) + else: + entry = config_db.get_entry('THRESHOLD_TABLE', key) + if entry: + config_db.set_entry('THRESHOLD_TABLE', key, None) + # # 'arp' command #### # @@ -397,6 +560,22 @@ def line(target, devicename): click.echo(output) sys.exit(exitstatus) +@cli.group('threshold') +def threshold(): + """Clear threshold breach entries""" + pass + +@threshold.command() +@click.argument('id', type=int, required=False) +def breach(id): + """Clear threshold breach entries all | event-id""" + if id is not None: + cmd = "thresholdbreach -c -cnt {}".format(id) + else: + cmd = 'thresholdbreach -c' + + run_command(cmd) + # # 'nat' group ("clear nat ...") # diff --git a/config/main.py b/config/main.py index f7cf8e32fc..015da399ed 100644 --- a/config/main.py +++ b/config/main.py @@ -9,6 +9,7 @@ import re import subprocess import sys +import swsssdk import threading import time @@ -766,6 +767,7 @@ def _reset_failed_services(config_db): 'snmp', 'swss', 'syncd', + 'tam', 'teamd', 'telemetry' ] @@ -793,6 +795,7 @@ def _restart_services(config_db): 'lldp', 'hostcfgd', 'nat', + 'tam', 'sflow', 'restapi', 'telemetry' @@ -3831,6 +3834,152 @@ def interval(interval): clicommon.run_command(command) +@config.group('priority-group') +def priority_group(): + """ Configure priority group thresholds """ + pass + +@priority_group.command('threshold') +@click.argument('port_name', metavar='', required=True) +@click.argument('pg_index', metavar='', required=True, type=int) +@click.argument('threshold_type', type=click.Choice(['shared', 'headroom'])) +@click.argument('threshold_value', metavar='', required=True, type=int) +@click.pass_context +def threshold(ctx, port_name, pg_index, threshold_type, threshold_value): + """ Configure priority group threshold value for a priority group on a port """ + + config_db = ConfigDBConnector() + config_db.connect() + + if pg_index not in range(0, 8): + ctx.fail("priority-group must be in range 0-7") + + if threshold_value not in range(1, 101): + ctx.fail("threshold value must be in range 1-100") + + if interface_name_is_valid(config_db, port_name) is False: + ctx.fail("Interface name is invalid!!") + + key = 'priority-group' + '|' + threshold_type + '|' + port_name + '|' + str(pg_index) + entry = config_db.get_entry('THRESHOLD_TABLE', key) + if entry is None: + config_db.set_entry('THRESHOLD_TABLE', key, {'threshold' : threshold_value}) + else: + entry_value = entry.get('threshold', []) + if entry_value != threshold_value: + config_db.mod_entry('THRESHOLD_TABLE', key, {'threshold' : threshold_value}) + +@config.group('queue') +def queue(): + """ Configure queue thresholds """ + pass + +@queue.command('threshold') +@click.argument('port_name', metavar='', required=True) +@click.argument('queue_index', metavar='', required=True, type=int) +@click.argument('queue_type', type=click.Choice(['unicast', 'multicast', 'CPU'])) +@click.argument('threshold_value', metavar='', required=True, type=int) +@click.pass_context +def threshold(ctx, port_name, queue_index, queue_type, threshold_value): + """ Configure queue threshold value for a queue on a port """ + config_db = ConfigDBConnector() + config_db.connect() + + if port_name != 'CPU': + if queue_index not in range(0, 8): + ctx.fail("queue index must be in range 0-7") + else: + if queue_index not in range(0, 47): + ctx.fail("CPU queue index must be in range 0-47") + + if threshold_value not in range(1, 101): + ctx.fail("threshold value must be in range 1-100") + + if (port_name.startswith("CPU") is False): + if interface_name_is_valid(config_db, port_name) is False: + ctx.fail("Interface name is invalid!!") + + ## Validate if queue index is supported. + counters_db = swsssdk.SonicV2Connector(host='127.0.0.1') + counters_db.connect(counters_db.COUNTERS_DB) + + counters_queue_name_map = counters_db.get_all(counters_db.COUNTERS_DB, "COUNTERS_QUEUE_NAME_MAP") + counters_queue_type_map = counters_db.get_all(counters_db.COUNTERS_DB, "COUNTERS_QUEUE_TYPE_MAP") + + if counters_queue_name_map is None or counters_queue_type_map is None: + ctx.fail("Queue maps not generated yet.") + + num_uc_queues = 0 + num_mc_queues = 0 + + for queue in counters_queue_name_map: + portstr = queue.split(':') + port = portstr[0] + if port == port_name: + if counters_queue_type_map[counters_queue_name_map[queue]] == "SAI_QUEUE_TYPE_MULTICAST": + num_mc_queues += 1 + elif counters_queue_type_map[counters_queue_name_map[queue]] == "SAI_QUEUE_TYPE_UNICAST": + num_uc_queues += 1 + + if queue_type == 'unicast': + if queue_index not in range(0, num_uc_queues): + ctx.fail("Invalid queue index provided. Only {} unicast queues supported on port.".format(num_uc_queues)) + elif queue_type == 'multicast': + if queue_index not in range(0, num_mc_queues): + ctx.fail("Invalid queue index provided. Only {} multicast queues supported on port.". format(num_mc_queues)) + + key = 'queue' + '|' + queue_type + '|' + port_name + '|' + str(queue_index) + if queue_type == 'CPU': + key = 'queue' + '|' + 'multicast' + '|' + 'CPU' + '|' + str(queue_index) + else: + key = 'queue' + '|' + queue_type + '|' + port_name + '|' + str(queue_index) + + entry = config_db.get_entry('THRESHOLD_TABLE', key) + if entry is None: + config_db.set_entry('THRESHOLD_TABLE', key, {'threshold' : threshold_value}) + else: + entry_value = entry.get('threshold', []) + if entry_value != threshold_value: + config_db.mod_entry('THRESHOLD_TABLE', key, {'threshold' : threshold_value}) + +@config.group('buffer-pool') +def buffer_pool(): + """ Configure Buffer pool thresholds """ + pass + +@buffer_pool.command('threshold') +@click.argument('pool_name', metavar='', required=True) +@click.argument('threshold_value', metavar='', required=True, type=int) +@click.pass_context +def threshold(ctx, pool_name, threshold_value): + """ Configure Buffer pool threshold value """ + + config_db = ConfigDBConnector() + config_db.connect() + + counters_db = swsssdk.SonicV2Connector(host='127.0.0.1') + counters_db.connect(counters_db.COUNTERS_DB) + + buffer_pool_name_to_oid_map = counters_db.get_all(counters_db.COUNTERS_DB, "COUNTERS_BUFFER_POOL_NAME_MAP") + if buffer_pool_name_to_oid_map is None: + ctx.fail("Buffer pools not created!!") + + if pool_name not in buffer_pool_name_to_oid_map: + ctx.fail("Pool name is invalid!!") + + if threshold_value not in range(1, 101): + ctx.fail("threshold value must be in range 1-100") + + key = pool_name + entry = config_db.get_entry('THRESHOLD_BUFFERPOOL_TABLE', key) + if entry is None: + config_db.set_entry('THRESHOLD_BUFFERPOOL_TABLE', key, {'threshold' : threshold_value}) + else: + entry_value = entry.get('threshold', []) + if entry_value != threshold_value: + config_db.mod_entry('THRESHOLD_BUFFERPOOL_TABLE', key, {'threshold' : threshold_value}) + + # # 'interface_naming_mode' subgroup ('config interface_naming_mode ...') # diff --git a/scripts/fast-reboot b/scripts/fast-reboot index e0450f802b..3b3f40eaa4 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -571,6 +571,11 @@ debug "Stopped sflow ..." container kill lldp &> /dev/null || debug "Docker lldp is not running ($?) ..." systemctl stop lldp +# Kill tam docker +debug "Stopping tam" +docker kill tam > /dev/null || true +debug "Stopped tam" + if [[ "$REBOOT_TYPE" = "fast-reboot" ]]; then debug "Stopping teamd ..." systemctl stop teamd diff --git a/scripts/thresholdbreach b/scripts/thresholdbreach new file mode 100755 index 0000000000..2fcab329d6 --- /dev/null +++ b/scripts/thresholdbreach @@ -0,0 +1,121 @@ +#!/usr/bin/python3 + +############################################################################ +# +# thresholdbreach is a tool for displaying threshold breaches. +# +############################################################################ + +import argparse +import sys +from natsort import natsorted +from tabulate import tabulate +from swsssdk import ConfigDBConnector + + +THRESHOLD_BREACH_TABLE_PREFIX = "THRESHOLD_BREACH_TABLE" +header = ['Event-id', 'Buffer', 'Type', 'Port', 'Index', 'Breach Value(%)', 'Breach Value(bytes)', 'Time-stamp'] + +class Thresholdbreach(object): + + def __init__(self): + # connect COUNTER DB + self.counters_db = ConfigDBConnector() + self.counters_db.db_connect('COUNTERS_DB') + + def get_threshold_breach_info(self, k): + breach_data = {} + key = THRESHOLD_BREACH_TABLE_PREFIX + ':' + k + # k is of the format "breach-report:1". Extract event-id "1" + id = k.split(':') + eventid = id[1] + breach_data['eventid'] = eventid + data = self.counters_db.get_all(self.counters_db.COUNTERS_DB, key) + if data is not None: + breach_data['buffer'] = data['buffer'] + breach_data['type'] = data['type'] + breach_data['port'] = data['port'] + breach_data['index'] = data['index'] + breach_data['breach_value'] = data['breach_value'] + breach_data['time-stamp'] = data['time-stamp'] + if data['type'] == 'shared': + breach_data['counter'] = data['SAI_INGRESS_PRIORITY_GROUP_STAT_SHARED_WATERMARK_BYTES'] + elif data['type'] == 'headroom': + breach_data['counter'] = data['SAI_INGRESS_PRIORITY_GROUP_STAT_XOFF_ROOM_WATERMARK_BYTES'] + elif data['type'] == 'unicast': + breach_data['counter'] = data['SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES'] + elif data['type'] == 'multicast': + breach_data['counter'] = data['SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES'] + elif data['type'] == 'ingress': + breach_data['counter'] = data['SAI_BUFFER_POOL_STAT_WATERMARK_BYTES'] + elif data['type'] == 'egress': + breach_data['counter'] = data['SAI_BUFFER_POOL_STAT_WATERMARK_BYTES'] + + return breach_data + + def get_print_all_threshold_breach(self, count): + table_data = self.counters_db.get_keys(THRESHOLD_BREACH_TABLE_PREFIX) + # Get data for all keys + table = [] + iter = 0 + maxcount = int(count) + + for k in natsorted(table_data, reverse=True): + if (maxcount != 0) and (iter == maxcount): + break + + if k == "event-id": + continue + + iter = iter + 1 + data = self.get_threshold_breach_info(k) + table.append((data['eventid'], data['buffer'], data['type'], data['port'], data['index'], + data['breach_value'], data['counter'], data['time-stamp'])) + + print(tabulate(table, header, tablefmt='simple', stralign='right')) + return + + def clear_all_threshold_breach(self, count): + if count is 0: + table_data = self.counters_db.get_keys(THRESHOLD_BREACH_TABLE_PREFIX) + # Get data for all keys + for k in natsorted(table_data): + self.counters_db.set_entry(THRESHOLD_BREACH_TABLE_PREFIX, k, None) + else: + key = "breach-report" + ":" + str(count) + entry = self.counters_db.get_entry(THRESHOLD_BREACH_TABLE_PREFIX, key) + if entry: + self.counters_db.set_entry(THRESHOLD_BREACH_TABLE_PREFIX, key, None) + return + +def main(): + + parser = argparse.ArgumentParser(description='Display the queue/pg/buffer-pool threshold breaches', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""") +Examples: + thresholdbreach + thresholdbreach -c + thresholdbreach -cnt count +""") + + parser.add_argument('-c', '--clear', action='store_true', help='Clear threshold breach entries') + parser.add_argument('-cnt', '--count', required=False, help='Display threshold breach entries as per count') + args = parser.parse_args() + + thresholdbreach = Thresholdbreach() + count = 0 + + if args.clear: + if args.count: + count = args.count + thresholdbreach.clear_all_threshold_breach(count) + else: + if args.count: + count = args.count + thresholdbreach.get_print_all_threshold_breach(count) + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/scripts/thresholdcfg b/scripts/thresholdcfg new file mode 100644 index 0000000000..ed71ba087e --- /dev/null +++ b/scripts/thresholdcfg @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 + +############################################################################ +# +# thresholdcfg is a tool for displaying queue and priority-group thresholds. +# +############################################################################ + +import argparse +import sys +import swsssdk +from swsssdk import ConfigDBConnector +from natsort import natsorted +from tabulate import tabulate + +headerPg = ['Port', 'PG0', 'PG1', 'PG2', 'PG3', 'PG4', 'PG5', 'PG6', 'PG7'] +headerUc = ['Port', 'UC0', 'UC1', 'UC2', 'UC3', 'UC4', 'UC5', 'UC6', 'UC7'] +headerMc = ['Port', 'MC0', 'MC1', 'MC2', 'MC3', 'MC4', 'MC5', 'MC6', 'MC7'] +headerCpu = ['Queue', 'Percent'] +headerBufferPool = ['Pool','threshold'] + + +THRESHOLD_DEFAULT = 0 + +QUEUE_TYPE_MC = 'MC' +QUEUE_TYPE_UC = 'UC' +QUEUE_TYPE_ALL = 'ALL' +SAI_QUEUE_TYPE_MULTICAST = "SAI_QUEUE_TYPE_MULTICAST" +SAI_QUEUE_TYPE_UNICAST = "SAI_QUEUE_TYPE_UNICAST" +SAI_QUEUE_TYPE_ALL = "SAI_QUEUE_TYPE_ALL" + +THRESHOLD_TABLE_PREFIX = "THRESHOLD_TABLE|" +THRESHOLD_BUFFERPOOL_TABLE_PREFIX = "THRESHOLD_BUFFERPOOL_TABLE|" + +COUNTERS_PORT_NAME_MAP = "COUNTERS_PORT_NAME_MAP" +COUNTERS_QUEUE_NAME_MAP = "COUNTERS_QUEUE_NAME_MAP" +COUNTERS_QUEUE_TYPE_MAP = "COUNTERS_QUEUE_TYPE_MAP" +COUNTERS_QUEUE_INDEX_MAP = "COUNTERS_QUEUE_INDEX_MAP" +COUNTERS_QUEUE_PORT_MAP = "COUNTERS_QUEUE_PORT_MAP" +COUNTERS_PG_NAME_MAP = "COUNTERS_PG_NAME_MAP" +COUNTERS_PG_PORT_MAP = "COUNTERS_PG_PORT_MAP" +COUNTERS_PG_INDEX_MAP = "COUNTERS_PG_INDEX_MAP" +COUNTERS_BUFFER_POOL_NAME_MAP = "COUNTERS_BUFFER_POOL_NAME_MAP" + + +class Thresholdcfg(object): + + def __init__(self): + # connect COUNTER DB + self.counters_db = swsssdk.SonicV2Connector(host='127.0.0.1') + self.counters_db.connect(self.counters_db.COUNTERS_DB) + + # connect APP DB + self.config_db = ConfigDBConnector() + self.config_db.connect() + + self.num_uc_queues = 0 + + def get_queue_type(table_id): + queue_type = self.counters_db.get(self.counters_db.COUNTERS_DB, COUNTERS_QUEUE_TYPE_MAP, table_id) + if queue_type is None: + print("Queue Type is not available!", table_id) + sys.exit(1) + elif queue_type == SAI_QUEUE_TYPE_MULTICAST: + return QUEUE_TYPE_MC + + elif queue_type == SAI_QUEUE_TYPE_UNICAST: + return QUEUE_TYPE_UC + elif queue_type == SAI_QUEUE_TYPE_ALL: + return QUEUE_TYPE_ALL + else: + print("Queue Type is invalid:", table_id, queue_type) + sys.exit(1) + + def get_queue_port(table_id): + port_table_id = self.counters_db.get(self.counters_db.COUNTERS_DB, COUNTERS_QUEUE_PORT_MAP, table_id) + if port_table_id is None: + print("Port is not available!", table_id) + sys.exit(1) + + return port_table_id + + def get_pg_port(table_id): + port_table_id = self.counters_db.get(self.counters_db.COUNTERS_DB, COUNTERS_PG_PORT_MAP, table_id) + if port_table_id is None: + print("Port is not available!", table_id) + sys.exit(1) + + return port_table_id + + # Get all ports + self.counter_port_name_map = self.counters_db.get_all(self.counters_db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP) + if self.counter_port_name_map is None: + print("COUNTERS_PORT_NAME_MAP is empty!") + sys.exit(1) + + self.port_uc_queues_map = {} + self.port_mc_queues_map = {} + self.port_pg_map = {} + self.port_name_map = {} + + for port in self.counter_port_name_map: + self.port_uc_queues_map[port] = {} + self.port_mc_queues_map[port] = {} + self.port_pg_map[port] = {} + self.port_name_map[self.counter_port_name_map[port]] = port + + # Get Queues for each port + counter_queue_name_map = self.counters_db.get_all(self.counters_db.COUNTERS_DB, COUNTERS_QUEUE_NAME_MAP) + if counter_queue_name_map is None: + print("COUNTERS_QUEUE_NAME_MAP is empty!") + sys.exit(1) + + for queue in counter_queue_name_map: + port = self.port_name_map[get_queue_port(counter_queue_name_map[queue])] + if get_queue_type(counter_queue_name_map[queue]) == QUEUE_TYPE_UC: + self.port_uc_queues_map[port][queue] = counter_queue_name_map[queue] + + elif get_queue_type(counter_queue_name_map[queue]) == QUEUE_TYPE_MC: + self.port_mc_queues_map[port][queue] = counter_queue_name_map[queue] + + # Get PGs for each port + counter_pg_name_map = self.counters_db.get_all(self.counters_db.COUNTERS_DB, COUNTERS_PG_NAME_MAP) + if counter_pg_name_map is None: + print("COUNTERS_PG_NAME_MAP is empty!") + sys.exit(1) + + for pg in counter_pg_name_map: + port = self.port_name_map[get_pg_port(counter_pg_name_map[pg])] + self.port_pg_map[port][pg] = counter_pg_name_map[pg] + + for queue in counter_queue_name_map: + port = self.port_name_map[get_queue_port(counter_queue_name_map[queue])] + if port == 'CPU': + continue + self.num_uc_queues = len(self.port_uc_queues_map[port]) + break + + self.threshold_types = { + "pg_headroom": {"message": "Ingress headroom threshold per PG:", + "obj_map": self.port_pg_map, + "idx_func": self.get_pg_index, + "th_name": "threshold", + "header": headerPg}, + "pg_shared": {"message": "Ingress shared pool threshold per PG:", + "obj_map": self.port_pg_map, + "idx_func": self.get_pg_index, + "th_name": "threshold", + "header": headerPg}, + "q_shared_uni": {"message": "Egress shared pool threshold per unicast queue:", + "obj_map": self.port_uc_queues_map, + "idx_func": self.get_queue_index, + "th_name": "threshold", + "header": headerUc}, + "q_shared_multi": {"message": "Egress shared pool threshold per multicast queue:", + "obj_map": self.port_mc_queues_map, + "idx_func": self.get_queue_index, + "th_name": "threshold", + "header": headerMc}, + "q_shared_multi_cpu": {"message": "Egress shared pool threshold per CPU queue:", + "obj_map": self.port_mc_queues_map, + "idx_func": self.get_queue_index, + "th_name": "threshold", + "header": headerCpu}, + "buffer_pool_all": {"message": "Buffer pool threshold:", + "th_name": "threshold", + "header": headerBufferPool} + } + + def get_queue_index(self, table_id): + queue_index = self.counters_db.get(self.counters_db.COUNTERS_DB, COUNTERS_QUEUE_INDEX_MAP, table_id) + if queue_index is None: + print("Queue index is not available!", table_id) + sys.exit(1) + + return queue_index + + def get_pg_index(self, table_id): + pg_index = self.counters_db.get(self.counters_db.COUNTERS_DB, COUNTERS_PG_INDEX_MAP, table_id) + if pg_index is None: + print("Priority group index is not available!", table_id) + sys.exit(1) + + return pg_index + + def get_counters(self, table_prefix, port, port_obj, idx_func, threshold, th_type): + """ + Get the threshold from specific table. + """ + + fields = ["0"]*8 + if th_type == "q_shared_uni" or th_type == "q_shared_multi": + fields = ["0"]*self.num_uc_queues + + elif th_type == "q_shared_multi_cpu": + fields = ["0"]*48 + + for name, obj_id in port_obj.items(): + pos = int(idx_func(obj_id)) % len(fields) + full_table_id = table_prefix + port + '|' + str(pos) + threshold_data = self.config_db.get(self.config_db.CONFIG_DB, full_table_id, threshold) + + if threshold_data is None: + fields[pos] = THRESHOLD_DEFAULT + elif fields[pos] != THRESHOLD_DEFAULT: + fields[pos] = str(int(threshold_data)) + + cntr = tuple(fields) + return cntr + + def get_print_buffer_pool_threshold(self, table_prefix, type, th_type): + table = [] + buffer_pool_name_to_oid_map = self.counters_db.get_all(self.counters_db.COUNTERS_DB, COUNTERS_BUFFER_POOL_NAME_MAP) + if buffer_pool_name_to_oid_map is None: + print("Buffer pools are empty.Not created") + return + + for buf_pool,buf_oid in natsorted(buffer_pool_name_to_oid_map.items()): + key = table_prefix + buf_pool + data = self.config_db.get(self.config_db.CONFIG_DB, key, "threshold") + + if data is None: + data = 0 + table.append((buf_pool, data)) + print(tabulate(table, type["header"], tablefmt='simple', stralign='right')) + return + + def get_print_cpu_queue_stat(self, table_prefix, type, th_type): + table = [] + array_of_cpu = self.port_mc_queues_map["CPU"] + for item in natsorted(array_of_cpu): + queue = item.split(':') + key = table_prefix + queue[1] + data = self.config_db.get(self.config_db.CONFIG_DB, key, "threshold") + if data is None: + data = 0 + table.append((item, data)) + print(tabulate(table, type["header"], tablefmt='simple', stralign='right')) + return + + def get_print_all_stat(self, table_prefix, type, th_type): + # Get stat for each port + table = [] + for port in natsorted(self.counter_port_name_map): + data = self.get_counters(table_prefix, port, + type["obj_map"][port], type["idx_func"], type["th_name"], th_type) + table.append((port, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7])) + print(tabulate(table, type["header"], tablefmt='simple', stralign='right')) + return + + +def main(): + + parser = argparse.ArgumentParser(description='Display the queue/pg thresholds', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Examples: + thresholdcfg -t pg_headroom + thresholdcfg -t pg_shared + thresholdcfg -t q_shared_uni + thresholdcfg -t q_shared_multi + thresholdcfg -t q_shared_multi_cpu + thresholdcfg -t buffer_pool_all +""") + + parser.add_argument('-t', '--type', required=True, action='store', + choices=['pg_headroom', 'pg_shared', 'q_shared_uni', 'q_shared_multi', 'q_shared_multi_cpu', 'buffer_pool_all'], + help='The type of threshold') + args = parser.parse_args() + th_type = args.type + + thresholdcfg = Thresholdcfg() + + if th_type is not None: + if th_type == "pg_shared": + table_prefix = THRESHOLD_TABLE_PREFIX + "priority-group" + "|" + "shared" + "|" + elif th_type == "pg_headroom": + table_prefix = THRESHOLD_TABLE_PREFIX + "priority-group" + "|" + "headroom" + "|" + elif th_type == "q_shared_uni": + table_prefix = THRESHOLD_TABLE_PREFIX + "queue" + "|" + "unicast" + "|" + elif th_type == "q_shared_multi": + table_prefix = THRESHOLD_TABLE_PREFIX + "queue" + "|" + "multicast" + "|" + elif th_type == "buffer_pool_all": + table_prefix = THRESHOLD_BUFFERPOOL_TABLE_PREFIX + thresholdcfg.get_print_buffer_pool_threshold(table_prefix, thresholdcfg.threshold_types[th_type], th_type) + sys.exit(0) + elif th_type == "q_shared_multi_cpu": + table_prefix = THRESHOLD_TABLE_PREFIX + "queue" + "|" + "multicast" + "|"+ "CPU" + "|" + thresholdcfg.get_print_cpu_queue_stat(table_prefix, thresholdcfg.threshold_types[th_type], th_type) + sys.exit(0) + + thresholdcfg.get_print_all_stat(table_prefix, thresholdcfg.threshold_types[th_type], th_type) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 2af6829029..edd294e59d 100644 --- a/setup.py +++ b/setup.py @@ -111,6 +111,8 @@ 'scripts/syseeprom-to-json', 'scripts/tempershow', 'scripts/update_json.py', + 'scripts/thresholdbreach', + 'scripts/thresholdcfg', 'scripts/warm-reboot', 'scripts/watermarkstat', 'scripts/watermarkcfg', diff --git a/show/main.py b/show/main.py index d7b0fdaac3..41856efcd9 100644 --- a/show/main.py +++ b/show/main.py @@ -582,6 +582,28 @@ def pwm_q_multi(): command = 'watermarkstat -p -t q_shared_multi' run_command(command) +@queue.group() +def threshold(): + """Show queue threshold config""" + pass + +@threshold.command('unicast') +def th_q_uni(): + """Show threshold config for unicast queues""" + command = 'thresholdcfg -t q_shared_uni' + run_command(command) + +@threshold.command('multicast') +def th_q_multi(): + """Show threshold config for multicast queues""" + command = 'thresholdcfg -t q_shared_multi' + run_command(command) + +@threshold.command('CPU') +def th_q_multi_cpu(): + """Show threshold config for CPU queues""" + command = 'thresholdcfg -t q_shared_multi_cpu' + run_command(command) # # 'priority-group' group ("show priority-group ...") @@ -625,6 +647,23 @@ def pwm_pg_shared(): command = 'watermarkstat -p -t pg_shared' run_command(command) +@priority_group.group() +def threshold(): + """Show priority group threshold config""" + pass + +@threshold.command('headroom') +def th_pg_headroom(): + """Show headroom threshold config for pg""" + command = 'thresholdcfg -t pg_headroom' + run_command(command) + +@threshold.command('shared') +def th_pg_shared(): + """Show shared threshold config for pg""" + command = 'thresholdcfg -t pg_shared' + run_command(command) + # # 'buffer_pool' group ("show buffer_pool ...") @@ -646,6 +685,16 @@ def pwm_buffer_pool(): command = 'watermarkstat -p -t buffer_pool' run_command(command) +@buffer_pool.group() +def threshold(): + """Show priority group threshold config""" + pass + +@threshold.command('buffer_pool_all') +def th_buffer_pool(): + """Show threshold config for buffer pool """ + command = 'thresholdcfg -t buffer_pool_all' + run_command(command) # # 'headroom-pool' group ("show headroom-pool ...") @@ -1529,6 +1578,29 @@ def line(brief, verbose): return +# +# 'threshold breaches' command ("show threshold breaches") +# + +@cli.group('threshold') +def threshold(): + """Show threshold breaches""" + pass + +@threshold.command('breaches') +@click.argument('count', metavar='', required=False, type=int) +def threshold_breaches(count): + """Show threshold breaches""" + + if count is not None: + cmd = "thresholdbreach -cnt {}".format(count) + else: + cmd = "thresholdbreach" + + run_command(cmd) + return + + # # 'ztp status' command ("show ztp status") #