#!/usr/bin/env python3
# Copyright (C) 2020-2023 NVIDIA Corporation. ALL RIGHTS RESERVED.
# Copyright 2016-2020 Cumulus Networks, LLC
#
"""
mlxcmd - command line utility for accessing the Mellanox SDK

Written by: Ken Yin
"""

import os
import sys
import argparse
import getopt
import json
import cumulus.platforms
import subprocess
import re
import pickle
import logging
import traceback
from collections import OrderedDict
from tabulate import tabulate
from pydash import py_
from pathlib import Path

logger = logging.getLogger("mlxcmd")

#After the upgrade if reboot is needed, do not load the latest
#sdk api libs

reboot_pending = os.path.isfile('/var/run/reboot-required')
if reboot_pending:
    print('Reboot pending to complete software upgrade')
    sys.exit(1)


sys.path.append("/usr/lib/python3/site-packages/python_sdk_api")
sys.path.append("/usr/lib/python3/dist-packages/python_sdk_api")
from sx_api import *

import cumulus.vxlan_check_mlx

from cumulus.utilities import is_bond,is_bond_member
from cumulus.mlx import mlx_open_connection
from cumulus.mlx import mlx_close_connection
from cumulus.mlx import mlx_invalid_port_id
from cumulus.mlx import mlx_get_ecmp_count
from cumulus.mlx import mlx_get_ecmp
from cumulus.mlx import mlx_get_interface
from cumulus.mlx import mlx_set_interface
from cumulus.mlx import mlx_get_neighbor
from cumulus.mlx import mlx_get_uc_route
from cumulus.mlx import mlx_get_mpls_table
from cumulus.mlx import mlx_get_uc_route_count
from cumulus.mlx import mlx_get_mc_route
from cumulus.mlx import mlx_get_vlan_ids
from cumulus.mlx import mlx_get_vlan_ports
from cumulus.mlx import mlx_get_free_buffer_counters
from cumulus.mlx import mlx_get_roce_counters
from cumulus.mlx import mlx_get_qos_mapping
from cumulus.mlx import mlx_get_roce_status
from cumulus.mlx import mlx_get_parser_length
from cumulus.mlx import mlx_get_pool_buffer_counters
from cumulus.mlx import mlx_get_roce_cnp_pool_list
from cumulus.mlx import mlx_clear_pool_buffer_counters
from cumulus.mlx import mlx_get_pool_buffer_limits
from cumulus.mlx import mlx_get_port_reserved_buffer_limits
from cumulus.mlx import mlx_get_port_shared_buffer_limits
from cumulus.mlx import mlx_get_pcpdei_prio_map
from cumulus.mlx import mlx_get_pcpdei_rewrite_map
from cumulus.mlx import mlx_get_dscp_prio_map
from cumulus.mlx import mlx_get_dscp_rewrite_map
from cumulus.mlx import mlx_get_port_priority_trust
from cumulus.mlx import mlx_get_port_default_priority
from cumulus.mlx import mlx_get_port_priority_rewrite
from cumulus.mlx import mlx_set_port_priority_rewrite
from cumulus.mlx import mlx_set_port_dscp_remark
from cumulus.mlx import mlx_get_pg_prio_map
from cumulus.mlx import mlx_get_prio_tc_map
from cumulus.mlx import mlx_get_prio_ieee_map
from cumulus.mlx import mlx_get_mc_aware
from cumulus.mlx import mlx_get_port_buffer_capabilities
from cumulus.mlx import mlx_get_flow_control
from cumulus.mlx import mlx_get_port_scheduler
from cumulus.mlx import mlx_get_port_counters
from cumulus.mlx import mlx_get_port_discards
from cumulus.mlx import mlx_clear_port_counters
from cumulus.mlx import mlx_clear_port_qos_counters
from cumulus.mlx import mlx_clear_port_ecn_counters
from cumulus.mlx import mlx_clear_port_prio_counters
from cumulus.mlx import mlx_clear_port_buff_counters
from cumulus.mlx import mlx_clear_port_tc_counters
from cumulus.mlx import mlx_get_port_pkt_size_counters
from cumulus.mlx import mlx_get_port_buffer_counters
from cumulus.mlx import mlx_clear_port_buffer_counters
from cumulus.mlx import mlx_get_port_ids
from cumulus.mlx import mlx_get_port_isolate
from cumulus.mlx import mlx_set_port_isolate
from cumulus.mlx import mlx_get_port_status
from cumulus.mlx import mlx_get_port_speed
from cumulus.mlx import mlx_get_port_rate
from cumulus.mlx import mlx_get_port_mtu
from cumulus.mlx import mlx_get_port_fec
from cumulus.mlx import mlx_get_port_width
from cumulus.mlx import mlx_get_fdb
from cumulus.mlx import mlx_get_mc_fdb
from cumulus.mlx import mlx_get_fdb_age_time
from cumulus.mlx import mlx_set_fdb_age_time
from cumulus.mlx import mlx_flush_uc_fdb_all
from cumulus.mlx import mlx_flush_uc_fdb_fid
from cumulus.mlx import mlx_flush_uc_fdb_log_port
from cumulus.mlx import mlx_get_all_mstp_instances
from cumulus.mlx import mlx_get_mstp_instance_vlans
from cumulus.mlx import mlx_get_mstp_port_state
from cumulus.mlx import mlx_get_lag_member_ids
from cumulus.mlx import mlx_get_lag_parameters
from cumulus.mlx import mlx_get_ecmp_parameters
from cumulus.mlx import mlx_set_port_sflow
from cumulus.mlx import mlx_get_port_sflow
from cumulus.mlx import mlx_get_port_sflow_statistics
from cumulus.mlx import mlx_get_port_sflow_sample_rate_max
from cumulus.mlx import mlx_get_bridge_vports
from cumulus.mlx import mlx_get_port_ecn_config
from cumulus.mlx import mlx_get_shared_buff_buffer_unit_size
from cumulus.mlx import mlx_get_shared_buff_limits
from cumulus.mlx import sdkApiFailed
from cumulus.mlx import noRoce
from cumulus.mlx import ingressEgressIdentical
from cumulus.mlx import mlx_clear_all_trap_stats
from cumulus.mlx import mlx_show_trap_stats
from cumulus.mlx import mlx_show_trap_group_stats
from cumulus.mlx import mlx_get_roce_mode_wrapper
from cumulus.mlx import mlx_get_sw_bondInfo
import cumulus.porttab


port_shared_buffer_alpha_dict = {0: "ALPHA_0",
                                 1: "ALPHA_1_128",
                                 2: "ALPHA_1_64",
                                 3: "ALPHA_1_32",
                                 4: "ALPHA_1_16",
                                 5: "ALPHA_1_8",
                                 6: "ALPHA_1_4",
                                 7: "ALPHA_1_2",
                                 8: "ALPHA_1",
                                 9: "ALPHA_2",
                                 10: "ALPHA_4",
                                 11: "ALPHA_8",
                                 12: "ALPHA_16",
                                 13: "ALPHA_32",
                                 14: "ALPHA_64",
                                 255: "ALPHA_INFINITY"}

# QoS mapping
sp_num    = 8 # 0..7
color_num = 3 # 0..2

na_string = 'N/A'

port_wildcard1 = "swp*"
port_wildcard2 = "swp+"
port_wildcard_all = "all"

g_pers_trap_stats_file = '/var/run/mlx_trap_stats_db'
mlxcmd_sx_sdk_pidfile = '/var/run/mlxcmd_sx_sdk.pid'
sx_sdk_pidfile = '/var/run/sx_sdk.pid'

roce_disabled = "0"
roce_lossy = "1"
roce_semi_lossless = "2"
roce_lossless = "3"

# Collect and dump all the bond info
global gBondInfo
global gBondInfo_set
gBondInfo_set = 0

global gSdkToLinux
global gSdkToLinux_set
gSdkToLinux_set = 0

def get_bond_info():
    global gBondInfo
    global gBondInfo_set
    if not gBondInfo_set:
        gBondInfo = mlx_get_sw_bondInfo()
        gBondInfo_set = 1

    return gBondInfo

def _print_json_output(_dict):
    # Dump JSON output without any indentation to help with parsing muti-line JSON outputs in script
    print((json.dumps(_dict)))

def get_sdk_to_linux():
    global gSdkToLinux
    global gSdkToLinux_set
    if gSdkToLinux_set:
        return gSdkToLinux

    gSdkToLinux = {}
    swid = 0
    pt = cumulus.porttab.porttab(use_hw=False)
    bondInfo = get_bond_info()
    for p in pt.get_linux_ports():
        gSdkToLinux[pt.linux2mlx(p)] = p
    for p in mlx_get_port_ids([swid])[swid]:
        if not gSdkToLinux.get(p,None):
            bond_list = []
            try:
                # Get the bond name if it is bond member
                if bondInfo:
                    bond_list = [bondInfo[_id]['linuxIntfName'] for _id in list(bondInfo.keys()) if int(p) == bondInfo[_id]['logicalPortId']]
            except:
                pass
            if bond_list:
                gSdkToLinux[p] = bond_list[0]
            else:
                # Populate portids for unknown ports to avoid KeyError
                gSdkToLinux[p] = 'Unknown'
    gSdkToLinux_set = 1

    return gSdkToLinux

def get_linux_to_sdk():
    linux_to_sdk = {}
    sdk_to_linux = get_sdk_to_linux()
    for sdkport in list(sdk_to_linux.keys()):
        linux_to_sdk[sdk_to_linux.get(sdkport)] = sdkport
    return linux_to_sdk

def get_port_type(port_id):
    # Get the port type from the SDK port portid
    return (port_id & SX_PORT_TYPE_ID_MASK) >> SX_PORT_TYPE_ID_OFFS

def _port_is_bond(sdk_to_linux, portid, use_json, ret):
    if sdk_to_linux and portid in sdk_to_linux and is_bond(sdk_to_linux[portid]):
        if use_json:
            if 'output' in ret:
                py_.merge(ret['output'], {'port': sdk_to_linux[portid], 'exception': 'portIsBond'})
        else:
            bond_name = sdk_to_linux[portid]
            print('port %s (0x%x) is a bond. Check the config on the bond members.' % (bond_name, portid))
        return True
    # Sometimes the LAG port is not present in switchd
    if get_port_type(portid) == SX_PORT_TYPE_LAG:
        if use_json:
            if 'output' in ret:
                py_.merge(
                    ret['output'],
                    {
                        'port': str(hex(portid)),
                        'exception': 'portIsBond'
                    }
                )
        else:
            print('port 0x%x is a bond - Not Supported' % (portid))
        return True
    return False

def _get_bond_id_from_mem_intf(sdk_to_linux, portid):
    bond_id = ''
    if sdk_to_linux and portid in sdk_to_linux and is_bond_member(sdk_to_linux[portid]):
        try:
            bondInfo = get_bond_info()
            if bondInfo:
                bond_id = [bondInfo[_id]['logicalPortId'] for _id in list(bondInfo.keys()) if int(portid) in list(bondInfo[_id]['slaveLogIds'])]
                if bond_id:
                    bond_id = bond_id[0]
        except:
            pass
    return bond_id

def _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
    if sdk_to_linux and portid in sdk_to_linux and is_bond_member(sdk_to_linux[portid]):
        if use_json:
            if 'output' in ret:
                ret['output'] = {'port': sdk_to_linux[portid], 'exception': 'portIsBondMember'}
        else:
            bond_name = ''
            try:
                bondInfo = get_bond_info()
                if bondInfo:
                    bond_name = [bondInfo[_id]['linuxIntfName'] for _id in list(bondInfo.keys()) if int(portid) in list(bondInfo[_id]['slaveLogIds'])]
                    if bond_name:
                        bond_name = ' %s' % bond_name[0]
            except:
                pass
            print('port %s (0x%x) is a bond member. Check the config on the bond intf%s itself.' % (sdk_to_linux[portid], portid, bond_name))
        return True
    return False

def _add_port_to_dict(sdk_to_linux, _dict, _key, portid):
    if sdk_to_linux.get(portid,None):
        _dict[_key] = sdk_to_linux[portid]
    else:
        _dict[_key] = portid

def _show_interface_table(use_json=False) :
    data = mlx_get_interface()
    if use_json:
        _print_json_output(data)
    else:
        print('Interface Table')
        print('ID\t', end=' ')
        for label in data['labels'] :
            print('%s\t' % label, end=' ')
        print('')
        for rif, entry in list(data['entries'].items()):
            print('%s\t' % rif, end=' ')
            for label in data['labels'] :
                value = entry[label]
                print('%s\t' % value, end=' ')
            print('')

def _set_router_interface(vrid=None, rif=0, use_json=False) :
    data = mlx_set_router_interface(vrid,rif)

def _show_uc_route_table(vrid=None, version=None, use_json=False) :
    rt_dict   = {}
    tabs_dict = { 4: '\t', 6: '\t\t\t\t\t'}
    if version == None :
        rt_dict[4] = mlx_get_uc_route(4, vrid);
        rt_dict[6] = mlx_get_uc_route(6, vrid);
    else :
        rt_dict[version] = mlx_get_uc_route(version, vrid);

    if use_json:
        for version, rt in list(rt_dict.items()) :
            _print_json_output(rt)
    else:
        for version, rt in list(rt_dict.items()) :
            print("\nVRF\tNetwork Address%sNext Hops\tAction\t\tType\t\tIntf ID\tECMP ID" % tabs_dict[version])
            for entry in rt:
                next_hop_count = rt[entry]['next_hop_cnt']
                spacing1 = "\t\t"
                spacing2 = "\t\t"
                intf_id = '<none>'
                ecmp_id = '<none>'
                if next_hop_count > 0 :
                    first_next_hop = rt[entry]['next_hops'][0]
                    if len(rt[entry]['next_hops'][0]) > 3:
                        spacing = "\t"
                    if (rt[entry]['ecmp_id'] > 0) and \
                       (next_hop_count > 1):
                        ecmp_id = '%d' % rt[entry]['ecmp_id']
                    elif (rt[entry]['ecmp_id'] > 0) and \
                         (next_hop_count == 1):
                        intf_id = '%d' % rt[entry]['erif']
                        ecmp_id = '%d' % rt[entry]['ecmp_id']
                else :
                    first_next_hop = '<none>'
                if rt[entry]['type'] == 'LOCAL' :
                    intf_id = '%d' % rt[entry]['erif']

                if len(rt[entry]['type']) > 5:
                    spacing2 = '\t'

                print("%d\t%s\t%s%s%s\t\t%s%s%s\t%s" % (rt[entry]['vrid'], entry.split()[1],
                                                        first_next_hop,
                                                        spacing1,
                                                        rt[entry]['action'],
                                                        rt[entry]['type'],
                                                        spacing2,
                                                        intf_id,
                                                        ecmp_id))

                for i in range(1,next_hop_count) :
                    if len(rt[entry]['next_hops'][i]) > 3:
                        nspace = "\t"
                    else:
                        nspace = "\t\t"

                    print("\t\t\t\t%s%s%s\t\t%s%s%s\t%s" % (rt[entry]['next_hops'][i],
                                                            nspace,
                                                            rt[entry]['action'],
                                                            rt[entry]['type'],
                                                            spacing2,
                                                            intf_id,
                                                            ecmp_id))

def _show_uc_route_count(vrid=None, version=None, use_json=False) :
    rt_dict   = {}
    tabs_dict = { 4: '\t', 6: '\t\t\t\t\t'}
    if version == None :
        rt_dict[4] = mlx_get_uc_routei_count(4, vrid);
        rt_dict[6] = mlx_get_uc_route_count(6, vrid);
    else :
        rt_dict[version] = mlx_get_uc_route_count(version, vrid);

    if use_json:
        for version, rt in list(rt_dict.items()) :
            _print_json_output(rt)
    else:
        for version, rt in list(rt_dict.items()) :
            print("number of routes for IPv%d: %d" % (version, rt['route_count']))

def _print_mc_route_entry(rt, entry, use_json=False):
    mroute_sg = entry.split()[1]
    source_addr = mroute_sg.split('/')[0]
    group_addr = mroute_sg.split('/')[1]
    iif_str = rt[entry]['iif']
    oif_str = ""

    for oif_list in rt[entry]['egress_rifs']:
        for oif in oif_list:
            if oif == 0x8c00000:
                oif_str = oif_str +  "TUN[0x8c00000] "
            else:
                oif_str = oif_str + str(oif) + " "

    print("%s %16s,%-16s   %4s %7s   %s" % (rt[entry]['vrid'],
                                            source_addr,
                                            group_addr,
                                            iif_str,
                                            rt[entry]['mc_container_id'],
                                            oif_str))

def _show_mc_route_table(vrid=None, version=4, use_json=False):
    mc_rt_dict = {}
    mc_rt_dict[version] = mlx_get_mc_route(version, vrid);
    if use_json:
        for version, rt in list(mc_rt_dict.items()) :
            _print_json_output(rt)
    else:
        for version, rt in list(mc_rt_dict.items()) :
            header_str = "Multicast Route"
            dash_str = "-"
            print("\nVRF %s IIF  ECMP-Id   Egress RIFs" % (header_str.center(33)))
            print("%s %s %s %s %s" %(dash_str.center(3, '-'),
                                     dash_str.center(33, '-'),
                                     dash_str.center(4, '-'),
                                     dash_str.center(8, '-'),
                                     dash_str.center(24, '-')))
            for entry in rt:
                _print_mc_route_entry(rt, entry, use_json)
                internal_mroute = rt[entry].get('internal_mroute')
                if internal_mroute != None:
                    _print_mc_route_entry(rt, internal_mroute, use_json)

def _show_ip_neighbor_table(version=None, rif=0, use_json=False):

    nt_dict   = {}
    tabs_dict = { 4: '\t', 6: '\t\t\t\t'}

    if version == None :
        nt_dict[4] = mlx_get_neighbor(4, rif)
        nt_dict[6] = mlx_get_neighbor(6, rif)
    else :
        nt_dict[version] = mlx_get_neighbor(version, rif)

    if use_json:
        for version, nt in list(nt_dict.items()) :
            _print_json_output(nt)
    else:
        for version, nt in list(nt_dict.items()) :
            print("\nVRID\tIP Address%sMAC Address\t\tAction\tPrio\tRtr Intf" % tabs_dict[version])
            for neigh in nt:
                print("%d\t%s\t%s\t%s\t%d\t%d" % (nt[neigh]['vrid'],neigh.split()[0],
                                                  nt[neigh]['mac'],
                                                  nt[neigh]['action'],
                                                  nt[neigh]['prio'],
                                                  nt[neigh]['rif']))

def _show_fdb_table(swid=0, filter_type = (None, None), use_json=False):

    fdb = mlx_get_fdb(swid, filter_type)

    if use_json:
        _print_json_output(fdb)
    else:
        print("Swid\tMAC Address\t\tAction\t\tEntry Type\tFID_VID\tVLAN ID\t Port")
        for mac in fdb:
            for fdb_info in fdb[mac]:
                if len(fdb_info['action']) > 3:
                    spacing = "\t"
                else:
                    spacing = "\t\t"

                print("%d\t%s\t%s%s%s\t%d\t%d\t %s" % (fdb_info['swid'], mac,
                                                       fdb_info['action'],
                                                       spacing,
                                                       fdb_info['entry_type'],
                                                       fdb_info['fid_vid'],
                                                       fdb_info['vlan_id'],
                                                       fdb_info['port_id']))

def _show_mc_fdb_table(swid=0, filter_type = (None, None), use_json=False):

    mc_fdb = mlx_get_mc_fdb(swid, filter_type)

    if use_json:
        _print_json_output(fdb)
    else:
        print("Multicast FDB table:")
        print("====================")

        for mac in mc_fdb:
            for fdb_info in mc_fdb[mac]:
                spacing = "\t\t"

                print("Swid: %d\tMAC: %s\tFID: %d\t" % (fdb_info['swid'], mac,
                                          fdb_info['vid']))
                print(spacing)
                print("\tport-list:")
                print("\t----------")
                print("\t    logical port \t vlan_id \t port_id")
                print("\t    --------------------------------------------")
                for port_count in range(fdb_info['count']):
                    print(("\t    %s \t\t %d \t\t %d" % (hex(fdb_info['port_list'][port_count]),
                            fdb_info['vlan_id'][port_count],
                            fdb_info['port_id'][port_count])))
                print(spacing)

def _show_fdb_age_time(swid_list=[0], use_json=False):
    age = mlx_get_fdb_age_time(swid_list)

    if use_json:
        print((json.dump(age, strean=screen)))
    else:
        print("Swid\tAge Time (sec)")
        for swid in age:
            print("%d\t%d" % (swid, age[swid]))

def _show_vlan_ids(swid_list=[0], use_json=False):
    vlan_id_in_use = mlx_get_vlan_ids(swid_list)

    if use_json:
        _print_json_output(vlan_id_in_use)
    else:
        print("Swid\tVLAN ID")
        for swid, vid in vlan_id_in_use:
            print("%d\t%d" % (swid, vid))

def _show_bridge_vports(swid=0, br_vid=None, use_json=False):
    vports = mlx_get_bridge_vports(swid, br_vid)

    if use_json:
        _print_json_output(vports)
    else:
        print("Logical Port ID")
        for port in vports:
            print(port)

def _show_vlan_ports(swid=0, vids=[], use_json=False):
    vlan_port_map = mlx_get_vlan_ports(swid, vids)

    if use_json:
        _print_json_output(vlan_port_map)
    else:
        print("Swid\tVLAN ID\t\tType\tDevice/VLAN ID\tPort ID\tSub-Port ID\tUntagged")
        for vid in vlan_port_map:
            for entry in vlan_port_map[vid]:
                print("%d\t%d\t\t%d\t%d\t\t%d\t%d\t\t%s" % (swid, vid, entry['type'],
                                                            entry['device_vlan_id'],
                                                            entry['port_id'],
                                                            entry['sub_port_id'],
                                                            str(bool(entry['untagged']))))

def __expand_port_name_or_id(port_id_cb, port_name_or_id, wc_name_allowed=False, use_json=False, params=None):
    one_large_list = []
    port_list = _get_port_list_from_name_or_id(port_name_or_id, wc_name_allowed, use_json)
    if len(port_list) == 0:
        if use_json:
            one_large_list.append({'port': port_name_or_id, 'exception': 'inputPortNotSupported'})
        else:
            print("%s is not a supported port_name_or_id" % port_name_or_id)
    sdk_to_linux = get_sdk_to_linux()
    ret = None
    for portid in port_list:
        if (params == None):
            out_dict = port_id_cb(sdk_to_linux, portid, use_json)
        else:
            out_dict = port_id_cb(sdk_to_linux, portid, use_json, params)
        # Combine the output of all dicts and print one line of json output
        if use_json and out_dict:
            one_large_list.append(out_dict)
        elif out_dict is False:
            ret = False

    if one_large_list:
        if params and 'ret_out' in params and params['ret_out']:
            return one_large_list
        else:
            if len(one_large_list) == 1:
                # For ensuring backwards compatible output for CLIs with single line json output
                _print_json_output(one_large_list[0])
            else:
                _print_json_output(one_large_list)

    return ret

def _show_port_counters(sdk_to_linux, portid, use_json=False):
    port_name = sdk_to_linux.get(portid, None)
    if not port_name or port_name == 'Unknown':
        if use_json:
            return {'exception': 'invalidOrUnknownPort'}
        else:
            print('ERROR: Invalid or Unknown port 0x%x' % portid)
        return

    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']

    if portid in sdk_to_linux and is_bond_member(sdk_to_linux[portid]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, portid)
    else:
        # Hack here to consider bondid as ingress port id when the port is not bond member
        bondid = portid
    counter_info = mlx_get_port_counters(portid, bondid)

    if use_json:
        return counter_info
    else:
        print("Counters for port %s (%d)" % (sdk_to_linux.get(portid), portid))
        cntr_type_list = ['per_port', 'perf', 'ieee', 'per_prio', 'per_pg', 'per_tc']
        for cntr_type in cntr_type_list :
            for label in counter_info[cntr_type]['labels']:
                for counter_set_idx in range(len(counter_info[cntr_type]['values'])) :
                    if cntr_type == 'ieee':
                        print('IEEE cntr', end=' ')
                    elif cntr_type == 'per_prio':
                        print("switch priority %d" % counter_info[cntr_type]['values'][counter_set_idx]['switch priority'], end=' ')
                    elif cntr_type == 'per_tc' :
                        print("traffic class   %d" % counter_info[cntr_type]['values'][counter_set_idx]['traffic class'], end=' ')
                    elif cntr_type == 'per_pg' :
                        print("priority group   %d" % counter_info[cntr_type]['values'][counter_set_idx]['priority group'], end=' ')
                    counter_value = counter_info[cntr_type]['values'][counter_set_idx][label]
                    if len(label) > 6:
                        space = "\t"
                    else:
                        space = "\t\t"
                    print("%s%s%d" % (label, space, counter_value))

def _show_port_counters_name_or_id(port_name_or_id, use_json=False):
    __expand_port_name_or_id(_show_port_counters, port_name_or_id, True, use_json)

def _show_port_pkt_size_counters(sdk_to_linux, portid, use_json=False):
    counter_info = mlx_get_port_pkt_size_counters(portid)

    if use_json:
        return counter_info
    else:
        print("Counters for port %s (%d)" % (sdk_to_linux.get(portid), portid))
        for cntr in counter_info:
            if cntr != 'port':
                if len(cntr) > 15:
                    space = "\t"
                elif len(cntr) < 8:
                    space = "\t\t\t"
                else:
                    space = "\t\t"
                print("%s%s%d" % (cntr, space, counter_info[cntr]))

def _show_port_pkt_size_counters_name_or_id(port_name_or_id, use_json=False):
    __expand_port_name_or_id(_show_port_pkt_size_counters, port_name_or_id, True, use_json)

def _clear_port_counters(sdk_to_linux, portid, use_json=False, params=None):
    mlx_clear_port_counters(portid, group_type=params['group_type'])

def _show_port_discards(sdk_to_linux, portid, use_json=False):
    discard_info = mlx_get_port_discards(portid)

    if use_json:
        return discard_info
    else:
        print("Discards from port %s (%d)" % (sdk_to_linux.get(portid), portid))
        for discard in discard_info:
            if discard != 'port':
                if len(discard) > 6:
                    space = "\t"
                else:
                    space = "\t\t"
                print("%s%s%d" % (discard, space, discard_info[discard]))

def _show_port_discards_name_or_id(port_name_or_id, use_json=False):
    __expand_port_name_or_id(_show_port_discards, port_name_or_id, True, use_json)

def _find_buffer_entry(limit_list, reg, pool_id='', pg_id='', tc_id='', sp_id=''):
    item_list = []
    for item in limit_list:
        if reg == 'INGRESS_PORT' and item['type'] == 'INGRESS_PORT':
            if item['pool'] == pool_id:
                item_list = item
                break
        if reg == 'INGRESS_PORT_DESCRIPTOR' and item['type'] == 'INGRESS_PORT_DESCRIPTOR':
            if item['pool'] == pool_id:
                item_list = item
                break
        if reg == 'EGRESS_PORT' and item['type'] == 'EGRESS_PORT':
            if item['pool'] == pool_id:
                item_list = item
                break
        if reg == 'EGRESS_PORT_DESCRIPTOR' and item['type'] == 'EGRESS_PORT_DESCRIPTOR':
            if item['pool'] == pool_id:
                item_list = item
                break
        if reg == 'INGRESS_PORT_PRIORITY_GROUP' and item['type'] == 'INGRESS_PORT_PRIORITY_GROUP':
            if item['pg'] == pg_id:
                item_list = item
                break
        if reg == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' and item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR':
            if item['pg'] == pg_id:
                item_list = item
                break
        if reg == 'EGRESS_PORT_TRAFFIC_CLASS' and item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS':
            if item['tc'] == tc_id:
                item_list = item
                break
        if reg == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR' and item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR':
            if item['tc'] == tc_id:
                item_list = item
                break
        if reg == 'MULTICAST' and item['type'] == 'MULTICAST':
            if item['sp'] == sp_id:
                item_list = item
                break
        if reg == 'MULTICAST_PORT' and item['type'] == 'MULTICAST_PORT':
            item_list = item
            break
    return item_list

def print_port_buffer_legends():
    cell_size = mlx_get_shared_buff_buffer_unit_size()
    print("Legends:")
    print("========")
    print("  Region: iPort.PG/ePort.TC/iPort/ePort/ePort.MC/Multicast.SP")
    print("  PoolID: Corresponding pool id where the regions are mapped to")
    print("  PoolMode: Dynamic/Static pool mode which governs the usage of the region mapped to it")
    print("  PG-Id: Corresponding PG id for iPort.PG region")
    print("  Lossy/Lossless: Only valid for iPort.PG region - lossless in case of PFC enabled")
    print("  XON Th: xon threshold value - only valid for lossless iPort.PG region")
    print("  XOFF Th: xoff threshold value - only valid for lossless iPort.PG region")
    print("  TC: Traffic Class corresponding to ePort.TC region")
    print("  HR: Headroom size corresponding to each iPort.PG region")
    print("  Reserved Size: Min guaranteed quota from shared buffer for corresponding region")
    print("  Shared_Max: Max quota of shared buffer buffer for packets in corresponding region - alpha in case of dynamic pool, cells in case of static pools")
    print("  HR/PL Usage: Current usage of headroom for iPort.PG region(in cells)")
    print("  HR/PL Max: Max peak usage of headroom for iPort.PG region(in cells)")
    print("  Current Usage: Current shared buffer usage of corresponding region(in cells)")
    print("  Max Usage: Maximum shared buffer usage of corresponding region(in cells)")
    print("  SP: Internal switch priority of the packet")
    print("     1 cell == %d bytes" % (cell_size))

def _show_port_buffer_counters_name_or_id(port_name_or_id, use_json=False, pg_buff=False, tc_buff=False, mc_buff=False, port_buff=False, descriptor_buff=False, use_cells=False):
    return __expand_port_name_or_id(_show_port_buffer_counters, port_name_or_id, True, use_json, params={'ret_out': use_json, 'pg_buff': pg_buff, 'tc_buff': tc_buff, 'mc_buff': mc_buff, 'port_buff': port_buff, 'descriptor_buff': descriptor_buff, 'use_cells': use_cells})

def _clear_port_buffer_counters(sdk_to_linux, portid, use_json=False):
    clear_pass = True
    port_name = None
    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        if use_json:
            return ret['output']
        else:
            return False
    if sdk_to_linux:
        port_name = sdk_to_linux.get(portid, None)
        if not port_name or port_name == 'Unknown':
            if use_json:
                return {'exception': 'Unknown port {}'.format(portid)}
            else:
                print(('Unknown port {}'.format(portid)))
            return False
    try:
        mlx_clear_port_buffer_counters(portid)
    except sdkApiFailed as e:
        if use_json:
            return {'port': port_name, 'exception': e.message}
        else:
            print(('{}: {}'.format(port_name, e.message)))
        clear_pass = False

    if not port_name:
        port_name = 'MC_PORT'

    if use_json:
        return {'port': port_name, 'result': 'success'}
    else:
        return clear_pass

def _show_port_buffer_counters(sdk_to_linux, portid, use_json=False, params=None):
    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    if sdk_to_linux:
        port_name = sdk_to_linux.get(portid, None)
        if not port_name or port_name == 'Unknown':
            if use_json:
                return {'exception': 'invalidOrUnknownPort'}
            else:
                print('ERROR: Invalid or Unknown port 0x%x' % portid)
            return

    json_counter_info = {'counter_list': []}
    counter_info = mlx_get_port_buffer_counters(portid)
    shared_limit_list = mlx_get_port_shared_buffer_limits(portid)
    res_limit_list = mlx_get_port_reserved_buffer_limits(portid)

    # Copy over the counter_info to json_counter_info except counter_list which we will curtail according to the input
    for key in counter_info:
        if key == 'counter_list':
            continue
        json_counter_info[key] = counter_info[key]

    cell_unit_size = int(counter_info['cell_unit_size'])
    table_list = []
    idx = 0
    for item in counter_info['counter_list'] :
        reg = item['type']
        res = '-'
        sp = '-'
        mode = '-'
        tc = '-'
        pool = '-'
        pg = '-'
        lossy = '-'
        xon = '-'
        xoff = '-'
        size = '-'
        hr = '-'
        hr_usage = '-'
        hr_max = '-'
        if (item['type'] == 'INGRESS_PORT' or item['type'] == 'EGRESS_PORT' or item['type'] == 'INGRESS_PORT_DESCRIPTOR' or item['type'] == 'EGRESS_PORT_DESCRIPTOR'):
            #Pool-Id
            pool = item['index']
            res_entry = _find_buffer_entry(res_limit_list,item['type'],pool_id=item['index'])
            sha_entry = _find_buffer_entry(shared_limit_list,item['type'],pool_id=item['index'])
            if 'cells' in res_entry:
                res = res_entry['cells']
            if 'mode' in sha_entry:
                mode = sha_entry['mode']
            if 'cells' in sha_entry:
                size = sha_entry['cells']
        if (item['type'] == 'INGRESS_PORT_PRIORITY_GROUP' or item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR'):
            #PG-Id
            pg = item['index']
            res_entry = _find_buffer_entry(res_limit_list,item['type'],pg_id=item['index'])
            sha_entry = _find_buffer_entry(shared_limit_list,item['type'],pg_id=item['index'])
            if 'lossy' in res_entry:
                lossy = res_entry['lossy']
            if 'hr' in res_entry:
                hr = res_entry['hr']
            if 'cells' in res_entry:
                res = res_entry['cells']
            if not lossy:
                hr = res_entry['cells']
                res = '-'
                if 'lossless_hr_curr' in item:
                    hr_usage = item['lossless_hr_curr']
                if 'lossless_hr_watermark' in item:
                    hr_max = item['lossless_hr_watermark']
            else:
                if 'lossy_hr_curr' in item:
                    hr_usage = item['lossy_hr_curr']
                if 'lossy_hr_watermark' in item:
                    hr_max = item['lossy_hr_watermark']
            if 'xon' in res_entry:
                xon = res_entry['xon']
            if 'xoff' in res_entry:
                xoff = res_entry['xoff']
            if 'mode' in sha_entry:
                mode = sha_entry['mode']
            if 'cells' in sha_entry:
                size = sha_entry['cells']
            if 'pool' in sha_entry:
                pool = sha_entry['pool']
        if (item['type'] == 'MULTICAST'):
            #MC-SP
            sp = item['index']
            res_entry = _find_buffer_entry(res_limit_list,item['type'],sp_id=item['index'])
            sha_entry = _find_buffer_entry(shared_limit_list,item['type'],sp_id=item['index'])
            if 'cells' in res_entry:
                res = res_entry['cells']
            if 'mode' in sha_entry:
                mode = sha_entry['mode']
            if 'cells' in sha_entry:
                size = sha_entry['cells']
            if 'pool' in sha_entry:
                pool = sha_entry['pool']
        if (item['type'] == 'MULTICAST_PORT'):
            #MC-Port
            res_entry = _find_buffer_entry(res_limit_list,item['type'])
            sha_entry = _find_buffer_entry(shared_limit_list,item['type'])
            if 'cells' in res_entry:
                res = res_entry['cells']
            if 'mode' in sha_entry:
                mode = sha_entry['mode']
            if 'cells' in sha_entry:
                size = sha_entry['cells']
            if 'pool' in sha_entry:
                pool = sha_entry['pool']
        if (item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS' or item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR'):
            #TC-Id
            tc = item['index']
            res_entry = _find_buffer_entry(res_limit_list,item['type'],tc_id=item['index'])
            sha_entry = _find_buffer_entry(shared_limit_list,item['type'],tc_id=item['index'])
            if 'cells' in res_entry:
                res = res_entry['cells']
            if 'mode' in sha_entry:
                mode = sha_entry['mode']
            if 'cells' in sha_entry:
                size = sha_entry['cells']
            if 'pool' in sha_entry:
                pool = sha_entry['pool']

        curr = item['current']
        max_usage = item['watermark']

        table_row = []
        table_row.append(reg)
        table_row.append(pool)
        item['pool_id'] = pool
        table_row.append(mode)
        item['mode'] = mode
        if (item['type'] == 'MULTICAST'):
            table_row.append(sp)
            item['switch_prio'] = sp
        else:
            if params and (params['pg_buff'] or params['descriptor_buff']):
                table_row.append(pg)
                item['pg_id'] = pg
            if params and params['pg_buff']:
                if (lossy == 1):
                    table_row.append("Lossy")
                elif (lossy == 0):
                    table_row.append("Lossless")
                else:
                    table_row.append(lossy)
                item['lossy'] = lossy
                if params['use_cells']:
                    table_row.append(xon)
                    item['xon_th'] = xon
                    table_row.append(xoff)
                    item['xoff_th'] = xoff
                else:
                    if xon != '-':
                        table_row.append(formatBytes(int(xon)*cell_unit_size))
                        item['xon_th'] = formatBytes(int(xon)*cell_unit_size)
                    else:
                        table_row.append(xon)
                        item['xon_th'] = xon
                    if xoff != '-':
                        table_row.append(formatBytes(int(xoff)*cell_unit_size))
                        item['xoff_th'] = formatBytes(int(xoff)*cell_unit_size)
                    else:
                        table_row.append(xoff)
                        item['xoff_th'] = xoff
            if params and (params['tc_buff'] or params['descriptor_buff']):
                table_row.append(tc)
                item['tclass'] = tc
            if params and params['pg_buff']:
                if hr != '-':
                    if params['use_cells']:
                        table_row.append(str(hr) + " cells")
                        item['headroom'] = hr
                    else:
                        table_row.append(formatBytes(int(hr)*cell_unit_size))
                        item['headroom'] = formatBytes(int(hr)*cell_unit_size)
                else:
                    table_row.append(hr)
                    item['headroom'] = hr
                if params['use_cells']:
                    table_row.append(hr_usage)
                    item['hr_curr_usage'] = hr_usage
                    table_row.append(hr_max)
                    item['hr_max_usage'] = hr_max
                else:
                    if hr_usage != '-':
                        table_row.append(formatBytes(int(hr_usage)*cell_unit_size))
                        item['hr_curr_usage'] = formatBytes(int(hr_usage)*cell_unit_size)
                    else:
                        table_row.append(hr_usage)
                        item['hr_curr_usage'] = hr_usage
                    if hr_max != '-':
                        table_row.append(formatBytes(int(hr_max)*cell_unit_size))
                        item['hr_max_usage'] = formatBytes(int(hr_max)*cell_unit_size)
                    else:
                        table_row.append(hr_max)
                        item['hr_max_usage'] = hr_max
        if res == '-':
            table_row.append(res)
            item['reserved'] = res
        elif res != 0:
            if item['type'] == 'INGRESS_PORT_DESCRIPTOR' or item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' or\
               item['type'] == 'EGRESS_PORT_DESCRIPTOR' or item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR':
                table_row.append(str(res) + " pkts")
                item['reserved'] = res
            else:
                if params['use_cells']:
                    table_row.append(str(res) + " cells")
                    item['reserved'] = res
                else:
                    table_row.append(formatBytes(int(res)*cell_unit_size))
                    item['reserved'] = formatBytes(int(res)*cell_unit_size)
        else:
            if params['use_cells']:
                table_row.append(res)
                item['reserved'] = res
            else:
                table_row.append(formatBytes(int(res)*cell_unit_size))
                item['reserved'] = formatBytes(int(res)*cell_unit_size)

        if mode == 'DYNAMIC':
            table_row.append(port_shared_buffer_alpha_dict[size])
            item['shared_max'] = port_shared_buffer_alpha_dict[size]
        else:
            if size == int('0xffffffff',16):
                table_row.append("INFINITY")
                item['shared_max'] = 'infinity'
            else:
                if item['type'] == 'INGRESS_PORT_DESCRIPTOR' or item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' or\
                    item['type'] == 'EGRESS_PORT_DESCRIPTOR' or item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR':
                    table_row.append(str(size) + " pkts")
                else:
                    if params['use_cells']:
                        table_row.append(str(size) + " cells")
                        item['shared_max'] = size
                    else:
                        table_row.append(formatBytes(int(size)*cell_unit_size))
                        item['shared_max'] = formatBytes(int(size)*cell_unit_size)
        if params['use_cells']:
            table_row.append(curr)
            table_row.append(max_usage)
        else:
            item['current'] = formatBytes(int(curr)*cell_unit_size)
            item['watermark'] = formatBytes(int(max_usage)*cell_unit_size)
            table_row.append(formatBytes(int(curr)*cell_unit_size))
            table_row.append(formatBytes(int(max_usage)*cell_unit_size))

        # Do not print the regions when it is not queried
        if reg == 'EGRESS_PORT_TRAFFIC_CLASS':
            if params and not params['tc_buff']:
                continue
        if reg == 'INGRESS_PORT_PRIORITY_GROUP':
            if params and not params['pg_buff']:
                continue
        if reg == 'INGRESS_PORT' or reg == 'EGRESS_PORT':
            if params and not params['port_buff']:
                continue
        if reg == 'MULTICAST_PORT':
            if params and not params['mc_buff']:
                continue
        if reg == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR' or reg == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' or reg == 'INGRESS_PORT_DESCRIPTOR' or reg == 'EGRESS_PORT_DESCRIPTOR':
            if params and not params['descriptor_buff']:
                continue

        # Change the index field to reflect the counter index instead
        idx += 1
        item['index'] = idx
        json_counter_info['counter_list'].append(item)

        if size == 0 and res == 0:
            # Do not print the regions for which no buffer is configured
            continue

        table_list.append(table_row)

    # Modify the port to reflect the meaningful port id as swp*
    if (portid == SX_MC_PORT_LOG_ID):
        json_counter_info['port'] = 'MC_PORT'
    elif (sdk_to_linux):
        json_counter_info['port'] = sdk_to_linux.get(portid)

    if use_json:
        return json_counter_info
    else:
        if (portid == SX_MC_PORT_LOG_ID):
            print("Reserved + shared buffer counters for port 0x%x" % (portid))
            print("===============================================")
            print((tabulate(table_list, headers=['Region', 'PoolID', 'PoolMode', 'SP', 'Reserved Size', 'Shared_Max/Dynamic_Quota', 'Current Usage', 'Max Usage'])))
            print("===============================================")
        else:
            if sdk_to_linux:
                print("Reserved + shared buffer counters for port %s" % (sdk_to_linux.get(portid)))
            else:
                print("Reserved + shared buffer counters for port 0x%x" % (portid))
            headers = ['Region', 'PoolID', 'PoolMode']
            if params and params['pg_buff'] or params['descriptor_buff']:
                headers.append('PG-Id')
            if params and params['pg_buff']:
                headers.extend(['Lossy/Lossless', 'XON Th', 'XOFF Th'])
            if params and params['tc_buff'] or params['descriptor_buff']:
                headers.append('TC')
            if params and params['pg_buff']:
                headers.extend(['HR', 'HR/PL Usage', 'HR/PL Max'])
            headers.extend(['Reserved Size', 'Shared_Max', 'Current Usage', 'Max Usage'])
            print("===============================================")
            print((tabulate(table_list, headers=headers)))
            print("===============================================")

def _show_port_shared_buffer_limits(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']

    limit_list = mlx_get_port_shared_buffer_limits(portid)
    cell_size = mlx_get_shared_buff_buffer_unit_size()

    limits_dict = {}
    _add_port_to_dict(sdk_to_linux, limits_dict, 'port', portid)

    limits_dict['list'] = limit_list
    limits_dict['cell_unit_size'] = cell_size

    if use_json:
        return limits_dict
    else:
        print("\nShared cell limit for port %s(0x%x) (1 cell == %d bytes)" % (limits_dict['port'],portid,cell_size))
        print('items: %d' % len(limit_list))
        for limit_item in limit_list :
            space = '\t'
            print(limit_item['type'], end=' ')
            print('pool ID', end=' ')
            print(limit_item['pool'], end=' ')
            if 'sp' in limit_item:
                print('switch priority', end=' ')
                print(limit_item['sp'], end=' ')
            if 'pg' in limit_item:
                print('priority group', end=' ')
                print(limit_item['pg'], end=' ')
            if 'tc' in limit_item:
                print('traffic class', end=' ')
                print(limit_item['tc'], end=' ')
            print(space, end=' ')
            print(limit_item['mode'], end=' ')
            if limit_item['mode'] == "DYNAMIC":
                print(port_shared_buffer_alpha_dict[limit_item['cells']])
            else:
                if limit_item['type'] == 'INGRESS_PORT_DESCRIPTOR' or limit_item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' or\
                   limit_item['type'] == 'EGRESS_PORT_DESCRIPTOR' or limit_item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR':
                    print('packets', end=' ')
                else:
                    print('cells', end=' ')
                print(limit_item['cells'])

def _show_port_reserved_buffer_limits(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']

    limit_list = mlx_get_port_reserved_buffer_limits(portid)
    cell_size = mlx_get_shared_buff_buffer_unit_size()

    limits_dict = {}
    _add_port_to_dict(sdk_to_linux, limits_dict, 'port', portid)

    limits_dict['list'] = limit_list
    limits_dict['cell_unit_size'] = cell_size

    if use_json:
        return limits_dict
    else:
        print("\nReserved cell limit for port %s(0x%x) (1 cell == %d bytes)" % (limits_dict['port'],portid,cell_size))
        print('items: %d' % len(limit_list))
        for limit_item in limit_list :
            space = '\t'
            print(limit_item['type'], end=' ')
            print('pool ID', end=' ')
            print(limit_item['pool'], end=' ')
            if 'sp' in limit_item:
                print('switch priority', end=' ')
                print(limit_item['sp'], end=' ')
            if 'pg' in limit_item:
                print('priority group', end=' ')
                print(limit_item['pg'], end=' ')
            if 'tc' in limit_item:
                print('traffic class', end=' ')
                print(limit_item['tc'], end=' ')
            if 'lossy' in limit_item:
                print('is lossy:', end=' ')
                print(limit_item['lossy'], end=' ')
            if 'xon' in limit_item:
                print('xon:', end=' ')
                print(limit_item['xon'], end=' ')
            if 'xoff' in limit_item:
                print('xoff:', end=' ')
                print(limit_item['xoff'], end=' ')
            print(space, end=' ')
            if limit_item['type'] == 'INGRESS_PORT_DESCRIPTOR' or limit_item['type'] == 'INGRESS_PORT_PRIORITY_GROUP_DESCRIPTOR' or\
               limit_item['type'] == 'EGRESS_PORT_DESCRIPTOR' or limit_item['type'] == 'EGRESS_PORT_TRAFFIC_CLASS_DESCRIPTOR':
                print('packets', end=' ')
            else:
                print('cells', end=' ')
            print(limit_item['cells'])

def _show_shared_buffer_limits(sdk_to_linux, portid, use_json=False, verbose=False):
    _info = mlx_get_shared_buff_limits()

    ret = {'output': _info}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']

    port_buff_cap = mlx_get_port_buffer_capabilities(portid)
    _add_port_to_dict(sdk_to_linux, port_buff_cap, 'port', portid)

    py_.merge(_info, port_buff_cap)

    if use_json:
        return _info
    else:
        print('\nPort %s(0x%x) Shared buffer limits' % (_info['port'],portid))
        print("\t Max Pool Size: {}".format(_info['pool_size']))
        print("\t Cell Unit Size: {}".format(_info['cell_unit_size']))
        print('\t Port Headroom Max: %s' % _info['port_headroom_max'])

def _show_pool_buffer_limits(use_json=False):
    buffer_info = mlx_get_pool_buffer_limits()
    cell_size = mlx_get_shared_buff_buffer_unit_size()
    if use_json:
        return buffer_info
    else :
        print("\nPool buffer limits (1 cell == %d bytes)" % (cell_size))
        for pool_id, pool_item in list(buffer_info.items()) :
            if (pool_item['type'] == 'User'):
                print('%s %s Pool %d' % (pool_item['type'], pool_item['buff-type'], pool_id), end=' ')
            else:
                print('%s Pool %d' % (pool_item['type'], pool_id), end=' ')
            space = '\t'
            print('mode', end=' ')
            print(pool_item['mode'], end=' ')
            if re.search('descriptor', pool_item['buff-type']):
                print('packets', end=' ')
            else:
                print('cells', end=' ')
            if pool_item.get('infinite'):
                print('inf')
            else:
                print(pool_item['cells'])

def _clear_pool_buffer_counters(use_json=False):
    clear_pass = True
    json_out = {}
    try:
        mlx_clear_pool_buffer_counters()
    except sdkApiFailed as e:
        if use_json:
            json_out = {'exception': e.message}
        else:
            print((e.message))
        clear_pass = False

    if not json_out:
        json_out = {'result': 'success'}

    if use_json:
        _print_json_output(json_out)

    return clear_pass

def _get_pool_buffer_counters(buffer_info,cells=False):
    pool_info = mlx_get_pool_buffer_limits()
    table_list = []
    cell_size = mlx_get_shared_buff_buffer_unit_size()
    for idx,usage_stat in list(buffer_info.items()) :
        pool = usage_stat['id']
        if usage_stat['type'] == 'User':
            pool_type = 'User %s' % usage_stat['buff-type']
        else:
            pool_type = usage_stat['type']
        mode = pool_info[pool]['mode']
        direction = pool_info[pool]['dir']
        size = pool_info[pool]['cells']
        size_bytes = pool_info[pool]['bytes']
        infinite_flag = pool_info[pool]['infinite']
        curr = usage_stat['current']
        max_usage = usage_stat['watermark']
        table_row = []
        table_row.append(pool)
        table_row.append(pool_type)
        table_row.append(direction)
        usage_stat['direction'] = direction
        table_row.append(mode)
        usage_stat['mode'] = mode
        if cells or re.search('descriptor', usage_stat['buff-type']):
            if infinite_flag:
                table_row.append('inf')
            elif re.search('descriptor', usage_stat['buff-type']):
                table_row.append(str(size) + " pkts")
            else:
                table_row.append(str(size) + " cells")
            usage_stat['pool_size'] = size
            table_row.append(curr)
            table_row.append(max_usage)
        else:
            if infinite_flag:
                table_row.append('inf')
            else:
                table_row.append(formatBytes(size_bytes))
            usage_stat['pool_size_bytes'] = size_bytes
            table_row.append(formatBytes(curr * cell_size))
            table_row.append(formatBytes(max_usage * cell_size))
        table_list.append(table_row)
    return table_list

def _show_pool_buffer_counters(cells, swid=0, use_json=False):
    buffer_info = mlx_get_pool_buffer_counters()
    if use_json:
        _print_json_output(buffer_info)
    else :
        cell_size = mlx_get_shared_buff_buffer_unit_size()
        print("\nPool buffer occupancy counters (1 cell == %d bytes)" % (cell_size))
        table_list = _get_pool_buffer_counters(buffer_info,cells)
        print((tabulate(table_list, headers=['PoolID', 'Pool Type', 'Direction', 'Mode', 'Pool Size', 'Current Usage', 'Max Usage'])))

def _show_free_buffer_counters(use_json=False):
    buffer_info = mlx_get_free_buffer_counters()
    if use_json:
        return buffer_info
    else :
        if 'cell_unit_size' in list(buffer_info.keys()) :
            print("\nUnallocated packet buffer cells (1 cell == %d bytes)" % (buffer_info['cell_unit_size']))
        else:
            print("\nUnallocated packet buffer cells")
        if 'ingress' in list(buffer_info.keys()) :
            print('Ingress cells: %d' % buffer_info['ingress'])
        if 'egress' in list(buffer_info.keys()) :
            print('Egress  cells: %d' % buffer_info['egress'])

def _show_roce_counters(sdk_to_linux, portid, use_json=False, params=None):
    if params == None:
        clear = False
        show_output = True
        cells = False
        roce_mode = None
    else:
        clear = params['clear']
        show_output = params['show_output']
        cells = params['cells']
        roce_mode = params['roce_mode']

    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']

    if portid in sdk_to_linux and is_bond_member(sdk_to_linux[portid]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, portid)
    else:
        # Hack here to consider bondid as port id when the port is not bond member
        bondid = portid

    try:
        # Pass bondid as arg to fetch some of the stats for the LAG itself instead of LAG member
        roce_info = mlx_get_roce_counters(portid, bondid, roce_mode, clear)
        roce_info['port'] = sdk_to_linux.get(portid)
    except sdkApiFailed as e:
        if use_json:
            return {'port': sdk_to_linux.get(portid), 'exception': e.message}
        else:
            print(('{}: {}'.format(sdk_to_linux.get(portid), e.message)))
            return False

    if show_output:
        if use_json:
            return roce_info
        else:
            print()
            print("Port: %s (%d)" % (sdk_to_linux.get(roce_info['port_id']), roce_info['port_id']))
            print()
            print('Rx:')
            print('RoCE PG packets: %d' % roce_info['rx_roce_pg_packets'])
            if cells:
                print('RoCE PG cells: %d' % int(roce_info['rx_roce_pg_bytes']/roce_info['cell_unit_size']))
            else:
                print('RoCE PG bytes: %s' % formatBytes(roce_info['rx_roce_pg_bytes']))
            print('RoCE no buffer discard: %d' % roce_info['rx_roce_no_buffer_discard'])
            print('General+CNP PG packets: %d' % roce_info['rx_cnp_pg_packets'])
            if cells:
                print('General+CNP PG cells: %d' % int(roce_info['rx_cnp_pg_bytes']/roce_info['cell_unit_size']))
            else:
                print('General+CNP PG bytes: %s' % formatBytes(roce_info['rx_cnp_pg_bytes']))
            print('General+CNP no buffer discard: %d' % roce_info['rx_cnp_no_buffer_discard'])
            print('RoCE PFC pause packets: %d' % roce_info['rx_roce_pfc_pause_packets'])
            print('RoCE PFC pause duration: %d' % roce_info['rx_roce_pfc_pause_duration'])
            if cells:
                print('RoCE buffer usage : %d cells' % roce_info['rx_roce_buffer_usage_cells'])
                print('RoCE buffer max usage : %d cells' % roce_info['rx_roce_buffer_max_usage_cells'])
                print('General+CNP buffer usage : %d cells' % roce_info['rx_cnp_buffer_usage_cells'])
                print('General+CNP buffer max usage : %d cells' % roce_info['rx_cnp_buffer_max_usage_cells'])
                print('RoCE PG usage : %d cells' % roce_info['rx_roce_pg_usage_cells'])
                print('RoCE PG max usage : %d cells' % roce_info['rx_roce_pg_max_usage_cells'])
                print('General+CNP PG usage : %d cells' % roce_info['rx_cnp_pg_usage_cells'])
                print('General+CNP PG max usage : %d cells' % roce_info['rx_cnp_pg_max_usage_cells'])
            else:
                print('RoCE buffer usage : %s' % formatBytes(roce_info['rx_roce_buffer_usage_bytes']))
                print('RoCE buffer max usage : %s' % formatBytes(roce_info['rx_roce_buffer_max_usage_bytes']))
                print('General+CNP buffer usage : %s' % formatBytes(roce_info['rx_cnp_buffer_usage_bytes']))
                print('General+CNP buffer max usage : %s' % formatBytes(roce_info['rx_cnp_buffer_max_usage_bytes']))
                print('RoCE PG usage : %s' % formatBytes(roce_info['rx_roce_pg_usage_bytes']))
                print('RoCE PG max usage : %s' % formatBytes(roce_info['rx_roce_pg_max_usage_bytes']))
                print('General+CNP PG usage : %s' % formatBytes(roce_info['rx_cnp_pg_usage_bytes']))
                print('General+CNP PG max usage : %s' % formatBytes(roce_info['rx_cnp_pg_max_usage_bytes']))
            print()
            print('Tx:')
            if (roce_info['tx_ecn_marked_packets'] == None):
                tx_ecn_marked_packets = na_string
            else:
                tx_ecn_marked_packets = str(roce_info['tx_ecn_marked_packets'])
            print('Port ECN marked packets: %s' % tx_ecn_marked_packets)
            print('RoCE TC packets: %d' % roce_info['tx_roce_tc_packets'])
            if cells:
                print('RoCE TC cells: %d' % int(roce_info['tx_roce_tc_bytes']/roce_info['cell_unit_size']))
            else:
                print('RoCE TC bytes: %s' % formatBytes(roce_info['tx_roce_tc_bytes']))
            print('RoCE unicast no buffer discard: %d' % roce_info['tx_roce_unicast_no_buffer_discard'])
            print('CNP TC packets: %d' % roce_info['tx_cnp_tc_packets'])
            if cells:
                print('CNP TC cells: %d' % int(roce_info['tx_cnp_tc_bytes']/roce_info['cell_unit_size']))
            else:
                print('CNP TC bytes: %s' % formatBytes(roce_info['tx_cnp_tc_bytes']))
            print('CNP unicast no buffer discard: %d' % roce_info['tx_cnp_unicast_no_buffer_discard'])
            print('RoCE PFC pause packets: %d' % roce_info['tx_roce_pfc_pause_packets'])
            print('RoCE PFC pause duration: %d' % roce_info['tx_roce_pfc_pause_duration'])
            if cells:
                print('RoCE buffer usage :%d cells' % roce_info['tx_roce_buffer_usage_cells'])
                print('RoCE buffer max usage : %d cells' % roce_info['tx_roce_buffer_max_usage_cells'])
                print('General+CNP buffer usage : %d cells' % roce_info['tx_cnp_buffer_usage_cells'])
                print('General+CNP buffer max usage : %d cells' % roce_info['tx_cnp_buffer_max_usage_cells'])
                print('RoCE TC usage : %d cells' % roce_info['tx_roce_tc_usage_cells'])
                print('RoCE TC max usage : %d cells' % roce_info['tx_roce_tc_max_usage_cells'])
                print('CNP TC usage : %d cells' % roce_info['tx_cnp_tc_usage_cells'])
                print('CNP TC max usage : %d cells' % roce_info['tx_cnp_tc_max_usage_cells'])
            else:
                print('RoCE buffer usage :%s' % formatBytes(roce_info['tx_roce_buffer_usage_bytes']))
                print('RoCE buffer max usage : %s' % formatBytes(roce_info['tx_roce_buffer_max_usage_bytes']))
                print('General+CNP buffer usage : %s' % formatBytes(roce_info['tx_cnp_buffer_usage_bytes']))
                print('General+CNP buffer max usage : %s' % formatBytes(roce_info['tx_cnp_buffer_max_usage_bytes']))
                print('RoCE TC usage : %s' % formatBytes(roce_info['tx_roce_tc_usage_bytes']))
                print('RoCE TC max usage : %s' % formatBytes(roce_info['tx_roce_tc_max_usage_bytes']))
                print('CNP TC usage : %s' % formatBytes(roce_info['tx_cnp_tc_usage_bytes']))
                print('CNP TC max usage : %s' % formatBytes(roce_info['tx_cnp_tc_max_usage_bytes']))
            print()
            print('1 cell == %d bytes' % roce_info['cell_unit_size'])
            print()

# Index the ingress priority map by SP and color
def _index_ingress_prio_map(pcp_dei_prio_map, dscp_prio_map):
    sp = {}
    # Create the index
    for current_sp in range(sp_num):
        sp[current_sp] = {}
        for current_color in range(color_num):
            sp[current_sp][current_color] = {}
            sp[current_sp][current_color]['pcp_dei'] = []
            sp[current_sp][current_color]['dscp'] = []
    # Add PCP/DEI values
    for prio_item in pcp_dei_prio_map:
        pcp_dei = {}
        pcp_dei['pcp'] = prio_item['pcp']
        pcp_dei['dei'] = prio_item['dei']
        sp[prio_item['sp']][prio_item['color']]['pcp_dei'].append(pcp_dei)
    # Add DSCP values
    for prio_item in dscp_prio_map:
        sp[prio_item['sp']][prio_item['color']]['dscp'].append(prio_item['dscp'])
    return sp

# Get the ingress priority map table
# This table is used for both textual and json display formats.
def _get_ingress_prio_map(pcp_dei_prio_map, dscp_prio_map, l2_trust, l3_trust):
    sp = _index_ingress_prio_map(pcp_dei_prio_map, dscp_prio_map)
    ingress_prio_map_table = []
    for current_sp in range(sp_num):
        for current_color in range(color_num):
            table_row = {}

            # PCP/DEI
            if (l2_trust == 'yes'):
                table_row['pcp_dei'] = sp[current_sp][current_color]['pcp_dei']
            else:
                table_row['pcp_dei'] = None

            # DSCP
            if (l3_trust == 'yes'):
                table_row['dscp'] = sp[current_sp][current_color]['dscp']
            else:
                table_row['dscp'] = None

            table_row['sp'] = current_sp
            table_row['color'] = current_color
            ingress_prio_map_table.append(table_row)
    return ingress_prio_map_table

# Print the ingress priority map in textual display format.
def _show_ingress_prio_map(ingress_prio_map_table):
    table_list = []
    for map_row in ingress_prio_map_table:
        table_row = []

        # PCP/DEI
        if map_row['pcp_dei'] == None:
            pcp_dei_list = na_string
        else:
            pcp_dei_list = ''
            for pcp_dei in map_row['pcp_dei']:
                pcp_dei_list += '%d(%d) ' % (pcp_dei['pcp'], pcp_dei['dei'])
        table_row.append(pcp_dei_list)

        # DSCP
        if map_row['dscp'] == None:
            dscp_list = na_string
        else:
            dscp_list = ''
            for dscp in map_row['dscp']:
                dscp_list += '%d ' % dscp
        table_row.append(dscp_list)

        table_row.append(map_row['sp'])
        table_row.append(map_row['color'])
        if dscp_list != na_string or pcp_dei_list != na_string:
            table_list.append(table_row)
    if len(table_list) != 0:
        print('PCP,DEI and DSCP to switch-priority, color mapping:')
        print()
        print((tabulate(table_list, headers=['PCP(DEI)', 'DSCP', 'switch-priority', 'color'])))

# Index the egress priority map by SP and color
def _index_egress_prio_map(tc_prio_map, pcpdei_rewrite_map, dscp_rewrite_map):
    sp_num = 8
    sp = {}
    # Create the index
    for current_sp in range(sp_num):
        sp[current_sp] = {}
        for current_color in range(color_num):
            sp[current_sp][current_color] = {}
    # Add TC values
    for prio_item in tc_prio_map:
        if prio_item['sp'] in sp:
            for current_color in range(color_num):
                sp[prio_item['sp']][current_color]['tc'] = prio_item['tc']
    # Add PCP/DEI rewrite values
    for prio_item in pcpdei_rewrite_map:
        if prio_item['sp'] in sp:
            sp[prio_item['sp']][prio_item['color']]['pcp'] = prio_item['pcp']
            sp[prio_item['sp']][prio_item['color']]['dei'] = prio_item['dei']
    # Add DSCP rewrite values
    for prio_item in dscp_rewrite_map:
        if prio_item['sp'] in sp:
            sp[prio_item['sp']][prio_item['color']]['dscp'] = prio_item['dscp']
    return sp

# Get the egress priority map table
# This table is used for both textual and json display formats.
def _get_egress_prio_map(tc_prio_map, pcpdei_rewrite_map, dscp_rewrite_map,
                          port_rewrite_pcpdei, port_rewrite_dscp, router_rewrite_pcpdei):
    sp = _index_egress_prio_map(tc_prio_map, pcpdei_rewrite_map, dscp_rewrite_map)
    egress_prio_map_table = []
    for current_sp in range(sp_num):
        for current_color in range(color_num):
            table_row = {}
            table_row['sp'] = current_sp
            table_row['color'] = current_color

            # PCP/DEI
            pcp_dei = {}
            pcp_dei['pcp'] = sp[current_sp][current_color]['pcp']
            pcp_dei['dei'] = sp[current_sp][current_color]['dei']
            if (port_rewrite_pcpdei == 'yes' or router_rewrite_pcpdei == 'enable'):
                table_row['pcp_dei'] = pcp_dei
            else:
                table_row['pcp_dei'] = None

            # DSCP
            if (port_rewrite_dscp == 'yes'):
                table_row['dscp'] = sp[current_sp][current_color]['dscp']
            else:
                table_row['dscp'] = None

            table_row['tc'] = sp[current_sp][current_color]['tc']
            egress_prio_map_table.append(table_row)
    return egress_prio_map_table

# Print the egress priority map in textual display format.
def _show_egress_prio_map(egress_prio_map_table):
    table_list = []
    for map_row in egress_prio_map_table:
        table_row = []
        table_row.append(map_row['sp'])
        table_row.append(map_row['color'])

        # PCP/DEI
        if map_row['pcp_dei'] == None:
            pcp_dei = na_string
        else:
            pcp_dei = '%d(%d)' % (map_row['pcp_dei']['pcp'],
                                  map_row['pcp_dei']['dei'])
        table_row.append(pcp_dei)

        # DSCP
        if map_row['dscp'] == None:
            dscp = na_string
        else:
            dscp = map_row['dscp']
        table_row.append(dscp)

        table_row.append(map_row['tc'])
        table_list.append(table_row)
    print((tabulate(table_list, headers=['switch-priority', 'color', 'PCP(DEI) rewrite', 'DSCP rewrite', 'TC'])))

# Show end-to-end (ingress to egress) QoS mapping
def _show_qos_mapping(ingress_port, egress_port, use_json=False):
    # Validate the ingress_port and egress_port parameters
    if not ingress_port:
        if use_json:
            _print_json_output({'exception': 'ingressPortRequired'})
        else:
            print("Ingress port is a required parameter for qos mapping")
        return
    if not egress_port:
        if use_json:
            _print_json_output({'exception': 'egressPortRequired'})
        else:
            print("Egress port is a required parameter for qos mapping")
        return

    # Find the ingress and egress port IDs.
    ingress_port_list = _get_port_list_from_name_or_id(ingress_port, False, use_json)
    if type(ingress_port_list) is not list or len(ingress_port_list) != 1:
        if use_json:
            _print_json_output({'port': ingress_port, 'exception': 'inputPortNotSupported'})
        else:
            print('ERROR: illegal ingress_port_list =', ingress_port)
        return
    ingress_port_id = ingress_port_list[0]
    egress_port_list = _get_port_list_from_name_or_id(egress_port, False, use_json)
    if type(egress_port_list) is not list or len(egress_port_list) != 1:
        if use_json:
            _print_json_output({'port': egress_port, 'exception': 'inputPortNotSupported'})
        else:
            print('ERROR: illegal egress_port_list =', egress_port)
        return
    egress_port_id = egress_port_list[0]

    # Get the raw unindexed mapping data.
    qos_mapping = mlx_get_qos_mapping(ingress_port_id, egress_port_id)

    # Index and build the display data.
    prio_map = {}
    prio_map['ingress'] = {}
    prio_map['ingress']['l2_trust'] = qos_mapping['ingress']['trust']['pcpdei']
    prio_map['ingress']['l3_trust'] = qos_mapping['ingress']['trust']['dscp']
    prio_map['ingress']['trust_port'] = qos_mapping['ingress']['trust']['trust_port']
    prio_map['ingress']['default_priority'] = qos_mapping['ingress']['default_priority']
    prio_map['ingress']['port_rewrite_pcpdei'] = qos_mapping['ingress']['port_rewrite']['pcpdei']
    prio_map['ingress']['port_rewrite_dscp'] = qos_mapping['ingress']['port_rewrite']['dscp']
    prio_map['ingress']['router_rewrite_pcpdei'] = qos_mapping['ingress']['router_rewrite']
    prio_map['ingress_table'] = _get_ingress_prio_map(qos_mapping['ingress']['pcp_dei_prio_map'],
                                                      qos_mapping['ingress']['dscp_prio_map'],
                                                      prio_map['ingress']['l2_trust'],
                                                      prio_map['ingress']['l3_trust'])
    prio_map['egress_table'] = _get_egress_prio_map(qos_mapping['egress']['tc_prio_map'],
                                                    qos_mapping['egress']['pcpdei_rewrite_map'],
                                                    qos_mapping['egress']['dscp_rewrite_map'],
                                                    prio_map['ingress']['port_rewrite_pcpdei'],
                                                    prio_map['ingress']['port_rewrite_dscp'],
                                                    prio_map['ingress']['router_rewrite_pcpdei'])

    if use_json:
        _print_json_output(prio_map)
    else:
        sdk_to_linux = get_sdk_to_linux()
        print()
        print("Ingress port: %s (%d)" % (sdk_to_linux.get(ingress_port_id), ingress_port_id))
        print()
        print('L2 trust (PCP/DEI): %s' % prio_map['ingress']['l2_trust'])
        print('L3 trust (DSCP): %s' % prio_map['ingress']['l3_trust'])
        print('Trust Port: %s' % prio_map['ingress']['trust_port'])
        print('Port Default Switch Priority: %s' % prio_map['ingress']['default_priority'])
        print('Rewrite PCP,DEI: %s' % prio_map['ingress']['port_rewrite_pcpdei'])
        print('Rewrite DSCP: %s' % prio_map['ingress']['port_rewrite_dscp'])
        print('Global Rewrite mode: %s' % prio_map['ingress']['router_rewrite_pcpdei'])
        print()
        _show_ingress_prio_map(prio_map['ingress_table'])
        print()

        print("Egress port: %s (%d)" % (sdk_to_linux.get(egress_port_id), egress_port_id))
        print()
        _show_egress_prio_map(prio_map['egress_table'])
        print()

def _show_roce_status(sdk_to_linux, portid, use_json=False, params=None):
    if params == None:
        roce_mode = None
    else:
        roce_mode = params['roce_mode']
    ret = {'output': {}, 'bond_name': {}}
    roce_status_dict = {}
    if sdk_to_linux and portid in sdk_to_linux and is_bond(sdk_to_linux[portid]):
        roce_status_dict = mlx_get_roce_status(portid, bond_id=portid, is_bond=True, roce_mode=roce_mode)
    elif sdk_to_linux and portid in sdk_to_linux and is_bond_member(sdk_to_linux[portid]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, portid)
        if bondid:
            roce_status_dict = mlx_get_roce_status(portid, bondid, is_bond=False, roce_mode=roce_mode)
    elif sdk_to_linux and sdk_to_linux.get(portid, 'Unknown') == 'Unknown':
        return {'port': 'Unknown', 'exception': 'invalidOrUnknownPort'}
    else:
        roce_status_dict = mlx_get_roce_status(portid, portid, is_bond=False, roce_mode=roce_mode)

    if not roce_status_dict:
        _add_port_to_dict(sdk_to_linux, roce_status_dict, 'port', portid)
        return roce_status_dict

    _add_port_to_dict(sdk_to_linux, roce_status_dict, 'port', portid)
    if use_json:
        return roce_status_dict
    else:
        print("Port: %s (%d)" % (sdk_to_linux.get(roce_status_dict['port_id']), roce_status_dict['port_id']))
        print()

        print('RoCE mode: %s' % roce_status_dict['roce_mode'])

        print('PFC %d TX enabled: %s, RX enabled: %s' % (roce_status_dict['pfc_priority'],
                                                         roce_status_dict['pfc_enabled']['tx_enabled'],
                                                         roce_status_dict['pfc_enabled']['rx_enabled']))

        tc_list_str = ""
        for tc in roce_status_dict['congestion-control']['ecn_tcs']:
            tc_list_str += " " + str(tc)
        if len(tc_list_str) == 0:
            tc_list_str = " NONE"
        print("Congestion Control:")
        print('\tECN enabled TCs:%s' % tc_list_str, end=' ')
        congestion_red = ""
        congestion_ecn = ""
        if roce_status_dict['congestion-control']['red_enabled']:
            congestion_red = "red"
        if roce_status_dict['congestion-control']['ecn_enabled']:
            congestion_ecn = "ecn"
        print('mode:%s,%s %s;' % (congestion_ecn,congestion_red,roce_status_dict['congestion-control']['mode']), end=' ')
        print('min:%s' % roce_status_dict['congestion-control']['min_threshold'], end=' ')
        print('max:%s' % roce_status_dict['congestion-control']['max_threshold'])

        print('L3 trust (DSCP): %s' % roce_status_dict['trust']['dscp'])
        print('L2 trust (PCP/DEI): %s' % roce_status_dict['trust']['pcpdei'])
        print('Trust Port: %s' % roce_status_dict['trust']['trust_port'])

        print('Switch-Prio Map:')
        print('\tRoCE DSCP %d' % roce_status_dict['roce_dscp']['dscp'], end=' ')
        print('PCP %d' % roce_status_dict['roce_pcp']['pcp'], end=' ')
        print('switch priority %d' % roce_status_dict['roce_dscp']['sp'])

        print('\tCNP DSCP %d' % roce_status_dict['cnp_dscp']['dscp'], end=' ')
        print('PCP %d' % roce_status_dict['cnp_pcp']['pcp'], end=' ')
        print('switch priority %d' % roce_status_dict['cnp_dscp']['sp'])

        print('RoCE Used TC-Map:')
        print('\tRoCE Switch priority %d' % roce_status_dict['roce_pg_map']['sp'], end=' ')
        print('priority group %d' % roce_status_dict['roce_pg_map']['pg'], end=' ')
        print('traffic class %d' % roce_status_dict['roce_tc_map']['tc'], end=' ')
        print('ETS %s' % roce_status_dict['roce_sched_map']['dwrr_mode'], end=' ')
        if roce_status_dict['roce_sched_map']['dwrr_mode'] == "dwrr":
            print(' {}%'.format(roce_status_dict['roce_sched_map']['dwrr_weight']))
        else:
            print()

        print('\tCNP Switch priority %d' % roce_status_dict['cnp_pg_map']['sp'], end=' ')
        print('priority group %d' % roce_status_dict['cnp_pg_map']['pg'], end=' ')
        print('traffic class %d' % roce_status_dict['cnp_tc_map']['tc'], end=' ')
        print('ETS %s' % roce_status_dict['cnp_sched_map']['dwrr_mode'], end=' ')
        if roce_status_dict['cnp_sched_map']['dwrr_mode'] == "dwrr":
            print(' {}%'.format(roce_status_dict['cnp_sched_map']['dwrr_weight']))
        else:
            print()

        print()

# Show only the RoCE and CNP related pools.
def _show_roce_pools(sdk_to_linux,portid,use_json=False,cells=False):
    roce_pools = {}
    pool_temp_cnt = {}
    elem = {}
    roce_pool_key = 0

    if sdk_to_linux.get(portid) and is_bond_member(sdk_to_linux[portid]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, portid)
    else:
        # Hack here to consider bondid as port id when the port is not bond member
        bondid = portid

    roce_cnp_pool_list = mlx_get_roce_cnp_pool_list(portid, bondid)

    pool_counters = mlx_get_pool_buffer_counters()
    for pool_key in pool_counters:
        pool_id = pool_counters[pool_key]['id']
        if (pool_id == roce_cnp_pool_list.get('ingress_cnp_port_pool', -1) or
            pool_id == roce_cnp_pool_list.get('ingress_roce_port_pool', -1) or
            pool_id == roce_cnp_pool_list.get('egress_cnp_port_pool', -1) or
            pool_id == roce_cnp_pool_list.get('egress_roce_port_pool', -1)):
            elem['current'] = pool_counters[pool_key]['current']
            elem['watermark'] = pool_counters[pool_key]['watermark']
            pool_temp_cnt[pool_id] = elem.copy()

    pool_info = mlx_get_pool_buffer_limits()
    for pool_key in pool_info:
        # If the pool is RoCE or CNP, set its name.
        pool_id = pool_key
        pool_name = None
        if pool_id == roce_cnp_pool_list.get('ingress_cnp_port_pool', -1):
            pool_name = 'lossy-default-ingress'
        if pool_id == roce_cnp_pool_list.get('ingress_roce_port_pool', -1):
            pool_name = 'roce-reserved-ingress'
        if pool_id == roce_cnp_pool_list.get('egress_cnp_port_pool', -1):
            pool_name = 'lossy-default-egress'
        if pool_id == roce_cnp_pool_list.get('egress_roce_port_pool', -1):
            pool_name = 'roce-reserved-egress'
        if pool_name != None:
            # Add the pool to roce_pools with its name.
            roce_pool = pool_info[pool_key].copy()
            roce_pool['name'] = pool_name
            roce_pool['current-usage'] = pool_temp_cnt[pool_id]['current']
            roce_pool['max-usage'] = pool_temp_cnt[pool_id]['watermark']
            roce_pool['current-usage-bytes'] = pool_temp_cnt[pool_id]['current'] * roce_pool['cell_unit_size']
            roce_pool['max-usage-bytes'] = pool_temp_cnt[pool_id]['watermark'] * roce_pool['cell_unit_size']
            roce_pools[pool_id] = roce_pool

    if use_json:
        return {sdk_to_linux.get(portid, 'NA'): roce_pools}
    else:
        cell_size = mlx_get_shared_buff_buffer_unit_size()
        # Tabulate the pools info.
        table_list = _get_pool_buffer_counters_roce(roce_pools,cells)
        print("\nRoCE Pool buffer occupancy counters for port %s (1 cell == %d bytes)" % (sdk_to_linux.get(portid, 'NA'),cell_size))
        print((tabulate(table_list, headers=['Pool Name', 'PoolID', 'Pool Type', 'Direction', 'Mode', 'Pool Size', 'Current Usage', 'Max Usage'])))

def formatBytes(B):
    B = float(B)
    KB = float(1024)
    MB = float(KB ** 2)
    GB = float(KB ** 3)

    if B < KB:
        return '{0} {1}'.format(B,'Bytes' if 0 == B > 1 else 'Byte')
    elif KB <= B < MB:
        return '{0:.2f} kB'.format(B/KB)
    elif MB <= B < GB:
        return '{0:.2f} MB'.format(B/MB)
    elif GB <= B:
        return '{0:.2f} GB'.format(B/GB)

def _get_pool_buffer_counters_roce(buffer_info,cells=False):
    table_list = []
    for idx,usage_stat in list(buffer_info.items()) :
        pool = idx
        table_row = []
        table_row.append(usage_stat['name'])
        table_row.append(pool)
        table_row.append(usage_stat['type'])
        table_row.append(usage_stat['dir'])
        table_row.append(usage_stat['mode'])
        if cells:
            if usage_stat.get('infinite'):
                table_row.append('inf')
            else:
                table_row.append(str(usage_stat['cells']) + " cells")
            table_row.append(usage_stat['current-usage'])
            table_row.append(usage_stat['max-usage'])
        else:
            if usage_stat.get('infinite'):
                table_row.append('inf')
            else:
                table_row.append(formatBytes(usage_stat['bytes']))
            table_row.append(formatBytes(usage_stat['current-usage'] * usage_stat['cell_unit_size']))
            table_row.append(formatBytes(usage_stat['max-usage'] * usage_stat['cell_unit_size']))
        table_list.append(table_row)
    return table_list

# Show RoCE system wide status, not related to a specific port.
def _show_roce_status_system(port, cells, use_json=False):
    roce_mode = mlx_get_roce_mode_wrapper()
    if (roce_mode == roce_disabled):
        if use_json:
            _print_json_output({'exception': 'noRoce'})
        else:
            print("RoCE is not configured")
    else:
        roce_status_system = {}
        roce_status_system['pools'] = \
                __expand_port_name_or_id(_show_roce_pools, port, True, use_json=use_json, params={'ret_out':use_json, 'cells':cells})
        if use_json:
            _print_json_output(roce_status_system)

def _show_parser_length(use_json=False):
    parser_length = mlx_get_parser_length()
    if use_json:
        return {'parser_length': parser_length}
    else :
        print('Parser length = %d' % parser_length)

def _show_port_default_priority(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    default_prio = {}
    default_prio['default_priority'] = mlx_get_port_default_priority(portid)

    _add_port_to_dict(sdk_to_linux, default_prio, 'port', portid)

    if use_json:
        return default_prio
    else :
        print('\nPort %s(0x%x) Default Switch Priority: %s' % (default_prio['port'],portid,default_prio['default_priority']))

def _show_port_priority_trust(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    trust_info = mlx_get_port_priority_trust(portid)

    _add_port_to_dict(sdk_to_linux, trust_info, 'port', portid)

    if use_json:
        return trust_info
    else :
        print('\nPort %s(0x%x) priority trust configuration' % (trust_info['port'],portid))
        print('L3 (DSCP):   %s' % trust_info['dscp'])
        print('L2 (PCPDEI): %s' % trust_info['pcpdei'])
        print('Trust Port: %s' % trust_info['trust_port'])

def _show_priority_group_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    port_name = sdk_to_linux.get(portid, None)
    if not port_name or port_name == 'Unknown':
        if use_json:
            return {'exception': 'invalidOrUnknownPort'}
        else:
            print('ERROR: Invalid or Unknown port 0x%x' % portid)
        return
    pg_map_list = mlx_get_pg_prio_map(portid)
    if use_json:
        return {'port': port_name, 'pg_map': pg_map_list}
    else :
        print('\nPort 0x%x switch priority to priority group map' % portid)
        for pg_map in pg_map_list :
            print('Switch priority %d' % pg_map['sp'], end=' ')
            print('priority group %d' % pg_map['pg'])

def _show_port_priority_rewrite(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    rewrite_info = mlx_get_port_priority_rewrite(portid)
    _add_port_to_dict(sdk_to_linux, rewrite_info, 'port', portid)

    if use_json:
        return rewrite_info
    else :
        print('\nPort %s(0x%x) priority rewrite configuration' % (rewrite_info['port'], portid))
        print('DSCP:   %s' % rewrite_info['dscp'])
        print('PCPDEI: %s' % rewrite_info['pcpdei'])
        print('EXP:    %s' % rewrite_info['exp'])

def _show_pcpdei_prio_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    prio_list = mlx_get_pcpdei_prio_map(portid)

    prio_map = {}
    _add_port_to_dict(sdk_to_linux, prio_map, 'port', portid)

    prio_map['list'] = prio_list

    if use_json:
        return prio_map
    else :
        print('\nPort %s(0x%x) PCPDEI packet priority to switch priority map' % (prio_map['port'],portid))
        for prio_item in prio_map['list']:
            print('PCP %d' % prio_item['pcp'], end=' ')
            print('DEI %d' % prio_item['dei'], end=' ')
            print('switch priority %d' % prio_item['sp'], end=' ')
            print('color %d' % prio_item['color'])

def _show_pcpdei_remark_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    pcpdei_list = mlx_get_pcpdei_rewrite_map(portid)
    pcpdei_map = {}
    _add_port_to_dict(sdk_to_linux, pcpdei_map, 'port', portid)

    pcpdei_map['list'] = pcpdei_list
    if use_json:
        return pcpdei_map
    else :
        print('\nPort %s(0x%x) switch priority to PCPDEI packet priority remark' % (pcpdei_map['port'],portid))
        for pcpdei_item in pcpdei_map['list']:
            print('switch priority %d' % pcpdei_item['sp'], end=' ')
            print('color %d' % pcpdei_item['color'], end=' ')
            print('PCP %d' % pcpdei_item['pcp'], end=' ')
            print('DEI %d' % pcpdei_item['dei'])

def _show_dscp_prio_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    prio_list = mlx_get_dscp_prio_map(portid)

    prio_map = {}
    _add_port_to_dict(sdk_to_linux, prio_map, 'port', portid)

    prio_map['list'] = prio_list

    if use_json:
        return prio_map
    else :
        print('\nPort %s(0x%x) DSCP packet priority to switch priority map' % (prio_map['port'],portid))
        for prio_item in prio_map['list']:
            print('DSCP %d' % prio_item['dscp'], end=' ')
            print('switch priority %d' % prio_item['sp'], end=' ')
            print('color %d' % prio_item['color'])

def _show_dscp_remark_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    dscp_list = mlx_get_dscp_rewrite_map(portid)
    dscp_map = {}
    _add_port_to_dict(sdk_to_linux, dscp_map, 'port', portid)

    dscp_map['list'] = dscp_list
    if use_json:
        return dscp_map
    else :
        print('\nPort %s(0x%x) switch priority to DSCP packet priority remark' % (dscp_map['port'],portid))
        for dscp_item in dscp_list:
            print('switch priority %d' % dscp_item['sp'], end=' ')
            print('color %d' % dscp_item['color'], end=' ')
            print('DSCP %d' % dscp_item['dscp'])

def _clear_qos_port_counters(port, ingress_port, egress_port, use_json=False):
    json_out = []
    clear_pass = True
    if port:
        if ingress_port or egress_port:
            if use_json:
                _print_json_output({'exception': 'ingressPortEgressPortNotRequired'})
            else:
                print('ERROR: With --port option, --ingress_port OR --egress_port are not required')
            return False
        # Find the port ID
        port_list = _get_port_list_from_name_or_id(port, True, use_json)
        if len(port_list) == 0:
            if use_json:
                _print_json_output({'port': port, 'exception': 'inputPortNotSupported'})
            else:
                print("%s is not a supported port_name_or_id" % port)
            return False
        ingress_port_list = port_list
        egress_port_list = port_list
    elif ingress_port and egress_port:
        # Find the ingress and egress port ID.
        ingress_port_list = _get_port_list_from_name_or_id(ingress_port, False, use_json)
        if type(ingress_port_list) is not list or len(ingress_port_list) != 1:
            if use_json:
                _print_json_output({'port': ingress_port, 'exception': 'inputPortNotSupported'})
            else:
                print('ERROR: illegal ingress_port_list =', ingress_port)
            return False
        egress_port_list = _get_port_list_from_name_or_id(egress_port, False, use_json)
        if type(egress_port_list) is not list or len(egress_port_list) != 1:
            if use_json:
                _print_json_output({'port': egress_port, 'exception': 'inputPortNotSupported'})
            else:
                print('ERROR: illegal egress_port_list =', egress_port)
            return False
    else:
        if use_json:
            _print_json_output({'exception': 'portInputRequired'})
        else:
            print('ERROR: Either give --port or give both --ingress_port and --egress_port as argument')
        return False

    sdk_to_linux = get_sdk_to_linux()
    for ingress_port_id in ingress_port_list:
        if sdk_to_linux and ingress_port_id in sdk_to_linux and is_bond(sdk_to_linux[ingress_port_id]):
            continue
        port_name = sdk_to_linux.get(ingress_port_id, None)
        if not port_name or port_name == 'Unknown':
            if use_json:
                json_out.append({'exception': 'Unknown port {}'.format(ingress_port_id)})
            else:
                print(('Unknown port {}'.format(ingress_port_id)))
            clear_pass = False
            continue
        if ingress_port_id in sdk_to_linux and is_bond_member(sdk_to_linux[ingress_port_id]):
            ingress_bond_id = _get_bond_id_from_mem_intf(sdk_to_linux, ingress_port_id)
        else:
            # Hack here to consider bondid as ingress port id when the port is not bond member
            ingress_bond_id = ingress_port_id
        try:
            mlx_clear_port_prio_counters(ingress_port_id)
            mlx_clear_port_buff_counters(ingress_port_id)
            mlx_clear_port_qos_counters(ingress_port_id)
            # ECN counters can be cleared on the LAG id only
            mlx_clear_port_ecn_counters(ingress_bond_id)
        except sdkApiFailed as e:
            if use_json:
                json_out.append({'port': port_name, 'exception': e.message})
            else:
                print(('{}: {}'.format(port_name, e.message)))
            clear_pass = False

    for egress_port_id in egress_port_list:
        if sdk_to_linux and egress_port_id in sdk_to_linux and is_bond(sdk_to_linux[egress_port_id]):
            continue
        port_name = sdk_to_linux.get(egress_port_id, None)
        if not port_name or port_name == 'Unknown':
            if use_json:
                json_out.append({'exception': 'Unknown port {}'.format(egress_port_id)})
            else:
                print(('Unknown port {}'.format(egress_port_id)))
            clear_pass = False
            continue
        if egress_port_id in sdk_to_linux and is_bond_member(sdk_to_linux[egress_port_id]):
            egress_bond_id = _get_bond_id_from_mem_intf(sdk_to_linux, egress_port_id)
        else:
            # Hack here to consider bondid as egress port id when the port is not bond member
            egress_bond_id = ingress_port_id
        try:
            mlx_clear_port_tc_counters(egress_port_id)
            if ingress_port_list != egress_port_list:
                mlx_clear_port_prio_counters(egress_port_id)
                mlx_clear_port_buff_counters(egress_port_id)
                mlx_clear_port_qos_counters(egress_port_id)
                # ECN counters can be cleared on the LAG id only
                mlx_clear_port_ecn_counters(egress_bond_id)
        except sdkApiFailed as e:
            if use_json:
                json_out.append({'port': port_name, 'exception': e.message})
            else:
                print(('{}: {}'.format(port_name, e.message)))
            clear_pass = False

    if not json_out:
        json_out = {'result': 'success'}

    if use_json:
        _print_json_output(json_out)

    return clear_pass

# Show RED ECN config
def _show_qos_port_redecn_config(sdk_to_linux, port_id, use_json= False):
    redecn_info = {}
    redecn_info = mlx_get_port_ecn_config(port_id, redecn_info)
    redecn_table_list = []
    redecn_flow_table = {SX_COS_REDECN_FLOW_TYPE_TCP_GREEN: 'TCP_GREEN', SX_COS_REDECN_FLOW_TYPE_TCP_YELLOW: 'TCP_YELLOW', SX_COS_REDECN_FLOW_TYPE_TCP_RED: 'TCP_RED', SX_COS_REDECN_FLOW_TYPE_NON_TCP_GREEN: 'NON_TCP_GREEN', SX_COS_REDECN_FLOW_TYPE_NON_TCP_YELLOW: 'NON_TCP_YELLOW', SX_COS_REDECN_FLOW_TYPE_NON_TCP_RED:'NON_TCP_RED'}
    redecn_mode_table = {SX_COS_REDECN_MODE_ABSOLUTE: 'REDECN_MODE_ABSOLUTE', SX_COS_REDECN_MODE_RELATIVE: 'REDECN_MODE_RELATIVE'}
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, port_id, use_json, ret):
        return ret['output']
    for tc in redecn_info['config']:
        for flow in redecn_info['config'][tc]['flow']:
            table_row = []
            table_row.append(tc)
            if redecn_info['config'][tc]['ecn_enabled']:
                table_row.append('TRUE')
            else:
                table_row.append('FALSE')
            if redecn_info['config'][tc]['red_enabled']:
                table_row.append('TRUE')
            else:
                table_row.append('FALSE')
            redecn_flow_dict = redecn_info['config'][tc]['flow'][flow]
            table_row.append(redecn_flow_table[flow])
            if redecn_flow_dict['mode'] in redecn_mode_table:
                table_row.append(redecn_mode_table[redecn_flow_dict['mode']])
            else:
                table_row.append(redecn_flow_dict['mode'])
            table_row.append(redecn_flow_dict['high_drop_percent'])
            if redecn_flow_dict['mode'] == SX_COS_REDECN_MODE_ABSOLUTE:
                table_row.append('{} cells'.format(redecn_flow_dict['min']))
                table_row.append('{} cells'.format(redecn_flow_dict['max']))
            elif redecn_flow_dict['mode'] == SX_COS_REDECN_MODE_RELATIVE:
                table_row.append('{} %'.format(redecn_flow_dict['min']))
                table_row.append('{} %'.format(redecn_flow_dict['max']))
            else:
                # Do not expect to get here
                table_row.append('NA')
                table_row.append('NA')
            redecn_table_list.append(table_row)

    if use_json:
        _add_port_to_dict(sdk_to_linux, redecn_info, 'port', port_id)
        return redecn_info
    else:
        if len(redecn_table_list) == 0:
            print('No RED/ECN config on the port %s (0x%x)' % (sdk_to_linux[port_id], port_id))
            return

        print('-------------------------------------------------')
        print('REDECN config for port %s (0x%x) (1 cell == %d bytes)' % (sdk_to_linux[port_id], port_id, redecn_info['cell_unit_size']))
        print('-------------------------------------------------')
        print((tabulate(redecn_table_list, headers=['Traffic-Class', 'ECN', 'RED', 'Flow', 'Mode', 'High Drop Percent', 'Min', 'Max'])))
        print('----------------------------------------')

def _show_qos_port_counters(sdk_to_linux, port_id, use_json=False, params=None):
    if params:
        ingress_port = params['ingress_port']
        egress_port = params['egress_port']
    if port_id:
        if params and (ingress_port or egress_port):
            if use_json:
                return {'exception': 'ingressPortEgressPortNotRequired'}
            else:
                print('ERROR: With --port option, --ingress_port OR --egress_port are not required')
            return
        ingress_port = sdk_to_linux[port_id]
        egress_port = sdk_to_linux[port_id]
    elif params and ingress_port and egress_port:
        pass
    else:
        if use_json:
            return {'exception': 'portInputRequired'}
        else:
            print('ERROR: Either give --port or give both --ingress_port and --egress_port as argument')
        return

    # Find the ingress and egress port ID.
    ingress_port_list = _get_port_list_from_name_or_id(ingress_port, False, use_json)
    if type(ingress_port_list) is not list or len(ingress_port_list) != 1:
        if use_json:
            _print_json_output({'port': ingress_port, 'exception': 'inputPortNotSupported'})
        else:
            print('ERROR: illegal ingress_port_list =', ingress_port)
        return
    ingress_port_id = ingress_port_list[0]
    egress_port_list = _get_port_list_from_name_or_id(egress_port, False, use_json)
    if type(egress_port_list) is not list or len(egress_port_list) != 1:
        if use_json:
            _print_json_output({'port': egress_port, 'exception': 'inputPortNotSupported'})
        else:
            print('ERROR: illegal egress_port_list =', egress_port)
        return
    egress_port_id = egress_port_list[0]

    sdk_to_linux = get_sdk_to_linux()
    port_name = sdk_to_linux.get(ingress_port_id, None)
    if not port_name or port_name == 'Unknown':
        if use_json:
            return {'exception': 'invalidOrUnknownPort'}
        else:
            print('ERROR: Invalid or Unknown ingress port')
        return
    port_name = sdk_to_linux.get(egress_port_id, None)
    if not port_name or port_name == 'Unknown':
        if use_json:
            return {'exception': 'invalidOrUnknownPort'}
        else:
            print('ERROR: Invalid or Unknown egress port')
        return

    ret = {'output': {}}
    if _port_is_bond(sdk_to_linux, ingress_port_id, use_json, ret):
        return ret['output']

    if _port_is_bond(sdk_to_linux, egress_port_id, use_json, ret):
        return ret['output']

    if ingress_port_id in sdk_to_linux and is_bond_member(sdk_to_linux[ingress_port_id]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, ingress_port_id)
    else:
        # Hack here to consider bondid as ingress port id when the port is not bond member
        bondid = ingress_port_id
    ing_port_counters = mlx_get_port_counters(ingress_port_id, bondid)
    if egress_port_id in sdk_to_linux and is_bond_member(sdk_to_linux[egress_port_id]):
        bondid = _get_bond_id_from_mem_intf(sdk_to_linux, egress_port_id)
    else:
        # Hack here to consider bondid as egress port id when the port is not bond member
        bondid = egress_port_id
    egr_port_counters = mlx_get_port_counters(egress_port_id, bondid)
    tc_table_list = []
    pg_table_list = []
    prio_table_list = []
    port_table_list = []

    qos_counters = {}
    for counter_set_idx in range(len(ing_port_counters['per_port']['values'])) :
        table_row = []
        qos_counters['port'] = {}
        qos_counters['port']['ingress_port'] = ingress_port
        table_row.append(ing_port_counters['per_port']['values'][counter_set_idx]['rx_frames'])
        qos_counters['port']['rx_frames'] = ing_port_counters['per_port']['values'][counter_set_idx]['rx_frames']
        table_row.append(ing_port_counters['per_port']['values'][counter_set_idx]['rx_uc'])
        qos_counters['port']['rx_uc'] = ing_port_counters['per_port']['values'][counter_set_idx]['rx_uc']
        table_row.append(ing_port_counters['per_port']['values'][counter_set_idx]['rx_mc'])
        qos_counters['port']['rx_mc'] = ing_port_counters['per_port']['values'][counter_set_idx]['rx_mc']
        table_row.append(ing_port_counters['per_port']['values'][counter_set_idx]['rx_bc'])
        qos_counters['port']['rx_bc'] = ing_port_counters['per_port']['values'][counter_set_idx]['rx_bc']

        qos_counters['port']['egress_port'] = egress_port
        table_row.append(egr_port_counters['per_port']['values'][counter_set_idx]['tx_frames'])
        qos_counters['port']['tx_frames'] = egr_port_counters['per_port']['values'][counter_set_idx]['tx_frames']
        table_row.append(egr_port_counters['per_port']['values'][counter_set_idx]['tx_uc'])
        qos_counters['port']['tx_uc'] = egr_port_counters['per_port']['values'][counter_set_idx]['tx_uc']
        table_row.append(egr_port_counters['per_port']['values'][counter_set_idx]['tx_mc'])
        qos_counters['port']['tx_mc'] = egr_port_counters['per_port']['values'][counter_set_idx]['tx_mc']
        table_row.append(egr_port_counters['per_port']['values'][counter_set_idx]['tx_bc'])
        qos_counters['port']['tx_bc'] = egr_port_counters['per_port']['values'][counter_set_idx]['tx_bc']
        table_row.append(ing_port_counters['per_port']['values'][counter_set_idx]['rx_buffer'])
        qos_counters['port']['rx_buffer_discards'] = ing_port_counters['per_port']['values'][counter_set_idx]['rx_buffer']
        table_row.append(egr_port_counters['perf']['values'][counter_set_idx]['mc_no_buffer'])
        qos_counters['port']['tx_mc_buffer_discards'] = egr_port_counters['perf']['values'][counter_set_idx]['mc_no_buffer']
        table_row.append(egr_port_counters['ecn']['values']['ecn_marked_packets'])
        qos_counters['port']['ecn_marked_packets'] = egr_port_counters['ecn']['values']['ecn_marked_packets']

        table_row.append(egr_port_counters['ieee']['values'][counter_set_idx]['rx_pause_mac_ctrl_frames'])
        qos_counters['port']['rx_pause_frames'] = egr_port_counters['ieee']['values'][counter_set_idx]['rx_pause_mac_ctrl_frames']
        table_row.append(ing_port_counters['ieee']['values'][counter_set_idx]['tx_pause_mac_ctrl_frames'])
        qos_counters['port']['tx_pause_frames'] = ing_port_counters['ieee']['values'][counter_set_idx]['tx_pause_mac_ctrl_frames']
        port_table_list.append(table_row)
    for counter_set_idx in range(len(ing_port_counters['per_pg']['values'])) :
        table_row = []
        pg_idx = 'pg_{}'.format(ing_port_counters['per_pg']['values'][counter_set_idx]['priority group'])
        qos_counters[pg_idx] = {}
        table_row.append(ing_port_counters['per_pg']['values'][counter_set_idx]['priority group'])
        table_row.append(ing_port_counters['per_pg']['values'][counter_set_idx]['rx_frames'])
        qos_counters[pg_idx]['rx_frames'] = ing_port_counters['per_pg']['values'][counter_set_idx]['rx_frames']
        table_row.append(ing_port_counters['per_pg']['values'][counter_set_idx]['rx_buffer_discard'])
        qos_counters[pg_idx]['rx_buffer_discard'] = ing_port_counters['per_pg']['values'][counter_set_idx]['rx_buffer_discard']
        table_row.append(ing_port_counters['per_pg']['values'][counter_set_idx]['rx_shared_buffer_discard'])
        qos_counters[pg_idx]['rx_shared_buffer_discard'] = ing_port_counters['per_pg']['values'][counter_set_idx]['rx_shared_buffer_discard']
        pg_table_list.append(table_row)
    for counter_set_idx in range(len(egr_port_counters['per_tc']['values'])) :
        table_row = []
        tc_idx = 'tc_{}'.format(egr_port_counters['per_tc']['values'][counter_set_idx]['traffic class'])
        qos_counters[tc_idx] = {}
        table_row.append(egr_port_counters['per_tc']['values'][counter_set_idx]['traffic class'])
        table_row.append(egr_port_counters['per_tc']['values'][counter_set_idx]['tx_frames'])
        qos_counters[tc_idx]['tx_frames'] = egr_port_counters['per_tc']['values'][counter_set_idx]['tx_frames']
        table_row.append(egr_port_counters['per_tc']['values'][counter_set_idx]['tx_bytes'])
        qos_counters[tc_idx]['tx_bytes'] = egr_port_counters['per_tc']['values'][counter_set_idx]['tx_bytes']
        tc = egr_port_counters['per_tc']['values'][counter_set_idx]['traffic class']
        table_row.append(egr_port_counters['ecn']['values']['red_dropped_packets'][tc])
        qos_counters[tc_idx]['red_dropped_packets'] = egr_port_counters['ecn']['values']['red_dropped_packets'][tc]
        table_row.append(egr_port_counters['per_tc']['values'][counter_set_idx]['tx_no_buffer_discard_uc'])
        qos_counters[tc_idx]['tx_uc_buffer_discards'] = egr_port_counters['per_tc']['values'][counter_set_idx]['tx_no_buffer_discard_uc']
        table_row.append(egr_port_counters['per_tc']['values'][counter_set_idx]['tx_wred_discard'])
        qos_counters[tc_idx]['tx_wred_discard'] = egr_port_counters['per_tc']['values'][counter_set_idx]['tx_wred_discard']
        tc_table_list.append(table_row)
    for counter_set_idx in range(len(ing_port_counters['per_prio']['values'])) :
        table_row = []
        sp_idx = 'sp_{}'.format(ing_port_counters['per_prio']['values'][counter_set_idx]['switch priority'])
        qos_counters[sp_idx] = {}
        table_row.append(ing_port_counters['per_prio']['values'][counter_set_idx]['switch priority'])
        table_row.append(ing_port_counters['per_prio']['values'][counter_set_idx]['rx_pause'])
        qos_counters[sp_idx]['rx_pause'] = ing_port_counters['per_prio']['values'][counter_set_idx]['rx_pause']
        table_row.append(ing_port_counters['per_prio']['values'][counter_set_idx]['rx_pause_duration'])
        qos_counters[sp_idx]['rx_pause_duration'] = ing_port_counters['per_prio']['values'][counter_set_idx]['rx_pause_duration']
        table_row.append(ing_port_counters['per_prio']['values'][counter_set_idx]['tx_pause'])
        qos_counters[sp_idx]['tx_pause'] = ing_port_counters['per_prio']['values'][counter_set_idx]['tx_pause']
        table_row.append(ing_port_counters['per_prio']['values'][counter_set_idx]['tx_pause_duration'])
        qos_counters[sp_idx]['tx_pause_duration'] = ing_port_counters['per_prio']['values'][counter_set_idx]['tx_pause_duration']
        prio_table_list.append(table_row)
    if use_json:
        return qos_counters
    else:
        print('-------------------------------------------------')
        print('Ingress Buffer(PG) statistics for port %s (0x%x)' % (ingress_port, ingress_port_id))
        print('-------------------------------------------------')
        print((tabulate(pg_table_list, headers=['Priority-Group', 'Rx Frames', 'Rx Buffer Discards', 'Rx Shared Buffer Discards'])))
        print('-------------------------------------------------')
        print('Egress Queue statistics for port %s (0x%x)' % (egress_port, egress_port_id))
        print('-------------------------------------------------')
        print((tabulate(tc_table_list, headers=['Traffic-Class', 'Tx Frames', 'Tx Bytes', 'RED Dropped Packets', 'Tx UC Buffer Discards', 'WRED Discards'])))
        print('----------------------------------------')
        print('PFC statistics for port %s (0x%x)' % (ingress_port, ingress_port_id))
        print('----------------------------------------')
        print((tabulate(prio_table_list, headers=['Switch Prio', 'Rx Pause Frames', 'Rx Pause Duration', 'Tx Pause Frames', 'Tx Pause Duration'])))
        print('------------------------------------------------------------------------------------------------------------------------------')
        if ingress_port_id == egress_port_id:
            print('Port statistics for port %s (0x%x)' % (ingress_port, ingress_port_id))
        else:
            print('Port statistics for ingress port(Rx) %s (0x%x) and egress port(Tx) %s (0x%x)' % (ingress_port, ingress_port_id, egress_port, egress_port_id))
        print('------------------------------------------------------------------------------------------------------------------------------')
        print((tabulate(port_table_list, headers=['Rx Frames', 'Rx UC Frames', 'Rx MC Frames', 'Rx BC Frames', 'Tx Frames', 'Tx UC Frames', 'Tx MC Frames', 'Tx BC Frames', 'Rx Buffer Discards', 'Tx MC Buffer Discards', 'ECN Marked Packets', 'Rx Pause Frames', 'Tx Pause Frames'])))

def _show_priority_tc_map(sdk_to_linux, portid, use_json=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    tc_list = mlx_get_prio_tc_map(portid)

    tc_map = {}
    _add_port_to_dict(sdk_to_linux, tc_map, 'port', portid)

    tc_map['list'] = tc_list
    if use_json:
        return tc_map
    else :
        print('\nPort %s(0x%x) switch priority to traffic class' % (tc_map['port'],portid))
        for tc_item in tc_map['list']:
            print('switch priority %d' % tc_item['sp'], end=' ')
            print('traffic class %d' % tc_item['tc'])

def _show_priority_ieee_map(use_json=False):
    ieee_list = mlx_get_prio_ieee_map()
    if use_json:
        return ieee_list
    else :
        print('\nGlobal switch priority to IEEE priority map')
        for ieee_item in ieee_list:
            print('switch priority %d' % ieee_item['sp'], end=' ')
            print('IEEE priority %d' % ieee_item['ieee'])

def _show_port_scheduler(sdk_to_linux, portid, use_json=False, verbose=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    element_list = mlx_get_port_scheduler(portid)

    sched_dict = {}
    _add_port_to_dict(sdk_to_linux, sched_dict, 'port', portid)

    sched_dict['scheduler-elements'] = element_list

    if use_json:
        return sched_dict
    else :
        print('\nPort %s(0x%x) scheduler configuration' % (sched_dict['port'],portid))
        if verbose == True:
            print('Per-port hierarchical scheduler:')
            print('\tLevel 0: root node')
            print('\tLevel 1: groups')
            print('\tLevel 2: sub-groups')
            print('\tLevel 3: port egress queues (a.k.a. traffic classes)')
            print('\'DWRR enable\' enables the DWRR mode and weight fields')
            print('\'DWRR mode\' selects between DWRR and strict priority')
            print('\'DWRR weight\' sets a weight 1..100 OR 0, which denotes strict priority (requires DWRR mode == SP)')

        current_level = -1
        for element in element_list:
            if current_level != element['level'] :
                print('')
                current_level = element['level']
            print('%s: %s,' % ('level', element['level']), end=' ')
            print('%s: %s,' % ('index', element['index']), end=' ')
            print('%s: %s,' % ('parent index', element['parent_index']), end=' ')
            print('%s: %s,' % ('DWRR enabled', element['dwrr_enable']), end=' ')
            print('%s: %s,' % ('DWRR mode', element['dwrr_mode']), end=' ')
            print('%s: %s,' % ('DWRR weight', element['dwrr_weight']), end=' ')
            print('%s: %s,' % ('min shaper enabled', element['min_shaper_enable']), end=' ')
            print('%s: %s,' % ('min shaper rate', element['min_shaper_rate']), end=' ')
            print('%s: %s,' % ('max shaper enabled', element['max_shaper_enable']), end=' ')
            print('%s: %s,' % ('max shaper rate', element['max_shaper_rate']), end=' ')
            print('%s: %s'  % ('shaper mode', element['shaper_mode']))

def _show_mc_aware(sdk_to_linux, portid, use_json=False, verbose=False):
    ret = {'output': {}}
    if _port_is_bond_member(sdk_to_linux, portid, use_json, ret):
        return ret['output']
    mc_aware_dict = mlx_get_mc_aware(portid)
    _add_port_to_dict(sdk_to_linux, mc_aware_dict, 'port', portid)

    if use_json:
        return mc_aware_dict
    else :
        if verbose == True:
            print('\nMC aware mode assigns multicast packets to separate egress queues')
            print('from unicast packets. This only changes how packets are scheduled,')
            print('not the packet buffer use, since multicast packets occupy the egress')
            print('buffers of the Multicast pseudo-port (logical port ID 0x40000000).')
        print('\nPort %s(0x%x) MC Aware configuration' % (mc_aware_dict['port'],portid))
        print('MC aware: %s' % mc_aware_dict['mc_aware'])

def _show_flow_control(sdk_to_linux, portid, use_json=False, verbose=False):

    fc_dict = mlx_get_flow_control(portid)
    _add_port_to_dict(sdk_to_linux, fc_dict, 'port', portid)

    if use_json:
        return fc_dict
    else :
        print('\nPort %s(0x%x) flow control configuration' % (fc_dict['port'],portid))
        print('Link pause TX enabled: %s, RX enabled %s' % (fc_dict['link_tx_enabled'],
                                                            fc_dict['link_rx_enabled']))
        for priority in list(fc_dict['pfc'].keys()) :
            print('PFC %d TX enabled: %s, RX enabled %s' % (priority,
                                                            fc_dict['pfc'][priority]['tx_enabled'],
                                                            fc_dict['pfc'][priority]['rx_enabled']))

def _show_datapath_config(sdk_to_linux, portid, use_json=False, verbose=False):

    mc_logical_port = SX_MC_PORT_LOG_ID
    json_out = []
    if not use_json:
        print('Datapath configuration for port %s and MC logical port 0x%x' % (sdk_to_linux.get(portid), mc_logical_port))
        print('\n---- Priority mapping ----- ')
    # _show_priority_group_map(sdk_to_linux, portid, use_json)
    ret = _show_port_priority_trust(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_port_default_priority(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_pcpdei_prio_map(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_dscp_prio_map(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_port_priority_rewrite(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_pcpdei_remark_map(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_dscp_remark_map(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_priority_tc_map(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_priority_ieee_map(use_json)
    if use_json:
        json_out.append(ret)

    if not use_json:
        print('\n------ Flow Control ----- ')
    ret = _show_flow_control(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)

    if not use_json:
        print('\n----- Unallocated packet buffer cells -------')
    ret = _show_free_buffer_counters(use_json)
    if use_json:
        json_out.append(ret)

    if not use_json:
        print('\n----- Parser length -------')
    ret = _show_parser_length(use_json)
    if use_json:
        json_out.append(ret)

    if not use_json:
        print('\n----- Allocated packet buffer cells -------')
    ret = _show_pool_buffer_limits(use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_mc_aware(sdk_to_linux, portid, use_json, verbose)
    if use_json:
        json_out.append(ret)
    ret = _show_port_reserved_buffer_limits(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_port_reserved_buffer_limits(sdk_to_linux, mc_logical_port, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_port_shared_buffer_limits(sdk_to_linux, portid, use_json)
    if use_json:
        json_out.append(ret)
    ret = _show_port_shared_buffer_limits(sdk_to_linux, mc_logical_port, use_json)
    if use_json:
        json_out.append(ret)

    if not use_json:
        print('\n ------- Scheduler Configuration ------- ')
    ret = _show_port_scheduler(sdk_to_linux, portid, use_json, verbose)
    if use_json:
        json_out.append(ret)

    if use_json:
        return json_out

def get_mpls_enabled():
    rc = False
    mpls_config_file = '/cumulus/switchd/config/traffic/mpls_enable'
    try:
        with open(mpls_config_file , 'r') as f:
            for line in f:
                if line.startswith("TRUE"):
                    return True
                else:
                    return False
    except:
        return rc
    return rc

def _get_ecmp_table_(use_json=False):
    mpls_enabled = False
    rt = {}
    versions = [4, 6]
    ecmp_ids = []
    table = {}

    eid_count = mlx_get_ecmp_count()
    if not eid_count:
        if use_json:
            _print_json_output(table)
        else:
            print("ECMP ID\t\tECMP TYPE\t\tRIF\t\tWEIGHT\t\tACTION\t\tCOUNTER ID\t\tPACKETS\t\tBYTES")
        return
    ecmp_ids = mlx_get_ecmp(None)
    for eid in ecmp_ids:
        t = mlx_get_ecmp(eid)
        if t:
            table[eid] = {}
            table[eid] = t[eid]

    if use_json:
        _print_json_output(table)
    else:
        print("ECMP ID\t\tECMP TYPE\t\tRIF\t\tWEIGHT\t\tACTION\t\tCOUNTER ID\t\tPACKETS\t\tBYTES")
        for eid in table:
            for i, (rif, weight, action, cid, cpkts, cbytes) in enumerate(table[eid]['nh_list']):
                if i == 0:
                    print("%d\t\t%-10s\t\t%d\t\t%d\t\t%s\t\t%d\t\t\t%d\t\t%d" % (eid,
                           table[eid]['ecmp_type'], rif, weight, action, cid,
                           cpkts, cbytes))
                else:
                    print("\t\t\t\t\t%d\t\t%d\t\t%s\t\t%d\t\t\t%d\t\t%d" % (rif,
                          weight, action, cid, cpkts, cbytes))

def _show_mpls_table_(use_json=False) :
    rt_dict = {}
    mpls_enabled = False
    mpls_enabled = get_mpls_enabled()
    if mpls_enabled is False:
        return rt_dict

    rt_dict = mlx_get_mpls_table()
    if use_json:
        _print_json_output(rt_dict)
    else:
        print("MPLS Label\tAction\tILM Action\tECMP ID\tOut Label\tIntf ID\t\tNext Hop")
        for rt,entry in list(rt_dict.items()) :
            next_hop_count = len(entry['next_hops'])
            ecmp_id = '%d' % entry['ecmp_id']
            ilm_action = entry['ilm_fwd_action']
            action = entry['action']
            for nh_idx in range(next_hop_count):
                label_str = ''
                intf_id = ''
                nhaddr = entry['next_hops'][nh_idx]['nhaddr']
                label_array = entry['next_hops'][nh_idx]['label_array']
                for label in label_array:
                    label_str += str(label) + ''
                if label_str == '':
                    label_str = 'None'
                intf_id = entry['next_hops'][nh_idx]['rif']
                if (nh_idx == 0):
                    print("%s\t\t%s\t%s\t\t%s\t%s\t\t%s\t\t%s" % (rt,
                                              action,
                                              ilm_action,
                                              ecmp_id,
                                              label_str,
                                              intf_id,
                                              nhaddr))
                else:
                    print("\t\t\t\t\t\t%s\t\t%s\t\t%s" % (label_str,
                                              intf_id,
                                              nhaddr))

def _show_lag_member_port_ids_(swid, logical_bond_id, use_json=False):
    lag_member_ports = mlx_get_lag_member_ids(swid, logical_bond_id)

    if use_json:
        _print_json_output(lag_member_ports)
    else:
        output = ""
        for pid in lag_member_ports:
            output += "%d " % pid

        output = output.strip()
        print(output)

def _show_ecmp_paramters(param_type, use_json=False):
    output = mlx_get_ecmp_parameters()

    if use_json:
        _print_json_output(output)
    else:
        print("Flag\t\tValue")
        ecmp_hash_keys = ["symmetric_hash", "seed", "type", "enabled_fields", "field_values"]
        for f in ecmp_hash_keys:
            if f not in list(output.keys()):
                continue
            if len(f) < 5:
                if isinstance(output[f], list):
                    print("%s %s " % (f, type(output[f])))
                    for index, line in enumerate(output[f]):
                        if line == "NONE":
                            continue
                        print(("\t\t{}".format(line)))
                else:
                    print("%s\t\t%s" % (f, str(output[f])))
            else:
                if isinstance(output[f], list):
                    print("%s" % (f))
                    for index, line in enumerate(output[f]):
                        if line == "NONE":
                            continue
                        print(("\t\t{}".format(line)))
                else:
                    print("%s\t%s" % (f, str(output[f])))

def _show_lag_paramters(param_type, use_json=False):
    lag_params = mlx_get_lag_parameters()

    output = {}
    if param_type == 'type':
        output['type'] = lag_params['type']
    elif param_type == 'flow_parameters':
        output['flow_params'] = lag_params['flow_params']
    elif param_type == 'seed':
        output['seed'] = lag_params['seed']
    else:
        output = lag_params

    if use_json:
        _print_json_output(output)
    else:
        print("Flag\t\tValue")
        lag_hash_keys = ["flow_params", "seed", "type", "enabled_fields", "field_values"]
        for f in lag_hash_keys:
            if f not in list(output.keys()):
                continue
            if len(f) < 5:
                if isinstance(output[f], list):
                    print("%s %s " % (f, type(output[f])))
                    for index, line in enumerate(output[f]):
                        print(("\t\t{}".format(line)))
                else:
                    print("%s\t\t%s" % (f, str(output[f])))
            else:
                if isinstance(output[f], list):
                    print("%s" % (f))
                    for index, line in enumerate(output[f]):
                        print(("\t\t{}".format(line)))
                else:
                    print("%s\t%s" % (f, str(output[f])))

def _show_all_mstp_instances_(swid, use_json=False):
    mstp_inst = mlx_get_all_mstp_instances(swid)

    if use_json:
        _print_json_output(mstp_inst)
    else:
        print("Switch ID\t\tMSTP Instance ID")
        for i in range(len(mstp_inst)):
            if i == 0:
                print("%d\t\t\t%d" % (swid, mstp_inst[i]))
            else:
                print("\t\t\t%d" % mstp_inst[i])

def _show_mstp_instance_vids_(swid, inst_id, use_json=False):
    vids = mlx_get_mstp_instance_vlans(swid, inst_id)

    if use_json:
        _print_json_output(vids)
    else:
        print("Switch ID\tMSTP Instance\tVLAN ID")
        for i in range(len(vids)):
            if i == 0:
                print("%d\t\t%d\t\t%d" % (swid, inst_id, vids[i]))
            else:
                print("\t\t\t\t%d" % vids[i])

def _get_mstp_instance_port_state_(swid, inst_id, port_list, use_json=False):
    state = {'discarding': [],
             'learning': [],
             'forwarding': []}

    for port_id in port_list:
        port_state = mlx_get_mstp_port_state(swid, inst_id, port_id)
        if port_state in state:
            state[port_state].append(port_id)

    if use_json:
        _print_json_output(state)
    else:
        begin = True
        print("Switch ID\tMSTP ID\tPort ID\tState")
        for i in range(len(state['discarding'])):
            if i == 0:
                print("%d\t\t%d\t\t%d\t\tdiscarding" % \
                      (swid, inst_id, state['discarding'][i]))
            else:
                print("\t\t\t\t\t%d\t\tdiscarding" % state['discarding'][i])

        for i in range(len(state['learning'])):
            if (i == 0) and begin:
                print("%d\t\t%d\t\t%d\t\tlearning" % \
                      (swid, inst_id, state['learning'][i]))
            else:
                print("\t\t\t\t\t%d\t\tlearning" % state['learning'][i])

        for i in range(len(state['forwarding'])):
            if (i == 0) and begin:
                print("%d\t\t%d\t\t%d\t\tforwarding" % \
                      (swid, inst_id, state['forwarding'][i]))
            else:
                print("\t\t\t\t\t%d\t\tforwarding" % state['forwarding'][i])

def _show_port_ids(swid_list=[0], use_json=False):
    port_id_dict = mlx_get_port_ids(swid_list)

    if use_json:
        _print_json_output(port_id_dict)
    else:
        print("Swid\tPort ID\tHex Port ID")
        for swid in sorted(port_id_dict.keys()):
            for portid in sorted(port_id_dict[swid]):
                print("%d\t%d\t0x%08x" % (swid, portid, portid))

def _show_port_isolate(swid_list=[0], use_json=False):
    port_iso_dict = mlx_get_port_isolate(swid_list)

    if use_json:
        _print_json_output(port_iso_dict)
    else:
        print("Destination\tIsolated Sources")
        for portid in sorted(port_iso_dict.keys()):
            iso_ports = ""
            for iso_port in port_iso_dict[portid]:
                iso_ports += " 0x%08x" % (iso_port)
            print("0x%08x\t%s" % (portid, iso_ports))

def _set_port_isolate(portid, iso_port_list=[], use_json=False):
    mlx_set_port_isolate(portid, iso_port_list)

def _set_port_priority_rewrite(portid, dscp='no', pcpdei='no', exp='no'):
    mlx_set_port_priority_rewrite(portid, dscp, pcpdei, exp)

def _set_port_dscp_remark(portid, priority, color, dscp):
    mlx_set_port_dscp_remark(portid, priority, color, dscp)

# Will return a tuple (result,msg):
# msg: return message to let user know of the issue
# result: bool True/False for status
def _traffic_fuse_node_reload(use_json=False, traffic_file=None, datapath_file=None):
    script_dir      = '/usr/cumulus/bin/'
    usr_config_dir  = '/etc/cumulus/'
    chip_config_dir = '/usr/lib/python3/dist-packages/cumulus/__chip_config/mlx/'
    traffic_reload_fuse_node = '/cumulus/switchd/config/traffic/reload'
    mlx_datapath_file = '/etc/mlx/datapath/datapath.conf'

    ret_msg = ''

    if traffic_file == None:
        traffic_file    = usr_config_dir  + 'datapath/traffic.conf'

    if datapath_file == None:
        datapath_file    = chip_config_dir + 'datapath.conf'

    command_str = [ script_dir + 'cl-consistency-check',
                    '--datapath-syntax-check', '-t', traffic_file,
                    '-d', datapath_file ]
    try:
        with open(os.devnull, 'w') as devnull:
            ret = subprocess.call(command_str,stdout=devnull,stderr=devnull)
    except BaseException as e:
        if use_json:
            ret_msg += 'CCFailedQoS:{0}'.format(e)
        else:
            ret_msg += 'Exception: {0}\n'.format(e)
            ret_msg += 'Errors detected while running consistency-checker with traffic config file %s. and datapath config file %s\n' % (traffic_file,datapath_file)
        return (False,ret_msg)

    if ret:
        if use_json:
            ret_msg += 'CCFailedQoS'
        else:
            ret_msg += 'Errors detected while running consistency-checker with traffic config file %s. and datapath config file %s\n' % (traffic_file,datapath_file)
        return (False,ret_msg)

    # Copy over the user datapath file to mlx datapath file
    command_str = [ 'cp', datapath_file, mlx_datapath_file ]
    try:
        with open(os.devnull, 'w') as devnull:
            ret = subprocess.call(command_str,stdout=devnull,stderr=devnull)
    except BaseException as e:
        if use_json:
            ret_msg += 'CopyDatapathFileFailed:{0}'.format(e)
        else:
            ret_msg += 'Exception: {0}\n'.format(e)
            ret_msg += 'Errors detected while copying usr datapath config file %s to mlx file %s\n' % (datapath_file, mlx_datapath_file)
        return (False,ret_msg)

    if ret:
        if use_json:
            ret_msg += 'CopyDatapathFileFailed'
        else:
            ret_msg += 'Errors detected while copying usr datapath config file %s to mlx file %s\n' % (datapath_file, mlx_datapath_file)
        return (False,ret_msg)

    if os.path.exists(traffic_reload_fuse_node):
        try:
            with open(traffic_reload_fuse_node, "w") as f:
                f.write("1")
        except BaseException as e:
            if use_json:
                ret_msg += 'TrafficFuseNodeReloadFailed:{0}'.format(e)
            else:
                ret_msg += 'Exception: {0}\n'.format(e)
                ret_msg += 'Errors detected while reconfiguring QoS config in traffic fuse node reload\n'
            return (False,ret_msg)
    else:
        if use_json:
            ret_msg += 'TrafficFuseNodeFileNotFound'
        else:
            ret_msg += "Traffic Fuse node reload file {} does not exists\n".format(traffic_reload_fuse_node)
        return (False,ret_msg)

    command_str = [ 'echo', '$?' ]

    try:
        with open(os.devnull, 'w') as devnull:
            ret = subprocess.call(command_str,stdout=devnull,stderr=devnull)
    except BaseException as e:
        if use_json:
            ret_msg += 'TrafficFuseNodeReloadFailed:{0}'.format(e)
        else:
            ret_msg += 'Exception: {0}\n'.format(e)
            ret_msg += 'Errors detected while reconfiguring QoS config in traffic fuse node reload\n'
        return (False,ret_msg)

    if ret:
        if use_json:
            ret_msg += 'TrafficFuseNodeReloadFailed'
        else:
            ret_msg += 'Errors detected while reconfiguring QoS config in traffic fuse node reload\n'
        return (False,ret_msg)

    return (True,ret_msg)

def _ports_info(swid_list=[0], use_json=False):
    try:
        platform = cumulus.platforms.probe()
    except cumulus.platforms.NoSuchPlatform as e:
        sys.stderr.write('WARNING: unknown platform: %s\n' % str(e))
        sys.exit(-1)
    use_rate_api = platform.use_rate_api
    port_id_dict = mlx_get_port_ids(swid_list)

    port_info_dict = {}
    sdk_to_lnx = get_sdk_to_linux()
    for swid in port_id_dict:
        for portid in port_id_dict[swid]:
            port_info_dict[portid] = {}
            if_name = sdk_to_lnx.get(portid,"None")
            port_info_dict[portid]["ifname"] = if_name
            port_info_dict[portid]["status"] = mlx_get_port_status(portid)
            if use_rate_api == True:
                port_info_dict[portid]["speed"]  = mlx_get_port_rate(portid)
            else:
                port_info_dict[portid]["speed"]  = mlx_get_port_speed(portid)
            port_info_dict[portid]["mtu"]    = mlx_get_port_mtu(portid)
            port_info_dict[portid]["fec"]    = mlx_get_port_fec(portid, use_rate_api)
            if is_bond(if_name) or if_name == "None":
                port_info_dict[portid]["width"]    = 0
            else:
                port_info_dict[portid]["width"]    = mlx_get_port_width(portid)
    if use_json:
        _print_json_output(port_info_dict)
    else:
        print("Port ID\tHex Port ID\tPort\tStatus\tMTU\tSpeed\tFEC")
        for portid in sorted(port_info_dict.keys()):
            print("%9d\t0x%08x\t%s\t%s\t%s\t%d Gbps\t%s" % (portid, portid, \
                port_info_dict.get(portid,{}).get("ifname", "None"), \
                port_info_dict.get(portid,{}).get("status", "None"), \
                port_info_dict.get(portid,{}).get("mtu", "None"), \
                port_info_dict.get(portid,{}).get("speed", 0)/(1*1000*1000*1000), \
                port_info_dict.get(portid,{}).get("fec", "Unknown")))

# Get the port and split numbers from a formatted or unformatted linux port name.
def _get_port_split_from_linux_port(linux_port):
    port = None
    split = None
    match = re.match("swp(\d+)s(\d+)", linux_port)
    if match:
        # Split port
        port = int(match.group(1))
        split = int(match.group(2))
    else:
        match = re.match("swp(\d+)", linux_port)
        if match:
            # Non-split port
            port = int(match.group(1))
    return port, split

def _get_port_list_from_name_or_id(string, wc_name_allowed=False, use_json=False):
    '''
    A valid port_name_or_id can be a port number, decimal or
    hexadecimal (starts with "0x"), a swp name such as "swp12", or
    if wc_name_allowed is True, a wildcard name such as "swp*" or "swp+".
    '''
    port_list = []
    swid = 0

    if string.startswith("0x"):
        try:
            portid = int(string,16)
        except:
            portid = mlx_invalid_port_id()
    else:
        try:
            portid = int(string)
        except:
            portid = mlx_invalid_port_id()

    if portid != mlx_invalid_port_id():
        sdk_to_linux = get_sdk_to_linux()
        if sdk_to_linux.get(portid,None):
            port_list.append(portid)
        elif portid in mlx_get_port_ids([swid])[swid]:
            # Accept port-channel logid as input
            port_list.append(portid)

    elif string == port_wildcard1 or string == port_wildcard2 or string == port_wildcard_all:
        if wc_name_allowed:
            swid_list=[0]
            port_id_dict = mlx_get_port_ids(swid_list)
            sdk_to_linux = get_sdk_to_linux()
            linux_to_sdk = get_linux_to_sdk()

            # Add all swp ports and exclude LAGs
            formatted_port_list = []
            # Number of digits to reserve for port and split for sorting format.
            digits = '3'
            for swid in port_id_dict:
                for portid in port_id_dict[swid]:
                    linux_port = sdk_to_linux.get(portid,None)
                    if linux_port:
                        # Format the port and slot numbers to fixed length for sorting.
                        port, split = _get_port_split_from_linux_port(linux_port)
                        if port != None and split != None:
                            # Split port
                            formatted_port = ("swp%0" + digits + "ds%0" + digits + "d") % (port, split)
                        elif port != None:
                            # Non-split port
                            formatted_port = ("swp%0" + digits + "d") % port
                        else:
                            # Non-physical port
                            formatted_port = linux_port
                        formatted_port_list.append(formatted_port)
            # After fixed length formatting, we can sort according to real system port order.
            formatted_port_list.sort()
            for formatted_port in formatted_port_list:
                # Convert back to unformatted linux port name.
                port, split = _get_port_split_from_linux_port(formatted_port)
                if port != None and split != None:
                    # Split port
                    linux_port = "swp%ds%d" % (port, split)
                elif port != None:
                    # Non-split port
                    linux_port = "swp%d" % port
                else:
                    # Non-physical port
                    linux_port = formatted_port
                if is_bond(linux_port) and string != port_wildcard_all:
                    # We need to exclude LAG
                    continue
                if string != port_wildcard_all and linux_port == 'Unknown':
                    # Bond interface with no member ports are marked as 'Unknown'
                    continue
                # Convert back to SDK port ID.
                portid = linux_to_sdk.get(linux_port, None)
                # Now add to the port list in true system port order.
                port_list.append(portid)
    else:
        linux_to_sdk = get_linux_to_sdk()
        portid = linux_to_sdk.get(string,None)
        if portid is not None:
            port_list.append(portid)

    return port_list

def _set_port_sflow(port_name_or_id, ing_rate, egr_rate, use_json=False):
    rate_max = mlx_get_port_sflow_sample_rate_max()
    if ing_rate > rate_max:
        print("ing_rate %u exceeds maximum sampling rate of %u" % (ing_rate, rate_max))
        exit(-1)

    if egr_rate > rate_max:
        print("egr_rate %u exceeds maximum sampling rate of %u" % (egr_rate, rate_max))
        exit(-1)

    port_list = _get_port_list_from_name_or_id(port_name_or_id, True, use_json)
    if len(port_list) == 0:
        if use_json:
            _print_json_output({'port': port_name_or_id, 'exception': 'inputPortNotSupported'})
        else:
            print("%s is not a supported port_name_or_id" % port_name_or_id)
        exit(-1)
    for portid in port_list:
        mlx_set_port_sflow(portid, ing_rate, egr_rate)

def _show_port_sflow(sdk_to_linux, portid, use_json=False):
    sflow_params = mlx_get_port_sflow(portid)
    if use_json:
        return sflow_params
    else:
        print("%s (0x%x):" % (sdk_to_linux.get(portid), portid))
        print("  ing_rate  %d" % sflow_params.get('ing_rate'))
        print("  egr_rate  %d" % sflow_params.get('egr_rate'))
        print("  deviation %d\n" % sflow_params.get('deviation'))

def _show_port_sflow_name_or_id(port_name_or_id, use_json=False):
    __expand_port_name_or_id(_show_port_sflow, port_name_or_id, True, use_json)

def _show_port_sflow_stats(sdk_to_linux, portid, use_json=False):
    sflow_statistics = mlx_get_port_sflow_statistics(portid)
    if use_json:
        return sflow_statistics
    else:
        print("%s (0x%x):" % (sdk_to_linux.get(portid), portid))
        print("  count_sample_drop  %d\n" % sflow_statistics.get('count_sample_drop'))

def _show_port_sflow_stats_name_or_id(port_name_or_id, use_json=False):
    __expand_port_name_or_id(_show_port_sflow_stats, port_name_or_id, True, use_json)

def intBase(intStr):
    '''
    Allow an integer to be specified with standard base representations:
    37 = decimal, 0x25 = hex, 045 = octal, 0b100101 = binary
    '''
    try:
        intVal = int(intStr,0)
    except:
        msg = "%s is not a valid integer" % (intStr,)
        raise argparse.ArgumentTypeError(msg)
    return intVal

def _store_pers_data(data, file_name):
    try:
        pers_db_file = open(file_name,'wb')
    except:
        return

    pickle.dump(data, pers_db_file)
    pers_db_file.close()

def _fetch_pers_data(file_name):
    try:
        pers_db_file = open(file_name,'rb')
    except:
        return ''

    try:
        data = pickle.load(pers_db_file)
    except:
        return ''
    pers_db_file.close()
    return data

def _read_sx_sdk_pid(filename):
    try:
        f = open(filename, 'r')
    except:
        return 0
    l = f.readlines();
    try:
        sx_sdk_pid = int(l[0])
    except:
        f.close()
        return 0
    f.close()
    return sx_sdk_pid

def _get_trap_stats_diff(old_data, new_data):
    cur_pid = _read_sx_sdk_pid(sx_sdk_pidfile)
    old_pid = _read_sx_sdk_pid(mlxcmd_sx_sdk_pidfile)
    if old_data != '' and cur_pid == old_pid:
        for trap_id, trap_stats_item in list(new_data.items()):
            cpu_pkts_diff = trap_stats_item['to_cpu_pkts'] - old_data[trap_id]['to_cpu_pkts']
            trap_stats_item['to_cpu_pkts_diff'] =  cpu_pkts_diff if cpu_pkts_diff >= 0 else '-'
            cpu_bytes_diff = trap_stats_item['to_cpu_bytes'] - old_data[trap_id]['to_cpu_bytes']
            trap_stats_item['to_cpu_bytes_diff'] = cpu_bytes_diff if cpu_bytes_diff >= 0 else '-'
            violation_pkts_diff = trap_stats_item['violation_counter_pkts'] - old_data[trap_id]['violation_counter_pkts']
            trap_stats_item['violation_counter_pkts_diff'] = violation_pkts_diff if violation_pkts_diff >= 0 else '-'
    else:
        for trap_id, trap_stats_item in list(new_data.items()):
            trap_stats_item['to_cpu_pkts_diff'] = '-'
            trap_stats_item['to_cpu_bytes_diff'] = '-'
            trap_stats_item['violation_counter_pkts_diff'] = '-'
        if cur_pid != old_pid and cur_pid != 0:
            f = open(mlxcmd_sx_sdk_pidfile,'w')
            f.write(str(cur_pid)+'\n')
            f.close()

def _clear_trap_stats(use_json=False):
    mlx_clear_all_trap_stats()
    pers_trap_stats_file = g_pers_trap_stats_file
    try:
        os.remove(pers_trap_stats_file)
    except:
        return

def _show_trap_stats(use_json=False, show_diff=True):
    pers_trap_stats_file = g_pers_trap_stats_file
    prev_buffer_info = _fetch_pers_data(pers_trap_stats_file)

    buffer_info = mlx_show_trap_stats()
    _store_pers_data(buffer_info, pers_trap_stats_file)
    _get_trap_stats_diff(prev_buffer_info, buffer_info)

    if use_json:
        _print_json_output(buffer_info)
    else:
        table_list = []
        for trap_id, trap_item in list(buffer_info.items()):
            table_row = []
            table_row.append(trap_item['trap_name'])
            table_row.append(trap_id)
            table_row.append(trap_item['trap_group_id'])
            table_row.append(trap_item['to_cpu_pkts'])
            if show_diff == True:
                table_row.append(trap_item['to_cpu_pkts_diff'])
            table_row.append(trap_item['to_cpu_bytes'])
            if show_diff == True:
                table_row.append(trap_item['to_cpu_bytes_diff'])
            table_row.append(trap_item['violation_counter_pkts'])
            if show_diff == True:
                table_row.append(trap_item['violation_counter_pkts_diff'])
            table_list.append(table_row)

        if show_diff == True:
            print((tabulate(table_list, headers=['Trap Name', 'Trap Id', 'Trap Group Id', 'To CPU Pkts', 'Diff', 'To CPU Bytes', 'Diff', 'Violated Pkts', 'Diff'])))
        else:
            print((tabulate(table_list, headers=['Trap Name', 'Trap Id', 'Trap Group Id', 'To CPU Pkts', 'To CPU Bytes', 'Violated Pkts'])))

def _show_trap_group_stats(use_json=False, is_copp_stats=True):
    buffer_info = mlx_show_trap_group_stats(is_copp_stats)
    if use_json:
        _print_json_output(buffer_info)
    else:
        table_list = []
        for trap_grp_id, trap_grp_item in list(buffer_info.items()):
            table_row = []
            if is_copp_stats:
                table_row.append(trap_grp_item['name'])
                table_row.append(trap_grp_item['pol_id'])
                table_row.append(trap_grp_item['policer_attr'])
            table_row.append(trap_grp_item['trap_group_id'])
            table_row.append(trap_grp_item['to_cpu_pkts'])
            table_row.append(trap_grp_item['to_cpu_bytes'])
            table_row.append(trap_grp_item['violation_counter_pkts'])
            table_list.append(table_row)
        if is_copp_stats:
            print((tabulate(table_list, headers=['Name', 'Policer Id', 'Policer Attr', 'Trap Group', 'To CPU Pkts', 'To CPU Bytes', 'Violated Packets'])))
        else:
            print((tabulate(table_list, headers=['Trap Group Id', 'To CPU Pkts', 'To CPU Bytes', 'Violated Packets'])))

class Formatters:
    stdout = logging.Formatter('%(asctime)s.%(msecs)03d — %(levelname)s — %(name)s — '
                               '%(message)s', datefmt='%Y-%m-%dT%H:%M:%S')

def valid_logfile(_path):
    pathobj = Path(_path)
    if not pathobj.parent.exists():
        raise ArgumentTypeError(f'Folder {pathobj.parent} does not exist')
    return pathobj

def add_logfile(logfile):
    file_handler = logging.FileHandler(logfile)
    file_handler.setFormatter(Formatters.stdout)
    logger.addHandler(file_handler)
    logger.debug(f'File logging has started in {logfile}.')
    return file_handler.stream

def mlxcmd_main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--json', action="store_true", help="Enable JSON output")
    parser.add_argument('--verbose', action="store_true", help='use more words')
    parser.add_argument('--out_file', type=argparse.FileType('w'), default=None, help='output file')
    parser.add_argument('--log-file', type=valid_logfile, help='Logging file for additional file logging. Default always logs to /var/log/mlxcmd.log')
    parser.add_argument('--log-level', choices=logging._levelToName.values(), default='INFO', help='Logging level')

    subparser = parser.add_subparsers()

    # L2 command set
    l2_parser = subparser.add_parser('l2', help="commands that are related to Layer 2")
    l2_subparser = l2_parser.add_subparsers(help="l2 choices")

    # Bridge command set under L2
    bridge_subparser = l2_subparser.add_parser('bridge')
    bridge_cmds = bridge_subparser.add_subparsers()
    bridge_vport = bridge_cmds.add_parser('vports')
    bridge_vport.add_argument('br_swid', type=int,
                                  metavar='<swid for bridge>')
    bridge_vport.add_argument('br_vid', type=int,
                              help='VLAN ID of the bridge')

    # FDB command set Under L2
    fdb_subparser = l2_subparser.add_parser('fdb', help='different commands for the fdb')

    fdb_cmds = fdb_subparser.add_subparsers()
    fdb_show = fdb_cmds.add_parser('show')
    fdb_sub_show = fdb_show.add_subparsers(help="l2 fdb show help")
    tbl_cmd = fdb_sub_show.add_parser('table')
    tbl_cmd.add_argument('switch_id', type=int, nargs='?', default=0,
                         help="switch ID of the FDB table to display")
    timers_cmd = fdb_sub_show.add_parser('timers', help="display the FDB aging time")
    timers_cmd.add_argument('fdb_timer_show', nargs='*', default=[0])

    fdb_mcshow = fdb_cmds.add_parser('mcshow')
    fdb_sub_mcshow = fdb_mcshow.add_subparsers(help="l2 fdb mcshow help")
    mtbl_cmd = fdb_sub_mcshow.add_parser('table')
    mtbl_cmd.add_argument('mcswitch_id', type=int, nargs='?', default=0,
                          help="switch ID of the MC FDB table to display")

    fdb_set = fdb_cmds.add_parser('set')
    fdb_sub_set = fdb_set.add_subparsers(help="l2 fdb set help")
    set_age_timer = fdb_sub_set.add_parser('aging')
    set_age_timer.add_argument('aging_timer', type=int,
                               help="value for the aging time to be set")
    set_age_timer.add_argument('swid_list', nargs="*", default=[0],
                               metavar="<swid>",
                               help="list of switch ID to change aging timers on")

    fdb_flush = fdb_cmds.add_parser('flush')
    fdb_sub_flush = fdb_flush.add_subparsers(help="l2 fdb flush help")
    uc_fdb = fdb_sub_flush.add_parser('uc', help="commnds for flushing unicast FDBs")
    uc_all_fdb = uc_fdb.add_subparsers(help="options for flushing unicast FDBs")
    uc_all_opts = uc_all_fdb.add_parser('all')
    uc_all_opts.add_argument('all_fdb_flush_swids', type=int, nargs="*", default=[0],
                             metavar='swid')
    uc_fid_opts = uc_all_fdb.add_parser('fid')
    uc_fid_opts.add_argument('fdb_fids', type=int, nargs=2,
                             metavar=('switch_id', 'fid'),
                             help="<switch ID> <fid>")
    uc_port_opts = uc_all_fdb.add_parser('port')
    uc_port_opts.add_argument('fdb_log_port_id', type=int,
                              metavar="logical_port_ID",
                              help="<logical port ID>")

    mc_fdb = fdb_sub_flush.add_parser('mc', help="commands for flushing multicast FDBs")

    # L3 command set
    l3_parser = subparser.add_parser('l3', help="Layer 3 commands")
    l3_parser.add_argument('l3_sub_cmd', choices=['route', 'neighbor',
                                                  'route6', 'neighbor6', 'ecmp_table',
                                                  'interface', 'route_count', 'route6_count',
                                                  'mpls_table'],
                            help="l3 choices")

    # LAG commands
    lag_parser = subparser.add_parser('lag')
    lag_sub_parser = lag_parser.add_subparsers()
    lag_show = lag_sub_parser.add_parser('show')
    lag_sub_show = lag_show.add_subparsers()
    lag_show_members = lag_sub_show.add_parser('members')
    lag_show_members.add_argument('lag_member_swid', type=int,
                                  metavar='<swid lag port is on>')
    lag_show_members.add_argument('lag_member_port_id', type=int,
                                 metavar="<logical port ID for lag>")
    lag_show_hash_flow_params = lag_sub_show.add_parser('hash')
    lag_show_hash_flow_params.add_argument('hash_params', choices=['type',
                                                                   'flow_parameters',
                                                                   'seed', 'all'])

    # ECMP commands
    ecmp_parser = subparser.add_parser('ecmp')
    ecmp_sub_parser = ecmp_parser.add_subparsers()
    ecmp_show = ecmp_sub_parser.add_parser('show')
    ecmp_sub_show = ecmp_show.add_subparsers()
    ecmp_show_hash_flow_params = ecmp_sub_show.add_parser('hash')
    ecmp_show_hash_flow_params.add_argument('ecmp_hash_params', choices=['all'])

    # MSTP command set
    mstp_parser = subparser.add_parser('mstp')
    mstp_sub_parser = mstp_parser.add_subparsers()
    mstp_inst_show = mstp_sub_parser.add_parser('instances')
    mstp_inst_show.add_argument('mstp_swid', type=int, nargs="?", default=0)
    mstp_inst_port_state = mstp_sub_parser.add_parser('port_state')
    mstp_inst_port_state.add_argument('port_state_swid', type=int,
                                      metavar="<switch id>")
    mstp_inst_port_state.add_argument('port_state_mstp_inst', type=int,
                                      metavar="<mstp instance>")
    mstp_inst_port_state.add_argument('port_state_ids', type=int, nargs="*",
                                      metavar="<logical port id>")
    mstp_inst_vids = mstp_sub_parser.add_parser('vids')
    mstp_inst_vids.add_argument('vid_swid', type=int)
    mstp_inst_vids.add_argument('vid_inst', type=int)

    # packet buffers command set
    buffers_parser = subparser.add_parser('buffers', help="Packet buffer commands")
    buffers_parser.add_argument('buffer_sub_cmd',
                                choices=['pool',
                                         'port_reserved',
                                         'port_shared',
                                         'free',
                                         'mc_aware',
                                         'shared_limits'],
                                help='buffer choices')
    buffers_parser.add_argument('buffer_type', choices=['limits', 'counters', 'other'], help='buffer type choices')
    buffers_parser.add_argument('--port', metavar='<port_name_or_id>', default='swp10', help='logical port ID (default is swp10)')
    buffers_parser.add_argument('--swid', type=int, default=0, help='switch ID (default is 0)')
    buffers_parser.add_argument('--clear', action="store_true", help='Clear the counters')
    buffers_parser.add_argument('--cells',action="store_true", help='Show storage in cells')
    buffers_parser.add_argument('--port-buff',action="store_true", help='Show only port.pool buffers')
    buffers_parser.add_argument('--pg-buff',action="store_true", help='Show only iport.pg buffers')
    buffers_parser.add_argument('--tc-buff',action="store_true", help='Show only eport.tc buffers')
    buffers_parser.add_argument('--mc-buff',action="store_true", help='Show only eport.mc buffers')
    buffers_parser.add_argument('--descriptor-buff',action="store_true", help='Show only descriptor buffers')

    # datapath configuration command set
    datapath_parser = subparser.add_parser('datapath', help='datapath configuration commands')
    datapath_parser.add_argument('datapath_sub_cmd',
                                 choices=['show'],
                                 help='datapath configuration actions')
    datapath_parser.add_argument('--port', metavar='<port_name_or_id>', default='swp10', help='logical port ID (default is swp10)')
    datapath_parser.add_argument('--cells',action="store_true", help='Show storage in cells')

    # QoS command set
    qos_parser = subparser.add_parser('qos', help="QoS commands")
    qos_parser.add_argument('qos_sub_cmd',
                                choices=['mapping',
                                         'redecn',
                                         'counters',
                                         'config'],
                                help='QoS choices')
    qos_parser.add_argument('qos_config_type', choices=['reload'], nargs='?', help='qos config choices.')
    qos_parser.add_argument('--clear', action="store_true", help='Clear the counters')
    qos_parser.add_argument('--ingress_port', metavar='<port_name_or_id>', help='logical ingress port name or ID - single port name only supported.')
    qos_parser.add_argument('--egress_port', metavar='<port_name_or_id>', help='logical egress port name or ID - single port name only supported.')
    qos_parser.add_argument('--port', metavar='<port_name_or_id>', help='logical port name or ID - supports wild card swp*. Only used for qos counters')
    qos_parser.add_argument('--cells',action="store_true", help='Show storage in cells')

    # RoCE command set
    roce_parser = subparser.add_parser('roce', help="RoCE commands")
    roce_parser.add_argument('roce_sub_cmd',
                                choices=['counters', 'status'],
                                help='RoCE choices')
    roce_parser.add_argument('--port', metavar='<port_name_or_id>', default=port_wildcard1, help='logical port name or ID (default is all ports)')
    roce_parser.add_argument('--system', action="store_true", help='Show global system RoCE status, not related to a specific port')
    roce_parser.add_argument('--cells',action="store_true", help='Show storage in cells')
    roce_clear_group = roce_parser.add_mutually_exclusive_group()
    roce_clear_group.add_argument('--clear', action="store_true", help="Clear the counters")
    roce_clear_group.add_argument('--read_clear', action="store_true", help="Read and clear the counters")

    # priority map command set
    prio_parser = subparser.add_parser('priority', help='switch priority map commands')
    prio_parser.add_argument('prio_sub_cmd',
                             choices=['pcpdei',
                                      'dscp',
                                      'traffic_class',
                                      'ieee',
                                      'priority_group',
                                      'trust',
                                      'default_port_priority',
                                      'rewrite'],
                             help='packet priority choices')
    prio_parser.add_argument('--remark', action='store_true', help='show priority remark mapping')
    prio_parser.add_argument('--port', metavar='<port_name_or_id>', default='swp10', help='logical port ID (default is swp10)')

    # scheduler command set
    sched_parser = subparser.add_parser('scheduler', help='port scheduler commands')
    sched_parser.add_argument('sched_sub_cmd', choices=['show'], help='port scheduler choices')
    sched_parser.add_argument('--port', metavar='<port_name_or_id>', default='swp10', help='logical port ID (default is swp10)')

    # flow control command set
    fc_parser = subparser.add_parser('flow_control', help='flow control commands')
    fc_parser.add_argument('fc_sub_cmd', choices=['show'], help='flow control choices')
    fc_parser.add_argument('--port', metavar='<port_name_or_id>', default='swp10', help='logical port ID (default is swp10)')

    # Ports command set
    ports_parser = subparser.add_parser('ports', help='Port configuration and counters')
    ports_sub_parser = ports_parser.add_subparsers()
    ports_show = ports_sub_parser.add_parser('show')
    ports_sub_show = ports_show.add_subparsers()
    ports_show_cntr = ports_sub_show.add_parser('counters')
    ports_show_cntr.add_argument('show_port_counters_port_name', metavar='<port_name_or_id>')
    ports_show_dist = ports_sub_show.add_parser('pktdist')
    ports_show_dist.add_argument('show_port_pkt_dist_port_name', metavar='<port_name_or_id>')
    ports_show_discards = ports_sub_show.add_parser('discards')
    ports_show_discards.add_argument('show_port_discards_port_name', metavar='<port_name_or_id>')
    ports_show_ids = ports_sub_show.add_parser('ids')
    ports_show_ids.add_argument('port_swids_list', type=int, nargs="*",
                          default=[0], metavar="<switch id>")
    ports_show_isolate = ports_sub_show.add_parser('isolate')
    ports_show_isolate.add_argument('show_port_isolate_swids', type=int, nargs="*",
                                    default=[0], metavar="<switch id>")
    ports_show_sflow = ports_sub_show.add_parser('sflow')
    ports_show_sflow.add_argument('show_port_sflow_port_name', metavar='<port_name_or_id>')
    ports_show_sflow_stats = ports_sub_show.add_parser('sflow_statistics')
    ports_show_sflow_stats.add_argument('show_port_sflow_stats_port_name', metavar='<port_name_or_id>')

    ports_clear = ports_sub_parser.add_parser('clear')
    ports_clear.add_argument('clear_port_id', metavar='<port_name_or_id>')
    ports_clear.add_argument('clear_cntr_grp_type', metavar="<type of counters to clear>",
                             type=int, nargs='?', default=SX_PORT_CNTR_GRP_ALL)
    ports_info = ports_sub_parser.add_parser('info')
    ports_info.add_argument('port_info_swids', type=int, nargs="*",
                          default=[0], metavar="<switch id>")
    ports_set = ports_sub_parser.add_parser('set')
    ports_sub_set = ports_set.add_subparsers()
    ports_set_isolate = ports_sub_set.add_parser('isolate')
    ports_set_isolate.add_argument('isolate_portid', metavar='<portid>', type=intBase)
    ports_set_isolate.add_argument('isolate_port_list', metavar='<portids>', type=intBase,
                                   nargs="*", default=[])
    ports_set_sflow = ports_sub_set.add_parser('sflow')
    ports_set_sflow.add_argument('set_port_sflow_port_name', metavar='<port_name_or_id>')
    ports_set_sflow.add_argument('set_port_sflow_ing_rate', metavar='<ing_rate>', type=intBase)
    ports_set_sflow.add_argument('set_port_sflow_egr_rate', metavar='<egr_rate>', type=intBase)

    ports_set_priority_rewrite = ports_sub_set.add_parser('priority_rewrite')
    ports_set_priority_rewrite.add_argument('set_port_priority_rewrite_port', metavar='<portid>', type=intBase)
    ports_set_priority_rewrite.add_argument('set_port_priority_rewrite_dscp', metavar='<dscp>')
    ports_set_priority_rewrite.add_argument('set_port_priority_rewrite_pcpdei', metavar='<pcpdei>')
    ports_set_priority_rewrite.add_argument('set_port_priority_rewrite_exp', metavar='<exp>')
    ports_set_dscp_remark = ports_sub_set.add_parser('dscp_remark')
    ports_set_dscp_remark.add_argument('set_port_dscp_remark_port_name', metavar='<portid>', type=intBase)
    ports_set_dscp_remark.add_argument('set_port_dscp_remark_priority', metavar='<priority>', type=intBase)
    ports_set_dscp_remark.add_argument('set_port_dscp_remark_color', metavar='<color>', type=intBase)
    ports_set_dscp_remark.add_argument('set_port_dscp_remark_dscp', metavar='<dscp>', type=intBase)
    # VLAN command set
    vlan_parser = subparser.add_parser('vlan', help='VLAN configuration')
    vlan_sub_parser = vlan_parser.add_subparsers()
    vlan_ids = vlan_sub_parser.add_parser('ids')
    vlan_ids.add_argument('vlan_swids_list', type=int, nargs="*",
                          default=[0], metavar="<switch id>")
    vlan_ports = vlan_sub_parser.add_parser('ports')
    vlan_ports.add_argument('swid_vlan_ports', metavar='<swid>', type=int,
                            nargs="?")
    vlan_ports.add_argument('vids_vlan_ports', metavar="<vid>", type=int,
                            nargs="*", default=[])

    #Traps command set
    traps_parser = subparser.add_parser('traps', help='TRAP operations')
    traps_sub_parser = traps_parser.add_subparsers()

    #Traps show command set
    traps_show = traps_sub_parser.add_parser('show')
    traps_show.add_argument('traps_show_sub_cmd',
                                choices=['stats', 'group-stats', 'copp-stats'],
                                help='Show Trap Statistics choices')

    #Traps clear command set
    traps_clear = traps_sub_parser.add_parser('clear')
    traps_clear.add_argument('traps_clear_sub_cmd',
                                choices=['stats'],
                                help='Clear Trap Statistics choices')

    # L3 command set
    file_parser = subparser.add_parser('cmds', help="command file")
    #file_parser.add_argument('file_sub_cmd', choices=['path'],
    #                        help="file choices")
    #file_parser.add_argument('--port', metavar='<port_name_or_id>', help='logical port name or ID - supports wild card swp*. Only used for qos counters')
    file_parser.add_argument('--cmds_file', type=argparse.FileType('r'), help='cmd files')

    args = parser.parse_args()

    # Add logger config for logging to file
    logger.setLevel(args.log_level)
    # Default will always log to mlxcmd.log
    add_logfile(valid_logfile('/var/log/mlxcmd.log'))
    if args.log_file:
        add_logfile(args.log_file)

    ret = True
    # Open the connection to the MLX SDK
    try:
        mlx_open_connection(logger)
    except SystemExit as exc:
        ret_value = exc.code
        if args.json:
            _print_json_output({'exception': f'SysExit={ret_value}'})
        else:
            print(f'Unable to open connection to mlx sdk.')
        sys.stdout.close()
        return False
    except:
        ex_type, ex_value, ex_traceback = sys.exc_info()
        mlxcmd_log_traceback(ex_type, ex_value, ex_traceback)
        if args.json:
            _print_json_output({'exception': f'Exception={ex_type}'})
        else:
            print(f'Exception: Type {ex_type} msg {ex_value}')
        return False

    if hasattr(args, 'cmds_file'):
        Lines = args.cmds_file.readlines()
        for line in Lines:
            print(line)
            cmds_args = parser.parse_args(line.split())
            logger.info(f'Input: {cmds_args}')
            try:
                rc = mlxcmd_main_process_cmds(cmds_args)
                # Only set the return code as false for the final
                # return code false when exit out of for loop
                if rc is not None and not rc:
                    ret = rc
            except SystemExit as exc:
                ret_value = exc.code
                if args.json:
                    _print_json_output({'exception': f'SysExit={ret_value}'})
                else:
                    print(f'Unable to execute input command.')
                # Reassign the original stdout for subsequent commands in case of exceptions
                if cur_out is not None:
                    sys.stdout.close()
                    sys.stdout = cur_out
                ret = False
            except:
                ex_type, ex_value, ex_traceback = sys.exc_info()
                mlxcmd_log_traceback(ex_type, ex_value, ex_traceback)
                if args.json:
                    _print_json_output({'exception': f'Exception={ex_type}'})
                else:
                    print(f'Exception: Type {ex_type} msg {ex_value}')
                # Reassign the original stdout for subsequent commands in case of exceptions
                if cur_out is not None:
                    sys.stdout.close()
                    sys.stdout = cur_out
                ret = False
            # we will continue with other command execution even when one cmd fails
    else:
        try:
            logger.info(f'Input: {args}')
            ret = mlxcmd_main_process_cmds(args)
        except SystemExit as exc:
            ret_value = exc.code
            if args.json:
                _print_json_output({'exception': f'SysExit={ret_value}'})
            else:
                print(f'Unable to execute input command.')
            sys.stdout.close()
            ret = False
        except:
            ex_type, ex_value, ex_traceback = sys.exc_info()
            mlxcmd_log_traceback(ex_type, ex_value, ex_traceback)
            if args.json:
                _print_json_output({'exception': f'Exception={ex_type}'})
            else:
                print(f'Exception: Type {ex_type} msg {ex_value}')
            ret = False
    # Close the connection
    mlx_close_connection()
    return ret

def mlxcmd_log_traceback(ex_type, ex_value, ex_traceback):
    logger.error('Got exception type: %s msg: %s' %(ex_type.__name__, ex_value))
    logger.error('Stack trace:')
    trace_back = traceback.extract_tb(ex_traceback)
    for trace in trace_back:
        logger.error("%s:%d %s, %s" % (trace[0], trace[1], trace[2], trace[3]))

def mlxcmd_main_process_cmds(args):
    global cur_out
    cur_out = None
    if args.json:
        JSON = True
    else:
        JSON = False

    if args.verbose:
        VERBOSE = True
    else :
        VERBOSE = False
    
    if hasattr(args, 'out_file') and args.out_file is not None:
         cur_out = sys.stdout
         sys.stdout = args.out_file

    if hasattr(args, 'br_swid') and \
       hasattr(args, 'br_vid'):
        _show_bridge_vports(swid=args.br_swid, br_vid=args.br_vid, use_json=JSON)

    if hasattr(args, 'switch_id'):
        _show_fdb_table(swid=args.switch_id, use_json=JSON)

    if hasattr(args, 'fdb_timer_show'):
        _show_fdb_age_time(swid_list=args.fdb_timer_show, use_json=JSON)

    if hasattr(args, 'mcswitch_id'):
        _show_mc_fdb_table(swid=args.mcswitch_id, use_json=JSON)

    if hasattr(args, 'aging_timer') and \
       hasattr(args, 'swid_list'):
        mlx_set_fdb_age_time(args.aging_timer,
                             swid_list=args.swid_list)

    if hasattr(args, 'all_fdb_flush_swids'):
        mlx_flush_uc_fdb_all(swid_list=args.all_fdb_flush_swids)

    if hasattr(args, 'fdb_fids'):
        mlx_flush_uc_fdb_fid(args.fdb_fids[1], swid=args.fdb_fids[0])

    if hasattr(args, 'l3_sub_cmd'):
        if args.l3_sub_cmd == 'route':
            _show_uc_route_table(version=4, use_json=JSON)
        elif args.l3_sub_cmd == 'neighbor':
            _show_ip_neighbor_table(version=4, use_json=JSON)
        elif args.l3_sub_cmd == 'route6':
            _show_uc_route_table(version=6, use_json=JSON)
        elif args.l3_sub_cmd == 'neighbor6':
            _show_ip_neighbor_table(version=6, use_json=JSON)
        elif args.l3_sub_cmd == 'ecmp_table':
            _get_ecmp_table_(use_json=JSON)
        elif args.l3_sub_cmd == 'route_count':
            _show_uc_route_count(version=4, use_json=JSON)
        elif args.l3_sub_cmd == 'route6_count':
            _show_uc_route_count(version=6, use_json=JSON)
        elif args.l3_sub_cmd == 'mpls_table':
            _show_mpls_table_(use_json=JSON)
        if args.l3_sub_cmd == 'interface':
            _show_interface_table(use_json=JSON)

    if hasattr(args, 'lag_member_swid') and \
       hasattr(args, 'lag_member_port_id'):
        _show_lag_member_port_ids_(args.lag_member_swid, args.lag_member_port_id,
                                   use_json=JSON)

    if hasattr(args, 'hash_params'):
        _show_lag_paramters(args.hash_params, use_json=JSON)

    if hasattr(args, 'ecmp_hash_params'):
        _show_ecmp_paramters(args.ecmp_hash_params, use_json=JSON)

    if hasattr(args, 'mstp_swid'):
         _show_all_mstp_instances_(args.mstp_swid, use_json=JSON)

    if hasattr(args, 'port_state_swid') and \
       hasattr(args, 'port_state_mstp_inst') and \
       hasattr(args, 'port_state_ids'):
        _get_mstp_instance_port_state_(args.port_state_swid,
                                       args.port_state_mstp_inst,
                                       args.port_state_ids, use_json=JSON)

    if hasattr(args, 'vid_swid') and \
       hasattr(args, 'vid_inst'):
        _show_mstp_instance_vids_(args.vid_swid, args.vid_inst, use_json=JSON)

    if hasattr(args, 'sched_sub_cmd') :
        if args.sched_sub_cmd == 'show':
            __expand_port_name_or_id(_show_port_scheduler, args.port, True, use_json=JSON)

    if hasattr(args, 'fc_sub_cmd'):
        if args.fc_sub_cmd == 'show':
            __expand_port_name_or_id(_show_flow_control, args.port, True, use_json=JSON)

    if hasattr(args, 'datapath_sub_cmd'):
        if args.datapath_sub_cmd == 'show':
            __expand_port_name_or_id(_show_datapath_config, args.port, True, use_json=JSON)

    if hasattr(args, 'prio_sub_cmd'):
        if args.prio_sub_cmd == 'default_port_priority':
            __expand_port_name_or_id(_show_port_default_priority, args.port, True, use_json=JSON)
        if args.prio_sub_cmd == 'trust':
            __expand_port_name_or_id(_show_port_priority_trust, args.port, True, use_json=JSON)
        if args.prio_sub_cmd == 'rewrite':
            __expand_port_name_or_id(_show_port_priority_rewrite, args.port, True, use_json=JSON)
        if args.prio_sub_cmd == 'priority_group':
            __expand_port_name_or_id(_show_priority_group_map, args.port, True, use_json=JSON)
        if args.prio_sub_cmd == 'pcpdei':
            if args.remark == True:
                __expand_port_name_or_id(_show_pcpdei_remark_map, args.port, True, use_json=JSON)
            else :
                __expand_port_name_or_id(_show_pcpdei_prio_map, args.port, True, use_json=JSON)
        elif args.prio_sub_cmd == 'dscp' :
            if args.remark == True:
                __expand_port_name_or_id(_show_dscp_remark_map, args.port, True, use_json=JSON)
            else :
                __expand_port_name_or_id(_show_dscp_prio_map, args.port, True, use_json=JSON)
        elif args.prio_sub_cmd == 'traffic_class':
            __expand_port_name_or_id(_show_priority_tc_map, args.port, True, use_json=JSON)
        elif args.prio_sub_cmd == 'ieee':
            ret = _show_priority_ieee_map(use_json=JSON)
            if JSON:
                _print_json_output(ret)

    if hasattr(args, 'buffer_sub_cmd'):
        if args.buffer_sub_cmd == 'mc_aware' :
            __expand_port_name_or_id(_show_mc_aware, args.port, True, use_json=JSON)
        if args.buffer_sub_cmd == 'free':
            ret = _show_free_buffer_counters(use_json=JSON)
            if JSON:
                _print_json_output(ret)
        elif args.buffer_sub_cmd == 'shared_limits':
            __expand_port_name_or_id(_show_shared_buffer_limits, args.port, True, use_json=JSON)
        elif args.buffer_sub_cmd == 'pool':
            if hasattr(args, 'buffer_type') :
                if args.buffer_type == 'limits' :
                    ret = _show_pool_buffer_limits(use_json=JSON)
                    if JSON:
                        _print_json_output(ret)
                if args.buffer_type == 'counters' :
                    if args.clear:
                        return _clear_pool_buffer_counters(use_json=JSON)
                    else:
                        _show_pool_buffer_counters(args.cells, use_json=JSON)
        elif args.buffer_sub_cmd == 'port_reserved':
            if hasattr(args, 'buffer_type') :
                if args.buffer_type == 'limits' :
                    __expand_port_name_or_id(_show_port_reserved_buffer_limits, args.port, True, use_json=JSON)
                if args.buffer_type == 'counters' :
                    if args.clear:
                        if args.mc_buff:
                            ret = _clear_port_buffer_counters(None, SX_MC_PORT_LOG_ID, use_json=JSON)
                            if JSON:
                                _print_json_output(ret)
                            else:
                                return ret
                        else:
                            return __expand_port_name_or_id(_clear_port_buffer_counters, args.port, True, use_json=JSON)
                    else:
                        # If all the optional attributes are false, will dump all port region buffers
                        if not args.pg_buff and not args.tc_buff and not args.mc_buff and not args.port_buff and not args.descriptor_buff:
                            args.pg_buff = True
                            args.tc_buff = True
                            args.mc_buff = True
                            args.port_buff = True
                            args.descriptor_buff = True
                        _port_buff_cnt = _show_port_buffer_counters_name_or_id(port_name_or_id=args.port, use_json=JSON, pg_buff=args.pg_buff, tc_buff=args.tc_buff, mc_buff=args.mc_buff, port_buff=args.port_buff, descriptor_buff=args.descriptor_buff, use_cells=args.cells)
                        if args.mc_buff:
                            _mc_port_buff_cnt = _show_port_buffer_counters(None, SX_MC_PORT_LOG_ID, use_json=JSON, params={'use_cells': args.cells})
                            if _mc_port_buff_cnt:
                                _port_buff_cnt.append(_mc_port_buff_cnt)
                        if JSON:
                            _print_json_output(_port_buff_cnt)
                        else:
                            print_port_buffer_legends()

        elif args.buffer_sub_cmd == 'port_shared':
            if hasattr(args, 'buffer_type') :
                if args.buffer_type == 'limits' :
                    __expand_port_name_or_id(_show_port_shared_buffer_limits, args.port, True, use_json=JSON)
                if args.buffer_type == 'counters' :
                    if args.clear:
                        if args.mc_buff:
                            ret = _clear_port_buffer_counters(None, SX_MC_PORT_LOG_ID, use_json=JSON)
                            if JSON:
                                _print_json_output(ret)
                            else:
                                return ret
                        else:
                            return __expand_port_name_or_id(_clear_port_buffer_counters, args.port, True, use_json=JSON)
                    else:
                        # If all the optional attributes are false, will dump all port region buffers
                        if not args.pg_buff and not args.tc_buff and not args.mc_buff and not args.port_buff and not args.descriptor_buff:
                            args.pg_buff = True
                            args.tc_buff = True
                            args.mc_buff = True
                            args.port_buff = True
                            args.descriptor_buff = True
                        _port_buff_cnt = _show_port_buffer_counters_name_or_id(port_name_or_id=args.port, use_json=JSON, pg_buff=args.pg_buff, tc_buff=args.tc_buff, mc_buff=args.mc_buff, port_buff=args.port_buff, descriptor_buff=args.descriptor_buff, use_cells=args.cells)
                        if args.mc_buff:
                            _mc_port_buff_cnt = _show_port_buffer_counters(None, SX_MC_PORT_LOG_ID, use_json=JSON, params={'use_cells': args.cells})
                            if _mc_port_buff_cnt:
                                _port_buff_cnt.append(_mc_port_buff_cnt)
                        if JSON:
                            _print_json_output(_port_buff_cnt)
                        else:
                            print_port_buffer_legends()

    if hasattr(args, 'qos_sub_cmd'):
        try:
            if args.qos_sub_cmd == 'mapping':
                _show_qos_mapping(args.ingress_port, args.egress_port, use_json=JSON)
            if args.qos_sub_cmd == 'redecn' :
                if args.ingress_port or args.egress_port:
                    if JSON:
                        _print_json_output({'exception': 'invalidInputPortParam'})
                    else:
                        print("--ingress_port or --egress_port not required as input param")
                    return
                if args.port:
                    __expand_port_name_or_id(_show_qos_port_redecn_config, args.port, True, use_json=JSON)
                else:
                    if JSON:
                        _print_json_output({'exception': 'inputPortParamRequired'})
                    else:
                        print("--port is required param for dumping redecn config")
            if args.qos_sub_cmd == 'counters' :
                if args.clear:
                    ret = _clear_qos_port_counters(args.port, args.ingress_port, args.egress_port, use_json=JSON)
                    return ret
                else:
                    if args.port:
                        __expand_port_name_or_id(_show_qos_port_counters, args.port, True, use_json=JSON, params={'ingress_port': args.ingress_port, 'egress_port': args.egress_port})
                    else:
                        ret = _show_qos_port_counters(None, None, use_json=JSON, params={'ingress_port': args.ingress_port, 'egress_port': args.egress_port})
                        if JSON:
                            _print_json_output(ret)

            if args.qos_sub_cmd == 'config' :
                if hasattr(args, 'qos_config_type'):
                    if args.qos_config_type == 'reload':
                        if args.ingress_port or args.egress_port or args.port:
                            if JSON:
                                _print_json_output({'exception': 'invalidInputPortParam'})
                            else:
                                print("input port param is not required input for this command")
                            return False
                        ret,msg = _traffic_fuse_node_reload(use_json=JSON)
                        if ret:
                            if JSON:
                                _print_json_output({'result': 'success'})
                            else:
                                print("Traffic fuse node reload successful")
                        else:
                            if JSON:
                                _print_json_output({'exception': msg, 'result': 'failed'})
                            else:
                                print("Traffic fuse node reload failed.\n msg:{}".format(msg))
                        return ret
                    else:
                        if JSON:
                            _print_json_output({'exception': 'invalidInputQoSConfigType'})
                        else:
                            print("Invalid QoS config type mlxcmd")
                else:
                    if JSON:
                        _print_json_output({'exception': 'invalidInputQoSConfig'})
                    else:
                        print("Invalid QoS config mlxcmd")

        except ingressEgressIdentical:
            if JSON:
                _print_json_output({'exception': 'ingressEgressIdentical'})
            else:
                print("Ingresss and egress ports cannot be identical")

    if hasattr(args, 'roce_sub_cmd'):
        try:
            if args.roce_sub_cmd == 'counters':
                roce_mode = mlx_get_roce_mode_wrapper()
                return __expand_port_name_or_id(_show_roce_counters, args.port, True, use_json=JSON, \
                            params={'clear': args.clear or args.read_clear, 'show_output': not args.clear, 'cells': args.cells, 'roce_mode': roce_mode})
            elif args.roce_sub_cmd == 'status' and (args.clear == 0 and args.read_clear == 0):
                roce_mode = mlx_get_roce_mode_wrapper()
                if args.system:
                    _show_roce_status_system(args.port,args.cells,use_json=JSON)
                else:
                    __expand_port_name_or_id(_show_roce_status, args.port, True, use_json=JSON, \
                            params={'roce_mode': roce_mode})
            elif args.roce_sub_cmd == 'status' and args.clear == 1:
                if JSON:
                    _print_json_output({'exception': 'syntaxError'})
                else:
                    print("mlxcmd roce: error: argument --clear: not allowed with argument status")
            elif args.roce_sub_cmd == 'status' and args.read_clear == 1:
                if JSON:
                    _print_json_output({'exception': 'syntaxError'})
                else:
                    print("mlxcmd roce: error: argument --read_clear: not allowed with argument status")
        except noRoce:
            if JSON:
                _print_json_output({'exception': 'noRoce'})
            else:
                print("RoCE is not configured")

    if hasattr(args, 'show_port_counters_port_name'):
        _show_port_counters_name_or_id(port_name_or_id=args.show_port_counters_port_name, use_json=JSON)

    if hasattr(args, 'show_port_buffers'):
        _show_port_buffers(args.show_port_buffers, use_json=JSON)

    if hasattr(args, 'show_port_discards_port_name'):
        _show_port_discards_name_or_id(port_name_or_id=args.show_port_discards_port_name, use_json=JSON)

    if hasattr(args, 'show_port_pkt_dist_port_name'):
        _show_port_pkt_size_counters_name_or_id(port_name_or_id=args.show_port_pkt_dist_port_name, use_json=JSON)

    if hasattr(args, 'clear_port_id'):
        grp_type = args.clear_cntr_grp_type
        __expand_port_name_or_id(_clear_port_counters, args.clear_port_id, True, use_json=JSON, params={'group_type': grp_type})

    if hasattr(args, 'vlan_swids_list'):
        _show_vlan_ids(swid_list=args.vlan_swids_list,
                       use_json=JSON)

    if hasattr(args, 'swid_vlan_ports') and \
       hasattr(args, 'vids_vlan_ports'):
        _show_vlan_ports(swid=args.swid_vlan_ports,
                         vids=args.vids_vlan_ports,
                         use_json=JSON)

    if hasattr(args, 'port_swids_list'):
        _show_port_ids(swid_list=args.port_swids_list, use_json=JSON)

    if hasattr(args, 'show_port_isolate_swids'):
        _show_port_isolate(swid_list=args.show_port_isolate_swids, use_json=JSON)

    if hasattr(args, 'isolate_portid') and \
       hasattr(args, 'isolate_port_list'):
        _set_port_isolate(portid=args.isolate_portid,
                          iso_port_list=args.isolate_port_list, use_json=JSON)

    if hasattr(args, 'set_port_priority_rewrite_port'):
        _set_port_priority_rewrite(portid=args.set_port_priority_rewrite_port,
                                   dscp=args.set_port_priority_rewrite_dscp,
                                   pcpdei=args.set_port_priority_rewrite_pcpdei,
                                   exp=args.set_port_priority_rewrite_exp)

    if hasattr(args, 'set_port_dscp_remark_port_name') :
        _set_port_dscp_remark(portid=args.set_port_dscp_remark_port_name,
                              priority=args.set_port_dscp_remark_priority,
                              color=args.set_port_dscp_remark_color,
                              dscp=args.set_port_dscp_remark_dscp)

    if hasattr(args, 'show_port_sflow_port_name'):
        _show_port_sflow_name_or_id(port_name_or_id=args.show_port_sflow_port_name, use_json=JSON)

    if hasattr(args, 'show_port_sflow_stats_port_name'):
        _show_port_sflow_stats_name_or_id(port_name_or_id=args.show_port_sflow_stats_port_name, use_json=JSON)

    if hasattr(args, 'set_port_sflow_port_name') and \
       hasattr(args, 'set_port_sflow_ing_rate') and \
       hasattr(args, 'set_port_sflow_egr_rate'):
        _set_port_sflow(port_name_or_id=args.set_port_sflow_port_name,
                        ing_rate=args.set_port_sflow_ing_rate,
                        egr_rate=args.set_port_sflow_egr_rate, use_json=JSON)

    if hasattr(args, 'port_info_swids'):
        _ports_info(swid_list=args.port_info_swids, use_json=JSON)

    if hasattr(args, 'traps_clear_sub_cmd'):
        if args.traps_clear_sub_cmd == 'stats':
            _clear_trap_stats(use_json=JSON)

    if hasattr(args, 'traps_show_sub_cmd'):
        if args.traps_show_sub_cmd == 'stats':
            _show_trap_stats(use_json=JSON)

        if args.traps_show_sub_cmd == 'group-stats':
            _show_trap_group_stats(use_json=JSON, is_copp_stats=False)

        if args.traps_show_sub_cmd == 'copp-stats':
            _show_trap_group_stats(use_json=JSON, is_copp_stats=True)

    if hasattr(args, 'out_file') and cur_out is not None:
        sys.stdout = cur_out
        args.out_file.close()


if __name__ == '__main__':
    ret = mlxcmd_main()
    if ret is not None:
        # Need this for NCLU to support calling mlxcmd
        if ret == True:
            sys.exit(0)
        else:
            sys.exit(1)
